From fa2c1bf41e6679de97adcba748f098a6fdae5277 Mon Sep 17 00:00:00 2001 From: Noah Gift Date: Thu, 17 Sep 2026 05:35:11 +0200 Subject: [PATCH 1/4] =?UTF-8?q?fix(cb200):=20the=200.68=20+2=20is=20real?= =?UTF-8?q?=20debt=20=E2=80=94=20one=20of=20the=20three=20additions=20was?= =?UTF-8?q?=20in=20scope=20(#PMAT-1098)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `pmat comply check` read 604 against a baseline of 602 on the release commit. The box had moved 3.40.1 -> 3.40.2, so the first question was whether the instrument moved rather than the tree. It did not: the last main commit of 2026-09-15 (7ee0ecaad, the commit the 602 was measured on) reads 602 under 3.40.2 as well, in its own detached worktree and its own index. The +2 is code that landed on 2026-09-16. The baseline is a count, not a roster, so the two definitions the report names are the worst-graded survivors, not the new ones. Diffing the two rosters out of pmat's own context.db (the derivation reproduces 602/481 files and 604 exactly) gives 3 added and 1 removed: + forward_qwen35.rs from_model_and_layers C+ (15) #3114 + evidence/.../apr_raw_logits.cpp main F (48) #3354 + evidence/.../raw_logits_compare.cpp main C+ (21) #3355 - infer/inference_result.rs validate_gpu_first_token B- (17) Only the first is inside this ticket's scope. It is fixed by extraction, no behaviour change: the qwen2/qwen35 metadata fallback chain becomes qwen35_ssm_meta, and the two per-layer struct literals become own_deltanet_layer / own_attention_layer. C+ (15) -> A+ (4), every helper A or A+, and forward_qwen35.rs now has no definition below B. Reading 604 -> 603. The function had no test, because it needs a real hybrid GGUF. The extraction leaves one that does not: qwen35_ssm_meta takes a metadata map, so four tests now pin the defaults, the qwen2-wins-over-qwen35 precedence, non-integer-falls-through, and the per-section RoPE defaulting — behaviours the inline closure chain encoded and nothing asserted. The remaining 1 over is the vendored llama.cpp reference harness under evidence/, which is outside scope_paths and is a policy call (refactor the artefact / exclude evidence/ / re-baseline to 603) rather than this ticket's. docs/audits/cb200-0.68-roster.md records both measurements, the roster diff, the before/after grades, and the three ways to close it. Pmat-Ticket: PMAT-1098 Co-Authored-By: Claude Fable 5.1 Co-Authored-By: Claude Opus 5 (1M context) --- .../gguf/inference/forward/forward_qwen35.rs | 485 ++++++++++++------ docs/audits/cb200-0.68-roster.md | 121 +++++ 2 files changed, 442 insertions(+), 164 deletions(-) create mode 100644 docs/audits/cb200-0.68-roster.md diff --git a/crates/aprender-serve/src/gguf/inference/forward/forward_qwen35.rs b/crates/aprender-serve/src/gguf/inference/forward/forward_qwen35.rs index b17a115e0f..b49aeb35bf 100644 --- a/crates/aprender-serve/src/gguf/inference/forward/forward_qwen35.rs +++ b/crates/aprender-serve/src/gguf/inference/forward/forward_qwen35.rs @@ -427,6 +427,235 @@ fn load_f32_vec(tensor_ref: &QuantizedTensorRef, data: &[u8]) -> Result .collect()) } +/// Metadata map of a GGUF file, as `GGUFModel::metadata` carries it. +type GGUFMetadata = std::collections::HashMap; + +/// The Gated `DeltaNet` shape Qwen3.5 records in GGUF metadata, under either the +/// `qwen2.*` or the `qwen35.*` key prefix. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) struct Qwen35SsmMeta { + pub(crate) head_k_dim: usize, + pub(crate) num_k_heads: usize, + pub(crate) num_v_heads: usize, + pub(crate) conv_kernel: usize, + pub(crate) rope_sections: [usize; 4], +} + +/// One unsigned metadata value, accepting either GGUF integer width. +fn metadata_usize(metadata: &GGUFMetadata, key: &str) -> Option { + match metadata.get(key) { + Some(crate::gguf::types::GGUFValue::UInt32(v)) => Some(*v as usize), + Some(crate::gguf::types::GGUFValue::Int32(v)) => Some(*v as usize), + _ => None, + } +} + +/// `key`, else `fallback`, else `default` — the `qwen2.*`/`qwen35.*` prefix pair. +fn metadata_usize_or(metadata: &GGUFMetadata, key: &str, fallback: &str, default: usize) -> usize { + metadata_usize(metadata, key) + .or_else(|| metadata_usize(metadata, fallback)) + .unwrap_or(default) +} + +/// The four M-RoPE section widths. Defaulted per section: a section the array does not +/// reach, or carries with a non-integer type, keeps its default. +fn rope_sections_from_metadata(metadata: &GGUFMetadata) -> [usize; 4] { + let mut rope_sections = [16, 24, 24, 0]; + if let Some(crate::gguf::types::GGUFValue::Array(arr)) = metadata + .get("qwen2.rope.dimension_sections") + .or_else(|| metadata.get("qwen35.rope.dimension_sections")) + { + for (i, val) in arr.iter().take(4).enumerate() { + if let crate::gguf::types::GGUFValue::UInt32(v) = val { + rope_sections[i] = *v as usize; + } else if let crate::gguf::types::GGUFValue::Int32(v) = val { + rope_sections[i] = *v as usize; + } + } + } + rope_sections +} + +/// Read the Gated `DeltaNet` shape, falling back to the Qwen3.5-0.8B defaults key by key. +pub(crate) fn qwen35_ssm_meta(metadata: &GGUFMetadata) -> Qwen35SsmMeta { + Qwen35SsmMeta { + head_k_dim: metadata_usize_or( + metadata, + "qwen2.ssm.state_size", + "qwen35.ssm.state_size", + 16, + ), + num_k_heads: metadata_usize_or( + metadata, + "qwen2.ssm.group_count", + "qwen35.ssm.group_count", + 1, + ), + num_v_heads: metadata_usize_or( + metadata, + "qwen2.ssm.time_step_rank", + "qwen35.ssm.time_step_rank", + 16, + ), + conv_kernel: metadata_usize_or( + metadata, + "qwen2.ssm.conv_kernel", + "qwen35.ssm.conv_kernel", + 4, + ), + rope_sections: rope_sections_from_metadata(metadata), + } +} + +/// The widths every hybrid layer's tensors are dequantised with. +struct Qwen35LayerDims { + hidden_dim: usize, + intermediate_dim: usize, + num_heads: usize, + num_kv_heads: usize, + num_v_heads: usize, + conv_dim: usize, + value_dim: usize, +} + +/// Own one Gated `DeltaNet` layer's tensors. +fn own_deltanet_layer( + d: &crate::gguf::qwen35_load::Qwen35DeltaNetLayer, + data: &[u8], + dims: &Qwen35LayerDims, +) -> Result { + Ok(Qwen35OwnedDeltaNetLayer { + attn_norm: load_f32_vec(&d.attn_norm, data)?, + attn_qkv: OwnedQuantizedTensor::from_ref_with_dims( + &d.attn_qkv, + data, + dims.hidden_dim, + dims.conv_dim, + ), + attn_gate: OwnedQuantizedTensor::from_ref_with_dims( + &d.attn_gate, + data, + dims.hidden_dim, + dims.value_dim, + ), + ssm_alpha: OwnedQuantizedTensor::from_ref_with_dims( + &d.ssm_alpha, + data, + dims.hidden_dim, + dims.num_v_heads, + ), + ssm_beta: OwnedQuantizedTensor::from_ref_with_dims( + &d.ssm_beta, + data, + dims.hidden_dim, + dims.num_v_heads, + ), + ssm_a: load_f32_vec(&d.ssm_a, data)?, + ssm_dt_bias: load_f32_vec(&d.ssm_dt_bias, data)?, + ssm_conv1d_weight: load_f32_vec(&d.ssm_conv1d_weight, data)?, + ssm_norm_weight: load_f32_vec(&d.ssm_norm_weight, data)?, + ssm_out: OwnedQuantizedTensor::from_ref_with_dims( + &d.ssm_out, + data, + dims.value_dim, + dims.hidden_dim, + ), + post_attention_norm: load_f32_vec(&d.post_attention_norm, data)?, + ffn_gate: OwnedQuantizedTensor::from_ref_with_dims( + &d.ffn_gate, + data, + dims.hidden_dim, + dims.intermediate_dim, + ), + ffn_up: OwnedQuantizedTensor::from_ref_with_dims( + &d.ffn_up, + data, + dims.hidden_dim, + dims.intermediate_dim, + ), + ffn_down: OwnedQuantizedTensor::from_ref_with_dims( + &d.ffn_down, + data, + dims.intermediate_dim, + dims.hidden_dim, + ), + }) +} + +/// Own one full-attention layer's tensors. The head width is `attn_q_norm`'s length +/// (256 for Qwen3.5 standard attention), not `hidden_dim / num_heads`, and `attn_q` is +/// gated, so it is twice as wide as the head fan-out. +fn own_attention_layer( + a: &crate::gguf::qwen35_load::Qwen35AttentionLayer, + data: &[u8], + dims: &Qwen35LayerDims, +) -> Result { + let attn_q_norm = load_f32_vec(&a.attn_q_norm, data)?; + let true_head_dim = attn_q_norm.len(); + + Ok(Qwen35OwnedAttentionLayer { + attn_norm: load_f32_vec(&a.attn_norm, data)?, + attn_q: OwnedQuantizedTensor::from_ref_with_dims( + &a.attn_q, + data, + dims.hidden_dim, + dims.num_heads * true_head_dim * 2, + ), + attn_k: OwnedQuantizedTensor::from_ref_with_dims( + &a.attn_k, + data, + dims.hidden_dim, + dims.num_kv_heads * true_head_dim, + ), + attn_v: OwnedQuantizedTensor::from_ref_with_dims( + &a.attn_v, + data, + dims.hidden_dim, + dims.num_kv_heads * true_head_dim, + ), + attn_q_norm, + attn_k_norm: load_f32_vec(&a.attn_k_norm, data)?, + attn_output: OwnedQuantizedTensor::from_ref_with_dims( + &a.attn_output, + data, + dims.num_heads * true_head_dim, + dims.hidden_dim, + ), + post_attention_norm: load_f32_vec(&a.post_attention_norm, data)?, + ffn_gate: OwnedQuantizedTensor::from_ref_with_dims( + &a.ffn_gate, + data, + dims.hidden_dim, + dims.intermediate_dim, + ), + ffn_up: OwnedQuantizedTensor::from_ref_with_dims( + &a.ffn_up, + data, + dims.hidden_dim, + dims.intermediate_dim, + ), + ffn_down: OwnedQuantizedTensor::from_ref_with_dims( + &a.ffn_down, + data, + dims.intermediate_dim, + dims.hidden_dim, + ), + }) +} + +/// Attention head width is `key_length` (= `attn_q_norm`'s width, 256), NOT +/// `hidden / heads` (128): the KV cache and the attention loop used 128-wide heads over +/// 256-wide q/k/v. A file with no full-attention layer keeps `fallback`. +fn attention_head_dim(layers: &[Qwen35OwnedLayer], fallback: usize) -> usize { + layers + .iter() + .find_map(|l| match l { + Qwen35OwnedLayer::Attention(a) => Some(a.attn_q_norm.len()), + Qwen35OwnedLayer::DeltaNet(_) => None, + }) + .unwrap_or(fallback) +} + impl<'a> Qwen35Model<'a> { /// Build the shared base (config, token embeddings, final norm, `lm_head`) without the /// dense layer loader, which refuses hybrid files. @@ -491,189 +720,49 @@ impl<'a> Qwen35Model<'a> { data: &[u8], ) -> Result { let refs = crate::gguf::qwen35_load::load_qwen35_layers(model, data)?; + let meta = qwen35_ssm_meta(&model.metadata); - let get_u32 = |key: &str| -> Result { - match model.metadata.get(key) { - Some(crate::gguf::types::GGUFValue::UInt32(v)) => Ok(*v as usize), - Some(crate::gguf::types::GGUFValue::Int32(v)) => Ok(*v as usize), - _ => Err(crate::RealizarError::InvalidShape { - reason: format!("Missing or invalid {}", key), - }), - } - }; - - let head_k_dim = get_u32("qwen2.ssm.state_size") - .or_else(|_| get_u32("qwen35.ssm.state_size")) - .unwrap_or(16); - let head_v_dim = head_k_dim; - let num_k_heads = get_u32("qwen2.ssm.group_count") - .or_else(|_| get_u32("qwen35.ssm.group_count")) - .unwrap_or(1); - let num_v_heads = get_u32("qwen2.ssm.time_step_rank") - .or_else(|_| get_u32("qwen35.ssm.time_step_rank")) - .unwrap_or(16); - let conv_kernel = get_u32("qwen2.ssm.conv_kernel") - .or_else(|_| get_u32("qwen35.ssm.conv_kernel")) - .unwrap_or(4); - let mut rope_sections = [16, 24, 24, 0]; - if let Some(crate::gguf::types::GGUFValue::Array(arr)) = model - .metadata - .get("qwen2.rope.dimension_sections") - .or_else(|| model.metadata.get("qwen35.rope.dimension_sections")) - { - for (i, val) in arr.iter().take(4).enumerate() { - if let crate::gguf::types::GGUFValue::UInt32(v) = val { - rope_sections[i] = *v as usize; - } else if let crate::gguf::types::GGUFValue::Int32(v) = val { - rope_sections[i] = *v as usize; - } - } - } - - let hidden_dim = base.config.hidden_dim; - let intermediate_dim = base.config.intermediate_dim; + let head_v_dim = meta.head_k_dim; let num_heads = base.config.num_heads; let num_kv_heads = base.config.num_kv_heads; - let head_dim = hidden_dim / num_heads; + let head_dim = base.config.hidden_dim / num_heads; - let key_dim = head_k_dim * num_k_heads; - let value_dim = head_v_dim * num_v_heads; - let conv_dim = key_dim * 2 + value_dim; + let key_dim = meta.head_k_dim * meta.num_k_heads; + let value_dim = head_v_dim * meta.num_v_heads; + let dims = Qwen35LayerDims { + hidden_dim: base.config.hidden_dim, + intermediate_dim: base.config.intermediate_dim, + num_heads, + num_kv_heads, + num_v_heads: meta.num_v_heads, + conv_dim: key_dim * 2 + value_dim, + value_dim, + }; let mut owned = Vec::with_capacity(refs.len()); for layer_ref in refs { - match layer_ref { + owned.push(match layer_ref { crate::gguf::qwen35_load::Qwen35Layer::DeltaNet(d) => { - owned.push(Qwen35OwnedLayer::DeltaNet(Qwen35OwnedDeltaNetLayer { - attn_norm: load_f32_vec(&d.attn_norm, data)?, - attn_qkv: OwnedQuantizedTensor::from_ref_with_dims( - &d.attn_qkv, - data, - hidden_dim, - conv_dim, - ), - attn_gate: OwnedQuantizedTensor::from_ref_with_dims( - &d.attn_gate, - data, - hidden_dim, - value_dim, - ), - ssm_alpha: OwnedQuantizedTensor::from_ref_with_dims( - &d.ssm_alpha, - data, - hidden_dim, - num_v_heads, - ), - ssm_beta: OwnedQuantizedTensor::from_ref_with_dims( - &d.ssm_beta, - data, - hidden_dim, - num_v_heads, - ), - ssm_a: load_f32_vec(&d.ssm_a, data)?, - ssm_dt_bias: load_f32_vec(&d.ssm_dt_bias, data)?, - ssm_conv1d_weight: load_f32_vec(&d.ssm_conv1d_weight, data)?, - ssm_norm_weight: load_f32_vec(&d.ssm_norm_weight, data)?, - ssm_out: OwnedQuantizedTensor::from_ref_with_dims( - &d.ssm_out, data, value_dim, hidden_dim, - ), - post_attention_norm: load_f32_vec(&d.post_attention_norm, data)?, - ffn_gate: OwnedQuantizedTensor::from_ref_with_dims( - &d.ffn_gate, - data, - hidden_dim, - intermediate_dim, - ), - ffn_up: OwnedQuantizedTensor::from_ref_with_dims( - &d.ffn_up, - data, - hidden_dim, - intermediate_dim, - ), - ffn_down: OwnedQuantizedTensor::from_ref_with_dims( - &d.ffn_down, - data, - intermediate_dim, - hidden_dim, - ), - })); + Qwen35OwnedLayer::DeltaNet(own_deltanet_layer(&d, data, &dims)?) }, crate::gguf::qwen35_load::Qwen35Layer::Attention(a) => { - let attn_q_norm = load_f32_vec(&a.attn_q_norm, data)?; - let true_head_dim = attn_q_norm.len(); // 256 for Qwen3.5 standard attention - - owned.push(Qwen35OwnedLayer::Attention(Qwen35OwnedAttentionLayer { - attn_norm: load_f32_vec(&a.attn_norm, data)?, - attn_q: OwnedQuantizedTensor::from_ref_with_dims( - &a.attn_q, - data, - hidden_dim, - num_heads * true_head_dim * 2, - ), - attn_k: OwnedQuantizedTensor::from_ref_with_dims( - &a.attn_k, - data, - hidden_dim, - num_kv_heads * true_head_dim, - ), - attn_v: OwnedQuantizedTensor::from_ref_with_dims( - &a.attn_v, - data, - hidden_dim, - num_kv_heads * true_head_dim, - ), - attn_q_norm, - attn_k_norm: load_f32_vec(&a.attn_k_norm, data)?, - attn_output: OwnedQuantizedTensor::from_ref_with_dims( - &a.attn_output, - data, - num_heads * true_head_dim, - hidden_dim, - ), - post_attention_norm: load_f32_vec(&a.post_attention_norm, data)?, - ffn_gate: OwnedQuantizedTensor::from_ref_with_dims( - &a.ffn_gate, - data, - hidden_dim, - intermediate_dim, - ), - ffn_up: OwnedQuantizedTensor::from_ref_with_dims( - &a.ffn_up, - data, - hidden_dim, - intermediate_dim, - ), - ffn_down: OwnedQuantizedTensor::from_ref_with_dims( - &a.ffn_down, - data, - intermediate_dim, - hidden_dim, - ), - })); + Qwen35OwnedLayer::Attention(own_attention_layer(&a, data, &dims)?) }, - } + }); } - // Attention head width is key_length (= attn_q_norm width, 256), NOT hidden/heads (128): - // the KV cache and the attention loop used 128-wide heads over 256-wide q/k/v. - let attn_head_dim = owned - .iter() - .find_map(|l| match l { - Qwen35OwnedLayer::Attention(a) => Some(a.attn_q_norm.len()), - Qwen35OwnedLayer::DeltaNet(_) => None, - }) - .unwrap_or(head_dim); + let attn_head_dim = attention_head_dim(&owned, head_dim); Ok(Self { base, layers: owned, head_dim: attn_head_dim, num_kv_heads, - num_v_heads, + num_v_heads: meta.num_v_heads, head_v_dim, - num_k_heads, - head_k_dim, - conv_kernel, - rope_sections, + num_k_heads: meta.num_k_heads, + head_k_dim: meta.head_k_dim, + conv_kernel: meta.conv_kernel, + rope_sections: meta.rope_sections, }) } @@ -1080,6 +1169,74 @@ pub fn run_qwen35_generate( Ok(tokens) } +#[cfg(test)] +mod qwen35_ssm_meta_tests { + use super::{qwen35_ssm_meta, Qwen35SsmMeta}; + use crate::gguf::types::GGUFValue; + use std::collections::HashMap; + + fn md(pairs: &[(&str, GGUFValue)]) -> HashMap { + pairs + .iter() + .map(|(k, v)| ((*k).to_string(), v.clone())) + .collect() + } + + #[test] + fn test_empty_metadata_gives_the_qwen35_0_8b_defaults() { + assert_eq!( + qwen35_ssm_meta(&HashMap::new()), + Qwen35SsmMeta { + head_k_dim: 16, + num_k_heads: 1, + num_v_heads: 16, + conv_kernel: 4, + rope_sections: [16, 24, 24, 0], + } + ); + } + + #[test] + fn test_qwen2_keys_win_and_qwen35_keys_are_the_fallback() { + let meta = qwen35_ssm_meta(&md(&[ + ("qwen2.ssm.state_size", GGUFValue::UInt32(128)), + ("qwen35.ssm.state_size", GGUFValue::UInt32(999)), + ("qwen35.ssm.group_count", GGUFValue::UInt32(2)), + ("qwen35.ssm.time_step_rank", GGUFValue::Int32(32)), + ("qwen35.ssm.conv_kernel", GGUFValue::UInt32(4)), + ])); + assert_eq!(meta.head_k_dim, 128, "the qwen2 key is preferred"); + assert_eq!(meta.num_k_heads, 2, "no qwen2 key: the qwen35 one is read"); + assert_eq!(meta.num_v_heads, 32, "Int32 is accepted like UInt32"); + assert_eq!(meta.conv_kernel, 4); + } + + #[test] + fn test_a_non_integer_value_falls_through_to_the_default() { + // A string where an integer belongs is not a value: it must not be read as 0. + let meta = qwen35_ssm_meta(&md(&[( + "qwen2.ssm.conv_kernel", + GGUFValue::String("four".to_string()), + )])); + assert_eq!(meta.conv_kernel, 4); + } + + #[test] + fn test_rope_sections_are_defaulted_per_section() { + // Two entries given: the last two sections keep their defaults, and a + // non-integer entry keeps its own. + let meta = qwen35_ssm_meta(&md(&[( + "qwen2.rope.dimension_sections", + GGUFValue::Array(vec![ + GGUFValue::UInt32(8), + GGUFValue::Float32(1.0), + GGUFValue::Int32(12), + ]), + )])); + assert_eq!(meta.rope_sections, [8, 24, 12, 0]); + } +} + #[cfg(test)] mod qwen35_math_tests { use super::*; diff --git a/docs/audits/cb200-0.68-roster.md b/docs/audits/cb200-0.68-roster.md new file mode 100644 index 0000000000..3af01a1b7f --- /dev/null +++ b/docs/audits/cb200-0.68-roster.md @@ -0,0 +1,121 @@ +# CB-200 at 0.68.0: the +2 is real debt, and 2 of the 3 additions are outside `crates/` + +PMAT-1098. Measured 2026-09-17 on the intel workstation with **pmat 3.40.2** (the version +the box now runs; `.pmat-gates.toml` records the 602 baseline as re-measured 2026-09-15 +under 3.40.1). + +## 1. Version delta, or real debt? + +`.pmat-gates.toml:97-103` holds `[tdg] baseline = 602`. The T-2 pre-publish dogfood read +604 and called it 2 over. A count that moves when the instrument moves is not debt, so the +first question was whether 3.40.1 -> 3.40.2 changed the reading. It did not: + +| Tree | Commit | pmat | CB-200 reading | +|------|--------|------|----------------| +| baseline (last `origin/main` commit of 2026-09-15) | `7ee0ecaad` | **3.40.2** | **602** definitions below B across 481 files — `Warn`, at baseline | +| head (`origin/main` @ measurement) | `94df98834` | **3.40.2** | **604** — `Fail`, 2 over | + +Both readings come from `pmat comply check --checks CB-200 --format json` run in two +worktrees under `/mnt/nvme-raid0/agent-wt/`, each with its own index +(`~/.cache/paiml-mcp-agent-toolkit/comply/index/-/context.db`; pmat indexes +outside the tree, #1008). Same binary, same invocation, only the tree contents differ. + +**Verdict: real debt.** The baseline commit reads 602 under 3.40.2, so the +2 is code that +landed on 2026-09-16, not an instrument artefact. A re-baseline would have hidden it. + +## 2. The roster, and its diff + +`baseline` is a count, so the report's two named definitions (`html.rs:42 generate` [F], +`lib.rs:84 dispatch` [F]) are the worst-graded *survivors* — neither is new, and neither is +touched here. The roster comes from each measurement's own `context.db`: + +```sql +select file_path, start_line, function_name, tdg_grade, complexity from functions +where tdg_grade in ('B-','C+','C','C-','D','F'); +``` +minus the five `[tdg] exclude` globs. That reproduces pmat's own numbers exactly — 602 +across **481 files** at the baseline (the report states both), 604 at head — so the roster +is the same set the gate counts, not a lookalike. + +Diff (keyed on file + definition name, because line numbers move): + +| | file:line | definition | grade | complexity | +|---|---|---|---|---| +| **+** | `crates/aprender-serve/src/gguf/inference/forward/forward_qwen35.rs:488` | `from_model_and_layers` | C+ | 15 | +| **+** | `evidence/parity/l0-1/intel/qwen35-cpu-reference/producer/apr_raw_logits.cpp:139` | `main` | F | 48 | +| **+** | `evidence/parity/l0-1/intel/qwen35-cpu-reference/producer/raw_logits_compare.cpp:74` | `main` | C+ | 21 | +| **-** | `crates/aprender-serve/src/infer/inference_result.rs:575` | `validate_gpu_first_token` | B- | 17 | + +3 added, 1 removed: net +2, which is the whole of the regression. Confirmed against git — +none of the three exists at `7ee0ecaad`: + +- `from_model_and_layers` came in with #3114 (`e822e5adb`, Qwen3.5/Qwen3.8 hybrid CPU forward). +- both `.cpp` producers came in with the parity evidence stack #3354/#3355/#3356 + (`944375ee2`, `f326f5c43`, `0ac2aab8c`). + +## 3. What this change fixes: one of the three + +`from_model_and_layers` is in scope and is fixed here by extraction only — same inputs, +same outputs, existing tests unchanged: + +| definition | before | after | +|---|---|---| +| `from_model_and_layers` | **C+**, cyclomatic 15 | **A+**, cyclomatic 4 | + +The extracted helpers each grade at or above the gate's minimum, so the fix does not move +debt sideways: `metadata_usize` A (5), `metadata_usize_or` A+ (1), +`rope_sections_from_metadata` A (5), `qwen35_ssm_meta` A+ (1), `own_deltanet_layer` A+ (1), +`own_attention_layer` A+ (1), `attention_head_dim` A+ (3). After the change +`forward_qwen35.rs` has **zero** definitions below B. + +The function had no test (it needs a real hybrid GGUF). The extraction creates a seam that +does not: `qwen35_ssm_meta` reads the Gated DeltaNet shape out of a metadata map, so +`mod qwen35_ssm_meta_tests` (4 tests) pins the defaults, the `qwen2.*`-wins-over-`qwen35.*` +precedence, the non-integer-falls-through-to-default rule, and the per-section RoPE +defaulting — the four behaviours the old inline closure chain encoded and nothing asserted. + +Reading after the fix: **603** — `Fail`, 1 over. The roster diff at that point is exactly +the two `.cpp` producers (and the same one removal). + +## 4. The remaining 1 is outside this ticket's scope — an orchestrator decision + +The last addition is a pair of C++ programs under `evidence/`, of which the F-graded +`apr_raw_logits.cpp:139 main` is the one still over the line. They are not shipped code: +they are the llama.cpp `d1d3c3396` reference harness that produced the Qwen3.5 CPU parity +evidence, vendored so the measurement is reproducible. `scope_paths` for PMAT-1098 covers +`crates/**/src/**/*.rs` and `crates/**/tests/**`; `evidence/**` is neither, and the scope +was not widened. + +Note what is *not* an argument here: the gate's universe is already repo-wide, not +crates-only — at the 602 baseline it counts 6 definitions under `experiments/`, 4 under +`scripts/` and 2 under `tools/`. So "it isn't under `crates/`" does not by itself make the +count wrong. The open question is narrower: **should a vendored reference harness, kept +verbatim so a parity measurement can be reproduced, be graded as this repo's debt?** + +Three ways to close it, in the orchestrator's hands: + +1. **Refactor `apr_raw_logits.cpp`** — splits `main` into helpers. Cheapest to the count, + but it edits the artefact whose value is that it is the program that produced the + evidence; a rebuilt binary is no longer bit-for-bit the one cited. +2. **Exclude `evidence/**` (or the producer directory) in `[tdg] exclude`** — states the + policy explicitly. `.pmat-gates.toml` is not editable under this ticket, and an exclude + silently drops `raw_logits_compare.cpp` too. +3. **Re-baseline to 603** — *only* legitimate if option 2's policy is rejected, i.e. if the + `.cpp` debt is accepted as debt. A baseline may only go down, so this would be a + deliberate ratchet release, and the gate text refuses it as the default answer. + +Not a fourth option: raising the baseline to 604 to make the original report green. That +would also have absorbed the `from_model_and_layers` regression, which was real and is now +fixed. + +## 5. Reproducing + +```bash +git worktree add --detach /mnt/nvme-raid0/agent-wt/cb200-base-7ee0ecaa 7ee0ecaad +cd /mnt/nvme-raid0/agent-wt/cb200-base-7ee0ecaa +pmat comply check --checks CB-200 --format json # -> 602, Warn +# and in the head worktree -> 604 before this change, 603 after. +``` +A re-run in the same directory reuses the index (~12 s); a first run, or a run after +`rm -rf` of that index directory, costs ~80 s. Delete the index directory before trusting +a reading taken after the tree changed. From 60f3d7ea963c808714391123dd974c4549943c3d Mon Sep 17 00:00:00 2001 From: Noah Gift Date: Thu, 17 Sep 2026 06:06:45 +0200 Subject: [PATCH 2/4] =?UTF-8?q?gate(tdg):=20evidence/**=20is=20a=20referen?= =?UTF-8?q?ce=20harness,=20not=20shipped=20code=20=E2=80=94=20exclude=20it?= =?UTF-8?q?=20and=20lower=20the=20CB-200=20baseline=20602=20=E2=86=92=2060?= =?UTF-8?q?1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 0.68.0 train T-2: pmat-comply RED on CB-200 (604 vs 602). Roster diff (worker, docs/audits/cb200-0.68-roster.md): three additions since the 09-15 baseline — forward_qwen35.rs::from_model_and_layers (C+, refactored to A+ in the previous commit) and two C++ mains of the vendored llama.cpp reference harness under evidence/. Excluding evidence/** and re-measuring gives 601; the baseline is lowered to that, never raised. Pmat-Ticket: PMAT-1098 Co-Authored-By: Claude Fable 5.1 --- .pmat-gates.toml | 11 ++++++++++- docs/audits/cb200-0.68-roster.md | 8 ++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/.pmat-gates.toml b/.pmat-gates.toml index 16a4608aaf..5cbeb4cb42 100644 --- a/.pmat-gates.toml +++ b/.pmat-gates.toml @@ -100,11 +100,20 @@ exclude = [ # the count did not. Between those readings #3004 added one B- definition # (registry/wgpu_probe.rs entry, 603) and merged, because no required job runs # `pmat comply`; it was split into four graded tables in the pin-bump PR, back to 602. -baseline = 602 +baseline = 601 +# 601 re-measured 2026-09-17 under pmat 3.40.2 on the 0.68.0 release tree: 604 at head = 602 + +# three additions since the 09-15 baseline (one Rust fn refactored to A+, two C++ reference +# `main`s under evidence/** now excluded above); 604 - 3 = 601. Lowered, never raised. # CB-200: 21 functions below A are in non-release crates (test-lib, test-cli, train/cuda) # These are complex GPU/inference functions where high cyclomatic complexity is inherent min_grade = "B" exclude = [ + # evidence/** holds vendored REFERENCE harnesses (e.g. the llama.cpp d1d3c3396 C++ + # producers under evidence/parity/l0-1/) that exist to reproduce a cited measurement + # bit-for-bit; refactoring them for a grade would invalidate the evidence they produce. + # Not shipped code. Excluded 2026-09-17 (0.68 train: two C++ `main`s were 2 of the 3 + # definitions added over the 602 baseline; the third, a Rust one, was refactored). + "evidence/**", "crates/aprender-test-lib/**", "crates/aprender-test-cli/**", "crates/aprender-verify-ml/**", diff --git a/docs/audits/cb200-0.68-roster.md b/docs/audits/cb200-0.68-roster.md index 3af01a1b7f..9ac6d2353a 100644 --- a/docs/audits/cb200-0.68-roster.md +++ b/docs/audits/cb200-0.68-roster.md @@ -119,3 +119,11 @@ pmat comply check --checks CB-200 --format json # -> 602, Warn A re-run in the same directory reuses the index (~12 s); a first run, or a run after `rm -rf` of that index directory, costs ~80 s. Delete the index directory before trusting a reading taken after the tree changed. + +## 5. Decision (orchestrator, 2026-09-17 04:05Z) + +`evidence/**` is excluded from the `[tdg]` ratchet: the two remaining additions are `main`s of the +vendored llama.cpp `d1d3c3396` reference harness, which exists to reproduce a cited measurement +bit-for-bit; grading it would either leave it permanently red or invalidate the evidence. Re-measured +with the exclusion under pmat 3.40.2: **601** below B. The baseline moves **602 → 601** — down, the only +direction the gate allows — so the Rust regression (`from_model_and_layers`) stays fixed, not absorbed. From 4f7e4b45b0bb12b46eb92ce9b0ba282c897b6987 Mon Sep 17 00:00:00 2001 From: Noah Gift Date: Thu, 17 Sep 2026 06:30:15 +0200 Subject: [PATCH 3/4] =?UTF-8?q?gate(tdg):=20cb200=5Fbaseline.txt=20moves?= =?UTF-8?q?=20with=20[tdg]=20baseline=20=E2=80=94=20602=20=E2=86=92=20601?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit check_complexity_ratchet.sh pairs the two: '[tdg] baseline = 601 but scripts/cb200_baseline.txt says 602; the two move together'. Pmat-Ticket: PMAT-1098 Co-Authored-By: Claude Fable 5.1 --- scripts/cb200_baseline.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/cb200_baseline.txt b/scripts/cb200_baseline.txt index 270043102b..55fc56ceee 100644 --- a/scripts/cb200_baseline.txt +++ b/scripts/cb200_baseline.txt @@ -1,2 +1,2 @@ # tool_version=pmat 3.40.1 -602 +601 From c3c7ea997bfa6970c3914c69b7723d33b708accf Mon Sep 17 00:00:00 2001 From: Noah Gift Date: Thu, 17 Sep 2026 06:47:32 +0200 Subject: [PATCH 4/4] =?UTF-8?q?chore(pr):=20re-run=20the=20=C2=A711.1=20bo?= =?UTF-8?q?dy=20guard=20after=20adding=20the=20ont-delta=20line?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pmat-Ticket: PMAT-1098 Co-Authored-By: Claude Fable 5.1