Skip to main content

memra_engine/
dflash.rs

1//! DFlash block-diffusion drafter (DFLASH-BRINGUP-PLAN.md, 2026-07-13).
2//!
3//! 5-layer qwen3-class mini-transformer that drafts a 16-token block in ONE non-causal
4//! forward, conditioned on the TARGET's hidden states at 6 tapped layers (concatenated
5//! through `fc` + `hidden_norm`). No embed / lm_head of its own — the round reuses the
6//! target's. Reference: z-lab/dflash `dflash/model.py` (semantics frozen in the plan doc);
7//! oracle: tools/dflash_oracle.py -> /data/cache/dflash-oracle.npz.
8//!
9//! FIRST LIGHT = f32-resident weights + fresh full-context forward (no draft KV cache) —
10//! correctness vs the oracle, then the cache/quant/window arms land measurement-gated.
11
12use crate::Engine;
13use crate::model::GpuTensor;
14use cudarc::driver::CudaSlice;
15
16pub struct DflashCfg {
17    pub hidden: usize,                // 5376
18    pub n_head: usize,                // 64
19    pub n_kv: usize,                  // 8
20    pub head_dim: usize,              // 128
21    pub n_ff: usize,                  // 10752
22    pub n_layer: usize,               // 5
23    pub eps: f32,                     // 1e-6
24    pub rope_theta: f32,              // 1e6
25    pub block_size: usize,            // 16
26    pub mask_token_id: u32,           // 4
27    pub target_layer_ids: Vec<usize>, // [1,12,23,35,46,57]
28    pub sliding_window: usize,        // 2048
29    /// true = sliding_attention for that layer (4x true + 1x false on the 31B draft).
30    pub layer_sliding: Vec<bool>,
31    /// Checkpoint training-strategy census (`dspark_strategy_census` over the raw
32    /// config.json): true = a SpecForge DSPARK-strategy export (shifted labels, ALL rows
33    /// supervised — the q38 arm-a family). Keys the HARVEST DEFAULT strategy-keyed,
34    /// never env-keyed (owner-ratified 2026-08-20 after B1 confirmed H1 ×5;
35    /// DSPARK-POSTMORTEM-20260820.md B0 default-flip plan).
36    pub strategy_dspark: bool,
37    /// Explicit top-level `is_causal` from config.json (z-lab reference: an explicit
38    /// value OVERRIDES the per-layer-type default). The DFlash2 q38 checkpoint carries
39    /// `"is_causal": false` — every sliding layer is NON-causal with a symmetric
40    /// +/-2048 window (model.py `_attention_mask`). None = key absent (historical
41    /// exports; the windowless-assert arm keeps handling those byte-identically).
42    pub is_causal: Option<bool>,
43}
44
45pub struct DflashLayer {
46    pub wq: GpuTensor,           // [nh*hd, hidden] row-major (out_f rows)
47    pub wk: GpuTensor,           // [nkv*hd, hidden]
48    pub wv: GpuTensor,           // [nkv*hd, hidden]
49    pub wo: GpuTensor,           // [hidden, nh*hd]
50    pub w_gate: GpuTensor,       // [n_ff, hidden]
51    pub w_up: GpuTensor,         // [n_ff, hidden]
52    pub w_down: GpuTensor,       // [hidden, n_ff]
53    pub ln_in: CudaSlice<f32>,   // [hidden]
54    pub ln_post: CudaSlice<f32>, // [hidden]
55    pub q_norm: CudaSlice<f32>,  // [hd]
56    pub k_norm: CudaSlice<f32>,  // [hd]
57}
58
59pub struct DflashDraft {
60    pub cfg: DflashCfg,
61    pub layers: Vec<DflashLayer>,
62    pub fc: GpuTensor,               // [hidden, n_taps*hidden]
63    pub hidden_norm: CudaSlice<f32>, // [hidden]
64    pub norm: CudaSlice<f32>,        // [hidden]
65    /// DSpark semi-AR markov head (present in the repo-root checkpoint variant):
66    /// draft logits at position k get + W2(W1[prev_realized_token]) — left-to-right
67    /// within the block (the patch's _markov_semiar_sample_block semantics, greedy).
68    /// w1 = raw bf16 [V, rank] (row-gathered by device token id); w2 = q8_0 [rank->V].
69    pub markov: Option<MarkovHead>,
70    /// DSpark accept-rate head (trained with confidence loss). sglang's DSPARK planner
71    /// consumes it to SIZE VERIFY WINDOWS (cumprod survival — v0.5.16 headline; the
72    /// earlier "reference serving loop never consumes it" note matched SpecForge's
73    /// legacy spec_generate only). memra schedules with it under
74    /// `MEMRA_DSPARK_VT=confidence` (the H4 fix, DSPARK-POSTMORTEM-20260820.md:
75    /// per-round verify window from cumprod survival, `dspark_confidence_vt`) and
76    /// keeps it census+parity-only under the default ladder. Host-resident (5k floats).
77    pub confidence: Option<ConfidenceHead>,
78    /// YaRN rope (q38 arm-a inherits the target's rope_parameters: rope_type yarn,
79    /// factor 32, original 8192, beta 32/1). ff = per-dim divisors for rope_neox_ff
80    /// (effective inv_freq_j = base^(-2j/d)/ff[j] = the HF-yarn remapped frequency,
81    /// verified vs Qwen3RotaryEmbedding to 1.6e-7), mscale = attention_scaling
82    /// (0.1*ln(factor)+1) applied to q/k post-rope — cos/sin scaling distributes onto
83    /// the rotated vector exactly. None = plain rope (gemma/z-lab drafters).
84    pub rope_yarn: Option<(CudaSlice<f32>, f32)>,
85    /// DFlash2 head (z-lab `DFlash2DraftModel`, DFLASH2-EVAL-20260820.md): grouped
86    /// dynamic causal convs around EVERY sublayer + the candidate path selector that
87    /// replaces the markov chain. A DISTINCT semantic program from the DSpark head
88    /// (no-generic-support law): present iff config `architectures` names
89    /// `DFlash2DraftModel`, and then ALL 23 family tensors are REQUIRED — loading the
90    /// 58 backbone tensors alone computes an untrained model (the census trap).
91    pub dflash2: Option<Dflash2Head>,
92}
93
94/// One `GroupedDynamicCausalConv` module (reference model.py): a causal 2-tap
95/// depthwise conv over the BLOCK rows (block-local — row 0 zero-pads its missing
96/// predecessor; stateless across rounds), with per-position dynamic per-group
97/// coefficients projected from the module INPUT. `prepare` convolves the sublayer
98/// input with base_kernel[0] + dyn half 0; `finish` convolves the sublayer OUTPUT
99/// with base_kernel[1] + dyn half 1 (both dyn halves come from the SAME projection
100/// of the pre-conv input).
101pub struct Dflash2Conv {
102    /// base_kernel [2, k, hidden] flattened f32 (half-major: prepare then finish).
103    pub base: CudaSlice<f32>,
104    /// kernel_projection.weight [2*k*groups, hidden] (row layout = view(2, k, groups)).
105    pub proj: GpuTensor,
106}
107
108pub struct Dflash2Head {
109    pub attn_conv: Vec<Dflash2Conv>, // per layer
110    pub mlp_conv: Vec<Dflash2Conv>,  // per layer
111    /// candidate_selector.hidden_projection.weight [rank, hidden].
112    pub hidden_proj: GpuTensor,
113    /// Codebooks [V, rank] raw bf16, HOST-resident: the walk gathers ~1+16 rows per
114    /// draft slot (~70KB/round) — host math beside the round's existing chain dtoh,
115    /// no device residency for 2x127MB tables. Checkpoint quirk: stored WITHOUT the
116    /// `.weight` suffix (reference from_pretrained installs a key_mapping).
117    pub pred_codebook: Vec<u8>,
118    pub succ_codebook: Vec<u8>,
119    pub rank: usize,       // selector_rank 256
120    pub top_k: usize,      // selector_top_k 16
121    pub conv_k: usize,     // conv_kernel_size 2
122    pub group_size: usize, // conv_group_size 16
123    pub vocab: usize,      // codebook rows (248320)
124}
125
126/// Resolve the named DFlash weight program. Keep this separate from loading so a typo cannot
127/// silently select q8 and invalidate a performance/default receipt.
128fn dflash_precision(raw: Option<&str>) -> Result<&str, String> {
129    let prec = raw.unwrap_or("q4");
130    match prec {
131        "q4" | "q8" | "mixed" | "bf16" | "fc" => Ok(prec),
132        other => Err(format!(
133            "MEMRA_DFLASH_PREC={other:?}: want q4, q8, mixed, bf16, or fc \
134             (q5 was measured defective and is not a serving mode)"
135        )),
136    }
137}
138
139/// One bf16 codebook row -> f32 (exact widening).
140fn cb_row(cb: &[u8], tok: usize, rank: usize) -> Vec<f32> {
141    bf16_to_f32(&cb[tok * rank * 2..(tok + 1) * rank * 2])
142}
143
144/// Greedy selector walk (reference `CandidateSelector.select` at T=0): per draft
145/// slot p, score(k) = unary[p,k] + <pred_codebook[prev] .* hidden_proj_row[p],
146/// succ_codebook[cand[p,k]]>, argmax over the top-k candidate set; the CHOSEN
147/// candidate seeds the next slot (sequential — the chain is the semantics, not an
148/// optimization). Host math (~nd*k*rank fused ops per round) over host-resident bf16
149/// codebooks; ties break to the LOWEST k (torch argmax convention). Pure so the
150/// selector semantics are CPU-gateable.
151///
152/// `unary`/`cand`: [nd, top_k] row-major; `hproj`: [nd, rank] row-major.
153#[allow(clippy::too_many_arguments)]
154pub fn dflash2_walk_greedy(
155    pred_codebook: &[u8],
156    succ_codebook: &[u8],
157    vocab: usize,
158    rank: usize,
159    top_k: usize,
160    unary: &[f32],
161    cand: &[u32],
162    hproj: &[f32],
163    anchor: u32,
164    nd: usize,
165) -> Vec<u32> {
166    dflash2_walk_greedy_q(
167        pred_codebook,
168        succ_codebook,
169        vocab,
170        rank,
171        top_k,
172        unary,
173        cand,
174        hproj,
175        anchor,
176        nd,
177    )
178    .0
179}
180
181/// [`dflash2_walk_greedy`] with the per-slot CONFIDENCE recorded (lane/glm5-loop-port,
182/// 2026-08-30): q[p] = softmax over the slot's candidate-set scores at T=1, of the chosen
183/// candidate — the greedy twin of `dflash2_walk_sampled`'s recorded `q_chosen` (same
184/// statistic family the owner's "take only high confidence offers" tau gate thresholds on
185/// the dspark route). The argmax selection is UNCHANGED (q is bookkeeping over the same
186/// scores, ~top_k exps per slot on host), so every existing greedy caller is byte-identical
187/// through the delegating wrapper. Pure, CPU-gateable like its siblings.
188#[allow(clippy::too_many_arguments)]
189pub fn dflash2_walk_greedy_q(
190    pred_codebook: &[u8],
191    succ_codebook: &[u8],
192    vocab: usize,
193    rank: usize,
194    top_k: usize,
195    unary: &[f32],
196    cand: &[u32],
197    hproj: &[f32],
198    anchor: u32,
199    nd: usize,
200) -> (Vec<u32>, Vec<f32>) {
201    let (kk, r) = (top_k, rank);
202    assert_eq!(unary.len(), nd * kk, "walk: unary shape");
203    assert_eq!(cand.len(), nd * kk, "walk: candidate shape");
204    assert_eq!(hproj.len(), nd * r, "walk: hidden-projection shape");
205    let mut path = Vec::with_capacity(nd);
206    let mut q_chosen = Vec::with_capacity(nd);
207    let mut prev = anchor;
208    for p in 0..nd {
209        assert!(
210            (prev as usize) < vocab,
211            "walk: predecessor token {prev} outside codebook vocab {vocab}"
212        );
213        let pr = cb_row(pred_codebook, prev as usize, r);
214        let hp = &hproj[p * r..(p + 1) * r];
215        // gate = pred_row .* hidden_proj (shared across the candidate set)
216        let gate: Vec<f32> = pr.iter().zip(hp).map(|(a, b)| a * b).collect();
217        let mut scores = vec![0f32; kk];
218        let (mut best, mut bi) = (f32::NEG_INFINITY, 0usize);
219        for (k, s) in scores.iter_mut().enumerate() {
220            let c = cand[p * kk + k] as usize;
221            assert!(c < vocab, "walk: candidate {c} outside codebook vocab");
222            let sr = cb_row(succ_codebook, c, r);
223            let mut acc = unary[p * kk + k];
224            for j in 0..r {
225                acc += gate[j] * sr[j];
226            }
227            *s = acc;
228            if acc > best {
229                best = acc;
230                bi = k;
231            }
232        }
233        // Recorded confidence: softmax at T=1 over the candidate set (f64 accumulation,
234        // the sampled walk's numeric discipline), of the argmaxed candidate.
235        let mut z = 0f64;
236        for &s in &scores {
237            z += ((s - best) as f64).exp();
238        }
239        q_chosen.push(if z > 0.0 { (1.0 / z) as f32 } else { 1.0 });
240        prev = cand[p * kk + bi];
241        path.push(prev);
242    }
243    (path, q_chosen)
244}
245
246impl Dflash2Head {
247    /// Greedy selector walk over this head's codebooks — see `dflash2_walk_greedy`.
248    pub fn walk_greedy(
249        &self,
250        unary: &[f32],
251        cand: &[u32],
252        hproj: &[f32],
253        anchor: u32,
254        nd: usize,
255    ) -> Vec<u32> {
256        dflash2_walk_greedy(
257            &self.pred_codebook,
258            &self.succ_codebook,
259            self.vocab,
260            self.rank,
261            self.top_k,
262            unary,
263            cand,
264            hproj,
265            anchor,
266            nd,
267        )
268    }
269
270    /// Greedy walk with the per-slot confidence recorded — see `dflash2_walk_greedy_q`.
271    pub fn walk_greedy_q(
272        &self,
273        unary: &[f32],
274        cand: &[u32],
275        hproj: &[f32],
276        anchor: u32,
277        nd: usize,
278    ) -> (Vec<u32>, Vec<f32>) {
279        dflash2_walk_greedy_q(
280            &self.pred_codebook,
281            &self.succ_codebook,
282            self.vocab,
283            self.rank,
284            self.top_k,
285            unary,
286            cand,
287            hproj,
288            anchor,
289            nd,
290        )
291    }
292
293    /// Sampled (T>0) selector walk — see `dflash2_walk_sampled`.
294    #[allow(clippy::too_many_arguments)]
295    pub fn walk_sampled(
296        &self,
297        unary: &[f32],
298        cand: &[u32],
299        hproj: &[f32],
300        anchor: u32,
301        nd: usize,
302        temp: f32,
303        uniforms: &mut dyn FnMut() -> f32,
304    ) -> (Vec<u32>, Vec<f32>, Vec<f32>) {
305        dflash2_walk_sampled(
306            &self.pred_codebook,
307            &self.succ_codebook,
308            self.vocab,
309            self.rank,
310            self.top_k,
311            unary,
312            cand,
313            hproj,
314            anchor,
315            nd,
316            temp,
317            uniforms,
318        )
319    }
320}
321
322/// AcceptRatePredictor: raw linear proj over [hidden ; markov_prev_embedding(rank)]
323/// (with_markov=true on the q38 arm-a export) — output is the PRE-sigmoid scalar.
324pub struct ConfidenceHead {
325    pub w: Vec<f32>, // [in_dim]
326    pub b: f32,
327    pub in_dim: usize,
328    pub with_markov: bool,
329}
330
331impl ConfidenceHead {
332    /// Host dot: the PRE-sigmoid accept score for one draft slot. `hidden` = the
333    /// drafter output row the slot is harvested from (the same row its logits use);
334    /// `emb` = the markov `w1` row of the slot's PREVIOUS chain token (required iff
335    /// `with_markov`) — the exact input contract the parity gate pins (prev ids =
336    /// `[anchor, chain[..nd-1]]`, dspark_q38_parity.rs stage 5).
337    pub fn raw_score(&self, hidden: &[f32], emb: Option<&[f32]>) -> f32 {
338        let mut acc = self.b;
339        for (w, x) in self.w.iter().zip(hidden) {
340            acc += w * x;
341        }
342        if self.with_markov {
343            let emb = emb.expect("with_markov confidence head scored without the markov embedding");
344            debug_assert_eq!(hidden.len() + emb.len(), self.in_dim);
345            for (w, x) in self.w[hidden.len()..].iter().zip(emb) {
346                acc += w * x;
347            }
348        } else {
349            debug_assert_eq!(hidden.len(), self.in_dim);
350        }
351        acc
352    }
353}
354
355pub struct MarkovHead {
356    pub w1_bf16: CudaSlice<u8>, // [V, rank] bf16 raw
357    pub w2: GpuTensor,          // [rank -> V] q8_0
358    pub rank: usize,
359    pub vocab: usize,
360}
361
362/// Draft-row harvest convention for DFlash-family block drafters
363/// (darklanes research/deepseek-flash-20260818/DSPARK-POSTMORTEM-20260820.md).
364///
365/// The DFlash and DSpark SpecForge training strategies supervise DIFFERENT rows of the
366/// same `[anchor, MASK x b-1]` block, so the row -> trunk-position mapping is a property
367/// of the CHECKPOINT's training strategy, not of the loader:
368///
369/// - **Dflash** (mask-fill; z-lab dflash / SpecForge `OnlineDFlashModel`): row k is
370///   trained to predict the token AT position anchor+k — "Labels: same-position
371///   prediction", `weight_mask *= (pos_in_block > 0)` excludes the anchor row
372///   (SpecForge `specforge/algorithms/common/dflash_family_model.py:453-472`).
373///   Drafts = rows 1..b-1; the anchor row's output is untrained.
374/// - **Dspark** (shifted; SpecForge `OnlineDSparkModel`, `training.strategy: dspark` —
375///   the q38 arm-a export): row k is trained to predict the token at anchor+k+1, ALL
376///   rows supervised INCLUDING the anchor row (`label_offsets = arange(1,
377///   block_size+1)`, `dflash_family_model.py:816`). sglang's DSPARK worker — the stack
378///   every arm-a bank number was measured on — harvests gamma = block_size drafts with
379///   the anchor row's output as draft 1 (verified on the v0.5.17 eval-pin tag:
380///   `dspark_components/dspark_draft.py:248,260,318`; `dspark_config.py:269`).
381///
382/// Mismatching the convention verifies every slot against a position the row was never
383/// trained for — the q38 accept collapse (2.9 -> 1.43) in the postmortem.
384#[derive(Clone, Copy, PartialEq, Eq, Debug)]
385pub enum DsparkHarvest {
386    /// mask-fill: drafts = rows 1..b-1, row k fills position anchor+k.
387    Dflash,
388    /// shifted: drafts = rows 0..b-1, row k predicts position anchor+k+1.
389    Dspark,
390}
391
392impl DsparkHarvest {
393    /// The served resolution: explicit `MEMRA_DSPARK_HARVEST={dflash|dspark}` wins
394    /// (unknown values REFUSE loudly — a typo silently reverting the convention would
395    /// re-open the postmortem's misalignment); UNSET defers to the CHECKPOINT's own
396    /// training-strategy census — the owner-ratified default flip (2026-08-20, after
397    /// B1 confirmed H1 interleaved ×5 on serving-class hardware: accept 1.38→2.41
398    /// agentic / 1.53→3.66 math, E2E ALL EXACT both arms). Strategy-keyed, not
399    /// env-keyed, per the B0 plan: a DSPARK-strategy export harvests shifted
400    /// (all-rows), a mask-fill export keeps the historical dflash arm byte-identical.
401    pub fn resolve(cfg: &DflashCfg) -> Self {
402        Self::resolve_value(
403            std::env::var("MEMRA_DSPARK_HARVEST").ok().as_deref(),
404            cfg.strategy_dspark,
405        )
406    }
407
408    pub fn resolve_value(v: Option<&str>, strategy_dspark: bool) -> Self {
409        match v {
410            None | Some("") => {
411                if strategy_dspark {
412                    DsparkHarvest::Dspark
413                } else {
414                    DsparkHarvest::Dflash
415                }
416            }
417            set => Self::from_env_value(set),
418        }
419    }
420
421    /// ENV-ONLY parser (no checkpoint census): unset = `Dflash`, the historical arm.
422    /// Kept for the explicit-value path of [`Self::resolve_value`] and the seam tests;
423    /// round arms resolve through [`Self::resolve`] so the default stays strategy-keyed.
424    pub fn from_env_value(v: Option<&str>) -> Self {
425        match v {
426            None | Some("") | Some("dflash") => DsparkHarvest::Dflash,
427            Some("dspark") => DsparkHarvest::Dspark,
428            Some(other) => panic!(
429                "MEMRA_DSPARK_HARVEST={other}: unknown harvest convention (dflash|dspark); \
430                 refusing — a wrong convention verifies every draft slot against a position \
431                 the drafter row was not trained for (DSPARK-POSTMORTEM-20260820.md)"
432            ),
433        }
434    }
435
436    /// Resolve the harvest convention for a LOADED drafter — FAMILY-keyed first, then
437    /// STRATEGY-keyed (v0.100 train merge of the two ratified keyings):
438    /// - DFlash2 is a mask-fill-family drafter by construction (reference
439    ///   `dflash_generate` harvests rows `1-verify_size:`; the card says "block size 8
440    ///   (7 draft tokens per verification step)" — DFLASH2-EVAL-20260820.md §3). An env
441    ///   value that CONTRADICTS the census REFUSES rather than silently re-keying the
442    ///   round.
443    /// - Every other checkpoint rides [`Self::resolve_value`]: explicit env wins (typos
444    ///   refuse loudly), unset defers to the checkpoint's own training-strategy census
445    ///   (the owner-ratified 2026-08-20 default flip).
446    pub fn for_draft(draft: &DflashDraft) -> Self {
447        Self::for_family_value(
448            draft.dflash2.is_some(),
449            std::env::var("MEMRA_DSPARK_HARVEST").ok().as_deref(),
450            draft.cfg.strategy_dspark,
451        )
452    }
453
454    pub fn for_family_value(is_dflash2: bool, env: Option<&str>, strategy_dspark: bool) -> Self {
455        if is_dflash2 {
456            if env == Some("dspark") {
457                panic!(
458                    "MEMRA_DSPARK_HARVEST=dspark with a DFlash2 checkpoint: DFlash2 \
459                     is mask-fill (b-1 drafts, anchor row is not a draft — reference \
460                     dflash_generate rows 1-verify_size:); the shifted harvest would \
461                     verify every slot one position early. Refusing (census-keyed, \
462                     not env-keyed)."
463                );
464            }
465            return DsparkHarvest::Dflash;
466        }
467        Self::resolve_value(env, strategy_dspark)
468    }
469
470    /// Manifest/serialized name (the oracle geometry manifest's `harvest` field).
471    pub fn name(self) -> &'static str {
472        match self {
473            DsparkHarvest::Dflash => "dflash",
474            DsparkHarvest::Dspark => "dspark",
475        }
476    }
477
478    pub fn from_name(v: &str) -> Option<Self> {
479        match v {
480            "dflash" => Some(DsparkHarvest::Dflash),
481            "dspark" => Some(DsparkHarvest::Dspark),
482            _ => None,
483        }
484    }
485
486    /// First drafter OUTPUT row consumed as a draft candidate.
487    pub fn first_row(self) -> usize {
488        match self {
489            DsparkHarvest::Dflash => 1,
490            DsparkHarvest::Dspark => 0,
491        }
492    }
493
494    /// Drafted tokens harvested per round from a `b`-row block.
495    pub fn n_drafts(self, b: usize) -> usize {
496        match self {
497            DsparkHarvest::Dflash => b - 1,
498            DsparkHarvest::Dspark => b,
499        }
500    }
501
502    /// The position offset (relative to the round anchor at the block's row 0) that
503    /// drafter output row `row` is TRAINED to predict under this convention.
504    pub fn trained_offset_of_row(self, row: usize) -> usize {
505        match self {
506            DsparkHarvest::Dflash => row,
507            DsparkHarvest::Dspark => row + 1,
508        }
509    }
510}
511
512/// Checkpoint training-strategy census over the raw config.json text (the loader's
513/// minimal-extractor idiom — no json dep in-tree). TRUE iff the export declares the
514/// DSPARK strategy: `architectures` naming a DSpark model class (`Qwen3DSparkModel`,
515/// the SpecForge OnlineDSparkModel export form) or `dflash_config.projector_type ==
516/// "dspark"`. z-lab / OnlineDFlashModel mask-fill exports carry neither signal. Pure,
517/// so the census is testable against config fragments without files.
518pub fn dspark_strategy_census(txt: &str) -> bool {
519    let arch = txt
520        .find("\"architectures\"")
521        .and_then(|i| {
522            let rest = &txt[i..];
523            let a = rest.find('[')?;
524            let b = rest.find(']')?;
525            Some(rest[a..b].contains("DSpark"))
526        })
527        .unwrap_or(false);
528    let proj = txt
529        .find("\"projector_type\"")
530        .map(|i| {
531            let rest = &txt[i..];
532            let after = rest.find(':').map(|c| &rest[c + 1..]).unwrap_or("");
533            after.trim_start().starts_with("\"dspark\"")
534        })
535        .unwrap_or(false);
536    arch || proj
537}
538
539/// Accepted-prefix length of a round's candidates against the trunk's verify argmaxes:
540/// `cand[0]` = the round anchor (already decided), `cand[1..]` = the drafts;
541/// `vam[j]` = the trunk's argmax prediction for position anchor+j+1. Returns m =
542/// number of accepted drafts (`cand[1..=m]` committed, `vam[m]` becomes the next
543/// anchor). Pure so the harvest-alignment fixture can exercise it CPU-side.
544pub fn dspark_accept_prefix(cand: &[u32], vam: &[u32], vt: usize) -> usize {
545    let mut m = 0usize;
546    while m < vt - 1 && cand[m + 1] == vam[m] {
547        m += 1;
548    }
549    m
550}
551
552/// Verify-window policy for the dspark round (H4, DSPARK-POSTMORTEM-20260820.md §3).
553///
554/// B2 measured the structural fork: the fixed full-block window (vt=8) buys 95–100%
555/// of the sglang accept bank but LOSES wall speed to the reactive ladder everywhere
556/// except math — at 0.2–0.5 slot rates, full-block verify pays 5–6 empty rows per
557/// round. The confidence policy is the mechanism both leading engines schedule with
558/// (sglang v0.5.16 `dspark_planner.py` cumprod survival; vLLM #47808): size EACH
559/// round's window from the drafter's own trained accept-rate head, so windows open
560/// on confident streaks (math/code) and shrink on bursty text without a 4-round
561/// ladder climb.
562#[derive(Clone, Copy, PartialEq, Debug)]
563pub enum DsparkVtPolicy {
564    /// The shipped reactive ladder: `vt = (m+2).clamp(3, vt_cap)` per round
565    /// (`MEMRA_DFLASH_ADAPT=0` pins vt at `vt_cap` = the fixed-window arm).
566    Ladder,
567    /// `MEMRA_DSPARK_VT=confidence`: per-round window from cumprod survival of the
568    /// confidence head's sigmoid scores, thresholded at `tau`
569    /// (`MEMRA_DSPARK_VT_TAU`, default 0.5). Raw sigmoid — no STS sidecar
570    /// calibration exists for this export; the postmortem names this the starting
571    /// policy.
572    Confidence { tau: f32 },
573    /// `MEMRA_DSPARK_VT=confidence-slot` (owner directive, 2026-08-20: "take only
574    /// high confidence offers"): submit only the longest draft PREFIX whose every
575    /// slot clears `tau` on its own sigmoid — the low-confidence tail never enters
576    /// verify. Same tau env. vs `Confidence`: if the head's per-row score is the
577    /// MARGINAL accept probability (it already sinks with depth), cumprod survival
578    /// double-counts the decay and over-truncates; if it is the CONDITIONAL,
579    /// per-slot under-truncates. Which statistic the q38 head emits is empirical —
580    /// both arms ride the A/B.
581    ConfidenceSlot { tau: f32 },
582}
583
584impl DsparkVtPolicy {
585    /// The served resolution: explicit `MEMRA_DSPARK_VT={ladder|confidence|
586    /// confidence-slot}` wins (unknown values REFUSE loudly — a typo silently
587    /// reverting the window policy would invalidate an A/B without a trace); UNSET
588    /// defaults to **`confidence-slot` at τ = `MEMRA_DSPARK_VT_TAU` (default 0.5)** —
589    /// the owner-ratified H4 flip (2026-08-20; cell 2's 4-arm A/B ×5 + cell 3's tau
590    /// ladder put the knee at τ=.5 for the slot arm: 94–98% of the fixed-8 accept bank
591    /// at wall ≥ the reactive ladder, exactness 11/11 ALL EXACT). Census-keyed per the
592    /// capacity-keyed-defaults law: a checkpoint WITHOUT an accept-rate head has no
593    /// signal to schedule with, so unset-env resolves to the ladder there (loudly, at
594    /// load) instead of panicking on a default; `MEMRA_DFLASH_ADAPT=0` (an explicit
595    /// fixed-window request) also keeps the ladder-family arm.
596    pub fn resolve(has_confidence_head: bool) -> Self {
597        Self::resolve_value(
598            std::env::var("MEMRA_DSPARK_VT").ok().as_deref(),
599            std::env::var("MEMRA_DSPARK_VT_TAU").ok().as_deref(),
600            std::env::var("MEMRA_DFLASH_ADAPT").ok().as_deref(),
601            has_confidence_head,
602        )
603    }
604
605    pub fn resolve_value(
606        vt: Option<&str>,
607        tau: Option<&str>,
608        adapt: Option<&str>,
609        has_confidence_head: bool,
610    ) -> Self {
611        match vt {
612            None | Some("") => {
613                if adapt == Some("0") || !has_confidence_head {
614                    DsparkVtPolicy::Ladder
615                } else {
616                    // The ratified default rides the SAME tau parse as the explicit
617                    // arm (a bad MEMRA_DSPARK_VT_TAU refuses, never silently ignored).
618                    Self::from_env_value(Some("confidence-slot"), tau, adapt)
619                }
620            }
621            set => Self::from_env_value(set, tau, adapt),
622        }
623    }
624
625    /// ENV-ONLY parser (no head census): unset = `Ladder`. Kept for the explicit-value
626    /// path of [`Self::resolve_value`] and the policy-gate tests; round arms resolve
627    /// through [`Self::resolve`] so the default stays head-census-keyed.
628    pub fn from_env_value(vt: Option<&str>, tau: Option<&str>, adapt: Option<&str>) -> Self {
629        match vt {
630            None | Some("") | Some("ladder") => DsparkVtPolicy::Ladder,
631            Some(mode @ ("confidence" | "confidence-slot")) => {
632                if adapt == Some("0") {
633                    panic!(
634                        "MEMRA_DSPARK_VT={mode} together with MEMRA_DFLASH_ADAPT=0 is \
635                         contradictory (a pinned fixed window vs a per-round confidence \
636                         window); unset one — refuse-on-ambiguity"
637                    );
638                }
639                let tau = tau
640                    .map(|t| {
641                        t.parse::<f32>()
642                            .unwrap_or_else(|_| panic!("MEMRA_DSPARK_VT_TAU={t}: not a float"))
643                    })
644                    .unwrap_or(0.5);
645                assert!(
646                    tau > 0.0 && tau < 1.0,
647                    "MEMRA_DSPARK_VT_TAU={tau}: confidence threshold must be in (0,1)"
648                );
649                if mode == "confidence" {
650                    DsparkVtPolicy::Confidence { tau }
651                } else {
652                    DsparkVtPolicy::ConfidenceSlot { tau }
653                }
654            }
655            Some(other) => panic!(
656                "MEMRA_DSPARK_VT={other}: unknown verify-window policy \
657                 (ladder|confidence|confidence-slot); refusing — a wrong policy \
658                 silently reverts the H4 arm (DSPARK-POSTMORTEM-20260820.md)"
659            ),
660        }
661    }
662
663    /// True for every head-scheduled arm (the loops gate the head requirement and
664    /// the embedding stash on this).
665    pub fn is_confidence(&self) -> bool {
666        !matches!(self, DsparkVtPolicy::Ladder)
667    }
668
669    /// Size this round's verify window from the head's pre-sigmoid slot scores.
670    /// `None` under the ladder (the caller keeps its carried vt).
671    pub fn size_window(&self, raws: &[f32], vt_cap: usize) -> Option<usize> {
672        match *self {
673            DsparkVtPolicy::Ladder => None,
674            DsparkVtPolicy::Confidence { tau } => Some(dspark_confidence_vt(raws, tau, vt_cap)),
675            DsparkVtPolicy::ConfidenceSlot { tau } => {
676                Some(dspark_slot_confidence_vt(raws, tau, vt_cap))
677            }
678        }
679    }
680}
681
682/// H4 window sizing (the sglang-planner/vLLM-#47808 mechanism, thresholded): `raws[k]`
683/// = the accept-rate head's PRE-sigmoid score for draft slot k+1; survival
684/// `S_k = prod_{j<=k} sigmoid(raws[j])`; the window keeps leading slots while
685/// `S_k >= tau`. Returns `vt` = 1 (anchor) + kept drafts, clamped to `[2, vt_cap]`:
686/// the draft forward is already paid, so at least one draft rides every verify — one
687/// extra verify row costs less than a guaranteed empty round. Pure, so the policy's
688/// knee is testable CPU-side like `dspark_accept_prefix`.
689pub fn dspark_confidence_vt(raws: &[f32], tau: f32, vt_cap: usize) -> usize {
690    let mut surv = 1.0f32;
691    let mut kept = 0usize;
692    for &r in raws {
693        surv *= 1.0 / (1.0 + (-r).exp());
694        if surv < tau {
695            break;
696        }
697        kept += 1;
698    }
699    (1 + kept).clamp(2, vt_cap.max(2))
700}
701
702/// Owner-directive arm (2026-08-20, "take only high confidence offers"): keep the
703/// longest draft PREFIX whose EVERY slot clears `tau` on its own sigmoid — truncate
704/// at the first sub-threshold slot, so the low-confidence tail (B2 measured 0.2–0.5
705/// slot rates at depth) never enters verify. Prefix truncation is forced by the
706/// accept rule anyway (`dspark_accept_prefix` stops at the first miss — a kept slot
707/// after a dropped one could never commit); the policy fork vs `dspark_confidence_vt`
708/// is only the stopping statistic (per-slot marginal vs cumulative survival). Same
709/// floor/cap contract.
710pub fn dspark_slot_confidence_vt(raws: &[f32], tau: f32, vt_cap: usize) -> usize {
711    let mut kept = 0usize;
712    for &r in raws {
713        let p = 1.0 / (1.0 + (-r).exp());
714        if p < tau {
715            break;
716        }
717        kept += 1;
718    }
719    (1 + kept).clamp(2, vt_cap.max(2))
720}
721
722// ================= SAMPLED ADMISSION (T>0) — lane/dspark-sampled-admission-20260820 =====
723// True rejection sampling for the dspark route (mystery A of DSPARK-POSTMORTEM-20260820):
724// draft slot j is DRAWN from a recorded proposal distribution q_j, the trunk's verify column
725// arbitrates with the Leviathan/Chen rule (accept x_j while u_j*q_j(x_j) < p_j(x_j); on
726// reject resample from norm(max(0, p-q)); on full accept the bonus ~ p at the last column),
727// so the committed stream's distribution equals trunk-only sampling from the FILTERED target
728// p — the same contract the frspec/MTP route ships (spec.rs sampled accept walk; kernels
729// oracled by sample_check). T==0/None keeps every greedy path byte-identical (the exactness
730// instrument and the kill-switch are the same code).
731//
732// Two proposal families, each recording the TRUE distribution its drafts were drawn from:
733// - Rows (dspark/dflash strategy checkpoints): per-slot FILTERED softmax of the draft-logits
734//   row — markov-corrected in place when the head is present (the sglang DSPARK worker's
735//   "chain rejection sampling over markov-corrected draft probs"), plain rows otherwise
736//   (the z-lab reference's independent-row T>0 arm).
737// - Selector (DFlash2): the candidate-path selector's per-slot softmax over its top-k
738//   candidate set at temperature ONLY — the reference applies no top-k/top-p to selector
739//   scores (z-lab model.py `CandidateSelector.select`: `_sampling_probs(scores, temperature)`
740//   with default filters) — with the candidate-set residual (`scatter_add_` of -q, clamped).
741
742/// Rejection-sampling prefix walk: accept draft j while `u_j * q_j < p_j` (strict, f64 —
743/// byte-identical to the frspec accept test). `p`/`q` are the FILTERED target/proposal
744/// probabilities of the drafted tokens; `u` the per-slot uniforms. Pure so the composition
745/// gate can pin the rule on CPU.
746pub fn rejection_accept_len(p: &[f32], q: &[f32], u: &[f32]) -> usize {
747    assert!(
748        q.len() >= p.len() && u.len() >= p.len(),
749        "accept walk shape"
750    );
751    let mut m = 0usize;
752    while m < p.len() && (u[m] as f64) * (q[m] as f64) < p[m] as f64 {
753        m += 1;
754    }
755    m
756}
757
758/// Sampled selector walk (reference `CandidateSelector.select`, temperature>0 arm): per
759/// draft slot the pair scores over the top-k candidate set become a softmax at `temp`
760/// (temperature ONLY — the reference passes no top-k/top-p here), one uniform draws the
761/// candidate (fixed-order CDF walk), and the CHOSEN candidate seeds the next slot exactly
762/// like the greedy chain. Returns (path, q_chosen[nd], q_rows[nd*top_k]) — q_rows are the
763/// recorded per-slot candidate probabilities (the residual's `scatter_add_` input), and
764/// q_chosen[j] == q_rows[j*top_k + chosen_j] is the accept-test q. Pure (uniforms injected)
765/// so the T->0 limit, the chain conditioning, and the recorded-q contract are CPU-gateable.
766#[allow(clippy::too_many_arguments)]
767pub fn dflash2_walk_sampled(
768    pred_codebook: &[u8],
769    succ_codebook: &[u8],
770    vocab: usize,
771    rank: usize,
772    top_k: usize,
773    unary: &[f32],
774    cand: &[u32],
775    hproj: &[f32],
776    anchor: u32,
777    nd: usize,
778    temp: f32,
779    uniforms: &mut dyn FnMut() -> f32,
780) -> (Vec<u32>, Vec<f32>, Vec<f32>) {
781    assert!(
782        temp > 0.0,
783        "sampled walk is the T>0 arm; T=0 is walk_greedy"
784    );
785    let (kk, r) = (top_k, rank);
786    assert_eq!(unary.len(), nd * kk, "walk: unary shape");
787    assert_eq!(cand.len(), nd * kk, "walk: candidate shape");
788    assert_eq!(hproj.len(), nd * r, "walk: hidden-projection shape");
789    let mut path = Vec::with_capacity(nd);
790    let mut q_chosen = Vec::with_capacity(nd);
791    let mut q_rows = Vec::with_capacity(nd * kk);
792    let mut prev = anchor;
793    for p in 0..nd {
794        assert!(
795            (prev as usize) < vocab,
796            "walk: predecessor token {prev} outside codebook vocab {vocab}"
797        );
798        let pr = cb_row(pred_codebook, prev as usize, r);
799        let hp = &hproj[p * r..(p + 1) * r];
800        let gate: Vec<f32> = pr.iter().zip(hp).map(|(a, b)| a * b).collect();
801        let mut scores = vec![0f32; kk];
802        for (k, s) in scores.iter_mut().enumerate() {
803            let c = cand[p * kk + k] as usize;
804            assert!(c < vocab, "walk: candidate {c} outside codebook vocab");
805            let sr = cb_row(succ_codebook, c, r);
806            let mut acc = unary[p * kk + k];
807            for j in 0..r {
808                acc += gate[j] * sr[j];
809            }
810            *s = acc;
811        }
812        // softmax over the candidate set at temp (f64 internals; recorded probs are the
813        // f32 values the CDF walk actually samples from — recorded q IS the proposal).
814        let mx = scores.iter().cloned().fold(f32::NEG_INFINITY, f32::max);
815        let mut z = 0f64;
816        let ex: Vec<f64> = scores
817            .iter()
818            .map(|&s| {
819                let e0 = (((s - mx) / temp) as f64).exp();
820                z += e0;
821                e0
822            })
823            .collect();
824        let probs: Vec<f32> = ex.iter().map(|&e0| (e0 / z) as f32).collect();
825        let u = uniforms() as f64;
826        let mut acc = 0f64;
827        // fp-residue fallback (u >= f32-accumulated mass, ~2^-24 events): the max-prob
828        // candidate — never a zero-prob one (host_u01's range includes 1.0 exactly).
829        let mut bi = probs
830            .iter()
831            .enumerate()
832            .max_by(|a, b| a.1.total_cmp(b.1))
833            .map(|(k, _)| k)
834            .unwrap_or(0);
835        for (k, &pk) in probs.iter().enumerate() {
836            acc += pk as f64;
837            if u < acc {
838                bi = k;
839                break;
840            }
841        }
842        prev = cand[p * kk + bi];
843        path.push(prev);
844        q_chosen.push(probs[bi]);
845        q_rows.extend_from_slice(&probs);
846    }
847    (path, q_chosen, q_rows)
848}
849
850/// `dflash2_propose_sampled`'s wire: (path, q_chosen, candidate ids, q_rows).
851pub(crate) type Dflash2SampledProposal = (Vec<u32>, Vec<f32>, Vec<u32>, Vec<f32>);
852
853/// Per-round proposal record for the sampled dspark round — everything the rejection
854/// walk needs to evaluate the TRUE per-slot proposal distribution q.
855pub(crate) enum DsparkDraftSample {
856    /// q lives in the round's draft-logits buffer `dl` (markov-biased in place when the
857    /// head is armed); per-slot FILTERED stats retained device-contiguous for the accept
858    /// gather + host-mirrored for the reject-slot residual.
859    Rows {
860        th: CudaSlice<f32>,          // [nd] filter thresholds (e-units), slot-indexed
861        z: CudaSlice<f32>,           // [nd] renorm masses
862        stats: Vec<(f32, f32, f32)>, // host (mx, th, z) per slot
863    },
864    /// DFlash2 candidate-path selector: q is the recorded candidate-set distribution.
865    Selector {
866        cand: Vec<u32>,     // [nd*top_k] candidate ids
867        q_rows: Vec<f32>,   // [nd*top_k] per-slot candidate probs
868        q_chosen: Vec<f32>, // [nd] prob of the drawn candidate (accept-test q)
869        top_k: usize,
870    },
871}
872
873/// The sampled round's verify+accept: filtered p gathered from the trunk's verify logits
874/// (row j arbitrates draft `cand[j+1]` — the position mapping the greedy prefix walk uses),
875/// the rejection walk over host uniforms, then `next` = bonus (full accept: filtered-Gumbel
876/// from the LAST verify row with its OWN fresh stats — the sampfix-20260805 law: that row is
877/// one past the gathered set) or the residual sample at the reject slot (family-keyed q:
878/// full-row logits for Rows, sparse candidate-set probs for Selector). Returns (m, next) —
879/// the exact (accepted-drafts, next-anchor) contract of the greedy `dspark_accept_prefix` +
880/// `vam[m]` pair, so both round bodies commit identically downstream.
881///
882/// PENALIZED SAMPLED (lane/dspark-penalized-sampled-20260821): when the request carries
883/// non-identity penalties, the vt verify columns are materialized ONCE into a penalized
884/// copy where row j's Keskar pass runs over `pen_win ++ cand[1..=j]` (window-capped) —
885/// the tokens committed before position j ON EVERY PATH WHERE ROW j IS CONSULTED,
886/// same-round accepts included (row j is only read when drafts 1..j were all accepted,
887/// i.e. exactly when `cand[1..=j]` is the committed prefix; the bonus row vt-1 is only
888/// read on full accept, when all nq drafts are committed). Every p read — the batched
889/// stats+gather, the bonus draw, the reject-slot residual column — points at that buffer,
890/// so p is the true penalized per-state target and the committed stream equals plain
891/// penalized sampling (the composition gate's penalty fixtures, self-hit included).
892/// q stays the RECORDED proposal the drafts were actually drawn from (unpenalized):
893/// rejection sampling is unbiased for ANY proposal with `u·q(x) < p(x)` + residual
894/// `norm(max(0, p−q))`; penalizing q would only buy acceptance overlap and would cost an
895/// evolving-history pass inside the sync-free device chain. `pen_win` is the caller's
896/// session window ALREADY trimmed to `min(penalty_last_n, PEN_WINDOW_MAX)` (empty when
897/// penalties are off — the unpenalized path is byte-untouched).
898#[allow(clippy::too_many_arguments)]
899pub(crate) fn dspark_accept_sampled(
900    e: &Engine,
901    tlogits: &CudaSlice<f32>,
902    cand: &[u32],
903    vt: usize,
904    n_vocab: usize,
905    dl: &CudaSlice<f32>,
906    prop: &DsparkDraftSample,
907    sp: &crate::spec::SpecSampling,
908    pen_win: &[u32],
909    sctr: &mut u32,
910    uctr: &mut u32,
911) -> Result<(usize, u32), Box<dyn std::error::Error>> {
912    let nq = vt - 1; // drafts under this round's verify window
913    debug_assert!(nq >= 1 && cand.len() > nq, "sampled accept shape");
914    // --- penalized verify columns (identity penalties: no copy, no launch, raw tlogits) ---
915    let pen_on = sp.pen_on();
916    let ptl: Option<CudaSlice<f32>> = if pen_on {
917        let win = sp.penalty_last_n.min(crate::spec::PEN_WINDOW_MAX);
918        debug_assert!(pen_win.len() <= win, "pen_win must arrive pre-trimmed");
919        let mut hist: Vec<u32> = Vec::with_capacity(pen_win.len() + nq);
920        hist.extend_from_slice(pen_win);
921        hist.extend_from_slice(&cand[1..=nq]); // drafted tokens: row j reads the first j
922        let hd = e.htod_u32_v(&hist)?;
923        let mut buf = e.clone_dtod(tlogits)?;
924        e.penalize_logits_rows_inc(
925            &mut buf,
926            &hd,
927            pen_win.len(),
928            sp.penalty_repeat,
929            sp.penalty_freq,
930            sp.penalty_present,
931            n_vocab,
932            vt,
933            win,
934        )?;
935        Some(buf)
936    } else {
937        None
938    };
939    let p_src: &CudaSlice<f32> = ptl.as_ref().unwrap_or(tlogits);
940    // --- filtered p at the drafted tokens (one batched stats + gather over rows 0..nq-1) ---
941    let rows: Vec<i32> = (0..nq as i32).collect();
942    let ids: Vec<u32> = cand[1..=nq].to_vec();
943    let rowsd = e.htod_i32(&rows)?;
944    let idsd = e.htod_u32_v(&ids)?;
945    let (mut pth, mut pz, mut pmx) = (e.zeros(nq)?, e.zeros(nq)?, e.zeros(nq)?);
946    e.filter_stats(
947        p_src, n_vocab, &rowsd, &mut pth, &mut pz, &mut pmx, n_vocab, nq, sp.temp, sp.top_k,
948        sp.top_p, sp.min_p,
949    )?;
950    let mut pj_d = e.zeros(nq)?;
951    e.softmax_gather_filtered(
952        p_src, n_vocab, &idsd, &rowsd, &pth, &pz, &mut pj_d, n_vocab, nq, sp.temp,
953    )?;
954    let pj = e.dtoh(&pj_d)?;
955    let (pthv, pzv, pmxv) = (e.dtoh(&pth)?, e.dtoh(&pz)?, e.dtoh(&pmx)?);
956    // --- q at the drafted tokens (the recorded proposal distribution) ---
957    let qj: Vec<f32> = match prop {
958        DsparkDraftSample::Rows { th, z, .. } => {
959            // dl row j is draft j's (bias-corrected) logits row; th/z are slot-indexed, and
960            // rows 0..nq-1 index both the buffer rows and the stat pairs.
961            let mut qd = e.zeros(nq)?;
962            e.softmax_gather_filtered(
963                dl, n_vocab, &idsd, &rowsd, th, z, &mut qd, n_vocab, nq, sp.temp,
964            )?;
965            e.dtoh(&qd)?
966        }
967        DsparkDraftSample::Selector { q_chosen, .. } => q_chosen[..nq].to_vec(),
968    };
969    // --- the rejection walk ---
970    let mut us = Vec::with_capacity(nq);
971    for _ in 0..nq {
972        us.push(crate::spec::host_u01(sp.seed, *uctr));
973        *uctr = uctr.wrapping_add(1);
974    }
975    let m = rejection_accept_len(&pj[..nq], &qj[..nq], &us);
976    // --- next anchor: bonus or residual ---
977    let next = if m == nq {
978        // FULL ACCEPT: bonus ~ filtered p at verify row vt-1 — fresh stats for THIS row.
979        // Under penalties p_src row vt-1 carries the FULL drafted block in its window
980        // (all nq drafts are committed on this path — the "drafted token penalizes its
981        // own successor" case the composition gate's self-hit fixture pins).
982        let rows_l = e.htod_i32(&[(vt - 1) as i32])?;
983        let (mut th1, mut z1, mut mx1) = (e.zeros(1)?, e.zeros(1)?, e.zeros(1)?);
984        e.filter_stats(
985            p_src, n_vocab, &rows_l, &mut th1, &mut z1, &mut mx1, n_vocab, 1, sp.temp, sp.top_k,
986            sp.top_p, sp.min_p,
987        )?;
988        let mut pb = e.zeros(n_vocab)?;
989        e.gumbel_perturb_filtered_col(
990            p_src,
991            vt - 1,
992            &mut pb,
993            n_vocab,
994            sp.seed,
995            *sctr,
996            sp.temp,
997            &mx1,
998            &th1,
999            0,
1000        )?;
1001        *sctr = sctr.wrapping_add(1);
1002        let td = e.argmax_token_device(&pb, n_vocab)?;
1003        e.dtoh_u32_one(&td)?
1004    } else {
1005        // REJECT at slot m: token ~ norm(max(0, p_m - q_m)); p row m's stats come from the
1006        // gathered set (rows 0..nq-1 cover every reject slot). Under penalties the column
1007        // copy MUST come from p_src (the penalized buffer) — a raw-tlogits residual is the
1008        // composition gate's "residual reads unpenalized p" tooth.
1009        let mut col = e.zeros(n_vocab)?;
1010        e.copy_view_into(
1011            &mut col,
1012            0,
1013            &p_src.slice(m * n_vocab..(m + 1) * n_vocab),
1014            n_vocab,
1015        )?;
1016        let p_stats = (pmxv[m], pthv[m], pzv[m]);
1017        let mut tok_d = e.alloc_u32_zeroed(1)?;
1018        let sc = *sctr;
1019        *sctr = sctr.wrapping_add(1);
1020        match prop {
1021            DsparkDraftSample::Rows { stats, .. } => {
1022                let mut qbuf = e.zeros(n_vocab)?;
1023                e.copy_view_into(
1024                    &mut qbuf,
1025                    0,
1026                    &dl.slice(m * n_vocab..(m + 1) * n_vocab),
1027                    n_vocab,
1028                )?;
1029                e.residual_sample_filtered(
1030                    &col,
1031                    Some(&qbuf),
1032                    n_vocab,
1033                    sp.temp,
1034                    sp.seed,
1035                    sc,
1036                    p_stats,
1037                    stats[m],
1038                    &mut tok_d,
1039                )?;
1040            }
1041            DsparkDraftSample::Selector {
1042                cand: cids,
1043                q_rows,
1044                top_k,
1045                ..
1046            } => {
1047                let k = *top_k;
1048                let ids_m = e.htod_u32_v(&cids[m * k..(m + 1) * k])?;
1049                let qs_m = e.htod(&q_rows[m * k..(m + 1) * k])?;
1050                e.residual_sample_sparse_q(
1051                    &col, &ids_m, &qs_m, k, n_vocab, sp.temp, sp.seed, sc, p_stats, &mut tok_d,
1052                )?;
1053            }
1054        }
1055        e.dtoh_u32(&tok_d)?[0]
1056    };
1057    Ok((m, next))
1058}
1059
1060/// Clip door for the DFlash2 windowed round attention (lane/dflash2-longctx, §10.6(c)).
1061/// Default ON: the lo-clipped kernel — byte-identical output (kernel_check
1062/// `sdpa_naive_w_lo`), O(window) key scan, and no T_kv*4-byte shared-mem launch bound, so
1063/// the route survives past ~12k ctx (GATES-SMOKE-20260821 B2: DriverError(
1064/// CUDA_ERROR_INVALID_VALUE) at ctx 16,571/30,157, last success 9,510).
1065/// MEMRA_DFLASH2_SDPA_CLIP=0 = the legacy full-scan kernel byte-for-byte — the rollback
1066/// seam and the long-ctx gate's crash-reproduction arm.
1067fn dflash2_sdpa_clip_on() -> bool {
1068    std::env::var("MEMRA_DFLASH2_SDPA_CLIP")
1069        .map(|v| v != "0")
1070        .unwrap_or(true)
1071}
1072
1073/// The DFlash2 round attention over the non-causal symmetric window: one seam for both the
1074/// first-light (`forward_block`) and cached (`forward_round`) arms, dispatching the clipped
1075/// kernel unless the rollback door is thrown.
1076#[allow(clippy::too_many_arguments)]
1077fn d2_windowed_attn(
1078    e: &Engine,
1079    q: &CudaSlice<f32>,
1080    k: &CudaSlice<f32>,
1081    v: &CudaSlice<f32>,
1082    attn: &mut CudaSlice<f32>,
1083    hd: usize,
1084    nh: usize,
1085    nkv: usize,
1086    t: usize,
1087    t_kv: usize,
1088    scale: f32,
1089    c: &DflashCfg,
1090) -> Result<(), Box<dyn std::error::Error>> {
1091    if dflash2_sdpa_clip_on() {
1092        e.sdpa_naive_w_lo(
1093            q,
1094            k,
1095            v,
1096            attn,
1097            hd,
1098            nh,
1099            nkv,
1100            t,
1101            t_kv,
1102            scale,
1103            false,
1104            c.sliding_window,
1105        )
1106    } else {
1107        e.sdpa_naive_w(
1108            q,
1109            k,
1110            v,
1111            attn,
1112            hd,
1113            nh,
1114            nkv,
1115            t,
1116            t_kv,
1117            scale,
1118            false,
1119            c.sliding_window,
1120        )
1121    }
1122}
1123
1124fn bf16_to_f32(bytes: &[u8]) -> Vec<f32> {
1125    bytes
1126        .chunks_exact(2)
1127        .map(|c| f32::from_bits((u16::from_le_bytes([c[0], c[1]]) as u32) << 16))
1128        .collect()
1129}
1130
1131fn validate_dflash_tensor(
1132    name: &str,
1133    info: &memra_gguf::safetensors::StInfo,
1134    expected: &[u64],
1135) -> Result<(), String> {
1136    if info.dtype != "BF16" {
1137        return Err(format!(
1138            "DFlash tensor {name} has dtype {}, expected BF16",
1139            info.dtype
1140        ));
1141    }
1142    let found = info.ne();
1143    if found != expected {
1144        return Err(format!(
1145            "DFlash tensor {name} has shape {found:?}, expected {expected:?}"
1146        ));
1147    }
1148    Ok(())
1149}
1150
1151fn validate_dflash_attention_geometry(
1152    n_head: usize,
1153    n_kv: usize,
1154    head_dim: usize,
1155) -> Result<(), String> {
1156    if n_head == 0 || n_kv == 0 || head_dim == 0 || !n_head.is_multiple_of(n_kv) {
1157        return Err(format!(
1158            "DFlash attention geometry requires nonzero n_head divisible by n_kv; got n_head={n_head}, n_kv={n_kv}, head_dim={head_dim}"
1159        ));
1160    }
1161    n_head
1162        .checked_mul(head_dim)
1163        .ok_or("DFlash query-head geometry overflow")?;
1164    n_kv.checked_mul(head_dim)
1165        .ok_or("DFlash key/value-head geometry overflow")?;
1166    Ok(())
1167}
1168
1169fn validate_selector_top_k(top_k: usize, vocab: usize) -> Result<(), String> {
1170    if top_k == 0 || top_k > vocab {
1171        return Err(format!(
1172            "DFlash2 selector_top_k {top_k} is outside codebook vocabulary 1..={vocab}"
1173        ));
1174    }
1175    Ok(())
1176}
1177
1178fn validate_layer_layout(layer_sliding: &[bool], n_layer: usize) -> Result<(), String> {
1179    if layer_sliding.len() != n_layer {
1180        return Err(format!(
1181            "DFlash layer_types has {} entries, expected num_hidden_layers {n_layer}",
1182            layer_sliding.len()
1183        ));
1184    }
1185    Ok(())
1186}
1187
1188/// Host q8_0 encode (ggml block layout: [d f16][32 x i8] = 34B/32 vals). The drafter's
1189/// weights ride the dp4a fast path at 1.6GB resident (bf16 3.1GB + the 31B trunk OOM'd
1190/// 24GB; f32 6.2GB worse). Drafter quantization moves ACCEPTANCE only — verify exactness
1191/// is structural.
1192fn encode_q8_0(vals: &[f32]) -> Vec<u8> {
1193    let mut out = Vec::with_capacity(vals.len() / 32 * 34);
1194    for blk in vals.chunks_exact(32) {
1195        let amax = blk.iter().fold(0f32, |a, v| a.max(v.abs()));
1196        let d = amax / 127.0;
1197        let id = if d > 0.0 { 1.0 / d } else { 0.0 };
1198        let dh = half_from_f32(d);
1199        out.extend_from_slice(&dh.to_le_bytes());
1200        for &v in blk {
1201            out.push(((v * id).round().clamp(-127.0, 127.0)) as i8 as u8);
1202        }
1203    }
1204    out
1205}
1206
1207/// Host q4_0 encode (ggml: [d f16][16B packed nibbles] = 18B/32 vals; q = round(v/d)+8,
1208/// d = amax/-7 sign trick NOT used — plain amax/7? ggml uses d = max/-8 .. follow ggml:
1209/// d = amax / -8 when the max is negative-dominant; reference quantize_row_q4_0: d =
1210/// max(|v|)/-8 signed-max form). Implemented to match ggml quantize_row_q4_0_ref.
1211fn encode_q4_0(vals: &[f32]) -> Vec<u8> {
1212    let mut out = Vec::with_capacity(vals.len() / 32 * 18);
1213    for blk in vals.chunks_exact(32) {
1214        // ggml ref: pick the value with the LARGEST |v| (keeping sign), d = that / -8
1215        let mut amax = 0f32;
1216        let mut mx = 0f32;
1217        for &v in blk {
1218            if v.abs() > amax {
1219                amax = v.abs();
1220                mx = v;
1221            }
1222        }
1223        let d = mx / -8.0;
1224        let id = if d != 0.0 { 1.0 / d } else { 0.0 };
1225        out.extend_from_slice(&half_from_f32(d).to_le_bytes());
1226        for j in 0..16 {
1227            let x0 = (blk[j] * id + 8.5).clamp(0.0, 15.0) as u8;
1228            let x1 = (blk[j + 16] * id + 8.5).clamp(0.0, 15.0) as u8;
1229            out.push(x0 | (x1 << 4));
1230        }
1231    }
1232    out
1233}
1234
1235fn half_from_f32(v: f32) -> u16 {
1236    // f32 -> IEEE f16 (round-to-nearest-even; range of q8_0 d values is tame)
1237    let b = v.to_bits();
1238    let sign = ((b >> 16) & 0x8000) as u16;
1239    let exp = ((b >> 23) & 0xff) as i32 - 127 + 15;
1240    let man = b & 0x7fffff;
1241    if exp <= 0 {
1242        return sign;
1243    } // flush tiny d to zero
1244    if exp >= 31 {
1245        return sign | 0x7c00;
1246    } // inf (unreachable for sane d)
1247    let mut h = sign | ((exp as u16) << 10) | ((man >> 13) as u16);
1248    // round to nearest even on the truncated 13 bits
1249    let rem = man & 0x1fff;
1250    if rem > 0x1000 || (rem == 0x1000 && (h & 1) == 1) {
1251        h += 1;
1252    }
1253    h
1254}
1255
1256impl DflashDraft {
1257    /// Load the backbone-only checkpoint dir (config.json + model.safetensors, bf16).
1258    /// Config scalars ride a minimal extractor (no json dep in-tree — HfConfig precedent).
1259    pub fn load(e: &Engine, dir: &std::path::Path) -> Result<Self, Box<dyn std::error::Error>> {
1260        let txt = std::fs::read_to_string(dir.join("config.json"))?;
1261        fn num(txt: &str, key: &str) -> Option<f64> {
1262            let i = txt.find(&format!("\"{key}\""))?;
1263            let rest = &txt[i..];
1264            let colon = rest.find(':')?;
1265            let val: String = rest[colon + 1..]
1266                .trim_start()
1267                .chars()
1268                .take_while(|c| {
1269                    c.is_ascii_digit()
1270                        || *c == '.'
1271                        || *c == '-'
1272                        || *c == 'e'
1273                        || *c == 'E'
1274                        || *c == '+'
1275                })
1276                .collect();
1277            val.parse().ok()
1278        }
1279        fn num_list(txt: &str, key: &str) -> Vec<usize> {
1280            let Some(i) = txt.find(&format!("\"{key}\"")) else {
1281                return Vec::new();
1282            };
1283            let rest = &txt[i..];
1284            let (Some(a), Some(b)) = (rest.find('['), rest.find(']')) else {
1285                return Vec::new();
1286            };
1287            rest[a + 1..b]
1288                .split(',')
1289                .filter_map(|s| s.trim().parse().ok())
1290                .collect()
1291        }
1292        /// Substring of the JSON OBJECT value of a top-level key (brace-balanced) —
1293        /// the explicit scoped parse the DFlash2 census demands: `dflash_config` and
1294        /// `rope_parameters` are nested objects, and finding their keys by global
1295        /// `txt.find` is luck, not a contract (DFLASH2-EVAL-20260820.md §5.1).
1296        fn scope<'a>(txt: &'a str, key: &str) -> Option<&'a str> {
1297            let i = txt.find(&format!("\"{key}\""))?;
1298            let rest = &txt[i..];
1299            let open = rest.find('{')?;
1300            let mut depth = 0usize;
1301            for (j, ch) in rest[open..].char_indices() {
1302                match ch {
1303                    '{' => depth += 1,
1304                    '}' => {
1305                        depth -= 1;
1306                        if depth == 0 {
1307                            return Some(&rest[open..open + j + 1]);
1308                        }
1309                    }
1310                    _ => {}
1311                }
1312            }
1313            None
1314        }
1315        // Family detection is the ARCHITECTURES string, not tensor presence: a DFlash2
1316        // checkpoint whose new tensors were stripped must REFUSE, not degrade into the
1317        // 58-tensor untrained program (DFLASH2-EVAL-20260820.md §3).
1318        let is_dflash2 = {
1319            let arch = scope_list(&txt, "architectures");
1320            arch.contains("DFlash2DraftModel")
1321        };
1322        fn scope_list(txt: &str, key: &str) -> String {
1323            let Some(i) = txt.find(&format!("\"{key}\"")) else {
1324                return String::new();
1325            };
1326            let rest = &txt[i..];
1327            match (rest.find('['), rest.find(']')) {
1328                (Some(a), Some(b)) if a < b => rest[a + 1..b].to_string(),
1329                _ => String::new(),
1330            }
1331        }
1332        // DFlash2 scalars parse from their OWN scopes; other families keep the
1333        // historical global-find behavior byte-identically.
1334        let d2_cfg_txt: Option<&str> = if is_dflash2 {
1335            Some(
1336                scope(&txt, "dflash_config")
1337                    .ok_or("DFlash2DraftModel config.json has no dflash_config object")?,
1338            )
1339        } else {
1340            None
1341        };
1342        let required_usize =
1343            |scope: &str, k: &str, label: &str| -> Result<usize, Box<dyn std::error::Error>> {
1344                let value = num(scope, k).ok_or_else(|| format!("{label} missing {k}"))?;
1345                if !value.is_finite()
1346                    || value < 0.0
1347                    || value.fract() != 0.0
1348                    || value > usize::MAX as f64
1349                {
1350                    return Err(format!("{label} {k}={value} is not a non-negative usize").into());
1351                }
1352                Ok(value as usize)
1353            };
1354        let g = |k: &str| required_usize(&txt, k, "config");
1355        let g2 = |k: &str| {
1356            required_usize(
1357                d2_cfg_txt.ok_or("DFlash2 config scope is unavailable")?,
1358                k,
1359                "dflash_config",
1360            )
1361        };
1362        // layer_types order: count entries, mark sliding ones
1363        let layer_sliding: Vec<bool> = {
1364            let i = txt
1365                .find("\"layer_types\"")
1366                .ok_or("config missing layer_types")?;
1367            let rest = &txt[i..];
1368            let a = rest.find('[').ok_or("layer_types is not an array")?;
1369            let b = rest.find(']').ok_or("layer_types array is unterminated")?;
1370            rest[a + 1..b]
1371                .split(',')
1372                .map(|s| s.contains("sliding_attention"))
1373                .collect()
1374        };
1375        // sliding_window is null on all-full-attention exports (q38 arm-a); the window
1376        // only constrains rounds when a sliding layer exists (reference: resolve_dflash_
1377        // attention_layout returns None when no layer slides).
1378        let sliding_window = if layer_sliding.iter().any(|&s| s) {
1379            g("sliding_window")?
1380        } else {
1381            num(&txt, "sliding_window")
1382                .map(|v| v as usize)
1383                .unwrap_or(usize::MAX)
1384        };
1385        // Explicit top-level is_causal (z-lab reference: overrides the layer-type
1386        // default). Parsed as a bare bool; absent = None (historical arms unchanged).
1387        let is_causal = txt
1388            .find("\"is_causal\"")
1389            .and_then(|i| txt[i..].find(':').map(|c| i + c + 1))
1390            .map(|v| txt[v..].trim_start().starts_with("true"));
1391        let cfg = DflashCfg {
1392            hidden: g("hidden_size")?,
1393            n_head: g("num_attention_heads")?,
1394            n_kv: g("num_key_value_heads")?,
1395            head_dim: g("head_dim")?,
1396            n_ff: g("intermediate_size")?,
1397            n_layer: g("num_hidden_layers")?,
1398            eps: num(&txt, "rms_norm_eps").ok_or("config missing rms_norm_eps")? as f32,
1399            // DFlash2 (transformers-5 style): rope_theta lives in the nested
1400            // rope_parameters object — parse it from its scope, not by global find.
1401            rope_theta: if is_dflash2 {
1402                let rp = scope(&txt, "rope_parameters")
1403                    .ok_or("DFlash2 config has no rope_parameters")?;
1404                if !rp.contains("\"default\"") {
1405                    return Err(format!(
1406                        "DFlash2 rope_parameters rope_type is not default; refusing {rp}"
1407                    )
1408                    .into());
1409                }
1410                num(rp, "rope_theta").ok_or("rope_parameters missing rope_theta")? as f32
1411            } else {
1412                num(&txt, "rope_theta").ok_or("config missing rope_theta")? as f32
1413            },
1414            block_size: if is_dflash2 {
1415                g2("block_size")?
1416            } else {
1417                g("block_size")?
1418            },
1419            mask_token_id: u32::try_from(if is_dflash2 {
1420                g2("mask_token_id")?
1421            } else {
1422                g("mask_token_id")?
1423            })
1424            .map_err(|_| "DFlash mask_token_id does not fit u32")?,
1425            target_layer_ids: if is_dflash2 {
1426                num_list(
1427                    d2_cfg_txt.ok_or("DFlash2 config scope is unavailable")?,
1428                    "target_layer_ids",
1429                )
1430            } else {
1431                num_list(&txt, "target_layer_ids")
1432            },
1433            sliding_window,
1434            layer_sliding,
1435            strategy_dspark: dspark_strategy_census(&txt),
1436            is_causal,
1437        };
1438        if cfg.n_layer == 0 || cfg.n_layer > 1_024 {
1439            return Err(format!(
1440                "DFlash num_hidden_layers {} is outside 1..=1024",
1441                cfg.n_layer
1442            )
1443            .into());
1444        }
1445        if cfg.hidden == 0
1446            || cfg.n_head == 0
1447            || cfg.n_kv == 0
1448            || cfg.head_dim == 0
1449            || cfg.n_ff == 0
1450            || !cfg.eps.is_finite()
1451            || cfg.eps <= 0.0
1452            || !cfg.rope_theta.is_finite()
1453            || cfg.rope_theta <= 0.0
1454        {
1455            return Err("DFlash config carries zero or non-finite model geometry".into());
1456        }
1457        validate_dflash_attention_geometry(cfg.n_head, cfg.n_kv, cfg.head_dim)?;
1458        validate_layer_layout(&cfg.layer_sliding, cfg.n_layer)?;
1459        if is_dflash2 {
1460            // The windowed round arm implements the reference's NON-causal symmetric
1461            // window only (config `is_causal: false` on the q38 DFlash2 export). A
1462            // causal DFlash2 variant is a different mask program — refuse it rather
1463            // than run the wrong one fluently.
1464            if cfg.is_causal != Some(false) {
1465                return Err(format!(
1466                    "DFlash2 requires explicit is_causal=false; got {:?}",
1467                    cfg.is_causal
1468                )
1469                .into());
1470            }
1471            if !cfg.layer_sliding.iter().all(|&sliding| sliding) {
1472                return Err(format!(
1473                    "DFlash2 expects all layers sliding_attention; got {:?}",
1474                    cfg.layer_sliding
1475                )
1476                .into());
1477            }
1478            if cfg.block_size > cfg.sliding_window {
1479                return Err(format!(
1480                    "DFlash2 block {} exceeds sliding window {}",
1481                    cfg.block_size, cfg.sliding_window
1482                )
1483                .into());
1484            }
1485        }
1486        let st = memra_gguf::safetensors::StModel::open(&dir.join("model.safetensors"))?;
1487        let validate = |name: &str,
1488                        info: &memra_gguf::safetensors::StInfo,
1489                        expected: &[u64]|
1490         -> Result<(), Box<dyn std::error::Error>> {
1491            validate_dflash_tensor(name, info, expected).map_err(Into::into)
1492        };
1493        // 1D norm weights ride raw slices; 2D matmul weights ride GpuTensor::Float
1494        // (cuBLASLt f32 arm — the Stage-A numeric class, right for oracle parity).
1495        let up =
1496            |name: &str, expected: &[u64]| -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
1497                let (info, bytes) = st
1498                    .raw(name)
1499                    .ok_or_else(|| format!("missing tensor {name}"))?;
1500                validate(name, info, expected)?;
1501                e.htod(&bf16_to_f32(bytes))
1502            };
1503        // Precision policy (MEMRA_DFLASH_PREC seam): "q4" = all q4_0 (DEFAULT since
1504        // lane/dflash2-head-trim 2026-08-25, owner-ratified): measured on BOTH engaging
1505        // card classes at unchanged acceptance — RTX PRO 6000 dspark_q38_gate x3
1506        // interleaved 157.9 vs q8 152.4 spec tok/s (accept 0.662 vs 0.656, ALL EXACT;
1507        // darklanes research/dflash2-pro6000-20260824/prec-ladder + trim cells) and the
1508        // 5090 rig cell that shipped the arm (PR #41). "q8" = all q8_0 (1.6GB, the
1509        // pre-flip default = the rollback seam); "mixed" = bf16 attn+fc (the
1510        // ctx-conditioning path) + q8_0 ffn (~2.2GB — fits the ~2.8GB headroom beside
1511        // the 31B trunk); "bf16" = all bf16 (parity runs, no target). The asymmetric
1512        // "q5" arm was measured DEFECTIVE (acceptance 0.656 -> 0.424) and never landed.
1513        let prec_env = std::env::var("MEMRA_DFLASH_PREC").ok();
1514        let prec = dflash_precision(prec_env.as_deref())?;
1515        let upw = |name: &str, expected: &[u64]| -> Result<GpuTensor, Box<dyn std::error::Error>> {
1516            let (info, bytes) = st
1517                .raw(name)
1518                .ok_or_else(|| format!("missing tensor {name}"))?;
1519            validate(name, info, expected)?;
1520            let shape = info.ne(); // ggml order: ne[0]=in_f, ne[1]=out_f
1521            let in_f = shape[0] as usize;
1522            let is_ffn = name.contains(".mlp.");
1523            let bf16 = prec == "bf16"
1524                || (prec == "mixed" && !is_ffn)
1525                || (prec == "fc" && name == "fc.weight");
1526            if bf16 {
1527                return Ok(GpuTensor::FloatBf16 {
1528                    data: e.upload_u8(bytes)?,
1529                    ne: shape.to_vec(),
1530                });
1531            }
1532            let f32s = bf16_to_f32(bytes);
1533            if prec == "q4" {
1534                let q = encode_q4_0(&f32s);
1535                return Ok(GpuTensor::Quant {
1536                    bytes: e.upload_u8(&q)?,
1537                    qtype: crate::QT_Q4_0,
1538                    row_bytes: in_f / 32 * 18,
1539                    ne: shape.to_vec(),
1540                    scale: 1.0,
1541                    rp: false,
1542                    #[cfg(memra_cutlass)]
1543                    cutlass: None,
1544                    fp8: None,
1545                    blk: None,
1546                    rp4: None,
1547                    f16: None,
1548                });
1549            }
1550            let q = encode_q8_0(&f32s);
1551            Ok(GpuTensor::Quant {
1552                bytes: e.upload_u8(&q)?,
1553                qtype: crate::QT_Q8_0,
1554                row_bytes: in_f / 32 * 34,
1555                ne: shape.to_vec(),
1556                scale: 1.0,
1557                rp: false,
1558                #[cfg(memra_cutlass)]
1559                cutlass: None,
1560                fp8: None,
1561                blk: None,
1562                rp4: None,
1563                f16: None,
1564            })
1565        };
1566        let hidden = cfg.hidden as u64;
1567        let q_width = cfg
1568            .n_head
1569            .checked_mul(cfg.head_dim)
1570            .ok_or("DFlash q geometry overflow")? as u64;
1571        let kv_width = cfg
1572            .n_kv
1573            .checked_mul(cfg.head_dim)
1574            .ok_or("DFlash kv geometry overflow")? as u64;
1575        let ff = cfg.n_ff as u64;
1576        let head_dim = cfg.head_dim as u64;
1577        let mut layers = Vec::with_capacity(cfg.n_layer);
1578        for i in 0..cfg.n_layer {
1579            let p = |s: &str| format!("layers.{i}.{s}");
1580            layers.push(DflashLayer {
1581                wq: upw(&p("self_attn.q_proj.weight"), &[hidden, q_width])?,
1582                wk: upw(&p("self_attn.k_proj.weight"), &[hidden, kv_width])?,
1583                wv: upw(&p("self_attn.v_proj.weight"), &[hidden, kv_width])?,
1584                wo: upw(&p("self_attn.o_proj.weight"), &[q_width, hidden])?,
1585                w_gate: upw(&p("mlp.gate_proj.weight"), &[hidden, ff])?,
1586                w_up: upw(&p("mlp.up_proj.weight"), &[hidden, ff])?,
1587                w_down: upw(&p("mlp.down_proj.weight"), &[ff, hidden])?,
1588                ln_in: up(&p("input_layernorm.weight"), &[hidden])?,
1589                ln_post: up(&p("post_attention_layernorm.weight"), &[hidden])?,
1590                q_norm: up(&p("self_attn.q_norm.weight"), &[head_dim])?,
1591                k_norm: up(&p("self_attn.k_norm.weight"), &[head_dim])?,
1592            });
1593        }
1594        let markov = if let Some((info, bytes)) = st.raw("markov_head.markov_w1.weight") {
1595            let sh = info.ne(); // [rank, vocab] in ggml order (safetensors [V, rank] reversed)
1596            if info.dtype != "BF16" || sh.len() != 2 {
1597                return Err("markov_head.markov_w1.weight must be rank-2 BF16".into());
1598            }
1599            let (rank, vocab) = (sh[0] as usize, sh[1] as usize);
1600            let (i2, b2) = st
1601                .raw("markov_head.markov_w2.weight")
1602                .ok_or("markov_w2 missing beside markov_w1")?;
1603            validate("markov_head.markov_w2.weight", i2, &sh)?;
1604            // w2 follows the precision seam: bf16 for parity runs (the q8_0 encode is a
1605            // serving-size choice and would put quant error inside the markov-logits gate),
1606            // q8_0 otherwise (acceptance-only impact, like the trunk weights).
1607            let w2 = if prec == "bf16" {
1608                GpuTensor::FloatBf16 {
1609                    data: e.upload_u8(b2)?,
1610                    ne: i2.ne().to_vec(),
1611                }
1612            } else {
1613                let w2f = bf16_to_f32(b2);
1614                let w2q = encode_q8_0(&w2f);
1615                GpuTensor::Quant {
1616                    bytes: e.upload_u8(&w2q)?,
1617                    qtype: crate::QT_Q8_0,
1618                    row_bytes: rank / 32 * 34,
1619                    ne: vec![rank as u64, vocab as u64],
1620                    scale: 1.0,
1621                    rp: false,
1622                    #[cfg(memra_cutlass)]
1623                    cutlass: None,
1624                    fp8: None,
1625                    blk: None,
1626                    rp4: None,
1627                    f16: None,
1628                }
1629            };
1630            Some(MarkovHead {
1631                w1_bf16: e.upload_u8(bytes)?,
1632                w2,
1633                rank,
1634                vocab,
1635            })
1636        } else {
1637            None
1638        };
1639        let confidence = if let Some((info, bytes)) = st.raw("confidence_head.proj.weight") {
1640            let sh = info.ne(); // ggml order: ne[0]=in_dim, ne[1]=1
1641            if info.dtype != "BF16" || sh.len() != 2 || sh[1] != 1 {
1642                return Err("confidence_head.proj.weight must be BF16 [in_dim, 1]".into());
1643            }
1644            let in_dim = sh[0] as usize;
1645            let (bi, bb) = st
1646                .raw("confidence_head.proj.bias")
1647                .ok_or("confidence bias missing beside weight")?;
1648            validate("confidence_head.proj.bias", bi, &[1])?;
1649            let with_markov = markov
1650                .as_ref()
1651                .map(|m| in_dim == cfg.hidden + m.rank)
1652                .unwrap_or(false);
1653            if !with_markov && in_dim != cfg.hidden {
1654                return Err(format!(
1655                    "confidence_head in_dim {in_dim} matches neither hidden {} nor hidden+rank",
1656                    cfg.hidden
1657                )
1658                .into());
1659            }
1660            Some(ConfidenceHead {
1661                w: bf16_to_f32(bytes),
1662                b: bf16_to_f32(bb)[0],
1663                in_dim,
1664                with_markov,
1665            })
1666        } else {
1667            None
1668        };
1669        // ---- DFlash2 family tensors (DFLASH2-EVAL-20260820.md §2): 10 conv modules
1670        // (base_kernel + kernel_projection around attention AND mlp in EVERY layer) +
1671        // the candidate path selector (hidden_projection + two codebooks). REQUIRED
1672        // when the arch says DFlash2DraftModel: a missing tensor is a refusal (`?`),
1673        // never a degraded program.
1674        let dflash2 = if is_dflash2 {
1675            if markov.is_some() || confidence.is_some() {
1676                return Err(
1677                    "DFlash2 checkpoint carries unsupported markov/confidence tensors".into(),
1678                );
1679            }
1680            let rank = g2("selector_rank")?;
1681            let top_k = g2("selector_top_k")?;
1682            let conv_k = g2("conv_kernel_size")?;
1683            let group_size = g2("conv_group_size")?;
1684            if rank == 0
1685                || top_k == 0
1686                || conv_k == 0
1687                || group_size == 0
1688                || !cfg.hidden.is_multiple_of(group_size)
1689            {
1690                return Err(
1691                    "DFlash2 selector/convolution geometry is zero or not divisible".into(),
1692                );
1693            }
1694            let groups = cfg.hidden / group_size;
1695            let load_conv = |name: &str| -> Result<Dflash2Conv, Box<dyn std::error::Error>> {
1696                let (bi, bb) = st
1697                    .raw(&format!("{name}.base_kernel"))
1698                    .ok_or_else(|| format!("DFlash2 census: missing {name}.base_kernel"))?;
1699                // safetensors [2, k, hidden] -> ggml ne reversed [hidden, k, 2]
1700                validate(
1701                    &format!("{name}.base_kernel"),
1702                    bi,
1703                    &[cfg.hidden as u64, conv_k as u64, 2],
1704                )?;
1705                let pname = format!("{name}.kernel_projection.weight");
1706                let (pi, _pb) = st
1707                    .raw(&pname)
1708                    .ok_or_else(|| format!("DFlash2 census: missing {pname}"))?;
1709                let projected = 2usize
1710                    .checked_mul(conv_k)
1711                    .and_then(|value| value.checked_mul(groups))
1712                    .ok_or("DFlash2 convolution projection geometry overflow")?;
1713                let expected = [cfg.hidden as u64, projected as u64];
1714                validate(&pname, pi, &expected)?;
1715                Ok(Dflash2Conv {
1716                    base: e.htod(&bf16_to_f32(bb))?,
1717                    proj: upw(&pname, &expected)?,
1718                })
1719            };
1720            let mut attn_conv = Vec::with_capacity(cfg.n_layer);
1721            let mut mlp_conv = Vec::with_capacity(cfg.n_layer);
1722            for i in 0..cfg.n_layer {
1723                attn_conv.push(load_conv(&format!("layers.{i}.attention_conv"))?);
1724                mlp_conv.push(load_conv(&format!("layers.{i}.mlp_conv"))?);
1725            }
1726            // Codebooks: stored WITHOUT `.weight` (checkpoint quirk; reference
1727            // from_pretrained maps the keys). Host-resident raw bf16.
1728            let cb = |name: &str| -> Result<(Vec<u8>, usize), Box<dyn std::error::Error>> {
1729                let (ci, cbytes) = st
1730                    .raw(&format!("candidate_selector.{name}"))
1731                    .ok_or_else(|| format!("DFlash2 census: missing candidate_selector.{name}"))?;
1732                let ne = ci.ne(); // ggml: [rank, V]
1733                if ci.dtype != "BF16" || ne.len() != 2 || ne[0] as usize != rank {
1734                    return Err(format!(
1735                        "candidate_selector.{name} must be rank-2 BF16 with inner rank {rank}; found {:?} {}",
1736                        ne, ci.dtype
1737                    )
1738                    .into());
1739                }
1740                Ok((cbytes.to_vec(), ne[1] as usize))
1741            };
1742            let (pred_codebook, v1) = cb("predecessor_codebook")?;
1743            let (succ_codebook, v2) = cb("successor_codebook")?;
1744            if v1 != v2 {
1745                return Err(format!("DFlash2 codebook vocab mismatch: {v1} != {v2}").into());
1746            }
1747            validate_selector_top_k(top_k, v1)?;
1748            let hp_name = "candidate_selector.hidden_projection.weight";
1749            let (hi, _hb) = st
1750                .raw(hp_name)
1751                .ok_or_else(|| format!("DFlash2 census: missing {hp_name}"))?;
1752            let hp_expected = [cfg.hidden as u64, rank as u64];
1753            validate(hp_name, hi, &hp_expected)?;
1754            Some(Dflash2Head {
1755                attn_conv,
1756                mlp_conv,
1757                hidden_proj: upw(hp_name, &hp_expected)?,
1758                pred_codebook,
1759                succ_codebook,
1760                rank,
1761                top_k,
1762                conv_k,
1763                group_size,
1764                vocab: v1,
1765            })
1766        } else {
1767            None
1768        };
1769        // CENSUS GATE: every tensor in the export must be consumed by the map above.
1770        // DSpark-class checkpoints (markov head present) and DFlash2 checkpoints
1771        // REFUSE on unrecognized names — an unmapped tensor is a semantic program we
1772        // would silently drop (house law). Plain dflash checkpoints keep the
1773        // historical warn-only behavior.
1774        {
1775            let mut consumed: std::collections::HashSet<String> = std::collections::HashSet::new();
1776            for i in 0..cfg.n_layer {
1777                for s in [
1778                    "self_attn.q_proj.weight",
1779                    "self_attn.k_proj.weight",
1780                    "self_attn.v_proj.weight",
1781                    "self_attn.o_proj.weight",
1782                    "self_attn.q_norm.weight",
1783                    "self_attn.k_norm.weight",
1784                    "input_layernorm.weight",
1785                    "post_attention_layernorm.weight",
1786                    "mlp.gate_proj.weight",
1787                    "mlp.up_proj.weight",
1788                    "mlp.down_proj.weight",
1789                ] {
1790                    consumed.insert(format!("layers.{i}.{s}"));
1791                }
1792                if dflash2.is_some() {
1793                    for s in [
1794                        "attention_conv.base_kernel",
1795                        "attention_conv.kernel_projection.weight",
1796                        "mlp_conv.base_kernel",
1797                        "mlp_conv.kernel_projection.weight",
1798                    ] {
1799                        consumed.insert(format!("layers.{i}.{s}"));
1800                    }
1801                }
1802            }
1803            for s in [
1804                "fc.weight",
1805                "hidden_norm.weight",
1806                "norm.weight",
1807                "markov_head.markov_w1.weight",
1808                "markov_head.markov_w2.weight",
1809                "confidence_head.proj.weight",
1810                "confidence_head.proj.bias",
1811            ] {
1812                consumed.insert(s.into());
1813            }
1814            if dflash2.is_some() {
1815                for s in [
1816                    "candidate_selector.hidden_projection.weight",
1817                    "candidate_selector.predecessor_codebook",
1818                    "candidate_selector.successor_codebook",
1819                ] {
1820                    consumed.insert(s.into());
1821                }
1822            }
1823            let leftovers: Vec<&String> = st.names().filter(|n| !consumed.contains(*n)).collect();
1824            if !leftovers.is_empty() {
1825                if markov.is_some() || dflash2.is_some() {
1826                    return Err(format!(
1827                        "dspark/dflash2 census: unrecognized tensors {leftovers:?}"
1828                    )
1829                    .into());
1830                }
1831                eprintln!("[dflash census] unmapped tensors (ignored): {leftovers:?}");
1832            }
1833        }
1834        // YaRN rope from config rope_parameters (HF _compute_yarn_parameters, verified
1835        // numerically vs Qwen3RotaryEmbedding on the arm-a export).
1836        let rope_yarn =
1837            if txt.contains("\"rope_type\": \"yarn\"") || txt.contains("\"rope_type\":\"yarn\"") {
1838                let factor = num(&txt, "factor").ok_or("yarn missing factor")?;
1839                let orig = num(&txt, "original_max_position_embeddings")
1840                    .ok_or("yarn missing original_max_position_embeddings")?;
1841                let beta_fast = num(&txt, "beta_fast").ok_or("yarn missing beta_fast")?;
1842                let beta_slow = num(&txt, "beta_slow").ok_or("yarn missing beta_slow")?;
1843                if !factor.is_finite()
1844                    || factor <= 0.0
1845                    || !orig.is_finite()
1846                    || orig <= 0.0
1847                    || !beta_fast.is_finite()
1848                    || !beta_slow.is_finite()
1849                {
1850                    return Err("yarn parameters must be finite and positive".into());
1851                }
1852                let base = cfg.rope_theta as f64;
1853                let d = cfg.head_dim as f64;
1854                let corr =
1855                    |r: f64| d * (orig / (r * 2.0 * std::f64::consts::PI)).ln() / (2.0 * base.ln());
1856                let low = corr(beta_fast).floor().max(0.0);
1857                let high = corr(beta_slow).ceil().min(d - 1.0);
1858                let half = cfg.head_dim / 2;
1859                let mut ff = Vec::with_capacity(half);
1860                for j in 0..half {
1861                    let base_inv = base.powf(-2.0 * j as f64 / d);
1862                    let ramp = (((j as f64) - low) / (high - low)).clamp(0.0, 1.0);
1863                    let ex = 1.0 - ramp; // extrapolation share
1864                    let yarn_inv = (base_inv / factor) * (1.0 - ex) + base_inv * ex;
1865                    ff.push((base_inv / yarn_inv) as f32);
1866                }
1867                let mscale = (0.1 * factor.ln() + 1.0) as f32;
1868                Some((e.htod(&ff)?, mscale))
1869            } else {
1870                None
1871            };
1872        let fc_in = cfg
1873            .target_layer_ids
1874            .len()
1875            .checked_mul(cfg.hidden)
1876            .ok_or("DFlash fc geometry overflow")? as u64;
1877        let fc = upw("fc.weight", &[fc_in, hidden])?;
1878        // Ratified-default receipts (capacity-keyed-defaults law: the active program is
1879        // NAMED at load, never inferred from silence). The boot output-sample gate greps
1880        // these lines; a run whose log lacks them did not load this code.
1881        eprintln!(
1882            "[dspark] precision={prec} (MEMRA_DFLASH_PREC {})",
1883            if prec_env.is_some() { "set" } else { "unset" },
1884        );
1885        eprintln!(
1886            "[dspark] harvest={} (checkpoint census dflash2={} strategy_dspark={}, \
1887             MEMRA_DSPARK_HARVEST {})",
1888            DsparkHarvest::for_family_value(
1889                dflash2.is_some(),
1890                std::env::var("MEMRA_DSPARK_HARVEST").ok().as_deref(),
1891                cfg.strategy_dspark,
1892            )
1893            .name(),
1894            dflash2.is_some(),
1895            cfg.strategy_dspark,
1896            match std::env::var("MEMRA_DSPARK_HARVEST") {
1897                Ok(v) if !v.is_empty() => "set",
1898                _ => "unset",
1899            },
1900        );
1901        eprintln!(
1902            "[dspark] verify-window={:?} (accept-rate head {}, MEMRA_DSPARK_VT {})",
1903            DsparkVtPolicy::resolve(confidence.is_some()),
1904            if confidence.is_some() {
1905                "present"
1906            } else {
1907                "ABSENT -> ladder"
1908            },
1909            match std::env::var("MEMRA_DSPARK_VT") {
1910                Ok(v) if !v.is_empty() => "set",
1911                _ => "unset",
1912            },
1913        );
1914        Ok(Self {
1915            fc,
1916            hidden_norm: up("hidden_norm.weight", &[hidden])?,
1917            norm: up("norm.weight", &[hidden])?,
1918            cfg,
1919            layers,
1920            markov,
1921            confidence,
1922            rope_yarn,
1923            dflash2,
1924        })
1925    }
1926
1927    /// Rope q or k rows in place: yarn (ff divisors + post-rope mscale) when the config
1928    /// carries it, plain neox otherwise. One primitive for all five drafter rope sites.
1929    fn rope_rows(
1930        &self,
1931        e: &Engine,
1932        x: &mut CudaSlice<f32>,
1933        pos_d: &CudaSlice<i32>,
1934        n_heads: usize,
1935        n_tokens: usize,
1936    ) -> Result<(), Box<dyn std::error::Error>> {
1937        let c = &self.cfg;
1938        match &self.rope_yarn {
1939            Some((ff, mscale)) => {
1940                e.rope_neox_ff(
1941                    x,
1942                    pos_d,
1943                    c.head_dim,
1944                    c.head_dim,
1945                    n_heads,
1946                    n_tokens,
1947                    c.rope_theta,
1948                    1.0,
1949                    ff,
1950                )?;
1951                e.scale_inplace(x, *mscale, n_tokens * n_heads * c.head_dim)?;
1952            }
1953            None => {
1954                e.rope_neox(
1955                    x,
1956                    pos_d,
1957                    c.head_dim,
1958                    c.head_dim,
1959                    n_heads,
1960                    n_tokens,
1961                    c.rope_theta,
1962                    1.0,
1963                )?;
1964            }
1965        }
1966        Ok(())
1967    }
1968
1969    /// f32 GEMM helper via the engine Float arm (cuBLASLt): y[t, out_f].
1970    fn mm(
1971        &self,
1972        e: &Engine,
1973        w: &GpuTensor,
1974        x: &CudaSlice<f32>,
1975        t: usize,
1976        _in_f: usize,
1977        _out_f: usize,
1978    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
1979        e.matmul(w, x, t)
1980    }
1981
1982    /// DFlash2 conv `prepare` (reference GroupedDynamicCausalConv.prepare): projects
1983    /// the pre-conv rows to BOTH dynamic kernels, convolves the rows with base half 0
1984    /// + dyn half 0, and returns (convolved rows, the dyn projection) — `finish`
1985    ///   reuses the SAME projection's half 1. Block-local causal shift (row 0 zero-pads).
1986    pub fn d2_conv_prepare(
1987        &self,
1988        e: &Engine,
1989        conv: &Dflash2Conv,
1990        xn: &CudaSlice<f32>,
1991        rows: usize,
1992    ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
1993        let d2 = self
1994            .dflash2
1995            .as_ref()
1996            .expect("d2_conv on a non-dflash2 draft");
1997        let h = self.cfg.hidden;
1998        let groups = h / d2.group_size;
1999        let dyn_ = self.mm(e, &conv.proj, xn, rows, h, 2 * d2.conv_k * groups)?;
2000        let mut out = e.uninit(rows * h)?;
2001        e.dflash2_dynconv(
2002            xn,
2003            &dyn_,
2004            &conv.base,
2005            &mut out,
2006            rows,
2007            h,
2008            d2.group_size,
2009            d2.conv_k,
2010            0,
2011        )?;
2012        Ok((out, dyn_))
2013    }
2014
2015    /// DFlash2 conv `finish`: convolves the sublayer OUTPUT rows with base half 1 +
2016    /// dyn half 1 (dyn from the matching `prepare`).
2017    pub fn d2_conv_finish(
2018        &self,
2019        e: &Engine,
2020        conv: &Dflash2Conv,
2021        y: &CudaSlice<f32>,
2022        dyn_: &CudaSlice<f32>,
2023        rows: usize,
2024    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
2025        let d2 = self
2026            .dflash2
2027            .as_ref()
2028            .expect("d2_conv on a non-dflash2 draft");
2029        let h = self.cfg.hidden;
2030        let mut out = e.uninit(rows * h)?;
2031        e.dflash2_dynconv(
2032            y,
2033            dyn_,
2034            &conv.base,
2035            &mut out,
2036            rows,
2037            h,
2038            d2.group_size,
2039            d2.conv_k,
2040            1,
2041        )?;
2042        Ok(out)
2043    }
2044
2045    /// DFlash2 proposal (reference `DFlash2DraftModel.propose`, greedy arm): device
2046    /// top-k over the draft logits + the rank-`r` hidden projection, ONE small dtoh
2047    /// (~nd*(2k+rank) floats — the same per-round sync slot the markov chain's token
2048    /// readback occupies), then the host codebook walk. Returns the nd drafted tokens
2049    /// (mask-fill rows 1..b-1; the anchor row is not a draft).
2050    #[allow(clippy::too_many_arguments)] // allow: the parameter list mirrors the kernel/FFI/call contract; bundling into a struct is a refactor, not a lint fix
2051    pub fn dflash2_propose_greedy(
2052        &self,
2053        e: &Engine,
2054        dl: &CudaSlice<f32>,
2055        rows: &CudaSlice<f32>,
2056        nd: usize,
2057        n_vocab: usize,
2058        anchor: u32,
2059        d2t: Option<&[u32]>,
2060    ) -> Result<Vec<u32>, Box<dyn std::error::Error>> {
2061        Ok(self
2062            .dflash2_propose_greedy_q(e, dl, rows, nd, n_vocab, anchor, d2t)?
2063            .0)
2064    }
2065
2066    /// [`Self::dflash2_propose_greedy`] with the walk's per-slot confidence returned
2067    /// (lane/glm5-loop-port, 2026-08-30): q[p] = the chosen candidate's softmax mass over
2068    /// its slot's candidate set at T=1 — the statistic the glm5 loop's MEMRA_SPEC_PMIN
2069    /// tau-slot truncation thresholds on. Same walk, same path, same one-DtoH sync slot.
2070    #[allow(clippy::too_many_arguments)]
2071    // allow: mirrors the greedy propose contract it wraps
2072    pub fn dflash2_propose_greedy_q(
2073        &self,
2074        e: &Engine,
2075        dl: &CudaSlice<f32>,
2076        rows: &CudaSlice<f32>,
2077        nd: usize,
2078        n_vocab: usize,
2079        anchor: u32,
2080        d2t: Option<&[u32]>,
2081    ) -> Result<(Vec<u32>, Vec<f32>), Box<dyn std::error::Error>> {
2082        let d2 = self
2083            .dflash2
2084            .as_ref()
2085            .expect("dflash2_propose on a non-dflash2 draft");
2086        if n_vocab > d2.vocab || d2.top_k > n_vocab {
2087            return Err(format!(
2088                "DFlash2 proposal geometry invalid: target vocab {n_vocab}, selector vocab {}, top_k {}",
2089                d2.vocab, d2.top_k
2090            )
2091            .into());
2092        }
2093        if let Some(map) = d2t
2094            && map.len() < n_vocab
2095        {
2096            return Err(format!(
2097                "DFlash2 d2t has {} entries, fewer than proposal vocab {n_vocab}",
2098                map.len()
2099            )
2100            .into());
2101        }
2102        let (vals_d, idx_d) = e.topk_rows(dl, nd, n_vocab, d2.top_k)?;
2103        let hproj_d = e.matmul(&d2.hidden_proj, rows, nd)?;
2104        let unary = e.dtoh(&vals_d)?;
2105        let mut cand = e.dtoh_u32(&idx_d)?;
2106        // TRIMMED draft head (lane/dflash2-head-trim, 2026-08-25): `dl` was scored over the
2107        // FR-Spec-gathered rows, so candidate index i names trimmed row i — remap to the true
2108        // token id BEFORE the selector walk (the codebooks and the verify block index the full
2109        // vocabulary). Same permute-the-proposal law as the MTP arm's spec.rs d2t map; verify
2110        // stays full-vocab, so the trim moves acceptance only, never output.
2111        if let Some(map) = d2t {
2112            for c in cand.iter_mut() {
2113                *c = map[*c as usize];
2114            }
2115        }
2116        let hproj = e.dtoh(&hproj_d)?;
2117        Ok(d2.walk_greedy_q(&unary, &cand, &hproj, anchor, nd))
2118    }
2119
2120    /// DFlash2 proposal, SAMPLED arm (reference `DFlash2DraftModel.propose` at T>0): same
2121    /// device top-k + hidden projection + one dtoh as the greedy arm, then the host
2122    /// candidate-set softmax walk (`dflash2_walk_sampled`) drawing one host-Philox uniform
2123    /// per slot from the session's `uctr` stream. Returns (path, q_chosen, cand, q_rows).
2124    #[allow(clippy::too_many_arguments)]
2125    pub(crate) fn dflash2_propose_sampled(
2126        &self,
2127        e: &Engine,
2128        dl: &CudaSlice<f32>,
2129        rows: &CudaSlice<f32>,
2130        nd: usize,
2131        n_vocab: usize,
2132        anchor: u32,
2133        temp: f32,
2134        seed: u64,
2135        uctr: &mut u32,
2136        d2t: Option<&[u32]>,
2137    ) -> Result<Dflash2SampledProposal, Box<dyn std::error::Error>> {
2138        let d2 = self
2139            .dflash2
2140            .as_ref()
2141            .expect("dflash2_propose on a non-dflash2 draft");
2142        if n_vocab > d2.vocab || d2.top_k > n_vocab {
2143            return Err(format!(
2144                "DFlash2 proposal geometry invalid: target vocab {n_vocab}, selector vocab {}, top_k {}",
2145                d2.vocab, d2.top_k
2146            )
2147            .into());
2148        }
2149        if let Some(map) = d2t
2150            && map.len() < n_vocab
2151        {
2152            return Err(format!(
2153                "DFlash2 d2t has {} entries, fewer than proposal vocab {n_vocab}",
2154                map.len()
2155            )
2156            .into());
2157        }
2158        let (vals_d, idx_d) = e.topk_rows(dl, nd, n_vocab, d2.top_k)?;
2159        let hproj_d = e.matmul(&d2.hidden_proj, rows, nd)?;
2160        let unary = e.dtoh(&vals_d)?;
2161        let mut cand = e.dtoh_u32(&idx_d)?;
2162        // Trimmed-head remap — see the greedy arm. The q the walk reports is the softmax
2163        // over the candidate SET it actually proposed (ids are labels, not indices into a
2164        // distribution), so the rejection-verify contract is unchanged by the remap.
2165        if let Some(map) = d2t {
2166            for c in cand.iter_mut() {
2167                *c = map[*c as usize];
2168            }
2169        }
2170        let hproj = e.dtoh(&hproj_d)?;
2171        let mut draw = || {
2172            let u = crate::spec::host_u01(seed, *uctr);
2173            *uctr = uctr.wrapping_add(1);
2174            u
2175        };
2176        let (path, q_chosen, q_rows) =
2177            d2.walk_sampled(&unary, &cand, &hproj, anchor, nd, temp, &mut draw);
2178        Ok((path, q_chosen, cand, q_rows))
2179    }
2180
2181    /// Sampled draft chain for the Rows families (T>0 twin of the greedy markov chain):
2182    /// slot k gets the markov bias of the PREVIOUS chain token added in place (when the
2183    /// head is armed — the sglang DSPARK worker's markov-corrected draft probs), then ONE
2184    /// draw from the row's FILTERED softmax (filter_stats -> device-stat gumbel perturb ->
2185    /// argmax into the chain buffer — the frspec eager-chain composition, stats kept on
2186    /// device so the chain stays sync-free like the greedy arm). Without a markov head the
2187    /// rows sample independently (the z-lab reference's T>0 arm for plain DFlash). `dl` is
2188    /// biased IN PLACE and retained by the caller: it is the accept walk's q source.
2189    #[allow(clippy::too_many_arguments)]
2190    pub(crate) fn dspark_chain_sampled(
2191        &self,
2192        e: &Engine,
2193        dl: &mut CudaSlice<f32>,
2194        nd: usize,
2195        n_vocab: usize,
2196        anchor: u32,
2197        sp: &crate::spec::SpecSampling,
2198        sctr: &mut u32,
2199        // H4 confidence-policy stash (v0.100 train merge): Some = copy each slot's
2200        // markov prev-token embedding (the exact `w1` row the chain gathers) into a
2201        // [nd, rank] buffer — the same d2d stash the greedy chain carries, so the
2202        // confidence window sizes identically at T>0.
2203        mut conf_emb: Option<&mut CudaSlice<f32>>,
2204    ) -> Result<(Vec<u32>, DsparkDraftSample), Box<dyn std::error::Error>> {
2205        let markov_on = std::env::var("MEMRA_DFLASH_MARKOV").as_deref() != Ok("0");
2206        let mut chain_d = e.stream().alloc_zeros::<u32>(nd + 1)?;
2207        e.set_u32_one(&mut chain_d, anchor)?;
2208        let mut th_all = e.zeros(nd)?;
2209        let mut z_all = e.zeros(nd)?;
2210        let mut mx_all = e.zeros(nd)?;
2211        let mut pb = e.zeros(n_vocab)?;
2212        for k in 0..nd {
2213            if let (Some(mk), true) = (&self.markov, markov_on) {
2214                let mut f = e.uninit(mk.rank)?;
2215                e.gather_row_bf16(&mk.w1_bf16, &chain_d, k, &mut f, mk.rank)?;
2216                if let Some(ce) = conf_emb.as_deref_mut() {
2217                    let fv = e.view(&f, mk.rank);
2218                    e.copy_view_into(ce, k * mk.rank, &fv, mk.rank)?;
2219                }
2220                let bias = e.matmul(&mk.w2, &f, 1)?;
2221                e.add_row_inplace(dl, &bias, n_vocab, k * n_vocab)?;
2222            } else if let (Some(ce), Some(mk)) = (conf_emb.as_deref_mut(), &self.markov) {
2223                // MARKOV=0 arm still stashes the embedding for the confidence head —
2224                // the greedy chain's exact behavior.
2225                let mut f = e.uninit(mk.rank)?;
2226                e.gather_row_bf16(&mk.w1_bf16, &chain_d, k, &mut f, mk.rank)?;
2227                let fv = e.view(&f, mk.rank);
2228                e.copy_view_into(ce, k * mk.rank, &fv, mk.rank)?;
2229            }
2230            let rows_k = e.htod_i32(&[k as i32])?;
2231            let (mut th1, mut z1, mut mx1) = (e.zeros(1)?, e.zeros(1)?, e.zeros(1)?);
2232            e.filter_stats(
2233                dl, n_vocab, &rows_k, &mut th1, &mut z1, &mut mx1, n_vocab, 1, sp.temp, sp.top_k,
2234                sp.top_p, sp.min_p,
2235            )?;
2236            e.gumbel_perturb_filtered_col(
2237                dl, k, &mut pb, n_vocab, sp.seed, *sctr, sp.temp, &mx1, &th1, 0,
2238            )?;
2239            *sctr = sctr.wrapping_add(1);
2240            e.argmax_token_device_col(&pb, 0, n_vocab, &mut chain_d, k + 1)?;
2241            e.copy_into(&mut th_all, k, &th1, 1)?;
2242            e.copy_into(&mut z_all, k, &z1, 1)?;
2243            e.copy_into(&mut mx_all, k, &mx1, 1)?;
2244        }
2245        let chain = e.dtoh_u32(&chain_d)?;
2246        let (thv, zv, mxv) = (e.dtoh(&th_all)?, e.dtoh(&z_all)?, e.dtoh(&mx_all)?);
2247        let stats = (0..nd).map(|i| (mxv[i], thv[i], zv[i])).collect();
2248        Ok((
2249            chain[1..].to_vec(),
2250            DsparkDraftSample::Rows {
2251                th: th_all,
2252                z: z_all,
2253                stats,
2254            },
2255        ))
2256    }
2257
2258    /// Family dispatch for the sampled proposal: Selector for DFlash2, Rows otherwise.
2259    /// Returns the drafted tokens (the round's `cand` tail) + the proposal record.
2260    #[allow(clippy::too_many_arguments)]
2261    pub(crate) fn dspark_propose_sampled(
2262        &self,
2263        e: &Engine,
2264        dl: &mut CudaSlice<f32>,
2265        rows: &CudaSlice<f32>,
2266        nd: usize,
2267        n_vocab: usize,
2268        anchor: u32,
2269        sp: &crate::spec::SpecSampling,
2270        sctr: &mut u32,
2271        uctr: &mut u32,
2272        conf_emb: Option<&mut CudaSlice<f32>>,
2273        d2t: Option<&[u32]>,
2274    ) -> Result<(Vec<u32>, DsparkDraftSample), Box<dyn std::error::Error>> {
2275        if let Some(d2) = self.dflash2.as_ref() {
2276            // The confidence stash is a markov-family program; DFlash2 has no
2277            // accept-rate head (the policy resolver never arms it for this family).
2278            debug_assert!(
2279                conf_emb.is_none(),
2280                "conf_emb stash requested on a DFlash2 selector proposal"
2281            );
2282            let (path, q_chosen, cand, q_rows) = self.dflash2_propose_sampled(
2283                e, dl, rows, nd, n_vocab, anchor, sp.temp, sp.seed, uctr, d2t,
2284            )?;
2285            Ok((
2286                path,
2287                DsparkDraftSample::Selector {
2288                    cand,
2289                    q_rows,
2290                    q_chosen,
2291                    top_k: d2.top_k,
2292                },
2293            ))
2294        } else {
2295            self.dspark_chain_sampled(e, dl, nd, n_vocab, anchor, sp, sctr, conf_emb)
2296        }
2297    }
2298
2299    /// FIRST-LIGHT forward (oracle contract): full non-causal attention over
2300    /// [ctx_features ; block], NO draft KV cache, NO sliding window (the oracle bypasses
2301    /// the reference mask machinery the same way — window/caching land in the round arm).
2302    ///
2303    /// `target_hidden`: [ctx, n_taps*hidden] (f32, device)  — raw tapped states.
2304    /// `noise_emb`:     [block, hidden] — target embed rows for [accepted, MASK x b-1].
2305    /// `pos`:           absolute positions for ctx rows THEN block rows (ctx+block i32).
2306    /// Returns final normed hidden [block, hidden] (feed target lm_head for draft logits).
2307    /// ctx features for `t` tapped rows: hidden_norm(fc(taps)) — the drafter's context
2308    /// representation, cacheable across rounds (append-only in committed-token order).
2309    pub fn ctx_features(
2310        &self,
2311        e: &Engine,
2312        taps: &CudaSlice<f32>,
2313        t: usize,
2314    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
2315        let c = &self.cfg;
2316        let n_taps = c.target_layer_ids.len();
2317        let fc_out = self.mm(e, &self.fc, taps, t, n_taps * c.hidden, c.hidden)?;
2318        let mut out = e.uninit(t * c.hidden)?;
2319        e.rms_norm(&fc_out, &self.hidden_norm, &mut out, c.hidden, t, c.eps)?;
2320        Ok(out)
2321    }
2322
2323    pub fn forward(
2324        &self,
2325        e: &Engine,
2326        target_hidden: &CudaSlice<f32>,
2327        noise_emb: &CudaSlice<f32>,
2328        pos: &[i32],
2329        ctx: usize,
2330    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
2331        let ctx_f = self.ctx_features(e, target_hidden, ctx)?;
2332        if let Ok(dir) = std::env::var("MEMRA_DFLASH_DUMP") {
2333            let v = e.dtoh(&ctx_f)?;
2334            let bytes: Vec<u8> = v.iter().flat_map(|f| f.to_le_bytes()).collect();
2335            std::fs::write(format!("{dir}/memra-ctx_features.f32"), bytes)?;
2336        }
2337        self.forward_block(e, &ctx_f, noise_emb, pos, ctx)
2338    }
2339
2340    /// Block forward over PRECOMPUTED ctx features (the round arm's entry: features are
2341    /// cached across rounds; only the block work repeats).
2342    pub fn forward_block(
2343        &self,
2344        e: &Engine,
2345        ctx_f: &CudaSlice<f32>,
2346        noise_emb: &CudaSlice<f32>,
2347        pos: &[i32],
2348        ctx: usize,
2349    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
2350        let c = &self.cfg;
2351        let (h, nh, nkv, hd) = (c.hidden, c.n_head, c.n_kv, c.head_dim);
2352        let b = c.block_size;
2353        assert_eq!(pos.len(), ctx + b, "pos covers ctx rows then block rows");
2354
2355        let pos_blk = e.htod_i32(&pos[ctx..])?;
2356
2357        let mut x = e.clone_dtod(noise_emb)?; // [b, hidden] residual stream
2358        for (li, l) in self.layers.iter().enumerate() {
2359            // input_layernorm on the block rows only (ctx features are norm-free per ref:
2360            // k/v project the SAME ctx_f every layer, un-layernormed).
2361            let mut xn = e.uninit(b * h)?;
2362            e.rms_norm(&x, &l.ln_in, &mut xn, h, b, c.eps)?;
2363            // DFlash2: dynamic conv WRAPS attention — q/k_noise/v_noise all project the
2364            // CONVOLVED block rows (reference decoder layer: prepare -> self_attn ->
2365            // finish, all inside the residual branch). ctx_f is never convolved.
2366            let mut attn_dyn: Option<CudaSlice<f32>> = None;
2367            if let Some(d2) = &self.dflash2 {
2368                let (xc, dyn_) = self.d2_conv_prepare(e, &d2.attn_conv[li], &xn, b)?;
2369                xn = xc;
2370                attn_dyn = Some(dyn_);
2371            }
2372
2373            // q from block; k/v from [ctx_f ; block-normed]
2374            let q0 = self.mm(e, &l.wq, &xn, b, h, nh * hd)?;
2375            let k0c = self.mm(e, &l.wk, ctx_f, ctx, h, nkv * hd)?;
2376            let v0c = self.mm(e, &l.wv, ctx_f, ctx, h, nkv * hd)?;
2377            let k0b = self.mm(e, &l.wk, &xn, b, h, nkv * hd)?;
2378            let v0b = self.mm(e, &l.wv, &xn, b, h, nkv * hd)?;
2379
2380            // per-head q/k rms norm (v passes through: ones weight trick not needed — the
2381            // qkv kernel norms rq+rk rows; concatenate k first).
2382            let mut k0 = e.uninit((ctx + b) * nkv * hd)?;
2383            e.copy_into(&mut k0, 0, &k0c, ctx * nkv * hd)?;
2384            e.copy_into(&mut k0, ctx * nkv * hd, &k0b, b * nkv * hd)?;
2385            let mut v = e.uninit((ctx + b) * nkv * hd)?;
2386            e.copy_into(&mut v, 0, &v0c, ctx * nkv * hd)?;
2387            e.copy_into(&mut v, ctx * nkv * hd, &v0b, b * nkv * hd)?;
2388
2389            if li == 0
2390                && let Ok(dir) = std::env::var("MEMRA_DFLASH_DUMP")
2391            {
2392                let v = e.dtoh(&q0)?;
2393                let bytes: Vec<u8> = v.iter().flat_map(|f| f.to_le_bytes()).collect();
2394                std::fs::write(format!("{dir}/memra-l0_q0.f32"), bytes)?;
2395            }
2396            let mut q = e.uninit(b * nh * hd)?;
2397            let mut k = e.uninit((ctx + b) * nkv * hd)?;
2398            // rms over head_dim rows: q has b*nh rows, k has (ctx+b)*nkv rows.
2399            e.rms_norm(&q0, &l.q_norm, &mut q, hd, b * nh, c.eps)?;
2400            if li == 0
2401                && let Ok(dir) = std::env::var("MEMRA_DFLASH_DUMP")
2402            {
2403                let v = e.dtoh(&q)?;
2404                let bytes: Vec<u8> = v.iter().flat_map(|f| f.to_le_bytes()).collect();
2405                std::fs::write(format!("{dir}/memra-l0_qn.f32"), bytes)?;
2406            }
2407            e.rms_norm(&k0, &l.k_norm, &mut k, hd, (ctx + b) * nkv, c.eps)?;
2408
2409            // rope: q at block positions, k at ctx-then-block positions (absolute).
2410            let norope = std::env::var("MEMRA_DFLASH_NOROPE").is_ok();
2411            if !norope {
2412                self.rope_rows(e, &mut q, &pos_blk, nh, b)?;
2413            }
2414            if li == 0
2415                && let Ok(dir) = std::env::var("MEMRA_DFLASH_DUMP")
2416            {
2417                let dump = |name: &str,
2418                            t: &cudarc::driver::CudaSlice<f32>|
2419                 -> Result<(), Box<dyn std::error::Error>> {
2420                    let v = e.dtoh(t)?;
2421                    let bytes: Vec<u8> = v.iter().flat_map(|f| f.to_le_bytes()).collect();
2422                    std::fs::write(format!("{dir}/memra-l0_{name}.f32"), bytes)?;
2423                    Ok(())
2424                };
2425                dump("xn", &xn)?;
2426                dump("q_prerope", &q)?;
2427            }
2428            // k rows are laid out [row, nkv, hd] with row-major tokens — rope_neox expects
2429            // (n_heads, n_tokens); ctx and block ropes run as one call over ctx+b tokens.
2430            let pos_all = e.htod_i32(pos)?;
2431            if !norope {
2432                self.rope_rows(e, &mut k, &pos_all, nkv, ctx + b)?;
2433            }
2434
2435            // full non-causal attention: every block query sees all ctx+b keys.
2436            let mut attn = e.uninit(b * nh * hd)?;
2437            let scale = 1.0f32 / (hd as f32).sqrt();
2438            // NAIVE SDPA for first light: fa_prefill's NON-CAUSAL arm with T != T_kv is
2439            // BROKEN (attn maxdiff 0.34 vs the torch oracle; q/k inputs bit-close — no
2440            // existing caller exercises that shape class, jsonl 2026-07-13). The 16 x
2441            // (ctx+16) block attention is tiny; the fa arm returns behind this seam once
2442            // its kernel is fixed + parity-gated.
2443            if self.dflash2.is_some() && c.layer_sliding[li] {
2444                // DFlash2 non-causal symmetric window (config is_causal=false, all
2445                // layers sliding). The kernel masks only keys OLDER than
2446                // q_pos-(window-1); the future side (k - q < window) never binds
2447                // because keys reach at most q_pos + block <= q_pos + window
2448                // (asserted at load). Positions must be contiguous — q_pos is derived
2449                // in-kernel as (T_kv - T) + qt.
2450                debug_assert!(pos.windows(2).all(|w| w[1] == w[0] + 1));
2451                d2_windowed_attn(e, &q, &k, &v, &mut attn, hd, nh, nkv, b, ctx + b, scale, c)?;
2452            } else if std::env::var("MEMRA_DFLASH_FA").is_ok() {
2453                e.fa_prefill(&q, &k, &v, &mut attn, hd, nh, nkv, b, ctx + b, scale, false)?;
2454            } else {
2455                e.sdpa_naive(&q, &k, &v, &mut attn, hd, nh, nkv, b, ctx + b, scale, false)?;
2456            }
2457
2458            let mut o = self.mm(e, &l.wo, &attn, b, nh * hd, h)?;
2459            if let (Some(d2), Some(dyn_)) = (&self.dflash2, &attn_dyn) {
2460                o = self.d2_conv_finish(e, &d2.attn_conv[li], &o, dyn_, b)?;
2461            }
2462            let mut x1 = e.uninit(b * h)?;
2463            e.add(&o, &x, &mut x1, b * h)?;
2464            if li == 0
2465                && let Ok(dir) = std::env::var("MEMRA_DFLASH_DUMP")
2466            {
2467                let dump = |name: &str,
2468                            t: &cudarc::driver::CudaSlice<f32>|
2469                 -> Result<(), Box<dyn std::error::Error>> {
2470                    let v = e.dtoh(t)?;
2471                    let bytes: Vec<u8> = v.iter().flat_map(|f| f.to_le_bytes()).collect();
2472                    std::fs::write(format!("{dir}/memra-l0_{name}.f32"), bytes)?;
2473                    Ok(())
2474                };
2475                dump("q", &q)?;
2476                dump("k", &k)?;
2477                dump("attn", &attn)?;
2478                dump("x1", &x1)?;
2479            }
2480
2481            // mlp (DFlash2: the same conv wrap — prepare on the post-ln rows, mlp on
2482            // the convolved rows, finish on the mlp output, then the residual add)
2483            let mut x1n = e.uninit(b * h)?;
2484            e.rms_norm(&x1, &l.ln_post, &mut x1n, h, b, c.eps)?;
2485            let mut mlp_dyn: Option<CudaSlice<f32>> = None;
2486            if let Some(d2) = &self.dflash2 {
2487                let (xc, dyn_) = self.d2_conv_prepare(e, &d2.mlp_conv[li], &x1n, b)?;
2488                x1n = xc;
2489                mlp_dyn = Some(dyn_);
2490            }
2491            let gate = self.mm(e, &l.w_gate, &x1n, b, h, c.n_ff)?;
2492            let up_ = self.mm(e, &l.w_up, &x1n, b, h, c.n_ff)?;
2493            let mut act = e.uninit(b * c.n_ff)?;
2494            e.silu_mul(&gate, &up_, &mut act, b * c.n_ff)?;
2495            let mut down = self.mm(e, &l.w_down, &act, b, c.n_ff, h)?;
2496            if let (Some(d2), Some(dyn_)) = (&self.dflash2, &mlp_dyn) {
2497                down = self.d2_conv_finish(e, &d2.mlp_conv[li], &down, dyn_, b)?;
2498            }
2499            let mut x2 = e.uninit(b * h)?;
2500            e.add(&down, &x1, &mut x2, b * h)?;
2501            x = x2;
2502            if let Ok(dir) = std::env::var("MEMRA_DFLASH_DUMP") {
2503                let v = e.dtoh(&x)?;
2504                let bytes: Vec<u8> = v.iter().flat_map(|f| f.to_le_bytes()).collect();
2505                std::fs::write(format!("{dir}/memra-layer{li}_out.f32"), bytes)?;
2506            }
2507        }
2508        let mut out = e.uninit(b * h)?;
2509        e.rms_norm(&x, &self.norm, &mut out, h, b, c.eps)?;
2510        Ok(out)
2511    }
2512}
2513
2514/// Draft KV cache (round-cost fix, 2026-07-13): per-layer normed+roped ctx K and raw ctx V,
2515/// append-only in committed order. Block K/V land TRANSIENTLY at [len..len+b] each round
2516/// (never committed — the reference crops them identically). Kills the per-round full-ctx
2517/// projection recompute (first light was O(ctx)/round -> 7 tok/s).
2518pub struct DflashKv {
2519    pub k: Vec<CudaSlice<f32>>, // per layer [cap + block, nkv*hd]
2520    pub v: Vec<CudaSlice<f32>>,
2521    pub len: usize,
2522    pub cap: usize,
2523    /// Trailing rows the drafter can still observe: `sliding_window + block_size`. Carried on
2524    /// the KV (not recomputed at call sites) so an export and an import cannot disagree about
2525    /// the geometry — see `DsparkSpecSession::draft_tail_rows`.
2526    window_rows: usize,
2527    /// `n_kv * head_dim * size_of::<f32>()` — the row unit for tail copies.
2528    row_bytes: usize,
2529}
2530
2531impl DflashKv {
2532    pub fn new(
2533        e: &Engine,
2534        cfg: &DflashCfg,
2535        cap: usize,
2536    ) -> Result<Self, Box<dyn std::error::Error>> {
2537        let rowsz = cfg.n_kv * cfg.head_dim;
2538        let mut k = Vec::with_capacity(cfg.n_layer);
2539        let mut v = Vec::with_capacity(cfg.n_layer);
2540        for _ in 0..cfg.n_layer {
2541            k.push(e.uninit((cap + cfg.block_size) * rowsz)?);
2542            v.push(e.uninit((cap + cfg.block_size) * rowsz)?);
2543        }
2544        Ok(Self {
2545            k,
2546            v,
2547            len: 0,
2548            cap,
2549            window_rows: cfg.sliding_window.saturating_add(cfg.block_size),
2550            row_bytes: rowsz * std::mem::size_of::<f32>(),
2551        })
2552    }
2553}
2554
2555impl DflashDraft {
2556    /// Ingest `t` NEW ctx-feature rows (committed order, absolute positions `pos_new`) into
2557    /// the draft KV: per layer k/v projections + k head-norm + rope, appended at kv.len.
2558    pub fn ingest_ctx(
2559        &self,
2560        e: &Engine,
2561        kv: &mut DflashKv,
2562        feats: &CudaSlice<f32>,
2563        pos_new: &[i32],
2564        t: usize,
2565    ) -> Result<(), Box<dyn std::error::Error>> {
2566        let c = &self.cfg;
2567        let (h, nkv, hd) = (c.hidden, c.n_kv, c.head_dim);
2568        assert!(kv.len + t <= kv.cap, "draft kv overflow");
2569        let pos_d = e.htod_i32(pos_new)?;
2570        for (li, l) in self.layers.iter().enumerate() {
2571            let k0 = self.mm(e, &l.wk, feats, t, h, nkv * hd)?;
2572            let v0 = self.mm(e, &l.wv, feats, t, h, nkv * hd)?;
2573            let mut kn = e.uninit(t * nkv * hd)?;
2574            e.rms_norm(&k0, &l.k_norm, &mut kn, hd, t * nkv, c.eps)?;
2575            self.rope_rows(e, &mut kn, &pos_d, nkv, t)?;
2576            e.copy_into(&mut kv.k[li], kv.len * nkv * hd, &kn, t * nkv * hd)?;
2577            e.copy_into(&mut kv.v[li], kv.len * nkv * hd, &v0, t * nkv * hd)?;
2578        }
2579        kv.len += t;
2580        Ok(())
2581    }
2582
2583    /// Block forward over the CACHED ctx KV: only the 16 block rows are projected per layer;
2584    /// block K/V land transiently at kv[len..len+b]. Bit-class-identical to forward_block
2585    /// (same kernels, same per-row programs; ONLY the ctx K/V recompute is cached).
2586    pub fn forward_round(
2587        &self,
2588        e: &Engine,
2589        kv: &mut DflashKv,
2590        noise_emb: &CudaSlice<f32>,
2591        pos_block: &[i32],
2592    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
2593        let c = &self.cfg;
2594        let (h, nh, nkv, hd) = (c.hidden, c.n_head, c.n_kv, c.head_dim);
2595        let b = c.block_size;
2596        assert_eq!(pos_block.len(), b);
2597        let ctx = kv.len;
2598        let pos_blk = e.htod_i32(pos_block)?;
2599        let mut x = e.clone_dtod(noise_emb)?;
2600        for (li, l) in self.layers.iter().enumerate() {
2601            let mut xn = e.uninit(b * h)?;
2602            e.rms_norm(&x, &l.ln_in, &mut xn, h, b, c.eps)?;
2603            // DFlash2: dynamic conv wraps attention (see forward_block).
2604            let mut attn_dyn: Option<CudaSlice<f32>> = None;
2605            if let Some(d2) = &self.dflash2 {
2606                let (xc, dyn_) = self.d2_conv_prepare(e, &d2.attn_conv[li], &xn, b)?;
2607                xn = xc;
2608                attn_dyn = Some(dyn_);
2609            }
2610            let q0 = self.mm(e, &l.wq, &xn, b, h, nh * hd)?;
2611            let k0b = self.mm(e, &l.wk, &xn, b, h, nkv * hd)?;
2612            let v0b = self.mm(e, &l.wv, &xn, b, h, nkv * hd)?;
2613            let mut q = e.uninit(b * nh * hd)?;
2614            let mut kb = e.uninit(b * nkv * hd)?;
2615            e.rms_norm(&q0, &l.q_norm, &mut q, hd, b * nh, c.eps)?;
2616            e.rms_norm(&k0b, &l.k_norm, &mut kb, hd, b * nkv, c.eps)?;
2617            self.rope_rows(e, &mut q, &pos_blk, nh, b)?;
2618            self.rope_rows(e, &mut kb, &pos_blk, nkv, b)?;
2619            e.copy_into(&mut kv.k[li], ctx * nkv * hd, &kb, b * nkv * hd)?;
2620            e.copy_into(&mut kv.v[li], ctx * nkv * hd, &v0b, b * nkv * hd)?;
2621            let mut attn = e.uninit(b * nh * hd)?;
2622            let scale = 1.0f32 / (hd as f32).sqrt();
2623            if self.dflash2.is_some() && c.layer_sliding[li] {
2624                // Non-causal symmetric window (config is_causal=false): kv row index
2625                // == absolute position for BOTH ctx rows (committed order) and the
2626                // transient block rows, so the kernel's q_pos = (T_kv - T) + qt is the
2627                // absolute position and the old-side mask is exact. The future side
2628                // never binds (block <= window, asserted at load).
2629                d2_windowed_attn(
2630                    e,
2631                    &q,
2632                    &kv.k[li],
2633                    &kv.v[li],
2634                    &mut attn,
2635                    hd,
2636                    nh,
2637                    nkv,
2638                    b,
2639                    ctx + b,
2640                    scale,
2641                    c,
2642                )?;
2643            } else if std::env::var("MEMRA_DFLASH_FA").is_ok() {
2644                e.fa_prefill(
2645                    &q,
2646                    &kv.k[li],
2647                    &kv.v[li],
2648                    &mut attn,
2649                    hd,
2650                    nh,
2651                    nkv,
2652                    b,
2653                    ctx + b,
2654                    scale,
2655                    false,
2656                )?;
2657            } else {
2658                e.sdpa_naive(
2659                    &q,
2660                    &kv.k[li],
2661                    &kv.v[li],
2662                    &mut attn,
2663                    hd,
2664                    nh,
2665                    nkv,
2666                    b,
2667                    ctx + b,
2668                    scale,
2669                    false,
2670                )?;
2671            }
2672            let mut o = self.mm(e, &l.wo, &attn, b, nh * hd, h)?;
2673            if let (Some(d2), Some(dyn_)) = (&self.dflash2, &attn_dyn) {
2674                o = self.d2_conv_finish(e, &d2.attn_conv[li], &o, dyn_, b)?;
2675            }
2676            let mut x1 = e.uninit(b * h)?;
2677            e.add(&o, &x, &mut x1, b * h)?;
2678            let mut x1n = e.uninit(b * h)?;
2679            e.rms_norm(&x1, &l.ln_post, &mut x1n, h, b, c.eps)?;
2680            let mut mlp_dyn: Option<CudaSlice<f32>> = None;
2681            if let Some(d2) = &self.dflash2 {
2682                let (xc, dyn_) = self.d2_conv_prepare(e, &d2.mlp_conv[li], &x1n, b)?;
2683                x1n = xc;
2684                mlp_dyn = Some(dyn_);
2685            }
2686            let gate = self.mm(e, &l.w_gate, &x1n, b, h, c.n_ff)?;
2687            let up_ = self.mm(e, &l.w_up, &x1n, b, h, c.n_ff)?;
2688            let mut act = e.uninit(b * c.n_ff)?;
2689            e.silu_mul(&gate, &up_, &mut act, b * c.n_ff)?;
2690            let mut down = self.mm(e, &l.w_down, &act, b, c.n_ff, h)?;
2691            if let (Some(d2), Some(dyn_)) = (&self.dflash2, &mlp_dyn) {
2692                down = self.d2_conv_finish(e, &d2.mlp_conv[li], &down, dyn_, b)?;
2693            }
2694            let mut x2 = e.uninit(b * h)?;
2695            e.add(&down, &x1, &mut x2, b * h)?;
2696            x = x2;
2697        }
2698        let mut out = e.uninit(b * h)?;
2699        e.rms_norm(&x, &self.norm, &mut out, h, b, c.eps)?;
2700        Ok(out)
2701    }
2702}
2703
2704/// Emit an accepted draft run under the `max_new` budget: check BEFORE each push — at
2705/// real acceptance the final round often accepts a draft at the boundary, and
2706/// push-then-check emitted max_new+1 tokens (plain emits exactly max_new; the E2E gate
2707/// read it as a length divergence at index max_new with the shared prefix
2708/// byte-identical). f8300340cd fixed generate_spec_dspark this way; generate_spec_dflash
2709/// kept the buggy shape until the hermes sweep (fixed 2026-08-23) — both now share this
2710/// one helper. Returns true when the caller must break (budget reached or EOS emitted).
2711fn emit_accepted_run(out: &mut Vec<u32>, accepted: &[u32], eos: &[u32], max_new: usize) -> bool {
2712    for &dt in accepted {
2713        if out.len() >= max_new {
2714            return true;
2715        }
2716        out.push(dt);
2717        if eos.contains(&dt) {
2718            return true;
2719        }
2720    }
2721    false
2722}
2723
2724// ===== THE DRAFT-SOURCE SEAM (lane/glm5-extract2, phase 2 of the extraction program) ======
2725//
2726// `DraftSourcePlan` (memra-gguf `model_plan.rs`) has always been general: it is the PLAN's
2727// statement of where a family's drafts come from. What was glm5-named was everything on the
2728// ENGINE side of it — the loaded-drafter holder, the flag-to-drafter load contract, and the
2729// tap-layer resolution. All three are family-agnostic by content, so they live here, in the
2730// general DFlash module, and glm5 is a CONSUMER.
2731//
2732// WHAT IS DELIBERATELY *NOT* HERE, and why (phase-1 discipline, restated):
2733// the PER-SESSION draft state (`glm_spec::Glm5DraftState`) and the source-keyed round /
2734// maintenance walks. Those are not family-agnostic today: each arm reaches into the family's
2735// own cache planes (glm5's MLA latent plane, `HcTapSink` hc-contract taps, the KDA rollback
2736// stash) and the retained-q type carries the family's rank space. A trait over them would have
2737// exactly ONE implementor whose associated types are all glm5 types — a decorative trait cut
2738// blind, on the hottest file in the lane program. The trigger for that cut is the SECOND
2739// hybrid spec family's session state, which is what tells us which half of the state is
2740// shared. The trait sketch is banked in the lane doc so the second consumer starts from it,
2741// not from scratch.
2742
2743/// A loaded alternate draft source: the drafter weights plus the byte identity they were
2744/// pinned by. Model-level (loaded ONCE per model, on the head engine where the trunk lm_head
2745/// it projects through lives — the MTP-head placement law); per-session state is the family's.
2746///
2747/// Generalized from `glm_spec::Glm5DflashDrafter`, which stays re-exported under its old name
2748/// for the glm5 call sites and gates.
2749pub struct DflashDrafter {
2750    pub draft: DflashDraft,
2751    /// First 8 hex of sha256(model.safetensors) — the boot-receipt identity pin
2752    /// (`b33c0347` for the probe-pinned incoai/GLM-5.3-Flash-DFlash2 @ dc77ff1c bytes).
2753    pub sha8: String,
2754}
2755
2756/// Resolve a drafter's tap layers against the trunk it will read features from.
2757///
2758/// PURE (no env, no engine): the drafter's own `target_layer_ids`, plus a caller-supplied
2759/// `shift` and the trunk bound. `shift` exists because the tap-shift RED ARM is a GATE
2760/// INSTRUMENT owned by the family that runs the gate (`MEMRA_GLM5_*_GATE_RED` is classified
2761/// as an instrument, never a serving flag, and never generalized) — the family reads its own
2762/// red-arm env, prints its own tag, and passes the shift in here.
2763pub fn resolve_tap_layers(
2764    target_layer_ids: &[usize],
2765    n_trunk: usize,
2766    shift: usize,
2767    what: &str,
2768) -> Result<Vec<usize>, String> {
2769    if target_layer_ids.is_empty() {
2770        return Err(format!("{what} drafter config carries no target_layer_ids"));
2771    }
2772    let taps: Vec<usize> = target_layer_ids.iter().map(|t| t + shift).collect();
2773    if let Some(&bad) = taps.iter().find(|&&t| t >= n_trunk) {
2774        return Err(format!(
2775            "{what} tap layer {bad} is outside the {n_trunk}-layer trunk"
2776        ));
2777    }
2778    Ok(taps)
2779}
2780
2781/// Load a DFlash2 drafter named by `flag` from `dir`, validating every contract that binds a
2782/// drafter to a TARGET — family-agnostic, because each one is a property of the pair, not of
2783/// the family:
2784///
2785/// * the checkpoint is a `DFlash2DraftModel` (the selector family is the only draft source
2786///   this seam serves);
2787/// * `cfg.hidden == n_embd` (the drafter consumes the target's features and projects through
2788///   the target's embed/lm_head);
2789/// * `cfg.target_layer_ids` name valid trunk layers;
2790/// * `cfg.mask_token_id` is inside the target vocab.
2791///
2792/// A set flag that cannot load is a LOUD failure, never a silent plain fallback. Every error
2793/// is prefixed `{flag}={dir}` so the operator sees the flag they typed; the glm5 call site's
2794/// message bytes are unchanged by construction.
2795pub fn load_drafter(
2796    e: &Engine,
2797    dir: &std::path::Path,
2798    flag: &str,
2799    n_trunk: usize,
2800    n_embd: usize,
2801    n_vocab: usize,
2802) -> Result<DflashDrafter, String> {
2803    let dpath = dir.display();
2804    let draft = DflashDraft::load(e, dir)
2805        .map_err(|err| format!("{flag}={dpath}: drafter load failed: {err}"))?;
2806    if draft.dflash2.is_none() {
2807        return Err(format!(
2808            "{flag}={dpath}: checkpoint is not a DFlash2DraftModel \
2809             (the glm5 draft source is the selector family only)"
2810        ));
2811    }
2812    if draft.cfg.hidden != n_embd {
2813        return Err(format!(
2814            "{flag}={dpath}: drafter hidden {} != target n_embd {n_embd} \
2815             (the drafter consumes target features and the target's embed/lm_head)",
2816            draft.cfg.hidden
2817        ));
2818    }
2819    if draft.cfg.target_layer_ids.is_empty()
2820        || draft.cfg.target_layer_ids.iter().any(|&t| t >= n_trunk)
2821    {
2822        return Err(format!(
2823            "{flag}={dpath}: target_layer_ids {:?} do not name valid \
2824             trunk layers (n_trunk {n_trunk})",
2825            draft.cfg.target_layer_ids
2826        ));
2827    }
2828    if draft.cfg.mask_token_id as usize >= n_vocab {
2829        return Err(format!(
2830            "{flag}={dpath}: mask token {} outside the target vocab {n_vocab}",
2831            draft.cfg.mask_token_id
2832        ));
2833    }
2834    let sha8 = crate::hybrid::sha256_file_hex8(&dir.join("model.safetensors"))
2835        .map_err(|err| format!("{flag}={dpath}: sha256 pin: {err}"))?;
2836    Ok(DflashDrafter { draft, sha8 })
2837}
2838
2839#[cfg(test)]
2840mod draft_source_seam_tests {
2841    use super::resolve_tap_layers;
2842
2843    #[test]
2844    fn taps_resolve_and_the_shift_is_the_callers() {
2845        assert_eq!(
2846            resolve_tap_layers(&[1, 12, 23], 46, 0, "glm5 DFlash2").unwrap(),
2847            vec![1, 12, 23]
2848        );
2849        // the red arm's +1 rides in as a parameter, not as an env read in here
2850        assert_eq!(
2851            resolve_tap_layers(&[1, 12, 23], 46, 1, "glm5 DFlash2").unwrap(),
2852            vec![2, 13, 24]
2853        );
2854    }
2855
2856    #[test]
2857    fn empty_and_out_of_trunk_taps_refuse_by_name() {
2858        let err = resolve_tap_layers(&[], 46, 0, "glm5 DFlash2").unwrap_err();
2859        assert!(err.contains("no target_layer_ids"), "{err}");
2860        let err = resolve_tap_layers(&[1, 46], 46, 0, "glm5 DFlash2").unwrap_err();
2861        assert!(
2862            err.contains("tap layer 46 is outside the 46-layer trunk"),
2863            "{err}"
2864        );
2865        // the SHIFTED tap is what gets bounds-checked — the red arm must not be able to
2866        // walk off the trunk silently
2867        let err = resolve_tap_layers(&[45], 46, 1, "glm5 DFlash2").unwrap_err();
2868        assert!(err.contains("tap layer 46 is outside"), "{err}");
2869    }
2870}
2871
2872#[cfg(test)]
2873mod emit_budget_tests {
2874    use super::emit_accepted_run;
2875
2876    #[test]
2877    fn accepted_run_never_exceeds_max_new() {
2878        // TOOTH (hermes finding, fixed 2026-08-23): the dflash accept loop pushed THEN
2879        // checked, emitting max_new+1 whenever the final round accepted at the boundary.
2880        let mut out = vec![1, 2, 3]; // 3 committed, budget 4: exactly ONE slot left
2881        let stop = emit_accepted_run(&mut out, &[10, 11, 12], &[], 4);
2882        assert!(stop, "hitting the budget must break the round loop");
2883        assert_eq!(
2884            out,
2885            vec![1, 2, 3, 10],
2886            "exactly max_new tokens, never max_new+1"
2887        );
2888        // EOS inside the run stops after emitting it (unchanged semantics).
2889        let mut out = vec![1];
2890        let stop = emit_accepted_run(&mut out, &[10, 99, 12], &[99], 8);
2891        assert!(stop);
2892        assert_eq!(out, vec![1, 10, 99]);
2893        // A run fitting the budget with no EOS lets the round continue.
2894        let mut out = vec![1];
2895        assert!(!emit_accepted_run(&mut out, &[10, 11], &[], 8));
2896        assert_eq!(out, vec![1, 10, 11]);
2897    }
2898}
2899
2900// ================= DFlash spec round (greedy, first light) =================
2901// Exact contract: identical output stream to plain greedy decode BY CONSTRUCTION — the
2902// target's batched verify argmax decides every committed token; the drafter only proposes.
2903// (Same verify+rewind pattern as generate_spec_gemma's eager round; t=16 verify rides the
2904// straddle-split-safe fa_decode_rows.)
2905impl crate::hybrid::HybridModel {
2906    pub fn generate_spec_dflash(
2907        &self,
2908        e: &Engine,
2909        draft: &DflashDraft,
2910        prompt: &[u32],
2911        max_new: usize,
2912        eos: &[u32],
2913    ) -> Result<Vec<u32>, Box<dyn std::error::Error>> {
2914        self.refuse_hyper("generate_spec_dflash")?;
2915        use crate::cache::{Cache, DflashTapSink};
2916        let n_embd = self.cfg.n_embd as usize;
2917        let c = &draft.cfg;
2918        assert!(
2919            draft.dflash2.is_none(),
2920            "DFlash2 drafters ride the qwen-hybrid dspark round (selector + windowed \
2921             attention); the gemma arm has no consumer for the family's ops"
2922        );
2923        assert_eq!(n_embd, c.hidden, "draft hidden must match target n_embd");
2924        let b = c.block_size;
2925        let n_taps = c.target_layer_ids.len();
2926        let max_ctx = prompt.len() + max_new + b + 8;
2927        // First light holds ctx <= sliding_window: the draft was trained with 4 sliding
2928        // layers (window 2048) and the first-light attention is windowless full — inside
2929        // the window the two are identical. The depth cell (1736 + 128) fits.
2930        assert!(
2931            max_ctx <= c.sliding_window,
2932            "first-light dflash round is windowless — ctx cap {} exceeds the draft window {}",
2933            max_ctx,
2934            c.sliding_window
2935        );
2936        let mut cache = Cache::new(e, &self.cfg, max_ctx)?;
2937
2938        // ---- prime with taps armed ----
2939        let tp = prompt.len();
2940        cache.dflash_taps = Some(DflashTapSink {
2941            layer_ids: c.target_layer_ids.clone(),
2942            buf: e.uninit(tp * n_taps * n_embd)?,
2943            hidden: n_embd,
2944            t: tp,
2945            base: 0,
2946        });
2947        let t_prime = std::time::Instant::now();
2948        let (logits, _h_seed, _hiddens) = self.prime_cache(e, prompt, &mut cache, 0)?;
2949        let mut last = crate::forward::argmax(&logits) as u32;
2950        // draft KV cache: ingest the prompt's ctx features once; per round only the kept
2951        // rows ingest + the block projects (round cost O(block), not O(ctx)).
2952        let mut dkv = DflashKv::new(e, &draft.cfg, max_ctx)?;
2953        {
2954            // CHUNKED ingest (depth OOM fix): the 1736-row prompt tap buffer is ~224MB f32;
2955            // running fc + 5-layer k/v projection over it in one shot stacks another
2956            // ~300MB of transients on the ~21.3GB trunk peak. 256-row windows bound the
2957            // transient set; identical values (row-independent ops).
2958            let taps = cache.dflash_taps.take().unwrap();
2959            let n_taps_h = n_taps * n_embd;
2960            let mut r0 = 0usize;
2961            while r0 < tp {
2962                let t_c = (tp - r0).min(256);
2963                let tv = e.view(&taps.buf, tp * n_taps_h);
2964                let win = tv.slice(r0 * n_taps_h..(r0 + t_c) * n_taps_h);
2965                let mut chunk = e.uninit(t_c * n_taps_h)?;
2966                e.copy_view_into(&mut chunk, 0, &win, t_c * n_taps_h)?;
2967                let f = draft.ctx_features(e, &chunk, t_c)?;
2968                let pos_c: Vec<i32> = ((r0 as i32)..(r0 + t_c) as i32).collect();
2969                draft.ingest_ctx(e, &mut dkv, &f, &pos_c, t_c)?;
2970                r0 += t_c;
2971            }
2972        }
2973        let mut ctx_len = tp;
2974        e.stream().synchronize()?;
2975        // published prime wall (the run-spec/gemma-gate timing contract subtracts it)
2976        crate::PRIME_NANOS.store(
2977            t_prime.elapsed().as_nanos() as u64,
2978            std::sync::atomic::Ordering::Relaxed,
2979        );
2980
2981        // embed-scale seam (MEMRA_DFLASH_EMB_SCALE): gemma trunks scale embeddings by
2982        // sqrt(n_embd) INSIDE the forward; whether the z-lab gemma4 training fed the
2983        // drafter scaled or raw embed rows is not visible from the reference (qwen path
2984        // uses raw embed_tokens). Acceptance arbitrates; default raw.
2985        let emb_scale = if std::env::var("MEMRA_DFLASH_EMB_SCALE").as_deref() == Ok("1") {
2986            (n_embd as f32).sqrt()
2987        } else {
2988            1.0
2989        };
2990
2991        let mut out = Vec::with_capacity(max_new);
2992        let n_vocab = self.output.out_features();
2993        // VERIFY WIDTH (MEMRA_DFLASH_VERIFY_T, default 8): the drafter always drafts a full
2994        // block (its trained mask pattern) but only the first vt rows go through the target
2995        // verify — the t=16 verify rides the untuned b16 tier at ~32% of the byte wall
2996        // (65ms/verify) while b8 rides the tuned r2 tier; with ~2.7 committed/round the
2997        // deep block positions almost never survive anyway. Exactness unaffected (verify
2998        // still decides every committed token).
2999        let vt_cap: usize = std::env::var("MEMRA_DFLASH_VERIFY_T")
3000            .ok()
3001            .and_then(|v| v.parse().ok())
3002            .unwrap_or(8)
3003            .clamp(2, b);
3004        // adaptive verify width (MEMRA_DFLASH_ADAPT!=0, MTP accepted+1 recipe): next round
3005        // verifies one past this round's accepted run, clamped [3, cap].
3006        let adapt = std::env::var("MEMRA_DFLASH_ADAPT").as_deref() != Ok("0");
3007        let mut vt = vt_cap;
3008        let mut attempted = 0usize;
3009        let mut accepted = 0usize;
3010        // The whole round runs in the decode-exact matmul scope: the m=16 draft mms were
3011        // otherwise falling into the prefill-GEMM class (770us/matmul, 17% of the depth
3012        // round). Prime (before this loop) keeps the prefill GEMM path. RAII: a `?` exit
3013        // anywhere in the loop restores the pre-scope value instead of latching exact ON
3014        // engine-wide (hermes finding, fixed 2026-08-23).
3015        let exact_scope = e.exact_scope(true);
3016        'outer: while out.len() < max_new {
3017            let start = cache.pos; // committed length
3018            // ---- draft: block = [last, MASK x b-1] ----
3019            let mut block: Vec<u32> = vec![c.mask_token_id; b];
3020            block[0] = last;
3021            let mut noise = e.htod(&self.embd.gather(n_embd, &block))?;
3022            if emb_scale != 1.0 {
3023                e.scale_inplace(&mut noise, emb_scale, b * n_embd)?;
3024            }
3025            if std::env::var("MEMRA_DFLASH_DEBUG").as_deref() == Ok("1") && start == cache.pos {
3026                let nv = e.dtoh(&noise)?;
3027                let r0: f32 = nv[..n_embd].iter().map(|x| x * x).sum::<f32>().sqrt();
3028                let r1: f32 = nv[n_embd..2 * n_embd]
3029                    .iter()
3030                    .map(|x| x * x)
3031                    .sum::<f32>()
3032                    .sqrt();
3033                eprintln!(
3034                    "[dflash noise] |row0(last)|={r0:.3} |row1(MASK id {})|={r1:.3}",
3035                    c.mask_token_id
3036                );
3037            }
3038            let pos_block: Vec<i32> = ((start as i32)..(start + b) as i32).collect();
3039            let dh = draft.forward_round(e, &mut dkv, &noise, &pos_block)?;
3040            // draft tokens = argmax(lm_head(h rows 1..b))
3041            let mut rows = e.uninit((b - 1) * n_embd)?;
3042            {
3043                let dv = e.view(&dh, b * n_embd);
3044                let tail = dv.slice(n_embd..b * n_embd);
3045                e.copy_view_into(&mut rows, 0, &tail, (b - 1) * n_embd)?;
3046            }
3047            let mut dl = e.matmul(&self.output, &rows, b - 1)?;
3048            // SEMI-AR MARKOV CHAIN (DSpark head, when present + MEMRA_DFLASH_MARKOV!=0):
3049            // left-to-right, logits_k += W2(W1[prev realized token]) — the whole chain
3050            // stays on-device (chain_d[0] = the pending token; argmax k writes
3051            // chain_d[k+1], the k+1 bias gathers from it). Greedy mirror of the patch's
3052            // _markov_semiar_sample_block.
3053            let markov_on = std::env::var("MEMRA_DFLASH_MARKOV").as_deref() != Ok("0");
3054            let mut chain_d = e.stream().alloc_zeros::<u32>(b)?;
3055            if let (Some(mk), true) = (&draft.markov, markov_on) {
3056                e.set_u32_one(&mut chain_d, last)?;
3057                for k in 0..(b - 1) {
3058                    let mut f = e.uninit(mk.rank)?;
3059                    e.gather_row_bf16(&mk.w1_bf16, &chain_d, k, &mut f, mk.rank)?;
3060                    let bias = e.matmul(&mk.w2, &f, 1)?;
3061                    e.add_row_inplace(&mut dl, &bias, n_vocab, k * n_vocab)?;
3062                    e.argmax_token_device_col(&dl, k, n_vocab, &mut chain_d, k + 1)?;
3063                }
3064            } else {
3065                for i in 0..(b - 1) {
3066                    e.argmax_token_device_col(&dl, i, n_vocab, &mut chain_d, i + 1)?;
3067                }
3068            }
3069            let chain = e.dtoh_u32(&chain_d)?;
3070            let dtoks = &chain[1..];
3071            for (i, &dt) in dtoks.iter().enumerate() {
3072                block[i + 1] = dt;
3073            }
3074            let dbg = std::env::var("MEMRA_DFLASH_DEBUG").as_deref() == Ok("1");
3075
3076            // ---- verify: one t=vt target forward with taps armed ----
3077            let vblock = &block[..vt];
3078            cache.dflash_taps = Some(DflashTapSink {
3079                layer_ids: c.target_layer_ids.clone(),
3080                buf: e.uninit(vt * n_taps * n_embd)?,
3081                hidden: n_embd,
3082                t: vt,
3083                base: 0,
3084            });
3085            let (vam, _vh) = self.gemma4_decode_step_t_am(e, vblock, start, &mut cache)?;
3086            let taps = cache.dflash_taps.take().unwrap();
3087            if dbg {
3088                eprintln!(
3089                    "[dflash r] start={start} last={last}\n  draft={:?}\n  vam  ={:?}",
3090                    &block[1..],
3091                    vam
3092                );
3093            }
3094
3095            // ---- accept ----
3096            let mut m = 0usize;
3097            while m < vt - 1 && block[m + 1] as usize == vam[m] as usize {
3098                m += 1;
3099            }
3100            attempted += vt - 1;
3101            accepted += m;
3102            out.push(last);
3103            if eos.contains(&last) {
3104                break 'outer;
3105            }
3106            if emit_accepted_run(&mut out, &block[1..=m], eos, max_new) {
3107                break 'outer;
3108            }
3109            let next = vam[m];
3110
3111            // ---- commit/rollback: keep m+1 of the b appended rows ----
3112            let keep = m + 1;
3113            for kvl in cache.kv.iter_mut().flatten() {
3114                kvl.len -= vt - keep;
3115                e.set_i32_one(&mut kvl.len_d, kvl.len as i32)?;
3116            }
3117            cache.pos -= vt - keep;
3118
3119            // ---- ingest the kept rows' ctx features into the draft KV ----
3120            {
3121                let tv = e.view(&taps.buf, vt * n_taps * n_embd);
3122                let keep_view = tv.slice(0..keep * n_taps * n_embd);
3123                let mut kept = e.uninit(keep * n_taps * n_embd)?;
3124                e.copy_view_into(&mut kept, 0, &keep_view, keep * n_taps * n_embd)?;
3125                let f = draft.ctx_features(e, &kept, keep)?;
3126                let pos_k: Vec<i32> = ((ctx_len as i32)..(ctx_len + keep) as i32).collect();
3127                draft.ingest_ctx(e, &mut dkv, &f, &pos_k, keep)?;
3128                ctx_len += keep;
3129            }
3130            last = next;
3131            if adapt {
3132                vt = (m + 2).clamp(3, vt_cap);
3133            }
3134        }
3135        drop(exact_scope);
3136        if std::env::var("MEMRA_SPEC_STATS").as_deref() == Ok("1") {
3137            eprintln!(
3138                "[dflash] acceptance {accepted}/{attempted} = {:.3}",
3139                accepted as f64 / attempted.max(1) as f64
3140            );
3141        }
3142        Ok(out)
3143    }
3144}
3145
3146// ================= Engine-bundle slice 1: batched GDN state snapshot ====================
3147// DSF-ROUNDCOST-20260820 §1.1 measured the dspark round's `cache.snapshot(e)` at 0.67 ms
3148// native wall — 48 linear layers x {conv, ssm} x (alloc_zeros + memcpy_dtod) of pure
3149// dispatch serialization, zero kernels. This batcher holds ONE persistent CacheSnapshot
3150// (buffers allocated on round 1, reused every round — kills the per-round alloc/memset
3151// churn) plus device pointer tables, so a round's snap is one small H2D table refresh
3152// (the ssm handles ping-pong per verify row, so live pointers are re-read each round;
3153// conv handles are rolled in place and never move) + TWO `copy_batch_uniform_f32`
3154// launches. Bytes, buffers and stream order are identical to `Cache::snapshot`; only the
3155// dispatch count changes, so acceptance and streams stay bit-identical (E2E-gated).
3156// `MEMRA_STATE_COPY_BATCH=0` reverts to the legacy per-layer snapshot.
3157
3158pub(crate) struct DsparkSnapBatch {
3159    pub(crate) snap: crate::cache::CacheSnapshot,
3160    /// Linear-attention layer indices, in `conv_table`/`ssm_table` order.
3161    lin: Vec<usize>,
3162    /// [src_0..src_{n-1}, dst_0..dst_{n-1}] — live conv states -> snapshot conv buffers.
3163    conv_table: CudaSlice<u64>,
3164    ssm_table: CudaSlice<u64>,
3165    host_ssm: Vec<u64>,
3166    conv_words: usize,
3167    ssm_words: usize,
3168}
3169
3170impl DsparkSnapBatch {
3171    /// Build from a fresh full snapshot (this IS round 1's snap — the caller uses
3172    /// `self.snap` directly after `new`). Returns None when the cache has no linear
3173    /// layers or their state sizes are non-uniform (a future hybrid shape) — the caller
3174    /// then stays on the legacy per-layer snapshot rather than copying wrong byte counts.
3175    pub(crate) fn new(
3176        e: &Engine,
3177        cache: &crate::cache::Cache,
3178    ) -> Result<Option<Self>, Box<dyn std::error::Error>> {
3179        use cudarc::driver::DevicePtr;
3180        let snap = cache.snapshot(e)?;
3181        let lin: Vec<usize> = (0..cache.recur.len())
3182            .filter(|&il| cache.recur[il].is_some())
3183            .collect();
3184        if lin.is_empty() {
3185            return Ok(None);
3186        }
3187        let first = cache.recur[lin[0]].as_ref().unwrap();
3188        let (conv_words, ssm_words) = (first.conv_state.len(), first.ssm_state.len());
3189        for &il in &lin {
3190            let rl = cache.recur[il].as_ref().unwrap();
3191            if rl.conv_state.len() != conv_words || rl.ssm_state.len() != ssm_words {
3192                return Ok(None);
3193            }
3194        }
3195        let n = lin.len();
3196        let mut host_conv = vec![0u64; 2 * n];
3197        let mut host_ssm = vec![0u64; 2 * n];
3198        {
3199            let s = &e.gpu.stream();
3200            for (k, &il) in lin.iter().enumerate() {
3201                let rl = cache.recur[il].as_ref().unwrap();
3202                let (pc, _g0) = rl.conv_state.device_ptr(s);
3203                let (ps, _g1) = rl.ssm_state.device_ptr(s);
3204                let (dc, _g2) = snap.conv[il].as_ref().unwrap().device_ptr(s);
3205                let (ds, _g3) = snap.ssm[il].as_ref().unwrap().device_ptr(s);
3206                host_conv[k] = pc;
3207                host_conv[n + k] = dc;
3208                host_ssm[k] = ps;
3209                host_ssm[n + k] = ds;
3210            }
3211        }
3212        let conv_table = e.htod_u64(&host_conv)?;
3213        let ssm_table = e.htod_u64(&host_ssm)?;
3214        Ok(Some(Self {
3215            snap,
3216            lin,
3217            conv_table,
3218            ssm_table,
3219            host_ssm,
3220            conv_words,
3221            ssm_words,
3222        }))
3223    }
3224
3225    /// The per-round snap: refresh kv lens/pos host-side (as `snapshot_into` does),
3226    /// re-read the live ssm handles into the table (gdn ping-pong moves them; the conv
3227    /// handles and every snapshot dst are stable), then two batched-copy launches.
3228    pub(crate) fn refresh(
3229        &mut self,
3230        e: &Engine,
3231        cache: &crate::cache::Cache,
3232    ) -> Result<(), Box<dyn std::error::Error>> {
3233        use cudarc::driver::DevicePtr;
3234        for il in 0..cache.kv.len() {
3235            self.snap.kv_len[il] = cache.kv[il].as_ref().map(|kvl| kvl.len);
3236        }
3237        self.snap.pos = cache.pos;
3238        let n = self.lin.len();
3239        {
3240            let s = &e.gpu.stream();
3241            for (k, &il) in self.lin.iter().enumerate() {
3242                let rl = cache.recur[il].as_ref().unwrap();
3243                let (ps, _g) = rl.ssm_state.device_ptr(s);
3244                self.host_ssm[k] = ps;
3245            }
3246        }
3247        e.htod_u64_into(&self.host_ssm, &mut self.ssm_table)?;
3248        e.copy_batch_uniform_f32(&self.conv_table, n, self.conv_words)?;
3249        e.copy_batch_uniform_f32(&self.ssm_table, n, self.ssm_words)?;
3250        Ok(())
3251    }
3252}
3253
3254// ================= DSpark spec round, QWEN-HYBRID target (lane/dspark-q38-recover) =====
3255// The q38 twin of generate_spec_dflash. Same drafter machinery (rounds, markov chain,
3256// draft KV, adaptive verify width); the TARGET side swaps gemma4's dense verify for the
3257// qwen serving-class verify funnel (dspark_verify_t_am) + snapshot/rollback, because the
3258// hybrid GDN conv/ssm state mutates in place — dense KV truncation cannot roll it back.
3259// Exactness contract unchanged: identical stream to plain greedy BY CONSTRUCTION (the
3260// target's verify argmax decides every committed token).
3261impl crate::hybrid::HybridModel {
3262    #[allow(clippy::type_complexity)] // allow: one-shot composite type; naming it would hide the shape that matters at the call site
3263    pub fn generate_spec_dspark(
3264        &self,
3265        e: &Engine,
3266        draft: &DflashDraft,
3267        prompt: &[u32],
3268        max_new: usize,
3269        eos: &[u32],
3270        sampling: Option<&crate::spec::SpecSampling>,
3271    ) -> Result<Vec<u32>, Box<dyn std::error::Error>> {
3272        self.refuse_hyper("generate_spec_dspark")?;
3273        use crate::cache::{Cache, DflashTapSink};
3274        assert!(
3275            !self.uses_gemma_program(),
3276            "gemma4 targets use generate_spec_dflash; this is the qwen-hybrid arm"
3277        );
3278        // SAMPLED ADMISSION (T>0, lane/dspark-sampled-admission-20260820): Some+temp>0
3279        // routes the round's proposal/accept through the rejection-sampling arms; None or
3280        // temp==0 keeps every greedy path byte-identical (the exactness instrument).
3281        let sp_on: Option<&crate::spec::SpecSampling> = sampling.filter(|s| s.temp > 0.0);
3282        // PENALTIES AT T==0 ARE A LOUD REFUSAL (lane/dspark-penalized-sampled-20260821):
3283        // the greedy walk argmaxes RAW verify columns, so a temp==0 config carrying
3284        // non-identity penalties would silently serve the UNPENALIZED greedy stream —
3285        // exactly the H-class silent-program-switch this route refuses everywhere else.
3286        // Penalized greedy stays on the plain path (worker admission owns the exclusion).
3287        if let Some(s) = sampling
3288            && s.temp <= 0.0
3289            && s.pen_on()
3290        {
3291            return Err(
3292                "dspark spec at temp==0 is the greedy route and would silently drop \
3293                     the request's penalties; penalized greedy is served on the plain path"
3294                    .into(),
3295            );
3296        }
3297        // Penalized-sampled state: the session window (pen_window_seed — one definition
3298        // across both spec routes), extended with every committed token; each round's
3299        // accept receives the trimmed tail (min(penalty_last_n, PEN_WINDOW_MAX)).
3300        let pen_on = sp_on.is_some_and(|s| s.pen_on());
3301        let mut pen_hist: Vec<u32> = if pen_on {
3302            crate::spec::pen_window_seed(&[], prompt, sp_on.unwrap().penalty_last_n)
3303        } else {
3304            Vec::new()
3305        };
3306        let (mut sctr, mut uctr) = (0u32, 0u32);
3307        let n_embd = self.cfg.n_embd as usize;
3308        let c = &draft.cfg;
3309        assert_eq!(n_embd, c.hidden, "draft hidden must match target n_embd");
3310        let b = c.block_size;
3311        let n_taps = c.target_layer_ids.len();
3312        let max_ctx = prompt.len() + max_new + b + 8;
3313        // DFlash2 implements the reference's non-causal symmetric sliding window in
3314        // the round attention (sdpa_naive_w), so depth past the window is admitted;
3315        // other families keep the historical windowless contract.
3316        assert!(
3317            draft.dflash2.is_some() || max_ctx <= c.sliding_window,
3318            "dspark round is windowless — ctx cap {} exceeds the draft window {}",
3319            max_ctx,
3320            c.sliding_window
3321        );
3322        let mut cache = Cache::new(e, &self.cfg, max_ctx)?;
3323
3324        // ---- prime with taps armed (chunked prime writes at chunk offsets via sink.base) ----
3325        let tp = prompt.len();
3326        cache.dflash_taps = Some(DflashTapSink {
3327            layer_ids: c.target_layer_ids.clone(),
3328            buf: e.uninit(tp * n_taps * n_embd)?,
3329            hidden: n_embd,
3330            t: tp,
3331            base: 0,
3332        });
3333        let t_prime = std::time::Instant::now();
3334        let (logits, _h_seed, _hiddens) = self.prime_cache(e, prompt, &mut cache, 0)?;
3335        // Boundary token: greedy takes the argmax (byte contract); sampled draws it from
3336        // the request's own filtered target through the session Philox stream — the same
3337        // shipped composition the frspec route uses (sample_check arm 9 oracles it).
3338        let mut last = match sp_on {
3339            Some(sp) => crate::spec::sample_boundary_token(
3340                e,
3341                &logits,
3342                sp,
3343                &pen_hist,
3344                &mut sctr,
3345                "dspark-prime",
3346            )?,
3347            None => crate::forward::argmax(&logits) as u32,
3348        };
3349        let mut dkv = DflashKv::new(e, &draft.cfg, max_ctx)?;
3350        {
3351            let taps = cache.dflash_taps.take().unwrap();
3352            let n_taps_h = n_taps * n_embd;
3353            let mut r0 = 0usize;
3354            while r0 < tp {
3355                let t_c = (tp - r0).min(256);
3356                let tv = e.view(&taps.buf, tp * n_taps_h);
3357                let win = tv.slice(r0 * n_taps_h..(r0 + t_c) * n_taps_h);
3358                let mut chunk = e.uninit(t_c * n_taps_h)?;
3359                e.copy_view_into(&mut chunk, 0, &win, t_c * n_taps_h)?;
3360                let f = draft.ctx_features(e, &chunk, t_c)?;
3361                let pos_c: Vec<i32> = ((r0 as i32)..(r0 + t_c) as i32).collect();
3362                draft.ingest_ctx(e, &mut dkv, &f, &pos_c, t_c)?;
3363                r0 += t_c;
3364            }
3365        }
3366        let mut ctx_len = tp;
3367        e.stream().synchronize()?;
3368        crate::PRIME_NANOS.store(
3369            t_prime.elapsed().as_nanos() as u64,
3370            std::sync::atomic::Ordering::Relaxed,
3371        );
3372
3373        let mut out = Vec::with_capacity(max_new);
3374        let n_vocab = self.output.out_features();
3375        // Harvest convention (DSPARK-POSTMORTEM-20260820.md): which drafter output rows
3376        // become draft candidates. nd = drafts/round; verify carries [anchor, drafts]
3377        // = up to nd+1 rows. FAMILY-keyed for DFlash2 (mask-fill by construction),
3378        // else default = the CHECKPOINT's own strategy census (owner-ratified flip,
3379        // 2026-08-20); explicit env still wins (contradiction refuses).
3380        let harvest = DsparkHarvest::for_draft(draft);
3381        let nd = harvest.n_drafts(b);
3382        let r0 = harvest.first_row();
3383        let vt_cap: usize = std::env::var("MEMRA_DFLASH_VERIFY_T")
3384            .ok()
3385            .and_then(|v| v.parse().ok())
3386            .unwrap_or(nd + 1)
3387            .clamp(2, nd + 1);
3388        let adapt = std::env::var("MEMRA_DFLASH_ADAPT").as_deref() != Ok("0");
3389        // Verify-window policy (H4, DSPARK-POSTMORTEM-20260820.md): default =
3390        // confidence-slot tau=.5 when the checkpoint carries an accept-rate head
3391        // (owner-ratified flip 2026-08-20; cell-3 tau ladder knee) — each round's
3392        // window is sized from the head's own slot scores, post-draft pre-verify.
3393        // Head-less checkpoints and MEMRA_DFLASH_ADAPT=0 keep the reactive ladder.
3394        let vt_policy = DsparkVtPolicy::resolve(draft.confidence.is_some());
3395        if vt_policy.is_confidence() {
3396            assert!(
3397                draft.confidence.is_some(),
3398                "MEMRA_DSPARK_VT={vt_policy:?} needs a checkpoint with an accept-rate \
3399                 head (confidence_head.* absent in this export)"
3400            );
3401        }
3402        let mut vt = vt_cap;
3403        let mut attempted = 0usize;
3404        let mut accepted = 0usize;
3405        // Engine-bundle slice 1: persistent batched snapshot (None until round 1; stays
3406        // None — legacy per-layer snapshot — under MEMRA_STATE_COPY_BATCH=0 or when the
3407        // batcher declines the cache shape).
3408        let mut snapb: Option<DsparkSnapBatch> = None;
3409        let mut snapb_off = !crate::spec::state_copy_batch_on();
3410        // Engine-bundle slice 2: deferred chain readback needs the resident embed table
3411        // (verify then embeds chain_d directly). Ladder/stash arms only — the confidence
3412        // policies size vt from a pre-verify head readback and keep the legacy order.
3413        let defer_rb = crate::spec::dspark_defer_readback_on() && !vt_policy.is_confidence();
3414        let (embd_qt, embd_rb) = self.embd.qt_and_row_bytes(n_embd);
3415        let embd_gpu = if !defer_rb || crate::spec::spec_host_embd() {
3416            None
3417        } else {
3418            Some(
3419                self.embd_gpu
3420                    .get_or_init(|| e.upload_u8(&self.embd.raw).expect("embed table upload")),
3421            )
3422        };
3423        // Engine-bundle slice 3: per-(segment, vt) verify graphs for the linear-layer runs
3424        // (rides the slice-2 deferred path only — device tokens keep the whole verify off
3425        // the host). PERSISTENT across generations on the model (rebuilding per call
3426        // re-captured ~80 graphs per prompt — measured 97.8 -> 79.1 tok/s e2e); the
3427        // captured bodies are cache-independent: all state reads go through per-round
3428        // refreshed pointer tables and ctx-owned slabs. None = eager walk, byte-identical.
3429        let mut vg_guard = self.dspark_vgraphs.lock().unwrap();
3430        if vg_guard.is_none() && embd_gpu.is_some() && crate::spec::dspark_verify_graph_on() {
3431            *vg_guard = crate::spec::DsparkVerifyGraphs::new(e, &cache, vt_cap, n_embd)?;
3432        }
3433        let vgraphs: &mut Option<crate::spec::DsparkVerifyGraphs> = &mut vg_guard;
3434        // per-phase economics counters (ns) — the verify-toll dataset
3435        let (mut ns_draft, mut ns_snap, mut ns_verify, mut ns_roll, mut ns_ingest) =
3436            (0u64, 0u64, 0u64, 0u64, 0u64);
3437        let mut rounds = 0usize;
3438        let stats = std::env::var("MEMRA_SPEC_STATS").as_deref() == Ok("1");
3439        let clock = |on: bool, e: &Engine| -> std::time::Instant {
3440            if on {
3441                let _ = e.stream().synchronize();
3442            }
3443            std::time::Instant::now()
3444        };
3445        'outer: while out.len() < max_new {
3446            rounds += 1;
3447            let start = cache.pos; // committed length
3448            // ---- draft: block = [last, MASK x b-1] (decode-exact class for the m=b mms) ----
3449            let t0 = clock(stats, e);
3450            // RAII: a `?` exit restores the pre-scope value instead of latching exact
3451            // ON engine-wide (hermes finding, fixed 2026-08-23).
3452            let exact_scope = e.exact_scope(true);
3453            let mut block: Vec<u32> = vec![c.mask_token_id; b];
3454            block[0] = last;
3455            let noise = e.htod(&self.embd.gather(n_embd, &block))?;
3456            let pos_block: Vec<i32> = ((start as i32)..(start + b) as i32).collect();
3457            let dh = draft.forward_round(e, &mut dkv, &noise, &pos_block)?;
3458            // Harvest: logits over rows r0..r0+nd (Dflash: mask rows 1..b-1, fill
3459            // semantics; Dspark: ALL b rows, shifted semantics — row k predicts
3460            // anchor+k+1, so col k of `dl` is the draft for position start+k+1).
3461            let mut rows = e.uninit(nd * n_embd)?;
3462            {
3463                let dv = e.view(&dh, b * n_embd);
3464                let src = dv.slice(r0 * n_embd..(r0 + nd) * n_embd);
3465                e.copy_view_into(&mut rows, 0, &src, nd * n_embd)?;
3466            }
3467            // TRIMMED DRAFT HEAD (lane/dflash2-head-trim, 2026-08-25): DFlash2 family
3468            // only — the selector consumes (value, candidate-id) pairs, so a d2t remap
3469            // after top-k restores true ids; the markov/chain arms argmax dl columns
3470            // into token ids DIRECTLY and must keep the full head. Reuses the FR-Spec
3471            // self-trim the load path builds on the MTP struct (MEMRA_FRSPEC_TRIM):
3472            // gathered rows of the target's own head, zero requant. Verify stays
3473            // full-vocab, so the trim moves draft acceptance only, never output.
3474            let trim = if draft.dflash2.is_some() {
3475                self.mtp
3476                    .as_ref()
3477                    .filter(|m| m.d2t_from_target_head)
3478                    .and_then(|m| m.shared_head_head.as_ref().zip(m.d2t.as_ref()))
3479                    // MEMRA_MTP_SKIP stub: the same target-head trimmed rows, parked in
3480                    // `dflash_trim` because the embedded MTP block was skipped (hybrid.rs;
3481                    // rows are target-head by construction; the loader refuses otherwise).
3482                    .or_else(|| self.dflash_trim.as_ref().map(|t| (&t.head, &t.d2t)))
3483                    .filter(|(_, d2t)| !d2t.is_empty())
3484            } else {
3485                None
3486            };
3487            let (dl_head, dl_vocab) = match trim {
3488                Some((head, d2t)) => (head, d2t.len()),
3489                None => (&self.output, n_vocab),
3490            };
3491            let trim_d2t = trim.map(|(_, d2t)| d2t.as_slice());
3492            let mut dl = e.matmul(dl_head, &rows, nd)?;
3493            // Family/sampling-keyed proposal (v0.100 train merge of the port and H4/
3494            // engine-bundle stacks — BOTH programs preserved):
3495            //  - SAMPLED (sp_on): rejection-sampling proposal, records the true per-slot
3496            //    q (family-keyed inside: selector for DFlash2, markov-corrected rows
3497            //    otherwise). Host CDF/readback syncs inside — slice-2 deferral N/A.
3498            //  - DFlash2 greedy: the candidate path selector REPLACES the markov chain
3499            //    (reference DFlash2DraftModel.propose — greedy arm).
3500            //  - markov/plain greedy chain: the engine-bundle arm; slice-2 readback
3501            //    deferral decided below (needs the ckpt arm reads).
3502            // Confidence policy: stash each slot's markov prev-token embedding (the
3503            // exact `w1` row the chain gathers) into a [nd, rank] buffer — d2d async,
3504            // read back beside `rows` in one host sync after the chain.
3505            let want_conf_emb = vt_policy.is_confidence()
3506                && draft.confidence.as_ref().is_some_and(|ch| ch.with_markov);
3507            let mut conf_emb: Option<CudaSlice<f32>> = match (&draft.markov, want_conf_emb) {
3508                (Some(mk), true) => Some(e.uninit(nd * mk.rank)?),
3509                (None, true) => unreachable!(
3510                    "with_markov confidence head without a markov table — the loader forbids it"
3511                ),
3512                _ => None,
3513            };
3514            let mut cand: Vec<u32> = Vec::with_capacity(nd + 1);
3515            let mut prop: Option<DsparkDraftSample> = None;
3516            let mut chain_dev: Option<CudaSlice<u32>> = None;
3517            if let Some(sp) = sp_on {
3518                let (tail, ds) = draft.dspark_propose_sampled(
3519                    e,
3520                    &mut dl,
3521                    &rows,
3522                    nd,
3523                    dl_vocab,
3524                    last,
3525                    sp,
3526                    &mut sctr,
3527                    &mut uctr,
3528                    conf_emb.as_mut(),
3529                    trim_d2t,
3530                )?;
3531                drop(exact_scope);
3532                cand.push(last);
3533                cand.extend_from_slice(&tail);
3534                prop = Some(ds);
3535            } else if draft.dflash2.is_some() {
3536                let path =
3537                    draft.dflash2_propose_greedy(e, &dl, &rows, nd, dl_vocab, last, trim_d2t)?;
3538                drop(exact_scope);
3539                cand.push(last);
3540                cand.extend_from_slice(&path);
3541            } else {
3542                let markov_on = std::env::var("MEMRA_DFLASH_MARKOV").as_deref() != Ok("0");
3543                let mut chain_d = e.stream().alloc_zeros::<u32>(nd + 1)?;
3544                if let (Some(mk), true) = (&draft.markov, markov_on) {
3545                    e.set_u32_one(&mut chain_d, last)?;
3546                    for k in 0..nd {
3547                        let mut f = e.uninit(mk.rank)?;
3548                        e.gather_row_bf16(&mk.w1_bf16, &chain_d, k, &mut f, mk.rank)?;
3549                        if let Some(ce) = conf_emb.as_mut() {
3550                            let fv = e.view(&f, mk.rank);
3551                            e.copy_view_into(ce, k * mk.rank, &fv, mk.rank)?;
3552                        }
3553                        let bias = e.matmul(&mk.w2, &f, 1)?;
3554                        e.add_row_inplace(&mut dl, &bias, n_vocab, k * n_vocab)?;
3555                        e.argmax_token_device_col(&dl, k, n_vocab, &mut chain_d, k + 1)?;
3556                    }
3557                } else {
3558                    if want_conf_emb {
3559                        // chain_d[0] must carry the anchor — slot 0's prev token.
3560                        e.set_u32_one(&mut chain_d, last)?;
3561                    }
3562                    for i in 0..nd {
3563                        if let (Some(ce), Some(mk)) = (conf_emb.as_mut(), &draft.markov) {
3564                            let mut f = e.uninit(mk.rank)?;
3565                            e.gather_row_bf16(&mk.w1_bf16, &chain_d, i, &mut f, mk.rank)?;
3566                            let fv = e.view(&f, mk.rank);
3567                            e.copy_view_into(ce, i * mk.rank, &fv, mk.rank)?;
3568                        }
3569                        e.argmax_token_device_col(&dl, i, n_vocab, &mut chain_d, i + 1)?;
3570                    }
3571                }
3572                drop(exact_scope);
3573                chain_dev = Some(chain_d);
3574            }
3575            // MEMRA_DSPARK_CKPT (default 1): verify with the MTP column-stash armed so a
3576            // partial accept restores state directly. =0 keeps the snapshot+replay arm
3577            // (the oracle the stash arm is gated against — MEMRA_DSPARK_CKPT_GATE=1 runs
3578            // BOTH per partial round and byte-compares the resulting cache state).
3579            // Read here (was at the verify site) — slice 2's deferral needs the arm
3580            // choice before deciding whether the chain readback can move past verify.
3581            let ckpt_on = std::env::var("MEMRA_DSPARK_CKPT").as_deref() != Ok("0");
3582            let ckpt_gate = std::env::var("MEMRA_DSPARK_CKPT_GATE").as_deref() == Ok("1");
3583            // SAMPLED x ckpt-gate refusal: the gate compares verify argmaxes across a
3584            // replay — a greedy-exactness instrument (port lane). Refuse loudly.
3585            if sp_on.is_some() && ckpt_gate {
3586                return Err(
3587                    "MEMRA_DSPARK_CKPT_GATE compares verify argmaxes across a replay \
3588                            — a greedy-exactness instrument; unset it for T>0 dspark rounds"
3589                        .into(),
3590                );
3591            }
3592            // Slice 2: under the stash/gate arms with a resident embed table, the GREEDY
3593            // chain readback is DEFERRED past verify dispatch and merged with the argmax
3594            // readback into one sync. The replay arm (CKPT=0) verifies host tokens and
3595            // keeps the legacy order; the sampled and DFlash2 proposals already synced
3596            // at the walk (chain_dev is None there).
3597            let deferred = chain_dev.is_some() && embd_gpu.is_some() && (ckpt_on || ckpt_gate);
3598            // ---- H4 confidence window: size THIS round's verify from the head ----
3599            if vt_policy.is_confidence() {
3600                let ch = draft.confidence.as_ref().expect("asserted at loop entry");
3601                let (rows_h, emb_h) = match conf_emb.as_ref() {
3602                    Some(ce) => {
3603                        let (a, b2) = e.dtoh_pair(&rows, ce)?;
3604                        (a, Some(b2))
3605                    }
3606                    None => (e.dtoh(&rows)?, None),
3607                };
3608                let rank = draft.markov.as_ref().map(|m| m.rank).unwrap_or(0);
3609                let mut raws = Vec::with_capacity(nd);
3610                for k in 0..nd {
3611                    let hrow = &rows_h[k * n_embd..(k + 1) * n_embd];
3612                    let emb = emb_h.as_ref().map(|eh| &eh[k * rank..(k + 1) * rank]);
3613                    raws.push(ch.raw_score(hrow, emb));
3614                }
3615                vt = vt_policy
3616                    .size_window(&raws, vt_cap)
3617                    .expect("confidence policies always size the window");
3618            }
3619            // Verify candidates: [anchor, draft 1..nd]. Under Dflash this is the
3620            // historical `block` content; under Dspark it is one longer than the
3621            // drafter's input block (nd = b drafts + the anchor). The sampled/DFlash2
3622            // proposals built `cand` at the walk; deferred greedy rounds build it after
3623            // the merged readback — the bytes are identical (chain_d is written before
3624            // either sync).
3625            if let Some(chain_d) = chain_dev.as_ref()
3626                && !deferred
3627            {
3628                let chain = e.dtoh_u32(chain_d)?;
3629                cand.push(last);
3630                cand.extend_from_slice(&chain[1..]);
3631            }
3632            ns_draft += clock(stats, e).duration_since(t0).as_nanos() as u64;
3633
3634            // ---- snapshot (GDN conv/ssm state + KV lens), then verify t=vt ----
3635            let t1 = std::time::Instant::now();
3636            // Slice 1: batched snap (one table refresh + two copy launches) with the
3637            // legacy per-layer snapshot as the kill-switch / non-uniform fallback.
3638            let mut snap_legacy: Option<crate::cache::CacheSnapshot> = None;
3639            if !snapb_off && snapb.is_none() {
3640                snapb = DsparkSnapBatch::new(e, &cache)?;
3641                snapb_off = snapb.is_none();
3642            } else if let Some(sb) = snapb.as_mut() {
3643                sb.refresh(e, &cache)?;
3644            }
3645            let snap: &crate::cache::CacheSnapshot = match snapb.as_ref() {
3646                Some(sb) => &sb.snap,
3647                None => {
3648                    snap_legacy = Some(cache.snapshot(e)?);
3649                    snap_legacy.as_ref().unwrap()
3650                }
3651            };
3652            let _ = &snap_legacy;
3653            ns_snap += clock(stats, e).duration_since(t1).as_nanos() as u64;
3654            let t2 = std::time::Instant::now();
3655            // Slice 3: the tap-sink buffer is persistent per vt in the graphs ctx
3656            // (captured segments bake its address); fully rewritten by every verify.
3657            let tap_buf = match vgraphs.as_mut().and_then(|g| g.tap_bufs.remove(&vt)) {
3658                Some(buf) => buf,
3659                None => e.uninit(vt * n_taps * n_embd)?,
3660            };
3661            cache.dflash_taps = Some(DflashTapSink {
3662                layer_ids: c.target_layer_ids.clone(),
3663                buf: tap_buf,
3664                hidden: n_embd,
3665                t: vt,
3666                base: 0,
3667            });
3668            // The whole fallible verify window runs inside a closure so the Err path can
3669            // return the sink buffer to the ctx pool before propagating (v0.98 review
3670            // carry-over): five `?`s span the window, and an early return would drop
3671            // `cache.dflash_taps` — freeing the buffer whose ADDRESS the model-persistent
3672            // captured graphs bake, so the next generation's replayed tap copies would
3673            // write freed memory. The never-orphan invariant below now holds on EVERY
3674            // exit, not just the EOS/budget break.
3675            let verify_res = (|cache: &mut crate::cache::Cache,
3676                               cand: &mut Vec<u32>,
3677                               vgraphs: &mut Option<crate::spec::DsparkVerifyGraphs>|
3678             -> Result<
3679                (
3680                    Vec<u32>,
3681                    Option<CudaSlice<f32>>,
3682                    Option<crate::spec::DsparkVerifyCkpt>,
3683                ),
3684                Box<dyn std::error::Error>,
3685            > {
3686                if sp_on.is_some() {
3687                    // SAMPLED: keep the raw verify logits — the accept walk gathers
3688                    // filtered p from them (argmaxes are the greedy arm's instrument,
3689                    // not this one's).
3690                    if ckpt_on {
3691                        let (tl, vck) =
3692                            self.dspark_verify_t_logits_ckpt(e, &cand[..vt], start, cache)?;
3693                        Ok((Vec::new(), Some(tl), Some(vck)))
3694                    } else {
3695                        Ok((
3696                            Vec::new(),
3697                            Some(self.dspark_verify_t_logits(e, &cand[..vt], start, cache)?),
3698                            None,
3699                        ))
3700                    }
3701                } else if deferred {
3702                    // Slice 2: verify embeds the DEVICE chain (cand layout by construction:
3703                    // chain_d[0] = anchor, chain_d[1..] = drafts), then ONE host sync reads
3704                    // chain + verify argmaxes together — the host dispatched snap + all of
3705                    // verify while the draft was still executing.
3706                    let chain_d = chain_dev.as_ref().expect("deferred implies greedy chain");
3707                    let g = embd_gpu.expect("deferred implies resident embed");
3708                    let (am_d, vck) = self.dspark_verify_t_am_ckpt_dev(
3709                        e,
3710                        chain_d,
3711                        vt,
3712                        start,
3713                        cache,
3714                        (g, embd_qt, embd_rb),
3715                        vgraphs.as_mut(),
3716                    )?;
3717                    let ch = e.stream().clone_dtoh(chain_d)?;
3718                    let am = e.stream().clone_dtoh(&am_d)?;
3719                    e.stream().synchronize()?;
3720                    cand.push(last);
3721                    cand.extend_from_slice(&ch[1..]);
3722                    Ok((am, None, Some(vck)))
3723                } else if ckpt_on || ckpt_gate {
3724                    let (vam, vck) = self.dspark_verify_t_am_ckpt(e, &cand[..vt], start, cache)?;
3725                    Ok((vam, None, Some(vck)))
3726                } else {
3727                    Ok((
3728                        self.dspark_verify_t_am(e, &cand[..vt], start, cache)?,
3729                        None,
3730                        None,
3731                    ))
3732                }
3733            })(&mut cache, &mut cand, vgraphs);
3734            let (vam, tl, vck) = match verify_res {
3735                Ok(v) => v,
3736                Err(err) => {
3737                    if let (Some(g), Some(taps)) = (vgraphs.as_mut(), cache.dflash_taps.take()) {
3738                        g.tap_bufs.insert(vt, taps.buf);
3739                    }
3740                    return Err(err);
3741                }
3742            };
3743            let taps = cache.dflash_taps.take().unwrap();
3744            // Return the tap buffer to the ctx pool IMMEDIATELY — an EOS/budget break
3745            // between accept and ingest must never orphan an address the captured
3746            // graphs bake (the next generation would alloc a fresh buffer and the
3747            // replayed tap copies would write freed memory). Ingest reads it borrowed.
3748            let tap_local: Option<CudaSlice<f32>> = match vgraphs.as_mut() {
3749                Some(g) => {
3750                    g.tap_bufs.insert(vt, taps.buf);
3751                    None
3752                }
3753                None => Some(taps.buf),
3754            };
3755            let tap_ref: &CudaSlice<f32> = match &tap_local {
3756                Some(b) => b,
3757                None => &vgraphs.as_ref().expect("ctx present above").tap_bufs[&vt],
3758            };
3759            ns_verify += clock(stats, e).duration_since(t2).as_nanos() as u64;
3760
3761            // ---- accept ----
3762            // Penalized-sampled: the anchor `last` is committed THIS round unconditionally
3763            // (the out.push below), so it joins the window before the accept walk — verify
3764            // row 0's state includes it. Accepted drafts extend the window after the walk;
3765            // `next` joins as the anchor of ITS round.
3766            if pen_on {
3767                pen_hist.push(last);
3768            }
3769            let (m, next) = match (sp_on, tl.as_ref()) {
3770                (Some(sp), Some(tl)) => {
3771                    let w0 = pen_hist
3772                        .len()
3773                        .saturating_sub(sp.penalty_last_n.min(crate::spec::PEN_WINDOW_MAX));
3774                    dspark_accept_sampled(
3775                        e,
3776                        tl,
3777                        &cand,
3778                        vt,
3779                        n_vocab,
3780                        &dl,
3781                        prop.as_ref()
3782                            .expect("sampled round without a proposal record"),
3783                        sp,
3784                        &pen_hist[w0..],
3785                        &mut sctr,
3786                        &mut uctr,
3787                    )?
3788                }
3789                _ => {
3790                    let m = dspark_accept_prefix(&cand, &vam, vt);
3791                    (m, vam[m])
3792                }
3793            };
3794            if pen_on {
3795                pen_hist.extend_from_slice(&cand[1..=m]);
3796            }
3797            attempted += vt - 1;
3798            accepted += m;
3799            out.push(last);
3800            if eos.contains(&last) {
3801                break 'outer;
3802            }
3803            if emit_accepted_run(&mut out, &cand[1..=m], eos, max_new) {
3804                break 'outer;
3805            }
3806
3807            // ---- commit/rollback: hybrid state cannot truncate — restore + replay kept ----
3808            let keep = m + 1;
3809            let t3 = std::time::Instant::now();
3810            // Slice 3: rounds whose linear column stash lives in the graphs ctx's slabs
3811            // commit through the slab twin (same semantics, slab-addressed sources).
3812            let slab_commit = vgraphs.as_ref().map(|g| g.round_slab).unwrap_or(false);
3813            if keep < vt {
3814                if ckpt_gate {
3815                    // GATE ARM: stash-restore, snapshot S1; then the replay oracle, snapshot
3816                    // S2; the two cache states must match BIT-FOR-BIT (kv lens, pos, every
3817                    // conv/ssm buffer). Continue from the replay state (proven identical).
3818                    if slab_commit {
3819                        self.dspark_commit_prefix_slab(
3820                            e,
3821                            &mut cache,
3822                            snap,
3823                            vgraphs.as_ref().expect("slab_commit implies ctx"),
3824                            keep,
3825                        )?;
3826                    } else {
3827                        let vck = vck.as_ref().expect("gate arm always fills the ckpt");
3828                        self.dspark_commit_prefix(e, &mut cache, snap, vck, keep)?;
3829                    }
3830                    // host-side state capture (NO device snapshot copies — two extra
3831                    // device snapshots per round OOM'd beside the 15GB trunk)
3832                    #[allow(clippy::type_complexity)]
3833                    // allow: one-shot composite type; naming it would hide the shape that matters at the call site
3834                    let capture = |cache: &Cache| -> Result<
3835                        (usize, Vec<Option<usize>>, Vec<(Vec<f32>, Vec<f32>)>),
3836                        Box<dyn std::error::Error>,
3837                    > {
3838                        let mut lens = Vec::new();
3839                        let mut states = Vec::new();
3840                        for il in 0..cache.kv.len() {
3841                            lens.push(cache.kv[il].as_ref().map(|k| k.len));
3842                            if let Some(rl) = &cache.recur[il] {
3843                                states.push((e.dtoh(&rl.conv_state)?, e.dtoh(&rl.ssm_state)?));
3844                            }
3845                        }
3846                        Ok((cache.pos, lens, states))
3847                    };
3848                    let (p1, l1, st1) = capture(&cache)?;
3849                    crate::pp::restore_cache_checkpoint(e, self, None, &mut cache, snap)?;
3850                    let ram = self.dspark_verify_t_am(e, &cand[..keep], start, &mut cache)?;
3851                    assert_eq!(
3852                        &ram[..],
3853                        &vam[..keep],
3854                        "prefix replay must reproduce the verify argmaxes"
3855                    );
3856                    let (p2, l2, st2) = capture(&cache)?;
3857                    assert_eq!(p1, p2, "ckpt-gate: pos mismatch");
3858                    assert_eq!(l1, l2, "ckpt-gate: kv_len mismatch");
3859                    for (il, ((c1, s1v), (c2, s2v))) in st1.iter().zip(&st2).enumerate() {
3860                        let bits = |a: &[f32], b: &[f32]| {
3861                            a.iter().zip(b).all(|(x, y)| x.to_bits() == y.to_bits())
3862                        };
3863                        assert!(
3864                            bits(c1, c2),
3865                            "ckpt-gate: linear layer {il} conv state differs"
3866                        );
3867                        assert!(
3868                            bits(s1v, s2v),
3869                            "ckpt-gate: linear layer {il} ssm state differs"
3870                        );
3871                    }
3872                } else if slab_commit {
3873                    // STASH ARM, slab twin (slice 3): same restore, slab-addressed.
3874                    self.dspark_commit_prefix_slab(
3875                        e,
3876                        &mut cache,
3877                        snap,
3878                        vgraphs.as_ref().expect("slab_commit implies ctx"),
3879                        keep,
3880                    )?;
3881                } else if let Some(vck) = vck.as_ref() {
3882                    // STASH ARM (default): column-state restore, no replay forward.
3883                    self.dspark_commit_prefix(e, &mut cache, snap, vck, keep)?;
3884                } else {
3885                    // REPLAY ARM (MEMRA_DSPARK_CKPT=0): the original snapshot+replay oracle.
3886                    crate::pp::restore_cache_checkpoint(e, self, None, &mut cache, snap)?;
3887                    debug_assert_eq!(cache.pos, start, "rollback landed off the round start");
3888                    let ram = self.dspark_verify_t_am(e, &cand[..keep], start, &mut cache)?;
3889                    if sp_on.is_none() {
3890                        // the argmax-reproduction oracle is greedy-only; the sampled arm
3891                        // replays purely to rebuild the cache state.
3892                        debug_assert_eq!(
3893                            &ram[..],
3894                            &vam[..keep],
3895                            "prefix replay must reproduce the verify argmaxes"
3896                        );
3897                    }
3898                }
3899            }
3900            ns_roll += clock(stats, e).duration_since(t3).as_nanos() as u64;
3901
3902            // ---- ingest the kept rows' ctx features into the draft KV ----
3903            let t4 = std::time::Instant::now();
3904            {
3905                let tv = e.view(tap_ref, vt * n_taps * n_embd);
3906                let keep_view = tv.slice(0..keep * n_taps * n_embd);
3907                let mut kept = e.uninit(keep * n_taps * n_embd)?;
3908                e.copy_view_into(&mut kept, 0, &keep_view, keep * n_taps * n_embd)?;
3909                let f = draft.ctx_features(e, &kept, keep)?;
3910                let pos_k: Vec<i32> = ((ctx_len as i32)..(ctx_len + keep) as i32).collect();
3911                draft.ingest_ctx(e, &mut dkv, &f, &pos_k, keep)?;
3912                ctx_len += keep;
3913            }
3914            ns_ingest += clock(stats, e).duration_since(t4).as_nanos() as u64;
3915            last = next;
3916            // Ladder update only — under the confidence policies vt is recomputed
3917            // from the head every round, post-draft pre-verify.
3918            if !vt_policy.is_confidence() && adapt {
3919                vt = (m + 2).clamp(3, vt_cap);
3920            }
3921        }
3922        if stats {
3923            let ms = |n: u64| n as f64 / 1e6;
3924            eprintln!(
3925                "[dspark-q38] acceptance {accepted}/{attempted} = {:.3} rounds={rounds} \
3926                 draft={:.1}ms snap={:.1}ms verify={:.1}ms rollback+replay={:.1}ms ingest={:.1}ms",
3927                accepted as f64 / attempted.max(1) as f64,
3928                ms(ns_draft),
3929                ms(ns_snap),
3930                ms(ns_verify),
3931                ms(ns_roll),
3932                ms(ns_ingest)
3933            );
3934        }
3935        Ok(out)
3936    }
3937}
3938
3939// ================= DSpark SERVING session (lane/dspark-q38-recover serve route) =========
3940// Burst-scoped state for the worker's dspark spec arm — the qwen-hybrid twin of
3941// GemmaSpecSession. Holds the trunk cache + draft KV + the round loop's carry state
3942// (`last`, ctx_len, adaptive vt) so the scheduler round-robins other sessions between
3943// bursts. The round body is generate_spec_dspark's loop, hoisted; that bin arm stays the
3944// banked oracle (E2E gate), and the serve-route smoke gates this twin byte-identical to
3945// a spec-off boot over the real HTTP surface. Exactness contract unchanged: the target's
3946// verify argmax decides every committed token, so the stream equals plain greedy BY
3947// CONSTRUCTION on every accept path (ckpt stash, gate, replay).
3948fn take_dspark_prefix_capture(
3949    slot: &mut Option<crate::spec::SpecBoundaryCapture>,
3950) -> Option<crate::spec::SpecBoundaryCapture> {
3951    slot.take()
3952}
3953
3954/// Deterministic preflight for the serving session's prompt-headroom requirement. Kept pure so
3955/// the worker can make the same decision before choosing whether to consume a prefix entry.
3956pub fn dspark_spec_prompt_fits(
3957    prompt_len: usize,
3958    ctx_cap: usize,
3959    block_size: usize,
3960    sliding_window: usize,
3961    is_dflash2: bool,
3962) -> bool {
3963    // PRIME FLOOR (incident 2026-08-25, second hit — the one that actually took prod down
3964    // twice). This predicate is the ONE admission gate the worker consumes for the dspark
3965    // route, and it only ever checked the ctx CEILING. A prompt shorter than
3966    // `PRIME_MIN_T` was therefore admitted and then panicked inside the cold prime, because
3967    // `prime_cache`'s batched arm asserts `T >= PRIME_MIN_T` and has no tokenwise twin that
3968    // fills the DFlash tap sink. A panic there is not a failed request: the GPU worker
3969    // exits 70 (poisoned-context contract) and every live session on the box dies, then the
3970    // guard relaunches into the same prompt — 20 panics and ~5 min of edge 502s on box10,
3971    // and a second loop on BOTH boxes when the route was redeployed. The trigger is
3972    // ordinary traffic: "Say OK." is 5 tokens, and our own watchdog sends that class.
3973    // Below the floor the route simply declines and the request serves on the plain path.
3974    if prompt_len < crate::hybrid_forward::PRIME_MIN_T {
3975        return false;
3976    }
3977    let max_ctx = if is_dflash2 {
3978        ctx_cap
3979    } else {
3980        ctx_cap.min(sliding_window)
3981    };
3982    prompt_len
3983        .checked_add(block_size)
3984        .and_then(|n| n.checked_add(8))
3985        .is_some_and(|need| need <= max_ctx)
3986}
3987
3988pub struct DsparkSpecSession {
3989    pub cache: crate::cache::Cache,
3990    /// One-shot prompt-end state for the worker's cross-request prefix cache. DFlash has no
3991    /// restorable draft plane, so this capture deliberately carries trunk snapshot + logits
3992    /// only; low-load DFlash requests ignore the resulting trunk-only entry while a later
3993    /// shed-to-plain request can consume it.
3994    prefix_capture: Option<crate::spec::SpecBoundaryCapture>,
3995    dkv: DflashKv,
3996    last: u32,
3997    ctx_len: usize,
3998    vt: usize,
3999    pub rounds: usize,
4000    max_ctx: usize,
4001    done: bool,
4002    /// Engine-bundle slice 1: persistent batched snapshot (buffers + pointer tables live
4003    /// with the session so bursts reuse them). None until the first round; stays None —
4004    /// legacy per-layer snapshot — when `snapb_off`.
4005    snapb: Option<DsparkSnapBatch>,
4006    snapb_off: bool,
4007    /// SAMPLED ADMISSION (T>0, lane/dspark-sampled-admission-20260820): the request's
4008    /// sampling config (None/temp==0 = the greedy route, byte-identical). Fixed for the
4009    /// session — the worker's admission owns the sampler identity.
4010    sampling: Option<crate::spec::SpecSampling>,
4011    /// Philox event counters, session-owned so randomness never repeats across bursts
4012    /// (the frspec session-continuity law): `sctr` = device sampling events (boundary,
4013    /// draft chain, bonus, residual), `uctr` = host uniforms (selector walk, accept tests).
4014    sctr: u32,
4015    uctr: u32,
4016    /// Penalized-sampled window (lane/dspark-penalized-sampled-20260821): seeded from
4017    /// the prompt tail (`pen_window_seed`), extended with every committed token, carried
4018    /// across bursts so a burst boundary never resets the stream the client asked us to
4019    /// penalize. Empty (and never touched) when the request carries no penalties.
4020    pen_hist: Vec<u32>,
4021}
4022
4023fn dspark_commit_limit(
4024    accepted_keep: usize,
4025    burst_out_len: usize,
4026    request_room: usize,
4027) -> (usize, bool) {
4028    let public_room = request_room.saturating_sub(burst_out_len);
4029    debug_assert!(public_room > 0);
4030    let keep = accepted_keep.min(public_room);
4031    (keep, keep < accepted_keep)
4032}
4033
4034impl DsparkSpecSession {
4035    /// How many trailing draft-KV rows a restore must carry for the drafter to be
4036    /// indistinguishable from one that cold-primed: the sliding window plus one block.
4037    ///
4038    /// WHY A TAIL IS SUFFICIENT, and why this is a fact about THIS export rather than a hope:
4039    /// every DFlash2 draft layer is `sliding_attention` (the port asserts
4040    /// `cfg.layer_sliding.iter().all(|&s| s)` at load and refuses otherwise), so the windowed
4041    /// SDPA never reads a key below the current block's window floor
4042    /// (`sdpa_naive_w_lo`, whose bit-identity at Tkv 4104 and legacy launch failure are both
4043    /// pinned by kernel_check). A round at context `pos` therefore reads rows
4044    /// `[pos - window + 1, pos + block)` and nothing older. Storing that tail is storing
4045    /// everything the drafter can observe.
4046    ///
4047    /// SIZE, the reason this is affordable at all: 5 layers x (2048 + 16) rows x 8 kv x 128
4048    /// dim x 4 B x 2 (k+v) is ~85 MB, against ~1,057 MB for the trunk planes of a
4049    /// 30k-token entry. Storing the FULL draft history instead would be ~1,229 MB — more than
4050    /// the trunk entry itself — which is what makes the tail the only viable form.
4051    pub fn draft_tail_rows(&self) -> usize {
4052        self.dkv.cfg_window_rows()
4053    }
4054
4055    /// The drafter's KV, for a worker publishing the tail into its cross-request prefix cache.
4056    pub fn draft_kv(&self) -> &DflashKv {
4057        &self.dkv
4058    }
4059}
4060
4061/// The tail-import refusal arms, PURE so they are testable without CUDA. These are the fence
4062/// in front of the deliberate uninitialised-rows-below-`base` design: rows the import does not
4063/// copy are unreadable ONLY if the tail actually covers the drafter's window ending exactly at
4064/// the logical length — every arm here is what makes that "only if" hold. A refusal that
4065/// silently stopped firing would let a session attend garbage without crashing, which is the
4066/// silent-quality-loss class, so each arm names itself.
4067#[allow(clippy::too_many_arguments)]
4068pub fn tail_geometry_ok(
4069    tail_layers: usize,
4070    tail_row_bytes: usize,
4071    tail_base: usize,
4072    tail_rows: usize,
4073    tail_len: usize,
4074    kv_layers: usize,
4075    kv_row_bytes: usize,
4076    kv_window_rows: usize,
4077    cap: usize,
4078) -> Result<(), &'static str> {
4079    if tail_layers != kv_layers {
4080        return Err("layer count differs from the live drafter");
4081    }
4082    if tail_row_bytes != kv_row_bytes {
4083        return Err("row geometry differs from the live drafter");
4084    }
4085    if tail_len > cap {
4086        return Err("logical length exceeds the session cap");
4087    }
4088    if tail_base + tail_rows != tail_len {
4089        return Err("tail does not end at its own logical length");
4090    }
4091    // The whole point of the tail: it must cover everything a round can read. A shorter
4092    // tail than the window is only acceptable when the tail IS the entire history.
4093    if tail_rows < kv_window_rows.min(tail_len) {
4094        return Err("tail shorter than the drafter's readable window");
4095    }
4096    Ok(())
4097}
4098
4099/// A DFlash draft-KV tail, per drafter layer, ready to ride a cross-request prefix-cache
4100/// entry: `(k, v)` f32 rows covering absolute positions `[base, base + rows)`.
4101///
4102/// Only the tail travels, and that is a fact about this export rather than an optimisation:
4103/// every DFlash2 draft layer is `sliding_attention` (the port asserts it at load), so a round
4104/// at context `pos` reads rows `[pos - window + 1, pos + block)` and nothing older. Storing
4105/// the whole history for a 30k-token prompt would be ~1,229 MB — MORE than the ~1,057 MB of
4106/// trunk planes it would ride with; the tail is ~85 MB.
4107pub struct DflashKvTail {
4108    pub layers: Vec<(CudaSlice<f32>, CudaSlice<f32>)>,
4109    /// Absolute position of the first stored row.
4110    pub base: usize,
4111    /// Rows stored per layer.
4112    pub rows: usize,
4113    /// Logical length the KV had when exported (`= pos`), so an import can restore the same
4114    /// absolute row addressing the rope positions were baked against.
4115    pub len: usize,
4116    /// Bytes per row per layer, carried so an import cannot disagree about the geometry.
4117    pub row_bytes: usize,
4118}
4119
4120impl DflashKvTail {
4121    pub fn bytes(&self) -> usize {
4122        self.layers.len() * self.rows * self.row_bytes * 2
4123    }
4124}
4125
4126impl DflashKv {
4127    /// Copy out the readable tail ending at `upto` (see `DflashKvTail`). `None` when there is
4128    /// nothing to publish or an allocation fails — publication is always optional.
4129    ///
4130    /// `upto` IS NOT `self.len`, and conflating them was the bug the first exactness-gate run
4131    /// caught: publication happens at the scheduler's drain sweep, by which time the session
4132    /// has committed generated rows, so `len` had run 35 rows past the capture boundary and
4133    /// every restore was refused with `draft KV len 30364 != prompt 30329`. The trunk planes
4134    /// are copied at the capture `pos` for the same reason; the tail must agree with them.
4135    pub fn export_tail(&self, e: &Engine, upto: usize) -> Option<DflashKvTail> {
4136        if upto == 0 || upto > self.len {
4137            return None;
4138        }
4139        let rowsz = self.row_bytes / std::mem::size_of::<f32>();
4140        let rows = self.window_rows.min(upto);
4141        let base = upto - rows;
4142        let mut layers = Vec::with_capacity(self.k.len());
4143        for li in 0..self.k.len() {
4144            let (Ok(mut k), Ok(mut v)) = (e.uninit(rows * rowsz), e.uninit(rows * rowsz)) else {
4145                return None;
4146            };
4147            if e.copy_range_into(&mut k, 0, &self.k[li], base * rowsz, rows * rowsz)
4148                .is_err()
4149                || e.copy_range_into(&mut v, 0, &self.v[li], base * rowsz, rows * rowsz)
4150                    .is_err()
4151            {
4152                return None;
4153            }
4154            layers.push((k, v));
4155        }
4156        Some(DflashKvTail {
4157            layers,
4158            base,
4159            rows,
4160            len: upto,
4161            row_bytes: self.row_bytes,
4162        })
4163    }
4164
4165    /// Rebuild a draft KV from a published tail: a fresh allocation at `cap`, the tail copied
4166    /// back to the SAME absolute rows it came from, and `len` restored so the next round
4167    /// addresses positions exactly as a cold-primed session would.
4168    ///
4169    /// Rows below `tail.base` are ZEROED, not left uninitialised. The clipped SDPA never reads
4170    /// below the block's window floor, but the legacy full-scan kernel
4171    /// (`MEMRA_DFLASH2_SDPA_CLIP=0`, the rollback seam) scans EVERY row into the score and the
4172    /// output, relying on masked rows contributing exactly zero — an identity that holds only
4173    /// for finite data (`0.0 * NaN = NaN`, and an uninit K row can produce a NaN score that
4174    /// poisons the softmax sum). Zeros keep that identity on both kernel arms, so a clip
4175    /// rollback on a restore-armed box stays byte-exact instead of decoding silent garbage
4176    /// (review round 3). Rows above `tail.len` stay uninit — equally unwritten and unread in
4177    /// the cold path, so restored matches cold there.
4178    ///
4179    /// This function still REFUSES rather than trusts the window math — if the tail does not
4180    /// cover the window, the caller gets `None` and must cold-prime.
4181    pub fn from_tail(e: &Engine, cfg: &DflashCfg, cap: usize, tail: &DflashKvTail) -> Option<Self> {
4182        let mut kv = Self::new(e, cfg, cap).ok()?;
4183        if let Err(why) = tail_geometry_ok(
4184            tail.layers.len(),
4185            tail.row_bytes,
4186            tail.base,
4187            tail.rows,
4188            tail.len,
4189            kv.k.len(),
4190            kv.row_bytes,
4191            kv.window_rows,
4192            cap,
4193        ) {
4194            eprintln!("[dspark] tail import refused: {why}");
4195            return None;
4196        }
4197        let rowsz = kv.row_bytes / std::mem::size_of::<f32>();
4198        for li in 0..kv.k.len() {
4199            let (src_k, src_v) = &tail.layers[li];
4200            if tail.base > 0 {
4201                // Finite zeros below the tail: the legacy full-scan kernel reads these rows
4202                // (see the doc above); NaN in either K or V poisons the row's contribution.
4203                e.memset_zeros_view(&mut kv.k[li].slice_mut(0..tail.base * rowsz))
4204                    .ok()?;
4205                e.memset_zeros_view(&mut kv.v[li].slice_mut(0..tail.base * rowsz))
4206                    .ok()?;
4207            }
4208            e.copy_range_into(
4209                &mut kv.k[li],
4210                tail.base * rowsz,
4211                src_k,
4212                0,
4213                tail.rows * rowsz,
4214            )
4215            .ok()?;
4216            e.copy_range_into(
4217                &mut kv.v[li],
4218                tail.base * rowsz,
4219                src_v,
4220                0,
4221                tail.rows * rowsz,
4222            )
4223            .ok()?;
4224        }
4225        kv.len = tail.len;
4226        Some(kv)
4227    }
4228
4229    /// Rows a restore must carry (see `DsparkSpecSession::draft_tail_rows`). Stored here
4230    /// because `DflashKv` owns the row geometry; the value comes from the drafter cfg.
4231    pub fn cfg_window_rows(&self) -> usize {
4232        self.window_rows
4233    }
4234
4235    /// Bytes per row per layer (`n_kv * head_dim * 4`), the unit both the export and the
4236    /// import address rows in.
4237    pub fn row_bytes(&self) -> usize {
4238        self.row_bytes
4239    }
4240
4241    /// Number of draft layers, i.e. how many per-layer planes an export produces.
4242    pub fn n_layer(&self) -> usize {
4243        self.k.len()
4244    }
4245}
4246
4247impl DsparkSpecSession {
4248    pub fn cache_max_ctx(&self) -> usize {
4249        self.max_ctx
4250    }
4251    pub fn finished(&self) -> bool {
4252        self.done
4253    }
4254    pub fn pos(&self) -> usize {
4255        self.cache.pos
4256    }
4257    /// Drain the prompt-end prefix capture exactly once. Publication is worker-owned so it can
4258    /// apply namespace isolation, dedupe and the shared byte budget at the scheduler boundary.
4259    pub fn take_prefix_capture(&mut self) -> Option<crate::spec::SpecBoundaryCapture> {
4260        take_dspark_prefix_capture(&mut self.prefix_capture)
4261    }
4262    /// DEMOTION HANDOFF (lane/dspark-spec-gate-demote, 2026-08-24): consume this session and
4263    /// hand its trunk cache + next-token prediction to the plain batched-decode path — the
4264    /// dspark twin of [`crate::spec::SpecSession::into_demoted`].
4265    ///
4266    /// WHY THIS IS EXACT (greedy). The burst-boundary invariant is `cache.pos == prompt rows
4267    /// + emitted tokens`: each round commits exactly `m+1` trunk rows (anchor + accepted
4268    /// drafts) and emits exactly those `m+1` tokens, so every emitted token has its KV row
4269    /// and nothing else does. `last` is the verify argmax at the LAST committed row — and
4270    /// verify-column argmax equality with plain decode is the very property the dspark E2E
4271    /// byte-identity gate pins (`dspark_q38_gate`: ALL EXACT). Handing (cache, last) to the
4272    ///   batched path therefore continues the stream from a state indistinguishable from one
4273    ///   the batched path produced itself.
4274    ///
4275    /// Unlike the MTP twin there is no carried-pending shape: the round commits its bonus
4276    /// inside the burst, so a session at a burst boundary is ALWAYS in handoff shape. The
4277    /// caller still cross-checks `pos()` against its fed-token count (a budget-clamped
4278    /// overshoot leaves cache rows past the public stream — those sessions finish, never
4279    /// demote). The draft KV, snapshot buffers and philox counters are DROPPED here
4280    /// (freeing their VRAM): the batched path never drafts, and the handoff is one-way.
4281    ///
4282    /// Sampled sessions must not be demoted (the caller excludes them, mirroring the MTP
4283    /// gate): their committed stream depends on the session-owned philox counters, and the
4284    /// plain batched sampler is a different random program mid-request.
4285    pub fn into_demoted(self) -> (crate::cache::Cache, u32) {
4286        (self.cache, self.last)
4287    }
4288}
4289
4290impl crate::hybrid::HybridModel {
4291    /// Turn-1 prime: trunk prefill with taps armed + chunked ctx ingest into the draft KV.
4292    /// Mirrors generate_spec_dspark's prime block exactly (chunk offsets via sink.base are
4293    /// handled inside prime_cache's tick loop; the 256-row ingest chunks match the bin arm).
4294    pub fn dspark_spec_session_new(
4295        &self,
4296        e: &Engine,
4297        draft: &DflashDraft,
4298        prompt: &[u32],
4299        ctx_cap: usize,
4300        sampling: Option<crate::spec::SpecSampling>,
4301        capture_prefix: bool,
4302    ) -> Result<DsparkSpecSession, Box<dyn std::error::Error>> {
4303        use crate::cache::{Cache, DflashTapSink};
4304        assert!(
4305            !self.uses_gemma_program(),
4306            "gemma4 targets use the assistant-drafter route; dspark is the qwen-hybrid arm"
4307        );
4308        // Penalized SAMPLED requests are IN scope (lane/dspark-penalized-sampled-20260821:
4309        // p-side penalties over the true per-state window, q the recorded proposal — the
4310        // accept walk's penalty arm). Penalties at temp==0 stay a LOUD refusal: the greedy
4311        // walk argmaxes RAW columns and would silently drop them — penalized greedy is
4312        // served exactly on the plain path (worker admission owns that exclusion).
4313        if let Some(sp) = sampling.as_ref()
4314            && sp.temp <= 0.0
4315            && sp.pen_on()
4316        {
4317            return Err(
4318                "dspark spec at temp==0 is the greedy route and would silently drop \
4319                     the request's penalties; penalized greedy is served on the plain path"
4320                    .into(),
4321            );
4322        }
4323        let n_embd = self.cfg.n_embd as usize;
4324        let c = &draft.cfg;
4325        assert_eq!(n_embd, c.hidden, "draft hidden must match target n_embd");
4326        let b = c.block_size;
4327        let n_taps = c.target_layer_ids.len();
4328        // The dspark round is windowless: every position the session will ever hold must
4329        // fit the draft window. Clamp the session ctx to it and refuse prompts that
4330        // cannot take even one round — admission falls back to the plain path.
4331        // DFlash2 rounds implement the reference's symmetric sliding window
4332        // (sdpa_naive_w), so its sessions take the full ctx cap.
4333        let is_dflash2 = draft.dflash2.is_some();
4334        let max_ctx = if is_dflash2 {
4335            ctx_cap
4336        } else {
4337            ctx_cap.min(c.sliding_window)
4338        };
4339        if !dspark_spec_prompt_fits(prompt.len(), ctx_cap, b, c.sliding_window, is_dflash2) {
4340            let need = prompt.len().saturating_add(b).saturating_add(8);
4341            return Err(format!(
4342                "dspark session needs {need} ctx (prompt {} + block {b} + 8), cap {max_ctx}",
4343                prompt.len()
4344            )
4345            .into());
4346        }
4347        let mut cache = Cache::new(e, &self.cfg, max_ctx)?;
4348        let tp = prompt.len();
4349        cache.dflash_taps = Some(DflashTapSink {
4350            layer_ids: c.target_layer_ids.clone(),
4351            buf: e.uninit(tp * n_taps * n_embd)?,
4352            hidden: n_embd,
4353            t: tp,
4354            base: 0,
4355        });
4356        let (logits, _h_seed, _hiddens) = self.prime_cache(e, prompt, &mut cache, 0)?;
4357        // Boundary token: greedy argmax (byte contract) or the request's own filtered
4358        // draw through the session Philox stream (the frspec boundary composition) —
4359        // penalized over the prompt window when the request carries penalties.
4360        let mut sctr0 = 0u32;
4361        let pen_hist: Vec<u32> = match sampling.as_ref().filter(|s| s.temp > 0.0 && s.pen_on()) {
4362            Some(sp) => crate::spec::pen_window_seed(&[], prompt, sp.penalty_last_n),
4363            None => Vec::new(),
4364        };
4365        let last = match sampling.as_ref().filter(|s| s.temp > 0.0) {
4366            Some(sp) => crate::spec::sample_boundary_token(
4367                e,
4368                &logits,
4369                sp,
4370                &pen_hist,
4371                &mut sctr0,
4372                "dspark-prime",
4373            )?,
4374            None => crate::forward::argmax(&logits) as u32,
4375        };
4376        let mut dkv = DflashKv::new(e, &draft.cfg, max_ctx)?;
4377        {
4378            let taps = cache.dflash_taps.take().unwrap();
4379            let n_taps_h = n_taps * n_embd;
4380            let mut r0 = 0usize;
4381            while r0 < tp {
4382                let t_c = (tp - r0).min(256);
4383                let tv = e.view(&taps.buf, tp * n_taps_h);
4384                let win = tv.slice(r0 * n_taps_h..(r0 + t_c) * n_taps_h);
4385                let mut chunk = e.uninit(t_c * n_taps_h)?;
4386                e.copy_view_into(&mut chunk, 0, &win, t_c * n_taps_h)?;
4387                let f = draft.ctx_features(e, &chunk, t_c)?;
4388                let pos_c: Vec<i32> = ((r0 as i32)..(r0 + t_c) as i32).collect();
4389                draft.ingest_ctx(e, &mut dkv, &f, &pos_c, t_c)?;
4390                r0 += t_c;
4391            }
4392        }
4393        e.stream().synchronize()?;
4394        // FULL-PROMPT ONLY. Unlike MTP, DFlash cannot restore its draft plane from a trunk
4395        // prefix, so there is no LCP/message-boundary split arm here. Mandatory draft-KV
4396        // allocation + ingest has already succeeded; the optional snapshot can no longer turn
4397        // a session that would have fit into a draft-allocation failure. Capture remains before
4398        // any speculative burst mutates the recurrent state.
4399        let prefix_capture = if capture_prefix {
4400            cache
4401                .snapshot(e)
4402                .ok()
4403                .map(|snap| crate::spec::SpecBoundaryCapture {
4404                    snap,
4405                    pos: tp,
4406                    logits: logits.clone(),
4407                    last_h: Vec::new(),
4408                    latent_tails: Vec::new(),
4409                })
4410        } else {
4411            None
4412        };
4413        // Verify carries [anchor, drafts] = up to n_drafts+1 rows (harvest-dependent;
4414        // DSPARK-POSTMORTEM-20260820.md; family-keyed for DFlash2, else checkpoint
4415        // strategy census).
4416        let nd = DsparkHarvest::for_draft(draft).n_drafts(b);
4417        let vt_cap: usize = std::env::var("MEMRA_DFLASH_VERIFY_T")
4418            .ok()
4419            .and_then(|v| v.parse().ok())
4420            .unwrap_or(nd + 1)
4421            .clamp(2, nd + 1);
4422        Ok(DsparkSpecSession {
4423            cache,
4424            prefix_capture,
4425            dkv,
4426            last,
4427            ctx_len: tp,
4428            vt: vt_cap,
4429            rounds: 0,
4430            max_ctx,
4431            done: false,
4432            snapb: None,
4433            snapb_off: !crate::spec::state_copy_batch_on(),
4434            sampling,
4435            sctr: sctr0,
4436            uctr: 0,
4437            pen_hist,
4438        })
4439    }
4440
4441    /// One scheduler burst: dspark rounds until >= `burst_target` tokens are committed,
4442    /// EOS lands, or the ctx cap is reached. `request_room` is the request's remaining
4443    /// public budget, which may be larger than the per-tick scheduler quantum. Returns
4444    /// (tokens, drafted, accepted) for this burst — mid-request quantum overshoot stays
4445    /// public, while only the true request boundary clamps the committed cache prefix.
4446    /// ADMISSION DEBT of this model's verify-graph pool, in bytes (lane/
4447    /// hermes-perf-fixes, 2026-08-23): the projected remaining growth the serve admission
4448    /// gate must reserve so sessions admitted while the pool is cold do not overcommit VRAM
4449    /// the pool will hold (it grows monotonically with no eviction by design — the pool's
4450    /// high-water is per-export and unknown until observed on the serving box; the 1.5 GiB
4451    /// SPEC_SHRINK_RESERVE never covered it). Projection contract and the self-measuring
4452    /// arithmetic live on [`crate::spec::dspark_vg_debt_projection`]; the observed bytes
4453    /// come from the device graph mem pool (`Engine::device_graph_mem_reserved`).
4454    ///
4455    /// CHARGED BY STRUCT, not by which route filled it (lane/graph-launch-guard-sweep-
4456    /// 20260831, fleet-peer refuted-read fix): the MTP spec route's verify-graph door
4457    /// (`MEMRA_SPEC_VERIFY_GRAPH`, family default for GDN+MoE) fills the SAME
4458    /// `dspark_vgraphs` pool with the same monotonic growth, and used to escape charging
4459    /// because the door check named only the dspark flags. 0 when EVERY door is closed
4460    /// (`MEMRA_DSPARK_VERIFY_GRAPH=0` and the MTP door off), frozen
4461    /// (`MEMRA_DSPARK_VG_MAX=0`), or the pool has not captured yet.
4462    pub fn dspark_vg_admission_debt(&self, e: &Engine) -> usize {
4463        let dspark_door =
4464            crate::spec::dspark_verify_graph_serve_on() || crate::spec::dspark_verify_graph_on();
4465        let mtp_door =
4466            crate::spec::spec_verify_graph_env().unwrap_or_else(|| self.vgraph_family_default());
4467        if !dspark_door && !mtp_door {
4468            return 0;
4469        }
4470        let reserved = e.device_graph_mem_reserved();
4471        self.dspark_vgraphs
4472            .lock()
4473            .unwrap()
4474            .as_mut()
4475            .map(|g| g.admission_debt(reserved))
4476            .unwrap_or(0)
4477    }
4478
4479    /// MULTI-TURN RESUME (lane/dflash2-session-reuse, 2026-08-25): continue a parked
4480    /// dspark session with the next turn's suffix — the dspark twin of the MTP pool
4481    /// resume. Trunk rows for the committed stream are already resident in `cache` and
4482    /// their ctx features in `dkv`, so turn N+1 primes ONLY its delta instead of
4483    /// re-priming the whole conversation (the route previously served every turn cold —
4484    /// a full-prompt prime whose cost grows with the conversation).
4485    ///
4486    /// EXACTNESS. The suffix prime is the same session-continuation `prime_cache` the
4487    /// serve path uses for split prompts and LCP restores (chunk N+1 attends chunk N's
4488    /// resident KV); the tap sink collects the suffix rows prompt-relative and the dkv
4489    /// ingest lands them at their absolute positions, exactly as the burst's per-round
4490    /// keep-ingest does. The boundary token re-derives as the cold prime does: greedy
4491    /// argmax of the suffix's last row, or the request's filtered draw through the
4492    /// SESSION's own Philox stream (`sctr` continues — the frspec session-continuity
4493    /// law), penalized over the session+suffix window. A resumed stream is therefore
4494    /// byte-identical to the stream a cold prime of the full concatenation produces —
4495    /// the verify arbitrates every committed token either way.
4496    ///
4497    /// EOS in the committed history is fine (a finished turn parks with EOS committed;
4498    /// the new user turn continues past it) — `done` resets here. Callers must pass a
4499    /// NON-EMPTY suffix for a `done` session (an empty-suffix continuation of a finished
4500    /// stream would re-emit from a terminal state); the worker's probe enforces it.
4501    /// Re-arm a dspark session from a RESTORED trunk cache plus a published draft tail —
4502    /// the long-answer half of lane/dspark-draft-plane-20260827.
4503    ///
4504    /// WHY THIS EXISTS. `dspark_spec_session_new` must prime the full prompt, because the draft
4505    /// KV derives from trunk hidden FEATURES the prime produces as a side effect. A cache hit
4506    /// returns trunk K/V, not features, so before this a speculating request had to discard even
4507    /// a full-prompt hit and re-prefill (~10 s at 30k tokens). With the drafter's readable tail
4508    /// travelling on the entry, both halves are restorable and the discard is unnecessary.
4509    ///
4510    /// WHY IT IS EQUIVALENT TO A COLD PRIME, field by field:
4511    /// * `cache` — the caller's restored trunk cache, already at `prompt.len()` with recurrent
4512    ///   state, which is why only WHOLE-ENTRY hits are eligible (a GDN trunk cannot rebuild
4513    ///   recurrent state mid-sequence, so there is no LCP arm here — same restriction as the
4514    ///   cold path's full-prompt-only rule).
4515    /// * `dkv` — byte-copied from the tail into the SAME absolute rows, so rope positions and
4516    ///   every row the windowed SDPA can read are identical to what the prime produced.
4517    /// * `last` — drawn from the entry's boundary logits with the request's own sampler, the
4518    ///   same composition the cold path applies to its prime logits.
4519    /// * `pen_hist` / `sctr` / `uctr` — seeded exactly as a cold session's are: the penalty
4520    ///   window from this prompt, the Philox counters fresh, because randomness is
4521    ///   session-owned by the frspec continuity law and a restore is a NEW session.
4522    /// * `prefix_capture` — `None`: the entry this restored FROM already exists, so
4523    ///   republishing the same key would be dropped by the worker's dedupe anyway.
4524    ///
4525    /// Refuses (rather than asserting) whenever the rebuilt draft KV and the cache disagree, so
4526    /// a caller that gets `Err` simply cold-primes.
4527    #[allow(clippy::too_many_arguments)]
4528    pub fn dspark_spec_session_from_restored(
4529        &self,
4530        e: &Engine,
4531        draft: &DflashDraft,
4532        cache: crate::cache::Cache,
4533        prompt: &[u32],
4534        // Draft KV ALREADY rebuilt from the entry's tail by the caller (`DflashKv::from_tail`)
4535        // while the prefix cache was borrowable. Taking the built KV rather than the tail is
4536        // what keeps the ~85 MB tail in the entry for other requests — `from_tail` copies OUT
4537        // of it, so no clone of the tail is ever needed.
4538        dkv: DflashKv,
4539        boundary_logits: &[f32],
4540        sampling: Option<crate::spec::SpecSampling>,
4541        ctx_cap: usize,
4542    ) -> Result<DsparkSpecSession, Box<dyn std::error::Error>> {
4543        assert!(
4544            !self.uses_gemma_program(),
4545            "gemma4 targets use the assistant-drafter route; dspark is the qwen-hybrid arm"
4546        );
4547        if let Some(sp) = sampling.as_ref()
4548            && sp.temp <= 0.0
4549            && sp.pen_on()
4550        {
4551            return Err("penalized greedy is served on the plain path".into());
4552        }
4553        let c = &draft.cfg;
4554        let b = c.block_size;
4555        let is_dflash2 = draft.dflash2.is_some();
4556        let max_ctx = if is_dflash2 {
4557            ctx_cap
4558        } else {
4559            ctx_cap.min(c.sliding_window)
4560        };
4561        let tp = prompt.len();
4562        if !dspark_spec_prompt_fits(tp, ctx_cap, b, c.sliding_window, is_dflash2) {
4563            return Err(format!("restored dspark session does not fit ctx {max_ctx}").into());
4564        }
4565        if cache.pos != tp {
4566            return Err(format!(
4567                "restored dspark session needs a whole-entry trunk cache: cache.pos {} !=                  prompt {tp}",
4568                cache.pos
4569            )
4570            .into());
4571        }
4572        if dkv.len != tp {
4573            return Err(format!("restored draft KV len {} != prompt {tp}", dkv.len).into());
4574        }
4575        if dkv.cap != max_ctx {
4576            return Err(
4577                format!("restored draft KV cap {} != session ctx {max_ctx}", dkv.cap).into(),
4578            );
4579        }
4580        if boundary_logits.is_empty() {
4581            return Err("restored dspark session needs the entry's boundary logits".into());
4582        }
4583        let mut sctr0 = 0u32;
4584        let pen_hist: Vec<u32> = match sampling.as_ref().filter(|s| s.temp > 0.0 && s.pen_on()) {
4585            Some(sp) => crate::spec::pen_window_seed(&[], prompt, sp.penalty_last_n),
4586            None => Vec::new(),
4587        };
4588        let last = match sampling.as_ref().filter(|s| s.temp > 0.0) {
4589            Some(sp) => crate::spec::sample_boundary_token(
4590                e,
4591                boundary_logits,
4592                sp,
4593                &pen_hist,
4594                &mut sctr0,
4595                "dspark-restore",
4596            )?,
4597            None => crate::forward::argmax(boundary_logits) as u32,
4598        };
4599        let nd = DsparkHarvest::for_draft(draft).n_drafts(b);
4600        let vt_cap: usize = std::env::var("MEMRA_DFLASH_VERIFY_T")
4601            .ok()
4602            .and_then(|v| v.parse().ok())
4603            .unwrap_or(nd + 1)
4604            .clamp(2, nd + 1);
4605        Ok(DsparkSpecSession {
4606            cache,
4607            prefix_capture: None,
4608            dkv,
4609            last,
4610            ctx_len: tp,
4611            vt: vt_cap,
4612            rounds: 0,
4613            max_ctx,
4614            done: false,
4615            snapb: None,
4616            snapb_off: !crate::spec::state_copy_batch_on(),
4617            sampling,
4618            sctr: sctr0,
4619            uctr: 0,
4620            pen_hist,
4621        })
4622    }
4623
4624    pub fn dspark_spec_session_resume(
4625        &self,
4626        e: &Engine,
4627        draft: &DflashDraft,
4628        sess: &mut DsparkSpecSession,
4629        suffix: &[u32],
4630    ) -> Result<(), Box<dyn std::error::Error>> {
4631        use crate::cache::DflashTapSink;
4632        let n_embd = self.cfg.n_embd as usize;
4633        let c = &draft.cfg;
4634        let b = c.block_size;
4635        let n_taps = c.target_layer_ids.len();
4636        let pos0 = sess.cache.pos;
4637        debug_assert_eq!(
4638            sess.ctx_len, pos0,
4639            "dspark resume: draft KV rows != trunk cache rows"
4640        );
4641        if suffix.is_empty() {
4642            return Err(
4643                "dspark resume needs a non-empty suffix (worker probe owns the \
4644                        empty-suffix exact-continuation case)"
4645                    .into(),
4646            );
4647        }
4648        // SHORT-SUFFIX FLOOR (incident 2026-08-25, box10 crash loop). The suffix prime goes
4649        // through `prime_cache`, which asserts `T >= PRIME_MIN_T` — the batched prefill arm
4650        // has no tokenwise twin that also fills the DFlash tap sink. A resumed turn shorter
4651        // than that floor (the watchdog's "Say OK." class, and any brief agent follow-up)
4652        // therefore PANICKED the GPU worker, which exits 70 and takes every session on the
4653        // box with it: 20 panics and ~5 minutes of 502s on box10 before MEMRA_REUSE_POOL=0
4654        // stopped it. The worker probe declines these before it ever gets here (its own
4655        // guard is the one that keeps the request on the cold path, which is exactly the
4656        // pre-lane behavior); this is the engine-side backstop so no future caller can
4657        // reintroduce the panic, and it is a refusal rather than an assert because a
4658        // too-short turn is ordinary traffic, not a bug.
4659        if suffix.len() < crate::hybrid_forward::PRIME_MIN_T {
4660            return Err(format!(
4661                "dspark resume suffix {} < PRIME_MIN_T {} (prime_cache has no tokenwise \
4662                 tap-filling twin); serve this turn cold",
4663                suffix.len(),
4664                crate::hybrid_forward::PRIME_MIN_T
4665            )
4666            .into());
4667        }
4668        let need = pos0
4669            .saturating_add(suffix.len())
4670            .saturating_add(b)
4671            .saturating_add(8);
4672        if need > sess.max_ctx {
4673            return Err(format!(
4674                "dspark resume needs {need} ctx (resident {pos0} + suffix {} + block {b} + 8), \
4675                 cap {}",
4676                suffix.len(),
4677                sess.max_ctx
4678            )
4679            .into());
4680        }
4681        let tp = suffix.len();
4682        sess.cache.dflash_taps = Some(DflashTapSink {
4683            layer_ids: c.target_layer_ids.clone(),
4684            buf: e.uninit(tp * n_taps * n_embd)?,
4685            hidden: n_embd,
4686            t: tp,
4687            base: 0,
4688        });
4689        let (logits, _h_seed, _hiddens) = self.prime_cache(e, suffix, &mut sess.cache, 0)?;
4690        let sp_pen = sess.sampling.filter(|s| s.temp > 0.0 && s.pen_on());
4691        if let Some(sp) = sp_pen.as_ref() {
4692            sess.pen_hist = crate::spec::pen_window_seed(&sess.pen_hist, suffix, sp.penalty_last_n);
4693        }
4694        let last = match sess.sampling.filter(|s| s.temp > 0.0) {
4695            Some(sp) => crate::spec::sample_boundary_token(
4696                e,
4697                &logits,
4698                &sp,
4699                &sess.pen_hist,
4700                &mut sess.sctr,
4701                "dspark-resume",
4702            )?,
4703            None => crate::forward::argmax(&logits) as u32,
4704        };
4705        {
4706            let taps = sess.cache.dflash_taps.take().unwrap();
4707            let n_taps_h = n_taps * n_embd;
4708            let mut r0 = 0usize;
4709            while r0 < tp {
4710                let t_c = (tp - r0).min(256);
4711                let tv = e.view(&taps.buf, tp * n_taps_h);
4712                let win = tv.slice(r0 * n_taps_h..(r0 + t_c) * n_taps_h);
4713                let mut chunk = e.uninit(t_c * n_taps_h)?;
4714                e.copy_view_into(&mut chunk, 0, &win, t_c * n_taps_h)?;
4715                let f = draft.ctx_features(e, &chunk, t_c)?;
4716                let pos_c: Vec<i32> = (((pos0 + r0) as i32)..((pos0 + r0 + t_c) as i32)).collect();
4717                draft.ingest_ctx(e, &mut sess.dkv, &f, &pos_c, t_c)?;
4718                r0 += t_c;
4719            }
4720        }
4721        e.stream().synchronize()?;
4722        sess.ctx_len += tp;
4723        sess.last = last;
4724        sess.done = false;
4725        Ok(())
4726    }
4727
4728    pub fn dspark_spec_session_burst(
4729        &self,
4730        e: &Engine,
4731        draft: &DflashDraft,
4732        sess: &mut DsparkSpecSession,
4733        burst_target: usize,
4734        request_room: usize,
4735        eos: &[u32],
4736    ) -> Result<(Vec<u32>, usize, usize), Box<dyn std::error::Error>> {
4737        use crate::cache::DflashTapSink;
4738        let n_embd = self.cfg.n_embd as usize;
4739        let c = &draft.cfg;
4740        let b = c.block_size;
4741        let n_taps = c.target_layer_ids.len();
4742        let n_vocab = self.output.out_features();
4743        // Harvest convention (DSPARK-POSTMORTEM-20260820.md) — identical to the bin arm
4744        // (family-keyed for DFlash2, else checkpoint strategy census; owner-ratified
4745        // flip 2026-08-20).
4746        let harvest = DsparkHarvest::for_draft(draft);
4747        let nd = harvest.n_drafts(b);
4748        let r0 = harvest.first_row();
4749        let vt_cap: usize = std::env::var("MEMRA_DFLASH_VERIFY_T")
4750            .ok()
4751            .and_then(|v| v.parse().ok())
4752            .unwrap_or(nd + 1)
4753            .clamp(2, nd + 1);
4754        let adapt = std::env::var("MEMRA_DFLASH_ADAPT").as_deref() != Ok("0");
4755        // Verify-window policy (H4, DSPARK-POSTMORTEM-20260820.md) — identical to the
4756        // bin arm: default = confidence-slot tau=.5 on a head-carrying checkpoint
4757        // (owner-ratified flip 2026-08-20); head-less (incl. the DFlash2 family) and
4758        // ADAPT=0 keep the ladder.
4759        let vt_policy = DsparkVtPolicy::resolve(draft.confidence.is_some());
4760        if vt_policy.is_confidence() {
4761            assert!(
4762                draft.confidence.is_some(),
4763                "MEMRA_DSPARK_VT={vt_policy:?} needs a checkpoint with an accept-rate \
4764                 head (confidence_head.* absent in this export)"
4765            );
4766        }
4767        // SAMPLED ADMISSION (T>0): session-fixed config; counters live on the session so
4768        // randomness never repeats across bursts. None/temp==0 = the greedy route.
4769        let sp_on: Option<crate::spec::SpecSampling> = sess.sampling.filter(|s| s.temp > 0.0);
4770        let pen_on = sp_on.as_ref().is_some_and(|s| s.pen_on());
4771        let mut out: Vec<u32> = Vec::with_capacity(burst_target + b);
4772        let mut drafted = 0usize;
4773        let mut accepted_n = 0usize;
4774        // Engine-bundle slice 2 — identical to the bin arm: deferred chain readback under
4775        // the stash arm with a resident embed table (ladder policy only).
4776        let defer_rb = crate::spec::dspark_defer_readback_on() && !vt_policy.is_confidence();
4777        let (embd_qt, embd_rb) = self.embd.qt_and_row_bytes(n_embd);
4778        let embd_gpu = if !defer_rb || crate::spec::spec_host_embd() {
4779            None
4780        } else {
4781            Some(
4782                self.embd_gpu
4783                    .get_or_init(|| e.upload_u8(&self.embd.raw).expect("embed table upload")),
4784            )
4785        };
4786        // Slice 3/4c SERVE ENGAGEMENT (graphs-serve lane; DSF-ROUNDCOST §9.3 -> §10): the
4787        // verify-graph pool lives on the MODEL (`dspark_vgraphs`, one per process) and its
4788        // keys — (segment, vt) and (vt, rung, hi) — carry NOTHING session-scoped, so ANY
4789        // session whose round matches a key replays the same capture (this is the
4790        // cache-reuse-pool the old bin-arm-only note asked for). Sharing is sound because
4791        // every per-session-varying address the captured bodies touch is indirect:
4792        // conv/ssm state and the ckpt stash resolve through the per-verify refreshed
4793        // pointer table (refresh_tables + copy_indirect_src_f32 — the slice-3
4794        // parity/lifetime law; a baked address is the known 12/12-divergence class), kv
4795        // bases through fa_table, residual/pos/tap through ctx-owned staging rewritten
4796        // every round; per-row t_kv derives in-kernel from pos_seq, and the per-round
4797        // host bookkeeping (parity swap, len bump) runs on THIS session's cache. The
4798        // guard spans the burst: the slab stash is live verify -> commit inside each
4799        // round, and the worker drives bursts from one scheduler thread
4800        // (step_dspark_spec), so sessions interleave at burst boundaries only.
4801        // DEFAULT ON on the serve route since the v0.103 train (owner-ratified
4802        // 2026-08-22, §10 re-gate at flip): MEMRA_DSPARK_VERIFY_GRAPH=0 is the
4803        // kill-switch that keeps this None — the eager walk, byte-identical (the
4804        // kill-switch arm of the serve battery). The bin arm keeps its own opt-in.
4805        let mut vg_guard = self.dspark_vgraphs.lock().unwrap();
4806        if vg_guard.is_none() && embd_gpu.is_some() && crate::spec::dspark_verify_graph_serve_on() {
4807            *vg_guard = crate::spec::DsparkVerifyGraphs::new(e, &sess.cache, vt_cap, n_embd)?;
4808            if vg_guard.is_some() {
4809                // Engagement receipt (the §8 dead-arm lesson): prove the door is LIVE on
4810                // the serve surface — S6b banked the tip server carrying zero door strings.
4811                eprintln!("[dspark-vg] serve pool ENGAGED (vt_cap={vt_cap})");
4812            }
4813        }
4814        let vgraphs: &mut Option<crate::spec::DsparkVerifyGraphs> = &mut vg_guard;
4815        'outer: while out.len() < burst_target && !sess.done {
4816            let start = sess.cache.pos;
4817            if start + nd + 1 > sess.max_ctx {
4818                sess.done = true;
4819                break;
4820            }
4821            sess.rounds += 1;
4822            let mut vt = sess.vt;
4823            // ---- draft: block = [last, MASK x b-1] (identical to the bin arm) ----
4824            // RAII: a `?` exit restores the pre-scope value instead of latching exact
4825            // ON engine-wide across every later request (hermes finding, fixed
4826            // 2026-08-23 — this burst had several `?`s between the manual true/false).
4827            let exact_scope = e.exact_scope(true);
4828            let mut block: Vec<u32> = vec![c.mask_token_id; b];
4829            block[0] = sess.last;
4830            let noise = e.htod(&self.embd.gather(n_embd, &block))?;
4831            let pos_block: Vec<i32> = ((start as i32)..(start + b) as i32).collect();
4832            let dh = draft.forward_round(e, &mut sess.dkv, &noise, &pos_block)?;
4833            // Harvest: logits over rows r0..r0+nd (see the bin arm / the postmortem).
4834            let mut rows = e.uninit(nd * n_embd)?;
4835            {
4836                let dv = e.view(&dh, b * n_embd);
4837                let src = dv.slice(r0 * n_embd..(r0 + nd) * n_embd);
4838                e.copy_view_into(&mut rows, 0, &src, nd * n_embd)?;
4839            }
4840            // TRIMMED DRAFT HEAD (lane/dflash2-head-trim, 2026-08-25): DFlash2 family
4841            // only — the selector consumes (value, candidate-id) pairs, so a d2t remap
4842            // after top-k restores true ids; the markov/chain arms argmax dl columns
4843            // into token ids DIRECTLY and must keep the full head. Reuses the FR-Spec
4844            // self-trim the load path builds on the MTP struct (MEMRA_FRSPEC_TRIM):
4845            // gathered rows of the target's own head, zero requant. Verify stays
4846            // full-vocab, so the trim moves draft acceptance only, never output.
4847            let trim = if draft.dflash2.is_some() {
4848                self.mtp
4849                    .as_ref()
4850                    .filter(|m| m.d2t_from_target_head)
4851                    .and_then(|m| m.shared_head_head.as_ref().zip(m.d2t.as_ref()))
4852                    // MEMRA_MTP_SKIP stub: the same target-head trimmed rows, parked in
4853                    // `dflash_trim` because the embedded MTP block was skipped (hybrid.rs;
4854                    // rows are target-head by construction; the loader refuses otherwise).
4855                    .or_else(|| self.dflash_trim.as_ref().map(|t| (&t.head, &t.d2t)))
4856                    .filter(|(_, d2t)| !d2t.is_empty())
4857            } else {
4858                None
4859            };
4860            let (dl_head, dl_vocab) = match trim {
4861                Some((head, d2t)) => (head, d2t.len()),
4862                None => (&self.output, n_vocab),
4863            };
4864            let trim_d2t = trim.map(|(_, d2t)| d2t.as_slice());
4865            let mut dl = e.matmul(dl_head, &rows, nd)?;
4866            // Family/sampling-keyed proposal — identical to the bin arm (see there for
4867            // the program law: sampled records the true q, DFlash2 rides the selector,
4868            // the markov/plain greedy chain keeps the slice-2 deferral). Confidence
4869            // policy: stash markov prev-token embeddings d2d during the chain, one host
4870            // readback after — identical to the bin arm.
4871            let want_conf_emb = vt_policy.is_confidence()
4872                && draft.confidence.as_ref().is_some_and(|ch| ch.with_markov);
4873            let mut conf_emb: Option<CudaSlice<f32>> = match (&draft.markov, want_conf_emb) {
4874                (Some(mk), true) => Some(e.uninit(nd * mk.rank)?),
4875                (None, true) => unreachable!(
4876                    "with_markov confidence head without a markov table — the loader forbids it"
4877                ),
4878                _ => None,
4879            };
4880            let mut cand: Vec<u32> = Vec::with_capacity(nd + 1);
4881            let mut prop: Option<DsparkDraftSample> = None;
4882            let mut chain_dev: Option<CudaSlice<u32>> = None;
4883            // Slice 2: arm choice read before the chain readback (see the bin arm; the
4884            // serve arm has no CKPT_GATE oracle — the bin arm carries it).
4885            let ckpt_on = std::env::var("MEMRA_DSPARK_CKPT").as_deref() != Ok("0");
4886            let mut deferred = false;
4887            if let Some(sp) = sp_on.as_ref() {
4888                // SAMPLED proposal (family-keyed; identical to the bin arm).
4889                let (tail, ds) = draft.dspark_propose_sampled(
4890                    e,
4891                    &mut dl,
4892                    &rows,
4893                    nd,
4894                    dl_vocab,
4895                    sess.last,
4896                    sp,
4897                    &mut sess.sctr,
4898                    &mut sess.uctr,
4899                    conf_emb.as_mut(),
4900                    trim_d2t,
4901                )?;
4902                drop(exact_scope);
4903                cand.push(sess.last);
4904                cand.extend_from_slice(&tail);
4905                prop = Some(ds);
4906            } else if draft.dflash2.is_some() {
4907                // DFlash2: candidate path selector replaces the markov chain
4908                // (identical to the bin arm).
4909                let path = draft
4910                    .dflash2_propose_greedy(e, &dl, &rows, nd, dl_vocab, sess.last, trim_d2t)?;
4911                drop(exact_scope);
4912                cand.push(sess.last);
4913                cand.extend_from_slice(&path);
4914            } else {
4915                let markov_on = std::env::var("MEMRA_DFLASH_MARKOV").as_deref() != Ok("0");
4916                let mut chain_d = e.stream().alloc_zeros::<u32>(nd + 1)?;
4917                if let (Some(mk), true) = (&draft.markov, markov_on) {
4918                    e.set_u32_one(&mut chain_d, sess.last)?;
4919                    for k in 0..nd {
4920                        let mut f = e.uninit(mk.rank)?;
4921                        e.gather_row_bf16(&mk.w1_bf16, &chain_d, k, &mut f, mk.rank)?;
4922                        if let Some(ce) = conf_emb.as_mut() {
4923                            let fv = e.view(&f, mk.rank);
4924                            e.copy_view_into(ce, k * mk.rank, &fv, mk.rank)?;
4925                        }
4926                        let bias = e.matmul(&mk.w2, &f, 1)?;
4927                        e.add_row_inplace(&mut dl, &bias, n_vocab, k * n_vocab)?;
4928                        e.argmax_token_device_col(&dl, k, n_vocab, &mut chain_d, k + 1)?;
4929                    }
4930                } else {
4931                    if want_conf_emb {
4932                        // chain_d[0] must carry the anchor — slot 0's prev token.
4933                        e.set_u32_one(&mut chain_d, sess.last)?;
4934                    }
4935                    for i in 0..nd {
4936                        if let (Some(ce), Some(mk)) = (conf_emb.as_mut(), &draft.markov) {
4937                            let mut f = e.uninit(mk.rank)?;
4938                            e.gather_row_bf16(&mk.w1_bf16, &chain_d, i, &mut f, mk.rank)?;
4939                            let fv = e.view(&f, mk.rank);
4940                            e.copy_view_into(ce, i * mk.rank, &fv, mk.rank)?;
4941                        }
4942                        e.argmax_token_device_col(&dl, i, n_vocab, &mut chain_d, i + 1)?;
4943                    }
4944                }
4945                drop(exact_scope);
4946                deferred = embd_gpu.is_some() && ckpt_on;
4947                chain_dev = Some(chain_d);
4948            }
4949            // ---- H4 confidence window: size THIS round's verify from the head ----
4950            if vt_policy.is_confidence() {
4951                let ch = draft.confidence.as_ref().expect("asserted at burst entry");
4952                let (rows_h, emb_h) = match conf_emb.as_ref() {
4953                    Some(ce) => {
4954                        let (a, b2) = e.dtoh_pair(&rows, ce)?;
4955                        (a, Some(b2))
4956                    }
4957                    None => (e.dtoh(&rows)?, None),
4958                };
4959                let rank = draft.markov.as_ref().map(|m| m.rank).unwrap_or(0);
4960                let mut raws = Vec::with_capacity(nd);
4961                for k in 0..nd {
4962                    let hrow = &rows_h[k * n_embd..(k + 1) * n_embd];
4963                    let emb = emb_h.as_ref().map(|eh| &eh[k * rank..(k + 1) * rank]);
4964                    raws.push(ch.raw_score(hrow, emb));
4965                }
4966                vt = vt_policy
4967                    .size_window(&raws, vt_cap)
4968                    .expect("confidence policies always size the window");
4969            }
4970            // Non-deferred greedy chain readback (the sampled and DFlash2 proposals
4971            // built `cand` at the walk; deferred rounds build it after the merged
4972            // readback — bytes identical, chain_d written before either sync).
4973            if let Some(chain_d) = chain_dev.as_ref()
4974                && !deferred
4975            {
4976                let chain = e.dtoh_u32(chain_d)?;
4977                cand.push(sess.last);
4978                cand.extend_from_slice(&chain[1..]);
4979            }
4980
4981            // ---- snapshot, then verify t=vt (ckpt stash default; oracle arms kept) ----
4982            // Slice 1: batched snap (see DsparkSnapBatch) with the legacy per-layer
4983            // snapshot as the kill-switch / non-uniform fallback.
4984            let mut snap_legacy: Option<crate::cache::CacheSnapshot> = None;
4985            if !sess.snapb_off && sess.snapb.is_none() {
4986                sess.snapb = DsparkSnapBatch::new(e, &sess.cache)?;
4987                sess.snapb_off = sess.snapb.is_none();
4988            } else if let Some(sb) = sess.snapb.as_mut() {
4989                sb.refresh(e, &sess.cache)?;
4990            }
4991            let snap: &crate::cache::CacheSnapshot = match sess.snapb.as_ref() {
4992                Some(sb) => &sb.snap,
4993                None => {
4994                    snap_legacy = Some(sess.cache.snapshot(e)?);
4995                    snap_legacy.as_ref().unwrap()
4996                }
4997            };
4998            let _ = &snap_legacy;
4999            // Slice 3: the tap-sink buffer is persistent per vt in the graphs ctx
5000            // (captured segments bake its address — a per-round alloc here would make
5001            // every session's replayed tap copies write freed memory); fully rewritten
5002            // by every verify, so pool ownership changes no bytes.
5003            let tap_buf = match vgraphs.as_mut().and_then(|g| g.tap_bufs.remove(&vt)) {
5004                Some(buf) => buf,
5005                None => e.uninit(vt * n_taps * n_embd)?,
5006            };
5007            sess.cache.dflash_taps = Some(DflashTapSink {
5008                layer_ids: c.target_layer_ids.clone(),
5009                buf: tap_buf,
5010                hidden: n_embd,
5011                t: vt,
5012                base: 0,
5013            });
5014            // Composition guard (sampled admission × model-owned pool, this train's
5015            // cross-product): the slab flag is a per-round statement, but only the
5016            // graphs-aware verify (`_am_ckpt_dev`) clears it. Serve sessions MIX arms
5017            // within one process-lifetime pool — a SAMPLED round rides the raw-logits
5018            // twins (no graphs param) and must not inherit `round_slab=true` from a
5019            // previous greedy session's captured round, or its commit is steered at
5020            // slabs the round never wrote. Clear at the round boundary; the deferred
5021            // arm re-derives it inside the verify. (The bin arm has the same shape but
5022            // fixes its sampling mode per process, so no mixed rounds exist there.)
5023            if let Some(g) = vgraphs.as_mut() {
5024                g.round_slab = false;
5025            }
5026            // The whole fallible verify window runs inside a closure so the Err path
5027            // can return the sink buffer to the ctx pool before propagating — the
5028            // serve-surface twin of the EOS-orphan lesson: a mid-verify error
5029            // propagates OUT of the burst, the request dies, the session's cache is
5030            // dropped — but the PROCESS (and the pool, with the tap-buffer address
5031            // baked into its captures) lives on. Recover the ctx-owned buffer before
5032            // the error escapes, or the next session's replayed tap copies write
5033            // freed memory. The bin arm has no such path (a gate-binary error ends
5034            // the process).
5035            #[allow(clippy::type_complexity)] // allow: one-shot composite type; naming it would hide the shape that matters at the call site
5036            let verify_out = (|| -> Result<
5037                (
5038                    Vec<u32>,
5039                    Option<CudaSlice<f32>>,
5040                    Option<crate::spec::DsparkVerifyCkpt>,
5041                ),
5042                Box<dyn std::error::Error>,
5043            > {
5044                if sp_on.is_some() {
5045                    // SAMPLED: raw verify logits for the rejection walk (bin-arm twin).
5046                    if ckpt_on {
5047                        let (tl, vck) = self.dspark_verify_t_logits_ckpt(
5048                            e,
5049                            &cand[..vt],
5050                            start,
5051                            &mut sess.cache,
5052                        )?;
5053                        Ok((Vec::new(), Some(tl), Some(vck)))
5054                    } else {
5055                        Ok((
5056                            Vec::new(),
5057                            Some(self.dspark_verify_t_logits(
5058                                e,
5059                                &cand[..vt],
5060                                start,
5061                                &mut sess.cache,
5062                            )?),
5063                            None,
5064                        ))
5065                    }
5066                } else if deferred {
5067                    // Slice 2: device-token verify + ONE merged readback (see the bin arm).
5068                    let chain_d = chain_dev.as_ref().expect("deferred implies greedy chain");
5069                    let g = embd_gpu.expect("deferred implies resident embed");
5070                    let (am_d, vck) = self.dspark_verify_t_am_ckpt_dev(
5071                        e,
5072                        chain_d,
5073                        vt,
5074                        start,
5075                        &mut sess.cache,
5076                        (g, embd_qt, embd_rb),
5077                        vgraphs.as_mut(),
5078                    )?;
5079                    let ch = e.stream().clone_dtoh(chain_d)?;
5080                    let am = e.stream().clone_dtoh(&am_d)?;
5081                    e.stream().synchronize()?;
5082                    cand.push(sess.last);
5083                    cand.extend_from_slice(&ch[1..]);
5084                    Ok((am, None, Some(vck)))
5085                } else if ckpt_on {
5086                    let (vam, vck) =
5087                        self.dspark_verify_t_am_ckpt(e, &cand[..vt], start, &mut sess.cache)?;
5088                    Ok((vam, None, Some(vck)))
5089                } else {
5090                    Ok((
5091                        self.dspark_verify_t_am(e, &cand[..vt], start, &mut sess.cache)?,
5092                        None,
5093                        None,
5094                    ))
5095                }
5096            })();
5097            let (vam, tl, vck) = match verify_out {
5098                Ok(v) => v,
5099                Err(err) => {
5100                    if let Some(taps) = sess.cache.dflash_taps.take()
5101                        && let Some(g) = vgraphs.as_mut()
5102                    {
5103                        g.tap_bufs.insert(vt, taps.buf);
5104                    }
5105                    return Err(err);
5106                }
5107            };
5108            let taps = sess.cache.dflash_taps.take().unwrap();
5109            // Return the tap buffer to the ctx pool IMMEDIATELY — an EOS/budget break
5110            // between accept and ingest must never orphan an address the captured graphs
5111            // bake (the bin arm's lesson, and it holds doubly here: the pool outlives
5112            // the SESSION, not just the round). Ingest reads it borrowed.
5113            let tap_local: Option<CudaSlice<f32>> = match vgraphs.as_mut() {
5114                Some(g) => {
5115                    g.tap_bufs.insert(vt, taps.buf);
5116                    None
5117                }
5118                None => Some(taps.buf),
5119            };
5120            let tap_ref: &CudaSlice<f32> = match &tap_local {
5121                Some(b) => b,
5122                None => &vgraphs.as_ref().expect("ctx present above").tap_bufs[&vt],
5123            };
5124
5125            // ---- accept ----
5126            // Penalized-sampled: anchor joins the window before the walk (committed this
5127            // round via the out.push below); accepted drafts extend it after — identical
5128            // to the bin arm.
5129            if pen_on {
5130                sess.pen_hist.push(sess.last);
5131            }
5132            let (m, next) = match (sp_on.as_ref(), tl.as_ref()) {
5133                (Some(sp), Some(tl)) => {
5134                    let w0 = sess
5135                        .pen_hist
5136                        .len()
5137                        .saturating_sub(sp.penalty_last_n.min(crate::spec::PEN_WINDOW_MAX));
5138                    dspark_accept_sampled(
5139                        e,
5140                        tl,
5141                        &cand,
5142                        vt,
5143                        n_vocab,
5144                        &dl,
5145                        prop.as_ref()
5146                            .expect("sampled round without a proposal record"),
5147                        sp,
5148                        &sess.pen_hist[w0..],
5149                        &mut sess.sctr,
5150                        &mut sess.uctr,
5151                    )?
5152                }
5153                _ => {
5154                    let m = dspark_accept_prefix(&cand, &vam, vt);
5155                    (m, vam[m])
5156                }
5157            };
5158            drafted += vt - 1;
5159            accepted_n += m;
5160            // keep = the rows this round adds to the PUBLIC stream. Without eos that is
5161            // the anchor + all accepted drafts (m+1). With eos it is the anchor + drafts
5162            // UP TO AND INCLUDING eos: the walk may accept real tokens past eos (they are
5163            // the model's own continuation), but emission stops at eos, and a parked
5164            // session whose cache holds rows past the public stream can never resume —
5165            // the park gate `pos() == fed` would refuse every eos-terminated stream
5166            // (measured: 7/8 turns on the mtreuse gate, overshoot 1-6 rows). Truncating
5167            // the commit at eos uses the SAME prefix-commit machinery as a mid-round
5168            // rejection, so the hybrid (GDN) state is exact by the same argument.
5169            // Emitted bytes are untouched — this only changes post-eos cache state.
5170            let mut keep = m + 1;
5171            let mut terminal = false;
5172            if eos.contains(&sess.last) {
5173                terminal = true;
5174                keep = 1;
5175            } else {
5176                for (j, &dt) in cand[1..=m].iter().enumerate() {
5177                    if eos.contains(&dt) {
5178                        terminal = true;
5179                        keep = j + 2; // anchor + drafts through eos
5180                        break;
5181                    }
5182                }
5183            }
5184            // The request's max_tokens boundary is also a commit boundary, not merely an
5185            // output slice. It is NOT the scheduler's smaller per-tick burst quantum: accepted
5186            // surplus crossing that quantum stays public and the session remains live. Only at
5187            // the true request boundary do we keep the publishable prefix so cache.pos == fed at
5188            // retire and mark the session terminal until a non-empty next-turn suffix resumes
5189            // it. This uses the same prefix-commit machinery as EOS/rejection and makes
5190            // max-token sessions safe to park instead of permanently cold (Hermes
5191            // `f22a180d1638b95a`).
5192            let (bounded_keep, budget_terminal) =
5193                dspark_commit_limit(keep, out.len(), request_room);
5194            keep = bounded_keep;
5195            terminal |= budget_terminal;
5196            out.push(sess.last);
5197            out.extend_from_slice(&cand[1..keep]);
5198            sess.done = terminal;
5199            if pen_on {
5200                // Only the PUBLIC drafts feed the penalty window — tokens accepted past
5201                // eos never reach the stream, and a resumed session must not penalize
5202                // ghosts (the parked pen_hist seeds the resume's window).
5203                sess.pen_hist.extend_from_slice(&cand[1..keep]);
5204            }
5205
5206            // ---- commit/rollback (stash arm default; replay oracle kept) ----
5207            // Slice 3: rounds whose linear column stash lives in the graphs ctx's slabs
5208            // commit through the slab twin (same semantics, slab-addressed sources) —
5209            // identical to the bin arm's dispatch.
5210            let slab_commit = vgraphs.as_ref().map(|g| g.round_slab).unwrap_or(false);
5211            if keep < vt {
5212                if slab_commit {
5213                    self.dspark_commit_prefix_slab(
5214                        e,
5215                        &mut sess.cache,
5216                        snap,
5217                        vgraphs.as_ref().expect("slab_commit implies ctx"),
5218                        keep,
5219                    )?;
5220                } else if let Some(vck) = vck.as_ref() {
5221                    self.dspark_commit_prefix(e, &mut sess.cache, snap, vck, keep)?;
5222                } else {
5223                    crate::pp::restore_cache_checkpoint(e, self, None, &mut sess.cache, snap)?;
5224                    debug_assert_eq!(sess.cache.pos, start, "rollback landed off the round start");
5225                    let ram = self.dspark_verify_t_am(e, &cand[..keep], start, &mut sess.cache)?;
5226                    if sp_on.is_none() {
5227                        // greedy-only oracle; the sampled arm replays to rebuild state.
5228                        debug_assert_eq!(
5229                            &ram[..],
5230                            &vam[..keep],
5231                            "prefix replay must reproduce the verify argmaxes"
5232                        );
5233                    }
5234                }
5235            }
5236
5237            // ---- ingest the kept rows' ctx features into the draft KV ----
5238            {
5239                let tv = e.view(tap_ref, vt * n_taps * n_embd);
5240                let keep_view = tv.slice(0..keep * n_taps * n_embd);
5241                let mut kept = e.uninit(keep * n_taps * n_embd)?;
5242                e.copy_view_into(&mut kept, 0, &keep_view, keep * n_taps * n_embd)?;
5243                let f = draft.ctx_features(e, &kept, keep)?;
5244                let pos_k: Vec<i32> =
5245                    ((sess.ctx_len as i32)..(sess.ctx_len + keep) as i32).collect();
5246                draft.ingest_ctx(e, &mut sess.dkv, &f, &pos_k, keep)?;
5247                sess.ctx_len += keep;
5248            }
5249            if sess.done {
5250                // EOS or the public budget landed this round: cache, draft KV and ctx_len are
5251                // all clamped to the public stream (park shape); `next` is beyond the terminal
5252                // boundary and must not become the anchor of a resumed session.
5253                break 'outer;
5254            }
5255            sess.last = next;
5256            // Ladder update only — the confidence policies recompute vt from the
5257            // head every round, post-draft pre-verify; their carry just keeps
5258            // observability (sess.vt = the last confidence-sized window).
5259            if vt_policy.is_confidence() {
5260                sess.vt = vt;
5261            } else if adapt {
5262                sess.vt = (m + 2).clamp(3, vt_cap);
5263            }
5264        }
5265        Ok((out, drafted, accepted_n))
5266    }
5267}
5268
5269// ================= Harvest-convention gate (CPU; DSPARK-POSTMORTEM-20260820.md) =========
5270// The parity oracle is row-count-agnostic (it reproduces the markov MODULE on whatever
5271// rows it is fed) and the E2E gate is harvest-independent (verify-side truth), so
5272// NEITHER can catch a wrong row->position mapping — that blindness is how the q38
5273// misalignment shipped. These tests pin the convention itself as logic the round
5274// consumes, so a mutation back to the mask-fill harvest under the Dspark variant fails
5275// HERE, naming the convention.
5276#[cfg(test)]
5277mod dflash2_tests {
5278
5279    /// The tail-import refusal arms (lane/dspark-draft-plane-20260827 review finding: these
5280    /// were claimed tested and were not). Pure, so they run everywhere; the geometry mirrors
5281    /// the served DFlash2 drafter (5 layers, 8 kv x 128 dim f32 rows, window 2048 + block 8).
5282    #[test]
5283    fn tail_import_refuses_every_geometry_disagreement_and_accepts_the_exported_shape() {
5284        let rb = 8 * 128 * 4; // n_kv * head_dim * f32
5285        let win = 2048 + 8; // window_rows = sliding_window + block
5286        // THE EXPORTED SHAPE: window_rows ending exactly at len, same geometry — accepted.
5287        assert!(
5288            super::tail_geometry_ok(5, rb, 30_329 - win, win, 30_329, 5, rb, win, 34_433).is_ok()
5289        );
5290        // A short history where the tail IS the whole history — accepted.
5291        assert!(super::tail_geometry_ok(5, rb, 0, 100, 100, 5, rb, win, 34_433).is_ok());
5292        // Every refusal arm, each by name:
5293        let arm =
5294            |l, r, b, rows, len, cap| super::tail_geometry_ok(l, r, b, rows, len, 5, rb, win, cap);
5295        assert_eq!(
5296            arm(4, rb, 30_329 - win, win, 30_329, 34_433).unwrap_err(),
5297            "layer count differs from the live drafter"
5298        );
5299        assert_eq!(
5300            arm(5, rb - 4, 30_329 - win, win, 30_329, 34_433).unwrap_err(),
5301            "row geometry differs from the live drafter"
5302        );
5303        assert_eq!(
5304            arm(5, rb, 30_329 - win, win, 30_329, 30_000).unwrap_err(),
5305            "logical length exceeds the session cap"
5306        );
5307        // THE RUN-2 BUG, pinned: a tail whose base+rows lands past its own logical length —
5308        // the export-at-current-length defect the gate caught on the box.
5309        assert_eq!(
5310            arm(5, rb, 30_364 - win, win, 30_329, 34_433).unwrap_err(),
5311            "tail does not end at its own logical length"
5312        );
5313        assert_eq!(
5314            arm(5, rb, 30_329 - (win - 100), win - 100, 30_329, 34_433).unwrap_err(),
5315            "tail shorter than the drafter's readable window"
5316        );
5317    }
5318
5319    use super::{
5320        DsparkHarvest, dflash2_walk_greedy, dflash2_walk_sampled, dspark_commit_limit,
5321        rejection_accept_len,
5322    };
5323
5324    #[test]
5325    fn max_tokens_caps_the_committed_prefix_not_only_the_visible_slice() {
5326        // A round crossing the scheduler's 32-token quantum is not terminal when the
5327        // request still has room. The whole accepted prefix stays public and committed.
5328        assert_eq!(dspark_commit_limit(5, 30, 100), (5, false));
5329        // The same round at the true request boundary is clamped and terminal so the
5330        // parked cache cannot contain rows the worker did not publish.
5331        assert_eq!(dspark_commit_limit(5, 30, 33), (3, true));
5332        assert_eq!(dspark_commit_limit(2, 3, 10), (2, false));
5333        assert_eq!(dspark_commit_limit(1, 0, 1), (1, false));
5334    }
5335
5336    /// f32 -> bf16 bytes (truncation; test values are bf16-exact small integers).
5337    fn bf16(vals: &[f32]) -> Vec<u8> {
5338        vals.iter()
5339            .flat_map(|v| ((v.to_bits() >> 16) as u16).to_le_bytes())
5340            .collect()
5341    }
5342
5343    const V: usize = 8; // test vocab
5344    const R: usize = 2; // selector rank
5345    const K: usize = 2; // top_k
5346
5347    /// Codebooks for the chain tests: pred rows are one-hot-ish, succ rows chosen so
5348    /// the slot-1 winner FLIPS with the slot-0 choice.
5349    #[allow(clippy::identity_op)] // allow: the explicit +0/*1/>>0 terms document the lane/byte symmetry of the reference layout
5350    fn books() -> (Vec<u8>, Vec<u8>) {
5351        let mut pred = vec![0f32; V * R];
5352        pred[0] = 1.0; // tok 0: [1, 0]  (the anchor)
5353        pred[1 * R + 1] = 1.0; // tok 1: [0, 1]
5354        pred[2 * R] = 1.0; // tok 2: [1, 0]
5355        let mut succ = vec![0f32; V * R];
5356        succ[1 * R] = 2.0; // tok 1: [2, 0]
5357        succ[2 * R + 1] = 5.0; // tok 2: [0, 5]
5358        succ[3 * R + 1] = 3.0; // tok 3: [0, 3]
5359        succ[4 * R] = 10.0; // tok 4: [10, 0]
5360        (bf16(&pred), bf16(&succ))
5361    }
5362
5363    #[test]
5364    fn selector_walk_is_a_chain_not_per_slot_argmax() {
5365        let (pred, succ) = books();
5366        // slot 0 candidates {1, 2}, slot 1 candidates {3, 4}; hproj all-ones.
5367        let cand: Vec<u32> = vec![1, 2, 3, 4];
5368        let hproj = vec![1.0f32; 2 * R];
5369        // Anchor 0 (pred [1,0]): slot 0 scores = <[1,0],succ> -> tok1: 2, tok2: 0
5370        // -> picks 1. Slot 1 must then walk from pred[1]=[0,1]: tok3 scores 3,
5371        // tok4 scores 0 -> picks 3. A mutation that seeds every slot from the ANCHOR
5372        // (pred[0]=[1,0]) scores tok3: 0 / tok4: 10 and picks 4 instead — the chain
5373        // IS the semantics (reference CandidateSelector.select: `predecessor` is the
5374        // previously CHOSEN candidate, seeded by anchor_ids).
5375        let path = dflash2_walk_greedy(&pred, &succ, V, R, K, &[0.0; 4], &cand, &hproj, 0, 2);
5376        assert_eq!(
5377            path,
5378            vec![1, 3],
5379            "walk must seed slot p from slot p-1's CHOSEN candidate \
5380             (z-lab model.py CandidateSelector.select)"
5381        );
5382    }
5383
5384    #[test]
5385    fn selector_walk_unary_term_participates() {
5386        let (pred, succ) = books();
5387        let cand: Vec<u32> = vec![1, 2, 3, 4];
5388        let hproj = vec![1.0f32; 2 * R];
5389        // unary +10 on slot-0 candidate 2 overrides the bilinear 2-vs-0 margin;
5390        // the chain then walks from pred[2]=[1,0] and slot 1 flips to tok 4.
5391        let path = dflash2_walk_greedy(
5392            &pred,
5393            &succ,
5394            V,
5395            R,
5396            K,
5397            &[0.0, 10.0, 0.0, 0.0],
5398            &cand,
5399            &hproj,
5400            0,
5401            2,
5402        );
5403        assert_eq!(
5404            path,
5405            vec![2, 4],
5406            "score = unary + bilinear (reference: `unary[:, position] + einsum(...)`); \
5407             dropping the unary term picks tok 1 here"
5408        );
5409    }
5410
5411    #[test]
5412    fn selector_walk_hidden_gate_participates() {
5413        let (pred, succ) = books();
5414        let cand: Vec<u32> = vec![1, 2, 3, 4];
5415        // hproj [0, .] zeroes the pred[0]=[1,0] gate for slot 0: tok1's bilinear 2
5416        // vanishes, and the unary tiebreak (+1 on tok2) decides. The chain from tok2
5417        // (pred [1,0]) with slot-1 hproj [1,1] then picks tok4 (10 vs 0).
5418        let hproj = vec![0.0f32, 1.0, 1.0, 1.0];
5419        let path = dflash2_walk_greedy(
5420            &pred,
5421            &succ,
5422            V,
5423            R,
5424            K,
5425            &[0.0, 1.0, 0.0, 0.0],
5426            &cand,
5427            &hproj,
5428            0,
5429            2,
5430        );
5431        assert_eq!(
5432            path,
5433            vec![2, 4],
5434            "the bilinear gate is pred_row .* HIDDEN_PROJECTION (reference: \
5435             `predecessor_codebook(predecessor) * hidden[:, position]`); ignoring \
5436             hproj leaves tok1's margin standing"
5437        );
5438    }
5439
5440    #[test]
5441    fn dflash2_harvest_is_census_keyed() {
5442        // DFlash2 is mask-fill BY CONSTRUCTION (reference dflash_generate harvests
5443        // rows 1-verify_size:; card: "7 draft tokens per verification step").
5444        assert_eq!(
5445            DsparkHarvest::for_family_value(true, None, false),
5446            DsparkHarvest::Dflash
5447        );
5448        assert_eq!(
5449            DsparkHarvest::for_family_value(true, Some("dflash"), false),
5450            DsparkHarvest::Dflash
5451        );
5452        // The family key BEATS the strategy census: a (hypothetical) DFlash2 export
5453        // whose config also strategy-censuses dspark still harvests mask-fill.
5454        assert_eq!(
5455            DsparkHarvest::for_family_value(true, None, true),
5456            DsparkHarvest::Dflash
5457        );
5458        // An env override to the SHIFTED harvest contradicts the census — REFUSE,
5459        // never re-key (the postmortem's misalignment class in reverse).
5460        assert!(
5461            std::panic::catch_unwind(|| DsparkHarvest::for_family_value(
5462                true,
5463                Some("dspark"),
5464                false
5465            ))
5466            .is_err(),
5467            "MEMRA_DSPARK_HARVEST=dspark on a DFlash2 checkpoint must refuse"
5468        );
5469        // Non-DFlash2 checkpoints ride the strategy-keyed resolution (env wins).
5470        assert_eq!(
5471            DsparkHarvest::for_family_value(false, Some("dspark"), false),
5472            DsparkHarvest::Dspark
5473        );
5474        assert_eq!(
5475            DsparkHarvest::for_family_value(false, None, false),
5476            DsparkHarvest::Dflash
5477        );
5478        assert_eq!(
5479            DsparkHarvest::for_family_value(false, None, true),
5480            DsparkHarvest::Dspark,
5481            "unset env on a DSPARK-strategy export must keep the ratified census flip"
5482        );
5483    }
5484
5485    // ============ SAMPLED ADMISSION (T>0) gates — lane/dspark-sampled-admission-20260820 =
5486    // The device kernels are oracled by sample_check (filter_stats/gumbel/residual arms);
5487    // these pin the HOST math the route ships — the selector's sampled walk, the accept
5488    // rule, and the round COMPOSITION (accept + residual + bonus must reproduce the target
5489    // distribution p exactly; a mis-composition leaves every kernel individually correct,
5490    // which is why the composition arm exists — sample_check arm 6's lesson).
5491
5492    #[test]
5493    fn sampled_walk_tiny_temp_matches_greedy() {
5494        // T->0 continuity: at tiny temperature the candidate softmax concentrates on the
5495        // argmax and the sampled walk must reproduce the greedy chain token-for-token
5496        // (the frspec gate-(1) shape). Same fixture as the chain test.
5497        let (pred, succ) = books();
5498        let cand: Vec<u32> = vec![1, 2, 3, 4];
5499        let hproj = vec![1.0f32; 2 * R];
5500        let greedy = dflash2_walk_greedy(&pred, &succ, V, R, K, &[0.0; 4], &cand, &hproj, 0, 2);
5501        let mut u = || 0.5f32;
5502        let (path, q_chosen, q_rows) = dflash2_walk_sampled(
5503            &pred, &succ, V, R, K, &[0.0; 4], &cand, &hproj, 0, 2, 1e-6, &mut u,
5504        );
5505        assert_eq!(
5506            path, greedy,
5507            "tiny-T sampled walk must equal the greedy chain"
5508        );
5509        assert_eq!(q_rows.len(), 2 * K);
5510        for (p, &q) in path.iter().zip(&q_chosen) {
5511            let _ = p;
5512            assert!(
5513                q > 0.999,
5514                "tiny-T chosen-candidate prob must be ~1, got {q}"
5515            );
5516        }
5517    }
5518
5519    #[test]
5520    fn sampled_walk_records_the_distribution_it_samples() {
5521        // The recorded q IS the proposal: per slot the q_rows sum to ~1, q_chosen is the
5522        // row value at the drawn candidate, and the CDF walk picks the candidate whose
5523        // cumulative bracket contains the uniform.
5524        let (pred, succ) = books();
5525        let cand: Vec<u32> = vec![1, 2, 3, 4];
5526        let hproj = vec![1.0f32; 2 * R];
5527        // slot-0 scores at anchor 0: tok1 = 2.0, tok2 = 0.0; at T=2.0 the softmax is
5528        // e^1/(e^1+e^0) ~= 0.731 for tok1.
5529        let q1 = (1f64.exp() / (1f64.exp() + 1.0)) as f32;
5530        for (u0, want0) in [(q1 - 0.01, 1u32), (q1 + 0.01, 2u32)] {
5531            let mut seq = vec![u0, 0.0f32].into_iter();
5532            let mut u = move || seq.next().unwrap();
5533            let (path, q_chosen, q_rows) = dflash2_walk_sampled(
5534                &pred, &succ, V, R, K, &[0.0; 4], &cand, &hproj, 0, 2, 2.0, &mut u,
5535            );
5536            assert_eq!(
5537                path[0], want0,
5538                "CDF walk must place u={u0} in the right candidate bracket"
5539            );
5540            let row0: f32 = q_rows[..K].iter().sum();
5541            assert!(
5542                (row0 - 1.0).abs() < 1e-5,
5543                "slot-0 q must sum to 1, got {row0}"
5544            );
5545            let ci = cand[..K].iter().position(|&c| c == path[0]).unwrap();
5546            assert_eq!(
5547                q_chosen[0], q_rows[ci],
5548                "q_chosen must be the recorded row prob of the drawn candidate"
5549            );
5550            assert!(
5551                (q_rows[0] - q1).abs() < 1e-4,
5552                "slot-0 tok1 prob must be softmax(scores/T), got {} want {q1}",
5553                q_rows[0]
5554            );
5555        }
5556    }
5557
5558    #[test]
5559    fn sampled_walk_chains_the_drawn_candidate() {
5560        // The chain conditions on the DRAWN candidate, not the argmax: forcing the
5561        // low-prob slot-0 candidate (tok 2) flips slot 1's winner (tok 4 over tok 3),
5562        // exactly like the greedy chain test — a walk that seeds every slot from the
5563        // anchor (or the argmax) fails here.
5564        let (pred, succ) = books();
5565        let cand: Vec<u32> = vec![1, 2, 3, 4];
5566        let hproj = vec![1.0f32; 2 * R];
5567        let mut seq = vec![0.99f32, 0.01].into_iter();
5568        let mut u = move || seq.next().unwrap();
5569        let (path, _, _) = dflash2_walk_sampled(
5570            &pred, &succ, V, R, K, &[0.0; 4], &cand, &hproj, 0, 2, 2.0, &mut u,
5571        );
5572        assert_eq!(path[0], 2, "u=0.99 must draw the low-prob candidate");
5573        assert_eq!(
5574            path[1], 4,
5575            "slot 1 must walk from pred[2] (the DRAWN token), which scores tok4 at 10 \
5576             — chaining from the anchor or the argmax picks tok3"
5577        );
5578    }
5579
5580    #[test]
5581    fn rejection_accept_walk_is_the_leviathan_rule() {
5582        // accept while u*q < p, strict, prefix-stop at the first reject.
5583        assert_eq!(
5584            rejection_accept_len(&[0.5, 0.5], &[0.5, 0.5], &[0.9, 0.9]),
5585            2
5586        );
5587        assert_eq!(
5588            rejection_accept_len(&[0.5, 0.5], &[0.5, 0.5], &[1.0, 0.0]),
5589            0
5590        );
5591        // u*q == p is a REJECT (strict <) — the frspec test byte-for-byte.
5592        assert_eq!(rejection_accept_len(&[0.25], &[0.5], &[0.5]), 0);
5593        // q == 0 with p > 0 accepts unconditionally (the skey exactness signature).
5594        assert_eq!(rejection_accept_len(&[1e-6], &[0.0], &[0.999]), 1);
5595        // prefix stop: slot 1 rejects, slot 2 never tested.
5596        assert_eq!(
5597            rejection_accept_len(&[0.9, 0.0, 0.9], &[0.1, 0.9, 0.1], &[0.5, 0.5, 0.5]),
5598            1
5599        );
5600    }
5601
5602    // ---- round composition: the committed-token distribution must equal the target p ----
5603    // CPU mirror of the shipped rule for the FIRST post-anchor slot: draft x ~ q, accept
5604    // iff u*q(x) < p(x) (rejection_accept_len — the shipped fn), else commit a residual
5605    // sample ~ norm(max(0, p - q)). The marginal of the committed token is exactly p —
5606    // for ANY q — which is the whole correctness claim of the route's sampled admission.
5607
5608    fn tv(a: &[f64], b: &[f64]) -> f64 {
5609        a.iter().zip(b).map(|(x, y)| (x - y).abs()).sum::<f64>() / 2.0
5610    }
5611
5612    /// One composed trial with an injectable accept rule; returns the committed token.
5613    #[allow(clippy::neg_cmp_op_on_partial_ord)] // allow: NaN must take this branch; !(a > b) is not a <= b under IEEE comparisons
5614    fn compose_once(
5615        p: &[f32],
5616        q: &[f32],
5617        u_draw: f32,
5618        u_accept: f32,
5619        u_resid: f32,
5620        invert_accept: bool,
5621        skip_q_in_residual: bool,
5622    ) -> usize {
5623        let n = p.len();
5624        // draft ~ q (CDF walk, the walk_sampled convention)
5625        let mut acc = 0f64;
5626        let mut x = n - 1;
5627        for (i, &qi) in q.iter().enumerate() {
5628            acc += qi as f64;
5629            if (u_draw as f64) < acc {
5630                x = i;
5631                break;
5632            }
5633        }
5634        let accepted = if invert_accept {
5635            !((u_accept as f64) * (q[x] as f64) < p[x] as f64)
5636        } else {
5637            rejection_accept_len(&p[x..=x], &q[x..=x], &[u_accept]) == 1
5638        };
5639        if accepted {
5640            return x;
5641        }
5642        // residual ~ norm(max(0, p - q)) (the device kernel's fixed-order CDF walk)
5643        let r: Vec<f64> = p
5644            .iter()
5645            .zip(q)
5646            .map(|(&pi, &qi)| {
5647                let qq = if skip_q_in_residual { 0.0 } else { qi as f64 };
5648                (pi as f64 - qq).max(0.0)
5649            })
5650            .collect();
5651        let total: f64 = r.iter().sum();
5652        let mut acc = 0f64;
5653        let target = u_resid as f64 * total;
5654        for (i, &ri) in r.iter().enumerate() {
5655            acc += ri;
5656            if acc >= target && ri > 0.0 {
5657                return i;
5658            }
5659        }
5660        n - 1
5661    }
5662
5663    fn compose_tv(q: &[f32], invert_accept: bool, skip_q_in_residual: bool) -> f64 {
5664        // target p: a spread-out 8-token distribution
5665        let p: Vec<f32> = vec![0.30, 0.22, 0.15, 0.12, 0.09, 0.06, 0.04, 0.02];
5666        let trials = 200_000usize;
5667        let mut counts = [0f64; V];
5668        for t in 0..trials {
5669            // three independent uniforms per trial off the host Philox stream
5670            let u_draw = crate::spec::host_u01(7, (t * 3) as u32);
5671            let u_accept = crate::spec::host_u01(7, (t * 3 + 1) as u32);
5672            let u_resid = crate::spec::host_u01(7, (t * 3 + 2) as u32);
5673            counts[compose_once(
5674                &p,
5675                q,
5676                u_draw,
5677                u_accept,
5678                u_resid,
5679                invert_accept,
5680                skip_q_in_residual,
5681            )] += 1.0;
5682        }
5683        let emp: Vec<f64> = counts.iter().map(|c| c / trials as f64).collect();
5684        let pf: Vec<f64> = p.iter().map(|&v| v as f64).collect();
5685        tv(&emp, &pf)
5686    }
5687
5688    #[test]
5689    fn sampled_round_composition_matches_the_target() {
5690        // Monte-Carlo floor at 200k draws over 8 tokens ~ 0.004 TV; bound 0.01.
5691        // (a) full-vocab q (the Rows families' shape), far from p;
5692        let q_rows: Vec<f32> = vec![0.02, 0.04, 0.06, 0.09, 0.12, 0.15, 0.22, 0.30];
5693        // (b) SPARSE candidate-set q (the DFlash2 selector shape: support on 2 of 8).
5694        let q_sparse: Vec<f32> = vec![0.0, 0.7, 0.0, 0.3, 0.0, 0.0, 0.0, 0.0];
5695        for (name, q) in [("rows", &q_rows), ("sparse", &q_sparse)] {
5696            let d = compose_tv(q, false, false);
5697            assert!(
5698                d < 0.01,
5699                "composition[{name}]: committed-token distribution must equal p \
5700                 (TV {d:.4} >= 0.01)"
5701            );
5702        }
5703    }
5704
5705    #[test]
5706    fn composition_teeth_inverted_accept_fails() {
5707        // DECISIVE teeth: the same harness with the accept inequality inverted must
5708        // MISS the target — otherwise the composition gate is vacuous.
5709        let q: Vec<f32> = vec![0.02, 0.04, 0.06, 0.09, 0.12, 0.15, 0.22, 0.30];
5710        let d = compose_tv(&q, true, false);
5711        assert!(
5712            d > 0.05,
5713            "inverted accept rule must fail the composition bound (TV {d:.4})"
5714        );
5715    }
5716
5717    #[test]
5718    fn composition_teeth_residual_without_q_fails() {
5719        // Sampling the reject slot from p instead of norm(max(0, p-q)) double-counts
5720        // the overlap mass min(p,q) — the committed distribution leaves p.
5721        let q: Vec<f32> = vec![0.0, 0.7, 0.0, 0.3, 0.0, 0.0, 0.0, 0.0];
5722        let d = compose_tv(&q, false, true);
5723        assert!(
5724            d > 0.05,
5725            "residual that skips the q subtraction must fail the bound (TV {d:.4})"
5726        );
5727    }
5728
5729    // ---- PENALIZED round composition (lane/dspark-penalized-sampled-20260821) ----
5730    // Multi-slot rounds where the penalty state EVOLVES within the round. The base trunk
5731    // logits are state-independent, so ALL context dependence flows through penalties —
5732    // the sharpest fixture for "verify row j's target is penalized by the tokens accepted
5733    // before j in the same round", and the proposal concentrates on ONE token, so the
5734    // dominant drafted block is a self-hit (a drafted token penalizing its own successor
5735    // — the within-round case a frozen round-start window cannot see). The reference is
5736    // EXACT (analytic chain p1(a)·p2(b|a), the plain sampler's semantics); the spec arm
5737    // is the shipped round rule — chain draw from q, `rejection_accept_len`, residual
5738    // norm(max(0, p−q)) at the reject slot, bonus from the one-past row on full accept —
5739    // with per-slot penalized p (mirroring penalize_logits_rows_inc_f32's window rule).
5740
5741    const PV: usize = 6;
5742    const PEN_REP: f32 = 1.6;
5743    const PEN_FREQ: f32 = 0.8;
5744    const PEN_PRESENT: f32 = 1.2;
5745
5746    fn pen_base() -> Vec<f32> {
5747        vec![1.5, 0.8, 0.3, -0.2, -0.7, -1.2]
5748    }
5749
5750    /// The proposal: heavy on token 0 so drafted blocks repeat it (the self-hit case).
5751    fn pen_q() -> Vec<f32> {
5752        vec![0.85, 0.06, 0.04, 0.03, 0.01, 0.01]
5753    }
5754
5755    /// CPU mirror of penalize_logits_f32 / the plain sampler's apply_penalties: first
5756    /// occurrence does the whole adjustment, cnt = occurrences in the window, rep
5757    /// divides positive logits and multiplies negative ones.
5758    fn pen_apply(logits: &mut [f32], window: &[u32]) {
5759        let mut seen: Vec<u32> = Vec::new();
5760        for &id in window {
5761            if seen.contains(&id) {
5762                continue;
5763            }
5764            seen.push(id);
5765            let cnt = window.iter().filter(|&&h| h == id).count() as f32;
5766            let v = &mut logits[id as usize];
5767            if *v > 0.0 {
5768                *v /= PEN_REP;
5769            } else {
5770                *v *= PEN_REP;
5771            }
5772            *v -= PEN_FREQ * cnt + PEN_PRESENT;
5773        }
5774    }
5775
5776    /// Penalized target at history `window` (temp 1.0, no truncation filters — those are
5777    /// orthogonal and covered by the unpenalized composition tests + kernel oracles).
5778    fn pen_target(window: &[u32]) -> Vec<f64> {
5779        let mut l = pen_base();
5780        pen_apply(&mut l, window);
5781        let mx = l.iter().cloned().fold(f32::NEG_INFINITY, f32::max) as f64;
5782        let ex: Vec<f64> = l.iter().map(|&v| ((v as f64) - mx).exp()).collect();
5783        let z: f64 = ex.iter().sum();
5784        ex.iter().map(|v| v / z).collect()
5785    }
5786
5787    /// Which penalty-arm mutation the harness runs — `None` is the shipped rule.
5788    #[derive(Clone, Copy, PartialEq)]
5789    enum PenMutation {
5790        None,
5791        /// All rows (walk + bonus) penalized with the ROUND-START window only — the
5792        /// within-round update dropped (the frspec per-round posture; what a flat
5793        /// `penalize_logits_rows` launch would ship).
5794        FrozenWindow,
5795        /// Reject-slot residual computed from the UNPENALIZED p (a raw-tlogits column
5796        /// copy instead of the penalized buffer).
5797        UnpenalizedResidual,
5798        /// Full-accept bonus drawn from the UNPENALIZED one-past row.
5799        UnpenalizedBonus,
5800    }
5801
5802    /// Emit `want` committed tokens through spec rounds of `k` drafts (the shipped round
5803    /// rule, penalty-aware) and return them. `hist0` = the pre-stream window (the prompt
5804    /// seed); uniforms come off the injected stream.
5805    fn pen_round_stream(
5806        hist0: &[u32],
5807        k: usize,
5808        want: usize,
5809        mutation: PenMutation,
5810        next_u: &mut dyn FnMut() -> f32,
5811    ) -> Vec<u32> {
5812        let q = pen_q();
5813        let mut hist: Vec<u32> = hist0.to_vec();
5814        let mut committed: Vec<u32> = Vec::new();
5815        while committed.len() < want {
5816            // draft k tokens ~ q (fixed-order CDF walk, the walk_sampled convention)
5817            let drafted: Vec<u32> = (0..k)
5818                .map(|_| {
5819                    let u = next_u() as f64;
5820                    let mut acc = 0f64;
5821                    let mut bi = 0usize;
5822                    for (i, &qi) in q.iter().enumerate() {
5823                        acc += qi as f64;
5824                        if u < acc {
5825                            bi = i;
5826                            break;
5827                        }
5828                    }
5829                    bi as u32
5830                })
5831                .collect();
5832            // per-slot penalized p at the drafted ids (row j's window = hist ++ drafted[..j])
5833            let pj: Vec<f32> = (0..k)
5834                .map(|j| {
5835                    let win: Vec<u32> = if mutation == PenMutation::FrozenWindow {
5836                        hist.clone()
5837                    } else {
5838                        hist.iter()
5839                            .copied()
5840                            .chain(drafted[..j].iter().copied())
5841                            .collect()
5842                    };
5843                    pen_target(&win)[drafted[j] as usize] as f32
5844                })
5845                .collect();
5846            let qj: Vec<f32> = drafted.iter().map(|&d| q[d as usize]).collect();
5847            let us: Vec<f32> = (0..k).map(|_| next_u()).collect();
5848            let m = rejection_accept_len(&pj, &qj, &us);
5849            committed.extend_from_slice(&drafted[..m]);
5850            hist.extend_from_slice(&drafted[..m]);
5851            let next: u32 = if m == k {
5852                // bonus ~ p at the one-past row (window carries the WHOLE drafted block)
5853                let win: Vec<u32> = if matches!(
5854                    mutation,
5855                    PenMutation::FrozenWindow | PenMutation::UnpenalizedBonus
5856                ) {
5857                    if mutation == PenMutation::UnpenalizedBonus {
5858                        Vec::new() // raw row: no penalties at all
5859                    } else {
5860                        hist[..hist.len() - m].to_vec() // round-start window
5861                    }
5862                } else {
5863                    hist.clone()
5864                };
5865                let p = pen_target(&win);
5866                let u = next_u() as f64;
5867                let mut acc = 0f64;
5868                let mut bi = PV - 1;
5869                for (i, &pi) in p.iter().enumerate() {
5870                    acc += pi;
5871                    if u < acc {
5872                        bi = i;
5873                        break;
5874                    }
5875                }
5876                bi as u32
5877            } else {
5878                // residual ~ norm(max(0, p_m − q)) at the reject slot's state
5879                let win: Vec<u32> = match mutation {
5880                    PenMutation::UnpenalizedResidual => Vec::new(),
5881                    PenMutation::FrozenWindow => hist[..hist.len() - m].to_vec(),
5882                    _ => hist.clone(),
5883                };
5884                let p = pen_target(&win);
5885                let r: Vec<f64> = p
5886                    .iter()
5887                    .zip(&q)
5888                    .map(|(&pi, &qi)| (pi - qi as f64).max(0.0))
5889                    .collect();
5890                let total: f64 = r.iter().sum();
5891                let target = next_u() as f64 * total;
5892                let mut acc = 0f64;
5893                let mut bi = PV - 1;
5894                for (i, &ri) in r.iter().enumerate() {
5895                    acc += ri;
5896                    if acc >= target && ri > 0.0 {
5897                        bi = i;
5898                        break;
5899                    }
5900                }
5901                bi as u32
5902            };
5903            committed.push(next);
5904            hist.push(next);
5905        }
5906        committed.truncate(want);
5907        committed
5908    }
5909
5910    /// Joint TV of the spec arm's first two committed tokens vs the EXACT penalized
5911    /// chain p1(a)·p2(b|a) — the plain sampler's distribution over the same two steps.
5912    fn pen_compose_tv(hist0: &[u32], k: usize, mutation: PenMutation) -> f64 {
5913        let trials = 300_000usize;
5914        let mut counts = vec![0f64; PV * PV];
5915        for t in 0..trials {
5916            // stride 64: a k<=2 round consumes <=2k+1 uniforms, <=2 rounds per trial
5917            let mut ctr = (t as u32) * 64;
5918            let mut next_u = move || {
5919                let u = crate::spec::host_u01(11, ctr);
5920                ctr = ctr.wrapping_add(1);
5921                u
5922            };
5923            let s = pen_round_stream(hist0, k, 2, mutation, &mut next_u);
5924            counts[s[0] as usize * PV + s[1] as usize] += 1.0;
5925        }
5926        let p1 = pen_target(hist0);
5927        let mut tv = 0f64;
5928        for a in 0..PV {
5929            let mut w: Vec<u32> = hist0.to_vec();
5930            w.push(a as u32);
5931            let p2 = pen_target(&w);
5932            for b in 0..PV {
5933                let refp = p1[a] * p2[b];
5934                tv += (counts[a * PV + b] / trials as f64 - refp).abs();
5935            }
5936        }
5937        tv / 2.0
5938    }
5939
5940    #[test]
5941    fn penalized_round_composition_matches_the_penalized_chain() {
5942        // MC floor at 300k trials over 36 cells ~ 0.004 TV; bound 0.01. Fixture (a):
5943        // k=2, empty prompt window — the drafted pair (0,0) dominates, so slot 2's
5944        // accept is the SELF-HIT case (its own predecessor was drafted this round).
5945        // Fixture (b): k=1, prompt window [1,1] — the bonus is the successor of a
5946        // same-round accepted draft, and cnt>1 exercises the freq×count path.
5947        for (name, hist0, k) in [
5948            ("k2-selfhit", vec![], 2usize),
5949            ("k1-bonus-successor", vec![1u32, 1u32], 1usize),
5950        ] {
5951            let d = pen_compose_tv(&hist0, k, PenMutation::None);
5952            eprintln!("penalized composition[{name}]: TV {d:.4} (bound 0.01)");
5953            assert!(
5954                d < 0.01,
5955                "penalized composition[{name}]: committed-token distribution must equal \
5956                 the penalized chain (TV {d:.4} >= 0.01)"
5957            );
5958        }
5959    }
5960
5961    #[test]
5962    fn penalized_composition_teeth_frozen_window_fails() {
5963        // DECISIVE teeth: penalizing every verify row with the ROUND-START window —
5964        // dropping the within-round penalty update, i.e. a flat penalize_logits_rows
5965        // launch where the route ships penalize_logits_rows_inc — must MISS the
5966        // penalized chain, or the composition gate cannot see the one thing this lane
5967        // adds over the frozen-window prior art.
5968        let d = pen_compose_tv(&[], 2, PenMutation::FrozenWindow);
5969        eprintln!("penalized teeth[frozen-window]: TV {d:.4} (must exceed 0.05)");
5970        assert!(
5971            d > 0.05,
5972            "within-round penalty update dropped (frozen round-start window) must FAIL \
5973             the composition bound (TV {d:.4})"
5974        );
5975    }
5976
5977    #[test]
5978    fn penalized_composition_teeth_unpenalized_residual_fails() {
5979        // The reject-slot residual must read the PENALIZED column: a raw-tlogits column
5980        // copy (p_raw − q) commits from the wrong measure. Non-empty prompt window so
5981        // even round-start reject slots hit the mutation (an empty-window fixture only
5982        // sees it on within-round rejects and the margin thins to ~0.055).
5983        let d = pen_compose_tv(&[1, 1], 2, PenMutation::UnpenalizedResidual);
5984        eprintln!("penalized teeth[unpenalized-residual]: TV {d:.4} (must exceed 0.05)");
5985        assert!(
5986            d > 0.05,
5987            "residual computed from the unpenalized p must FAIL the composition bound \
5988             (TV {d:.4})"
5989        );
5990    }
5991
5992    #[test]
5993    fn penalized_composition_teeth_unpenalized_bonus_fails() {
5994        // The full-accept bonus row must carry the whole drafted block in its window:
5995        // a raw one-past row draw commits the unpenalized measure right after a
5996        // same-round accept.
5997        let d = pen_compose_tv(&[1, 1], 1, PenMutation::UnpenalizedBonus);
5998        eprintln!("penalized teeth[unpenalized-bonus]: TV {d:.4} (must exceed 0.05)");
5999        assert!(
6000            d > 0.05,
6001            "bonus drawn from the unpenalized one-past row must FAIL the composition \
6002             bound (TV {d:.4})"
6003        );
6004    }
6005}
6006
6007#[cfg(test)]
6008mod dspark_harvest_tests {
6009    use super::{DsparkHarvest, DsparkVtPolicy, dspark_accept_prefix, dspark_strategy_census};
6010
6011    const B: usize = 7; // q38 arm-a block_size
6012
6013    #[test]
6014    fn dspark_strategy_requires_shifted_harvest() {
6015        let h = DsparkHarvest::Dspark;
6016        assert_eq!(
6017            h.first_row(),
6018            0,
6019            "DSPARK-strategy checkpoints (SpecForge OnlineDSparkModel, \
6020             training.strategy=dspark — the q38 arm-a export) supervise ALL rows with \
6021             SHIFTED labels: label_offsets = arange(1, block_size+1), i.e. the ANCHOR \
6022             row's output is draft 1 (specforge/algorithms/common/\
6023             dflash_family_model.py:816; sglang v0.5.17 dspark_draft.py:248,260). \
6024             Harvesting from row 1 re-opens the DSPARK-POSTMORTEM-20260820 slot \
6025             misalignment (accept 2.9 -> 1.43)."
6026        );
6027        assert_eq!(
6028            h.n_drafts(B),
6029            B,
6030            "DSpark harvests gamma = block_size drafts per round (sglang \
6031             dspark_config.py:269, verify_num_draft_tokens = gamma+1); b-1 is the \
6032             DFlash mask-fill count and drops the best-trained slot \
6033             (DSPARK-POSTMORTEM-20260820.md §3-H1)."
6034        );
6035        for row in 0..B {
6036            assert_eq!(
6037                h.trained_offset_of_row(row),
6038                row + 1,
6039                "OnlineDSparkModel trains row k to predict anchor+k+1 \
6040                 (dflash_family_model.py:816); a same-position (mask-fill) mapping \
6041                 here verifies every slot one position early — the postmortem's \
6042                 collapse."
6043            );
6044        }
6045    }
6046
6047    #[test]
6048    fn dflash_strategy_keeps_mask_fill_harvest() {
6049        // Guards the reverse mutation: z-lab dflash checkpoints (the gemma arm) are
6050        // mask-fill — row k FILLS anchor+k, the anchor row is loss-excluded
6051        // (dflash_family_model.py:453-472). Shifting THEM would break the gemma arm.
6052        let h = DsparkHarvest::Dflash;
6053        assert_eq!(h.first_row(), 1, "DFlash drafts start at mask row 1");
6054        assert_eq!(h.n_drafts(B), B - 1, "DFlash harvests block_size-1 drafts");
6055        for row in 1..B {
6056            assert_eq!(h.trained_offset_of_row(row), row);
6057        }
6058    }
6059
6060    #[test]
6061    fn every_candidate_verifies_the_position_its_row_was_trained_for() {
6062        // The round's invariant: draft candidate i (1-based; verified against the
6063        // trunk's prediction for anchor+i) is filled from drafter output row
6064        // first_row + i - 1. Alignment == that row was TRAINED for offset i.
6065        for h in [DsparkHarvest::Dflash, DsparkHarvest::Dspark] {
6066            for i in 1..=h.n_drafts(B) {
6067                let row = h.first_row() + i - 1;
6068                assert_eq!(
6069                    h.trained_offset_of_row(row),
6070                    i,
6071                    "{h:?}: candidate {i} rides row {row}, which is trained for \
6072                     offset {} — harvest misaligned",
6073                    h.trained_offset_of_row(row)
6074                );
6075            }
6076        }
6077    }
6078
6079    #[test]
6080    fn env_seam_parses_and_refuses() {
6081        assert_eq!(
6082            DsparkHarvest::from_env_value(None),
6083            DsparkHarvest::Dflash,
6084            "the ENV-ONLY parser keeps the historical arm; the ratified strategy-keyed \
6085             default lives in resolve_value (checkpoint census), not here"
6086        );
6087        assert_eq!(
6088            DsparkHarvest::from_env_value(Some("dspark")),
6089            DsparkHarvest::Dspark
6090        );
6091        assert_eq!(
6092            DsparkHarvest::from_env_value(Some("dflash")),
6093            DsparkHarvest::Dflash
6094        );
6095        assert!(
6096            std::panic::catch_unwind(|| DsparkHarvest::from_env_value(Some("shifted"))).is_err(),
6097            "unknown harvest values must REFUSE, not default"
6098        );
6099        assert_eq!(
6100            DsparkHarvest::from_name("dspark"),
6101            Some(DsparkHarvest::Dspark)
6102        );
6103        assert_eq!(
6104            DsparkHarvest::from_name("dflash"),
6105            Some(DsparkHarvest::Dflash)
6106        );
6107        assert_eq!(DsparkHarvest::from_name("mask-fill"), None);
6108    }
6109
6110    /// The owner-ratified default flips (2026-08-20). Each assertion names its
6111    /// evidence; mutating either resolve back to the old default fails these.
6112    #[test]
6113    fn ratified_default_harvest_is_strategy_keyed() {
6114        // DSPARK-strategy checkpoint + unset env = the shifted harvest (B1: accept
6115        // 1.38->2.41 agentic / 1.53->3.66 math, E2E ALL EXACT x5, interleaved x5).
6116        assert_eq!(
6117            DsparkHarvest::resolve_value(None, true),
6118            DsparkHarvest::Dspark,
6119            "owner-ratified 2026-08-20: unset env defaults a DSPARK-strategy \
6120             checkpoint to the shifted harvest (DSPARK-POSTMORTEM-20260820.md B1)"
6121        );
6122        // mask-fill checkpoint + unset env = the historical arm, byte-identical.
6123        assert_eq!(
6124            DsparkHarvest::resolve_value(None, false),
6125            DsparkHarvest::Dflash
6126        );
6127        assert_eq!(
6128            DsparkHarvest::resolve_value(Some(""), false),
6129            DsparkHarvest::Dflash
6130        );
6131        // Explicit env overrides the census in BOTH directions (the A/B seam).
6132        assert_eq!(
6133            DsparkHarvest::resolve_value(Some("dflash"), true),
6134            DsparkHarvest::Dflash
6135        );
6136        assert_eq!(
6137            DsparkHarvest::resolve_value(Some("dspark"), false),
6138            DsparkHarvest::Dspark
6139        );
6140        // Unknown values still REFUSE through the resolve path.
6141        assert!(
6142            std::panic::catch_unwind(|| DsparkHarvest::resolve_value(Some("shifted"), true))
6143                .is_err()
6144        );
6145    }
6146
6147    #[test]
6148    fn strategy_census_reads_the_checkpoint_not_the_env() {
6149        // The q38 arm-a export shape: both signals present.
6150        let q38 = r#"{"architectures": ["Qwen3DSparkModel"], "block_size": 7,
6151            "dflash_config": {"projector_type": "dspark", "markov_rank": 256}}"#;
6152        assert!(dspark_strategy_census(q38));
6153        // Either signal alone suffices.
6154        assert!(dspark_strategy_census(
6155            r#"{"architectures": ["Qwen3DSparkModel"]}"#
6156        ));
6157        assert!(dspark_strategy_census(
6158            r#"{"dflash_config": {"projector_type": "dspark"}}"#
6159        ));
6160        // A mask-fill DFlash export carries neither -> historical default.
6161        let dflash = r#"{"architectures": ["Qwen3DFlashModel"],
6162            "dflash_config": {"attention_mode": "gqa"}}"#;
6163        assert!(!dspark_strategy_census(dflash));
6164        assert!(!dspark_strategy_census("{}"));
6165    }
6166
6167    #[test]
6168    fn ratified_default_vt_is_confidence_slot_tau_half() {
6169        // Head-carrying checkpoint + unset env = confidence-slot tau=.5 (H4 cell 3:
6170        // the tau ladder's knee; cell 2: 93.9%/97.7% of fixed-8 accept at wall >=
6171        // the reactive ladder, exactness 11/11 ALL EXACT).
6172        assert_eq!(
6173            DsparkVtPolicy::resolve_value(None, None, None, true),
6174            DsparkVtPolicy::ConfidenceSlot { tau: 0.5 },
6175            "owner-ratified 2026-08-20: unset MEMRA_DSPARK_VT defaults to \
6176             confidence-slot tau=.5 on a head-carrying checkpoint (H4 cells 2-3)"
6177        );
6178        // tau env still steers the default arm (and a bad tau still refuses).
6179        assert_eq!(
6180            DsparkVtPolicy::resolve_value(None, Some("0.35"), None, true),
6181            DsparkVtPolicy::ConfidenceSlot { tau: 0.35 }
6182        );
6183        assert!(
6184            std::panic::catch_unwind(|| DsparkVtPolicy::resolve_value(
6185                None,
6186                Some("nan-ish"),
6187                None,
6188                true
6189            ))
6190            .is_err()
6191        );
6192        // Census: no accept-rate head -> nothing to schedule with -> ladder.
6193        assert_eq!(
6194            DsparkVtPolicy::resolve_value(None, None, None, false),
6195            DsparkVtPolicy::Ladder
6196        );
6197        // MEMRA_DFLASH_ADAPT=0 is an explicit fixed-window request: honored.
6198        assert_eq!(
6199            DsparkVtPolicy::resolve_value(None, None, Some("0"), true),
6200            DsparkVtPolicy::Ladder
6201        );
6202        // Explicit values keep their exact prior semantics through resolve.
6203        assert_eq!(
6204            DsparkVtPolicy::resolve_value(Some("ladder"), None, None, true),
6205            DsparkVtPolicy::Ladder
6206        );
6207        assert_eq!(
6208            DsparkVtPolicy::resolve_value(Some("confidence"), Some("0.35"), None, true),
6209            DsparkVtPolicy::Confidence { tau: 0.35 }
6210        );
6211        // Explicit confidence mode with ADAPT=0 stays a refusal.
6212        assert!(
6213            std::panic::catch_unwind(|| DsparkVtPolicy::resolve_value(
6214                Some("confidence-slot"),
6215                None,
6216                Some("0"),
6217                true
6218            ))
6219            .is_err()
6220        );
6221    }
6222
6223    /// End-to-end alignment fixture in miniature: a mock drafter whose row r argmaxes
6224    /// to token BASE + (its trained offset under the DSPARK strategy), and a mock trunk
6225    /// whose prediction for anchor+j is BASE + j. The DSpark harvest accepts the whole
6226    /// block; feeding the same drafter through the mask-fill harvest accepts ZERO —
6227    /// the postmortem's collapse reproduced as pure logic.
6228    #[test]
6229    fn dspark_trained_rows_through_mask_fill_harvest_accept_nothing() {
6230        const BASE: u32 = 1000;
6231        let anchor: u32 = BASE; // token at the round anchor position (offset 0)
6232        // trunk verify argmaxes: vam[j] = prediction for anchor offset j+1
6233        let vam: Vec<u32> = (1..=B as u32 + 1).map(|j| BASE + j).collect();
6234        // drafter rows trained under the DSPARK strategy: row r predicts offset r+1
6235        let dspark_trained_row_argmax =
6236            |r: usize| BASE + DsparkHarvest::Dspark.trained_offset_of_row(r) as u32;
6237
6238        // Correct (shifted) harvest: candidate i <- row i-1.
6239        let h = DsparkHarvest::Dspark;
6240        let mut cand = vec![anchor];
6241        for i in 1..=h.n_drafts(B) {
6242            cand.push(dspark_trained_row_argmax(h.first_row() + i - 1));
6243        }
6244        let vt = h.n_drafts(B) + 1;
6245        assert_eq!(
6246            dspark_accept_prefix(&cand, &vam, vt),
6247            vt - 1,
6248            "aligned harvest must accept the full block"
6249        );
6250
6251        // Mask-fill harvest of the SAME dspark-trained drafter: candidate i <- row i,
6252        // which was trained for offset i+1 — every slot one position late.
6253        let wrong = DsparkHarvest::Dflash;
6254        let mut cand_wrong = vec![anchor];
6255        for i in 1..=wrong.n_drafts(B) {
6256            cand_wrong.push(dspark_trained_row_argmax(wrong.first_row() + i - 1));
6257        }
6258        let vt_wrong = wrong.n_drafts(B) + 1;
6259        assert_eq!(
6260            dspark_accept_prefix(&cand_wrong, &vam, vt_wrong),
6261            0,
6262            "mask-fill harvest of a dspark-trained drafter verifies every slot against \
6263             a position the row was not trained for (DSPARK-POSTMORTEM-20260820.md)"
6264        );
6265    }
6266}
6267
6268// ================= Verify-window policy gate (CPU; H4, DSPARK-POSTMORTEM-20260820.md) ===
6269// Pins the confidence-vt semantics as logic the round consumes: cumprod survival over
6270// sigmoid scores, thresholded, anchor + kept drafts, floor 2 / cap vt_cap — and the env
6271// seam's refuse-on-ambiguity. Mutating the policy (per-slot threshold instead of
6272// survival, off-by-one on the anchor, silent unknown-value fallback) fails HERE.
6273#[cfg(test)]
6274mod dspark_vt_tests {
6275    use super::{ConfidenceHead, DsparkVtPolicy, dspark_confidence_vt, dspark_slot_confidence_vt};
6276
6277    /// Pre-sigmoid logit for a target probability: sigmoid(logit(p)) == p.
6278    fn logit(p: f32) -> f32 {
6279        (p / (1.0 - p)).ln()
6280    }
6281
6282    #[test]
6283    fn confidence_vt_is_cumprod_survival_not_per_slot_threshold() {
6284        // sigmoids = [0.9, 0.8, 0.9, ...]: every PER-SLOT score clears tau=0.5, but
6285        // cumulative survival sinks below it at slot 6 (0.9, 0.72, 0.648, 0.583,
6286        // 0.525, then 0.472 < 0.5) — the window must stop where the EXPECTED
6287        // accepted-prefix stops paying, not where a slot looks locally fine.
6288        let raws: Vec<f32> = [0.9, 0.8, 0.9, 0.9, 0.9, 0.9, 0.9]
6289            .iter()
6290            .map(|&p| logit(p))
6291            .collect();
6292        assert_eq!(
6293            dspark_confidence_vt(&raws, 0.5, 8),
6294            6,
6295            "keeps 5 drafts + anchor"
6296        );
6297        // Tighter threshold closes the window sooner; looser opens it to the cap.
6298        assert_eq!(
6299            dspark_confidence_vt(&raws, 0.7, 8),
6300            3,
6301            "tau=0.7 keeps 2 drafts"
6302        );
6303        assert_eq!(
6304            dspark_confidence_vt(&raws, 0.05, 8),
6305            8,
6306            "tau→0 = full block"
6307        );
6308    }
6309
6310    #[test]
6311    fn slot_arm_truncates_at_first_low_confidence_slot() {
6312        // Owner directive (2026-08-20): submit only the longest prefix whose EVERY
6313        // slot clears tau on its own sigmoid. On the survival test's raws
6314        // ([0.9, 0.8, 0.9 x5], tau=0.5) every slot clears per-slot, so the slot arm
6315        // opens the full block where survival stopped at 6 — the two stopping
6316        // statistics must stay distinct arms.
6317        let raws: Vec<f32> = [0.9, 0.8, 0.9, 0.9, 0.9, 0.9, 0.9]
6318            .iter()
6319            .map(|&p| logit(p))
6320            .collect();
6321        assert_eq!(dspark_slot_confidence_vt(&raws, 0.5, 8), 8);
6322        assert_eq!(dspark_confidence_vt(&raws, 0.5, 8), 6);
6323        // A low-confidence tail never enters verify: [0.9, 0.9, 0.3, 0.9, ...]
6324        // truncates at slot 3 REGARDLESS of the confident slots behind it — a kept
6325        // slot after a dropped one could never commit (prefix accept rule).
6326        let tail: Vec<f32> = [0.9, 0.9, 0.3, 0.9, 0.9, 0.9, 0.9]
6327            .iter()
6328            .map(|&p| logit(p))
6329            .collect();
6330        assert_eq!(
6331            dspark_slot_confidence_vt(&tail, 0.5, 8),
6332            3,
6333            "2 drafts + anchor"
6334        );
6335        // Tighter tau keeps less.
6336        assert_eq!(
6337            dspark_slot_confidence_vt(&tail, 0.95, 8),
6338            2,
6339            "floor at tau=0.95"
6340        );
6341    }
6342
6343    #[test]
6344    fn confidence_vt_floor_and_cap() {
6345        // A hopeless round still verifies ONE draft (the draft forward is paid;
6346        // vt=1 would guarantee an empty round at the same cost class).
6347        let cold: Vec<f32> = [0.1f32, 0.1, 0.1].iter().map(|&p| logit(p)).collect();
6348        assert_eq!(
6349            dspark_confidence_vt(&cold, 0.5, 8),
6350            2,
6351            "floor = anchor + 1 draft"
6352        );
6353        assert_eq!(
6354            dspark_slot_confidence_vt(&cold, 0.5, 8),
6355            2,
6356            "slot arm same floor"
6357        );
6358        // The MEMRA_DFLASH_VERIFY_T cap still binds a confident round.
6359        let hot: Vec<f32> = vec![logit(0.99); 7];
6360        assert_eq!(dspark_confidence_vt(&hot, 0.5, 5), 5, "vt_cap binds");
6361        assert_eq!(
6362            dspark_confidence_vt(&hot, 0.5, 8),
6363            8,
6364            "full block when confident"
6365        );
6366        assert_eq!(
6367            dspark_slot_confidence_vt(&hot, 0.5, 5),
6368            5,
6369            "slot arm same cap"
6370        );
6371        // No scores (defensive): floor.
6372        assert_eq!(dspark_confidence_vt(&[], 0.5, 8), 2);
6373        assert_eq!(dspark_slot_confidence_vt(&[], 0.5, 8), 2);
6374    }
6375
6376    #[test]
6377    fn vt_policy_env_seam_parses_and_refuses() {
6378        assert_eq!(
6379            DsparkVtPolicy::from_env_value(None, None, None),
6380            DsparkVtPolicy::Ladder,
6381            "default stays the shipped ladder — the H4 arm is opt-in"
6382        );
6383        assert_eq!(
6384            DsparkVtPolicy::from_env_value(Some(""), None, None),
6385            DsparkVtPolicy::Ladder
6386        );
6387        assert_eq!(
6388            DsparkVtPolicy::from_env_value(Some("ladder"), None, Some("0")),
6389            DsparkVtPolicy::Ladder,
6390            "ladder + ADAPT=0 = the fixed-window arm, untouched"
6391        );
6392        assert_eq!(
6393            DsparkVtPolicy::from_env_value(Some("confidence"), None, None),
6394            DsparkVtPolicy::Confidence { tau: 0.5 },
6395            "tau defaults to 0.5 (raw sigmoid, no STS sidecar — postmortem §3-H4)"
6396        );
6397        assert_eq!(
6398            DsparkVtPolicy::from_env_value(Some("confidence"), Some("0.35"), Some("1")),
6399            DsparkVtPolicy::Confidence { tau: 0.35 }
6400        );
6401        assert_eq!(
6402            DsparkVtPolicy::from_env_value(Some("confidence-slot"), Some("0.6"), None),
6403            DsparkVtPolicy::ConfidenceSlot { tau: 0.6 },
6404            "the owner-directive per-slot arm parses with the same tau env"
6405        );
6406        assert!(
6407            std::panic::catch_unwind(|| DsparkVtPolicy::from_env_value(
6408                Some("confidence-slot"),
6409                None,
6410                Some("0")
6411            ))
6412            .is_err(),
6413            "confidence-slot + MEMRA_DFLASH_ADAPT=0 must REFUSE like confidence"
6414        );
6415        assert!(
6416            std::panic::catch_unwind(|| DsparkVtPolicy::from_env_value(Some("static"), None, None))
6417                .is_err(),
6418            "unknown policy values must REFUSE, not default — a typo silently \
6419             reverting the window policy invalidates an A/B"
6420        );
6421        assert!(
6422            std::panic::catch_unwind(|| DsparkVtPolicy::from_env_value(
6423                Some("confidence"),
6424                None,
6425                Some("0")
6426            ))
6427            .is_err(),
6428            "confidence + MEMRA_DFLASH_ADAPT=0 is contradictory and must REFUSE"
6429        );
6430        for bad in ["0", "1", "1.5", "-0.1", "nan"] {
6431            assert!(
6432                std::panic::catch_unwind(|| DsparkVtPolicy::from_env_value(
6433                    Some("confidence"),
6434                    Some(bad),
6435                    None
6436                ))
6437                .is_err(),
6438                "tau={bad} must REFUSE (survival threshold lives in (0,1))"
6439            );
6440        }
6441    }
6442
6443    #[test]
6444    fn raw_score_matches_the_parity_gate_dot() {
6445        // The head is a raw linear proj over [hidden ; markov_prev_embedding] + b —
6446        // the exact stage-5 contract in dspark_q38_parity.rs.
6447        let ch = ConfidenceHead {
6448            w: vec![0.5, -1.0, 2.0, 0.25, -0.5],
6449            b: 0.125,
6450            in_dim: 5,
6451            with_markov: true,
6452        };
6453        let hidden = [1.0f32, 2.0, 3.0];
6454        let emb = [4.0f32, 8.0];
6455        let want = 0.125 + 0.5 * 1.0 - 1.0 * 2.0 + 2.0 * 3.0 + 0.25 * 4.0 - 0.5 * 8.0;
6456        assert_eq!(ch.raw_score(&hidden, Some(&emb)), want);
6457        let ch_plain = ConfidenceHead {
6458            w: vec![0.5, -1.0, 2.0],
6459            b: -0.25,
6460            in_dim: 3,
6461            with_markov: false,
6462        };
6463        let want_plain = -0.25 + 0.5 * 1.0 - 1.0 * 2.0 + 2.0 * 3.0;
6464        assert_eq!(ch_plain.raw_score(&hidden, None), want_plain);
6465    }
6466}
6467
6468#[cfg(test)]
6469mod dspark_prefix_capture_tests {
6470    use super::{dspark_spec_prompt_fits, take_dspark_prefix_capture};
6471
6472    /// INCIDENT REGRESSION (2026-08-25). This gate is the only admission check the dspark
6473    /// route has, and it checked the ctx ceiling ONLY — so a short prompt was admitted and
6474    /// then panicked in the cold prime (`prime_cache needs T >= 16`), inside the GPU worker
6475    /// thread, which exits 70 and kills every live session on the box. Two crash loops and
6476    /// ~5 minutes of customer 502s came from a 5-token "Say OK." — the class our own
6477    /// watchdog sends. The floor belongs HERE, in the gate, not in each caller.
6478    #[test]
6479    fn a_prompt_below_the_prime_floor_never_enters_the_dspark_route() {
6480        let floor = crate::hybrid_forward::PRIME_MIN_T;
6481        for short in [1usize, 5, floor - 1] {
6482            assert!(
6483                !dspark_spec_prompt_fits(short, 262_144, 8, 2_048, true),
6484                "a {short}-token prompt must decline to the plain path, not prime"
6485            );
6486        }
6487        // At and above the floor the route admits exactly as before (ceiling still applies).
6488        assert!(dspark_spec_prompt_fits(floor, 262_144, 8, 2_048, true));
6489        assert!(dspark_spec_prompt_fits(512, 262_144, 8, 2_048, true));
6490        assert!(!dspark_spec_prompt_fits(512, 300, 8, 2_048, true));
6491    }
6492
6493    #[test]
6494    fn session_prompt_preflight_matches_dflash2_and_windowed_caps() {
6495        // DFlash2 uses the request ctx cap: prompt + block + 8 fits exactly, one row less does not.
6496        assert!(dspark_spec_prompt_fits(96, 111, 7, 2_048, true));
6497        assert!(!dspark_spec_prompt_fits(96, 110, 7, 2_048, true));
6498
6499        // Legacy/windowed drafts are additionally bounded by their own sliding window.
6500        assert!(dspark_spec_prompt_fits(113, 8_192, 7, 128, false));
6501        assert!(!dspark_spec_prompt_fits(114, 8_192, 7, 128, false));
6502        assert!(!dspark_spec_prompt_fits(
6503            usize::MAX,
6504            usize::MAX,
6505            7,
6506            usize::MAX,
6507            true,
6508        ));
6509    }
6510
6511    #[test]
6512    fn prompt_end_capture_is_full_prompt_and_one_shot() {
6513        let prompt_len = 96;
6514        let mut slot = Some(crate::spec::SpecBoundaryCapture {
6515            snap: crate::cache::CacheSnapshot {
6516                kv_len: Vec::new(),
6517                tp_kv_len: Vec::new(),
6518                conv: Vec::new(),
6519                ssm: Vec::new(),
6520                pos: prompt_len,
6521            },
6522            pos: prompt_len,
6523            logits: vec![1.0, 2.0],
6524            last_h: Vec::new(),
6525            latent_tails: Vec::new(),
6526        });
6527
6528        let capture = take_dspark_prefix_capture(&mut slot).expect("first drain gets capture");
6529        assert_eq!(capture.pos, prompt_len, "capture is at full prompt end");
6530        assert_eq!(capture.snap.pos, prompt_len);
6531        assert!(
6532            capture.last_h.is_empty(),
6533            "DFlash publishes no hidden anchor"
6534        );
6535        assert!(
6536            take_dspark_prefix_capture(&mut slot).is_none(),
6537            "capture drains exactly once",
6538        );
6539    }
6540}
6541
6542#[cfg(test)]
6543mod dflash_precision_tests {
6544    use super::dflash_precision;
6545
6546    #[test]
6547    fn default_and_supported_precision_programs_are_explicit() {
6548        assert_eq!(dflash_precision(None), Ok("q4"));
6549        for prec in ["q4", "q8", "mixed", "bf16", "fc"] {
6550            assert_eq!(dflash_precision(Some(prec)), Ok(prec));
6551        }
6552    }
6553
6554    #[test]
6555    fn q5_and_typos_refuse_instead_of_silently_selecting_q8() {
6556        for prec in ["q5", "Q4", "", "typo"] {
6557            let err = dflash_precision(Some(prec)).unwrap_err();
6558            assert!(err.contains("want q4, q8, mixed, bf16, or fc"));
6559        }
6560    }
6561}
6562
6563#[cfg(test)]
6564mod dflash_tensor_contract_tests {
6565    use super::{
6566        validate_dflash_attention_geometry, validate_dflash_tensor, validate_layer_layout,
6567        validate_selector_top_k,
6568    };
6569    use memra_gguf::safetensors::StInfo;
6570
6571    #[test]
6572    fn named_tensor_contract_refuses_wrong_dtype_rank_and_shape_before_cuda() {
6573        let valid = StInfo {
6574            dtype: "BF16".into(),
6575            shape: vec![8, 4],
6576            data_offsets: [0, 64],
6577        };
6578        assert!(validate_dflash_tensor("w", &valid, &[4, 8]).is_ok());
6579
6580        let mut bad = valid.clone();
6581        bad.dtype = "F32".into();
6582        assert!(
6583            validate_dflash_tensor("w", &bad, &[4, 8])
6584                .unwrap_err()
6585                .contains("dtype")
6586        );
6587        bad = valid.clone();
6588        bad.shape = vec![32];
6589        assert!(
6590            validate_dflash_tensor("w", &bad, &[4, 8])
6591                .unwrap_err()
6592                .contains("shape")
6593        );
6594        assert!(
6595            validate_dflash_tensor("w", &valid, &[8, 4])
6596                .unwrap_err()
6597                .contains("expected")
6598        );
6599    }
6600
6601    #[test]
6602    fn attention_and_selector_geometry_refuse_before_cuda() {
6603        assert!(validate_dflash_attention_geometry(64, 8, 128).is_ok());
6604        assert!(validate_dflash_attention_geometry(63, 8, 128).is_err());
6605        assert!(validate_dflash_attention_geometry(64, 0, 128).is_err());
6606        assert!(validate_dflash_attention_geometry(usize::MAX, 1, 2).is_err());
6607        assert!(validate_selector_top_k(16, 128).is_ok());
6608        assert!(validate_selector_top_k(0, 128).is_err());
6609        assert!(validate_selector_top_k(129, 128).is_err());
6610        assert!(validate_layer_layout(&[true, true], 2).is_ok());
6611        assert!(validate_layer_layout(&[true], 2).is_err());
6612    }
6613}