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