From 1285997aa151f1f33520c3e606b22a049a2fa618 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Wed, 29 Apr 2026 22:49:23 -0400 Subject: [PATCH 01/10] WIP: nimble-pike-489 --- src/v3/grounding_lifetime/src/extract.rs | 57 +++++++----------------- src/v3/grounding_lifetime/src/lib.rs | 24 ++++++++++ 2 files changed, 41 insertions(+), 40 deletions(-) diff --git a/src/v3/grounding_lifetime/src/extract.rs b/src/v3/grounding_lifetime/src/extract.rs index e8f33915b5d..e16f83b3e13 100644 --- a/src/v3/grounding_lifetime/src/extract.rs +++ b/src/v3/grounding_lifetime/src/extract.rs @@ -1,23 +1,23 @@ //! Dag → `LifetimeProgram` projection (R2 extraction). //! -//! Bootstrap fixtures (`Dag::new()`) only carry declarations whose spans live under -//! checked-in authority prefixes; those are not yet lowered into the bind/use graph, -//! so extraction returns an **empty** program (the structural fold is validated via -//! [`LifetimeProgram`] fixtures). +//! Bootstrap fixtures (`Dag::new()`) embed a large declaration snapshot that is not yet +//! lowered into the bind/use graph this lane analyzes, so extraction returns an **empty** +//! program for fixture-only rows (the structural fold is validated via [`LifetimeProgram`] +//! fixtures). //! -//! Any **`data` or `fn` declaration** rooted in a **non-authority** source file (user -//! or test modules) is treated as load-bearing program surface we cannot project yet: +//! Any runtime-appended **`data` or `fn` declaration** with load-bearing surface (`data` body +//! or `fn` / `Arrow` connective) is treated as program surface we cannot project yet: //! fail-closed per C-8 instead of returning `Ok(empty)` and dropping facts. //! -//! ## Transitional authority gate +//! ## Authority gate (structural boundary) //! -//! **Path-prefix authority detection is intentional staging.** It will migrate to a -//! substrate-declared post-bootstrap boundary on [`Dag`] (so consumers can distinguish -//! fixture-loaded declarations from runtime-appended ones without trusting -//! caller-controlled `span.file`), once Substrate lands that metadata; see cross-manager -//! request on issue #1130. Until then, `span.file` under an authority prefix is trusted -//! only for the embedded corpus shape (same transitional-debt pattern as the lane-local -//! `EmissionDiagnostic` mirror noted in #1216). +//! Fixture vs user rows are distinguished by **Substrate-declared declaration identity**, not +//! by path-prefix matching on caller-controlled `span.file` strings on each declaration. +//! After **PR #1221**, [`Dag::post_bootstrap_declaration_append_begin`] and +//! [`Dag::is_runtime_appended_declaration`] stamp the first [`DeclarationId::raw`] allocated +//! after embedded bootstrap construction; runtime lowering (`compile_to_dag`, etc.) appends +//! rows at or above that bound. **Path-prefix staging** from #1218 / #1220 is **retired** here +//! in favor of that boundary (cross-manager request #1130 satisfied on the compiler side). use v3_compiler::dag::{Dag, TypeConnective}; @@ -29,32 +29,9 @@ use crate::program::{BindingId, LifetimeProgram}; use std::collections::BTreeMap; -/// Span roots that appear on [`Dag::new()`] bootstrap fixtures (regenerated snapshots). -/// -/// Keep aligned with `bootstrap_generated.rs` / `bootstrap_generated_without_parse_surface.rs` -/// when new fixture corpora land; otherwise user/test modules may be misclassified. -/// -/// Note: only **three** `src/v3/compiler/*.dag` stubs ship inside the fixture — not every path -/// under `src/v3/compiler/` (tests live there too). -fn is_bootstrap_fixture_authority_source_file(file: &str) -> bool { - if file.starts_with("dsl/std/") - || file.starts_with("dsl/extdeps/") - || file.starts_with("src/v3/std/") - || file.starts_with("src/v3/spec/") - { - return true; - } - matches!( - file, - "src/v3/compiler/operators.dag" - | "src/v3/compiler/pipeline.dag" - | "src/v3/compiler/regen.dag" - ) -} - -fn first_non_authority_lifetime_surface_declaration(dag: &Dag) -> Option<(String, String)> { +fn first_runtime_appended_lifetime_surface_declaration(dag: &Dag) -> Option<(String, String)> { for decl in dag.declarations() { - if is_bootstrap_fixture_authority_source_file(&decl.span.file) { + if !dag.is_runtime_appended_declaration(decl.id) { continue; } let surface = @@ -75,7 +52,7 @@ fn first_non_authority_lifetime_surface_declaration(dag: &Dag) -> Option<(String /// /// Fail-closed on constructs the R2 analyzer does not model (once lowering surfaces them). pub fn extract_lifetime_program(dag: &Dag) -> Result { - if let Some((name, file)) = first_non_authority_lifetime_surface_declaration(dag) { + if let Some((name, file)) = first_runtime_appended_lifetime_surface_declaration(dag) { return Err(EmissionDiagnostic::LifetimeProgramExtractionPending { detail: format!("{name} ({file})"), }); diff --git a/src/v3/grounding_lifetime/src/lib.rs b/src/v3/grounding_lifetime/src/lib.rs index 3a3610bbd61..5352a074d15 100644 --- a/src/v3/grounding_lifetime/src/lib.rs +++ b/src/v3/grounding_lifetime/src/lib.rs @@ -165,6 +165,30 @@ mod tests { } } + /// User-range `data` must be gated by structural declaration identity (`Dag` append + /// boundary), not by `span.file` prefixes: an authority-looking path must not exempt + /// runtime-appended surface from fail-closed extraction. + #[test] + fn extract_fail_closed_user_data_with_authority_looking_span_file() { + let source = "data spoofed_authority_span_bait: Int = 0"; + let file = "dsl/std/bootstrap_fake.dag"; + let dag = v3_compiler::compile_to_dag(source, file).expect("minimal compile"); + let err = extract_lifetime_program(&dag).expect_err("extraction must fail closed"); + match err { + EmissionDiagnostic::LifetimeProgramExtractionPending { detail } => { + assert!( + detail.contains("spoofed_authority_span_bait"), + "unexpected detail: {detail}" + ); + assert!( + detail.contains(file), + "detail should include the spoof span path for debugging: {detail}" + ); + } + other => panic!("unexpected diagnostic: {other:?}"), + } + } + #[test] fn function_param_indeterminate_growability_fails_closed_even_when_borrowed() { let mut bindings = BTreeMap::new(); From 881a602ccbba1acfda592d8b242e61389633e5f2 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Thu, 30 Apr 2026 11:10:40 -0400 Subject: [PATCH 02/10] WIP: nimble-pike-489 --- Cargo.toml | 2 + src/v3/grounding_tests/Cargo.toml | 14 + src/v3/grounding_tests/src/diagnostic.rs | 63 ++++ src/v3/grounding_tests/src/lib.rs | 117 +++++++ src/v3/grounding_tests/src/stratum_a.rs | 400 +++++++++++++++++++++++ 5 files changed, 596 insertions(+) create mode 100644 src/v3/grounding_tests/Cargo.toml create mode 100644 src/v3/grounding_tests/src/diagnostic.rs create mode 100644 src/v3/grounding_tests/src/lib.rs create mode 100644 src/v3/grounding_tests/src/stratum_a.rs diff --git a/Cargo.toml b/Cargo.toml index d1cad8f521e..e059572a2d3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,8 @@ members = [ "src/v3/grounding_lifetime", # T-Ground-Coercion-Fold — structural emission fold scaffold (stub until LanguageSpec + boundary gates). "src/v3/grounding_coercion_fold", + # T-Ground-Tests — L4 routing-stability Stratum A (name-keyed parity) vs LanguageSpec rows. + "src/v3/grounding_tests", # Linux ExecuteCommand unshare bootstrap — fd3 sentinel + FD_CLOEXEC before execvp "src/v3/execute_command_bootstrap", # v1 compiler — ARCHIVED. No longer in the build or generation path. diff --git a/src/v3/grounding_tests/Cargo.toml b/src/v3/grounding_tests/Cargo.toml new file mode 100644 index 00000000000..a097483d958 --- /dev/null +++ b/src/v3/grounding_tests/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "v3-grounding-tests" +version.workspace = true +edition.workspace = true +license.workspace = true + +# T-Ground-Tests — L4 routing-stability Stratum A (name-keyed parity) against +# LanguageSpec Phase 1 `MethodTemplateContract` row authorities. Sibling to +# grounding_pilot / grounding_engine / grounding_lifetime / grounding_coercion_fold; +# does not touch `src/v3/compiler/` SG-0. + +[dependencies] +v3-compiler = { path = "../compiler" } +v3-grounding-lifetime = { path = "../grounding_lifetime" } diff --git a/src/v3/grounding_tests/src/diagnostic.rs b/src/v3/grounding_tests/src/diagnostic.rs new file mode 100644 index 00000000000..a6d264abde9 --- /dev/null +++ b/src/v3/grounding_tests/src/diagnostic.rs @@ -0,0 +1,63 @@ +//! Lane-local fail-closed outcomes for T-Ground-Tests (Stratum A scaffold). +//! +//! Converges toward the shared `EmissionDiagnostic` substrate carrier per +//! `docs/briefs/t-ground-diagnostic.md` / manager #1216 — this crate keeps a +//! small structural sum until that hand-off lands. + +use std::fmt; + +/// Typed failure for Stratum A routing-parity checks (`t-ground-tests.md` §Test plan item 1). +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum GroundingTestsDiagnostic { + /// `List` row count drift vs Director-locked Phase 1 shape. + StratumARowCountMismatch { + list_name: String, + expected: usize, + actual: usize, + }, + /// `dag_method: MethodRef` did not resolve to a `MethodDeclaration` name literal. + StratumARegistryResolutionFailed { + list_name: String, + row_index: usize, + detail: String, + }, + /// Bootstrap Dag vs standalone `compile_to_dag` authority disagreed on a row. + StratumALockstepMismatch { + list_name: String, + method_name: String, + detail: String, + }, +} + +impl fmt::Display for GroundingTestsDiagnostic { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + GroundingTestsDiagnostic::StratumARowCountMismatch { + list_name, + expected, + actual, + } => write!( + f, + "Stratum A row-count mismatch for `{list_name}`: expected {expected}, got {actual}" + ), + GroundingTestsDiagnostic::StratumARegistryResolutionFailed { + list_name, + row_index, + detail, + } => write!( + f, + "Stratum A registry resolution failed for `{list_name}` row {row_index}: {detail}" + ), + GroundingTestsDiagnostic::StratumALockstepMismatch { + list_name, + method_name, + detail, + } => write!( + f, + "Stratum A lockstep mismatch for `{list_name}` method `{method_name}`: {detail}" + ), + } + } +} + +impl std::error::Error for GroundingTestsDiagnostic {} diff --git a/src/v3/grounding_tests/src/lib.rs b/src/v3/grounding_tests/src/lib.rs new file mode 100644 index 00000000000..ba284dbc4a1 --- /dev/null +++ b/src/v3/grounding_tests/src/lib.rs @@ -0,0 +1,117 @@ +//! T-Ground-Tests — L4 routing-correctness verification (Stratum A scaffold). +//! +//! See `docs/briefs/t-ground-tests.md` (lane 10 / `routing_correctness_l4_verified` gate). +//! This crate generalizes the pilot Stratum A pattern (`src/v3/grounding_pilot/src/lib.rs:408-460`) +//! onto Phase 1 `MethodTemplateContract` row authorities (`#1195` / `#1196` / `#1210`). +//! +//! **SG-0:** no `src/v3/compiler/` edits — consumers only. + +pub mod diagnostic; +mod stratum_a; + +pub use diagnostic::GroundingTestsDiagnostic; +pub use stratum_a::{ + stratum_a_list_digest, verify_stratum_a_lockstep_all_targets, RowFingerprint, + EXPECTED_STRATUM_A_ROW_COUNTS, +}; + +#[cfg(test)] +mod tests { + use v3_compiler::dag::Dag; + use v3_compiler::generated_full_bootstrap_dag; + use v3_grounding_lifetime::{ + analyze_lifetime_facts, axes::LanguageSpecAxes, extract_lifetime_program, + }; + + use super::stratum_a::{ + fingerprints_for_list, stratum_a_list_digest, verify_stratum_a_lockstep_all_targets, + RUST_LIST, + }; + use super::GroundingTestsDiagnostic; + + /// Sibling dep smoke: bootstrap `Dag::new()` stays empty for lifetime extraction. + #[test] + fn grounding_lifetime_sibling_wires_through_bootstrap() { + let dag = Dag::new(); + let axes = LanguageSpecAxes::example_rust_string_family(); + let report = analyze_lifetime_facts(&dag, &axes).expect("bootstrap ok"); + assert!(report.is_empty()); + let program = extract_lifetime_program(&dag).expect("extract empty"); + assert!(program.bindings.is_empty()); + } + + #[test] + fn stratum_a_routing_parity_lockstep_bootstrap_vs_standalone() { + verify_stratum_a_lockstep_all_targets().unwrap_or_else(|e| panic!("{e}")); + } + + #[test] + fn stratum_a_row_counts_match_director_phase1() { + let dag = generated_full_bootstrap_dag(); + for &(name, expected) in super::EXPECTED_STRATUM_A_ROW_COUNTS { + let n = dag + .declaration_by_name(name) + .and_then(|d| d.value_body.as_ref()) + .and_then(|b| match b { + v3_compiler::dag::ValueBody::List(rows) => Some(rows.len()), + _ => None, + }) + .unwrap_or(0); + assert_eq!( + n, expected, + "`{name}` row count drift — update EXPECTED_STRATUM_A_ROW_COUNTS or substrate" + ); + } + } + + /// Determinism: multiset digest is identical whether rows are walked forward or backward + /// before insertion into the canonical `BTreeMap` keying (`t-ground-tests.md` item 3). + #[test] + fn stratum_a_determinism_independent_of_list_walk_order() { + let dag = generated_full_bootstrap_dag(); + let rows = dag + .declaration_by_name(RUST_LIST) + .expect(RUST_LIST) + .value_body + .as_ref() + .and_then(|b| match b { + v3_compiler::dag::ValueBody::List(r) => Some(r.as_slice()), + _ => None, + }) + .expect("list body"); + + let mut forward = std::collections::BTreeMap::new(); + for (idx, row) in rows.iter().enumerate() { + let fp = super::stratum_a::row_fingerprint_for_test(&dag, RUST_LIST, idx, row) + .unwrap_or_else(|e| panic!("{e}")); + forward.insert(fp.method_name.clone(), fp); + } + let mut backward = std::collections::BTreeMap::new(); + for (idx, row) in rows.iter().enumerate().rev() { + let fp = super::stratum_a::row_fingerprint_for_test(&dag, RUST_LIST, idx, row) + .unwrap_or_else(|e| panic!("{e}")); + backward.insert(fp.method_name.clone(), fp); + } + assert_eq!(forward, backward); + + let d1 = stratum_a_list_digest(&dag, RUST_LIST).unwrap(); + let d2 = stratum_a_list_digest(&dag, RUST_LIST).unwrap(); + assert_eq!(d1, d2); + + let via_helper = fingerprints_for_list(&dag, RUST_LIST).unwrap(); + assert_eq!( + super::stratum_a::list_digest_from_fingerprints_for_test(&via_helper), + d1 + ); + } + + #[test] + fn stratum_a_diagnostic_display_is_non_empty() { + let e = GroundingTestsDiagnostic::StratumARowCountMismatch { + list_name: "x".to_string(), + expected: 1, + actual: 2, + }; + assert!(!e.to_string().is_empty()); + } +} diff --git a/src/v3/grounding_tests/src/stratum_a.rs b/src/v3/grounding_tests/src/stratum_a.rs new file mode 100644 index 00000000000..d6ce89fc238 --- /dev/null +++ b/src/v3/grounding_tests/src/stratum_a.rs @@ -0,0 +1,400 @@ +//! Stratum A — name-keyed routing parity for `MethodTemplateContract` Phase 1 rows. +//! +//! Source: `docs/briefs/t-ground-tests.md` (test plan item 1). Generalizes the pilot +//! kernel-integer checks (`src/v3/grounding_pilot/src/lib.rs:408-460`) onto the +//! per-target row lists `rust_*` / `python_*` / `go_*` from T-Ground-LanguageSpec #1195. + +use std::collections::BTreeMap; + +use v3_compiler::dag::{ + Dag, DeclarationId, FieldValue, LiteralBits, TypeConnective, ValueBody, +}; + +use crate::diagnostic::GroundingTestsDiagnostic; + +const RUST_LIST: &str = "rust_method_template_contracts"; +const PYTHON_LIST: &str = "python_method_template_contracts"; +const GO_LIST: &str = "go_method_template_contracts"; + +const RUST_SOURCE: &str = include_str!("../std/rust_method_template_contracts.dag"); +const RUST_PATH: &str = "src/v3/std/rust_method_template_contracts.dag"; +const PYTHON_SOURCE: &str = include_str!("../std/python_method_template_contracts.dag"); +const PYTHON_PATH: &str = "src/v3/std/python_method_template_contracts.dag"; +const GO_SOURCE: &str = include_str!("../std/go_method_template_contracts.dag"); +const GO_PATH: &str = "src/v3/std/go_method_template_contracts.dag"; + +/// Director-locked Phase 1 row counts (`t-ground-tests.md`; Go is 14 rows on main — `chars` deferred per file header). +pub const EXPECTED_STRATUM_A_ROW_COUNTS: &[(&str, usize)] = &[ + (RUST_LIST, 9), + (PYTHON_LIST, 18), + (GO_LIST, 14), +]; + +fn list_rows<'a>(dag: &'a Dag, list_name: &str) -> &'a [FieldValue] { + let decl = dag + .declaration_by_name(list_name) + .unwrap_or_else(|| panic!("`{list_name}` missing from Dag")); + let body = decl + .value_body + .as_ref() + .unwrap_or_else(|| panic!("`{list_name}` has no value body")); + let ValueBody::List(rows) = body else { + panic!("`{list_name}`: expected List body, got {body:?}"); + }; + rows +} + +fn method_emit_template_variant_label(dag: &Dag, constructor: DeclarationId) -> String { + let method_emit_template = dag + .declaration_by_name("MethodEmitTemplate") + .expect("MethodEmitTemplate"); + let TypeConnective::Disj { variants } = &method_emit_template.connective else { + panic!("MethodEmitTemplate must be a Disj"); + }; + variants + .iter() + .find(|variant| variant.ty == constructor) + .unwrap_or_else(|| panic!("unknown MethodEmitTemplate constructor {constructor:?}")) + .label + .clone() +} + +fn placeholder_variant_label(dag: &Dag, constructor: DeclarationId) -> String { + let root = dag + .declaration_by_name("PlaceholderConvention") + .expect("PlaceholderConvention"); + let TypeConnective::Disj { variants } = &root.connective else { + panic!("PlaceholderConvention must be a Disj"); + }; + variants + .iter() + .find(|variant| variant.ty == constructor) + .unwrap_or_else(|| panic!("unknown PlaceholderConvention constructor {constructor:?}")) + .label + .clone() +} + +fn method_registry_name(dag: &Dag, method_decl_id: DeclarationId) -> Result { + let decl = dag.declaration(method_decl_id); + let vb = decl + .value_body + .as_ref() + .ok_or_else(|| format!("declaration {:?} has no value_body", decl.name))?; + let ValueBody::Structural { fields } = vb else { + return Err(format!( + "declaration {:?}: value_body not Structural: {vb:?}", + decl.name + )); + }; + let name_field = fields + .iter() + .find(|(label, _)| label == "name") + .ok_or_else(|| format!("declaration {:?}: missing `name` field", decl.name))?; + match &name_field.1 { + FieldValue::Literal(LiteralBits::String(s)) => Ok(s.clone()), + other => Err(format!( + "declaration {:?}: `name` not a string literal: {other:?}", + decl.name + )), + } +} + +fn emit_template_canonical(dag: &Dag, emit: &FieldValue) -> Result { + let FieldValue::Variant { + constructor, + payload, + } = emit + else { + return Err(format!("emit_template not a variant: {emit:?}")); + }; + let label = method_emit_template_variant_label(dag, *constructor); + match label.as_str() { + "SingleTemplate" => { + let Some(FieldValue::Literal(LiteralBits::String(s))) = payload.first() else { + return Err(format!("SingleTemplate payload: {payload:?}")); + }; + Ok(format!("SingleTemplate({s})")) + } + "HigherOrderTemplates" => { + if payload.len() != 2 { + return Err(format!("HigherOrderTemplates arity: {payload:?}")); + } + let FieldValue::Literal(LiteralBits::String(inline)) = &payload[0] else { + return Err(format!("inline_template: {:?}", payload[0])); + }; + let FieldValue::Literal(LiteralBits::String(fn_ref)) = &payload[1] else { + return Err(format!("fn_ref_template: {:?}", payload[1])); + }; + Ok(format!("HigherOrderTemplates({inline}|{fn_ref})")) + } + _ => Err(format!("unknown MethodEmitTemplate variant `{label}`")), + } +} + +/// One `MethodTemplateContract` row projected to deterministic strings for parity / digests. +#[derive(Clone, Eq, PartialEq, Ord, PartialOrd)] +pub struct RowFingerprint { + pub method_name: String, + pub runtime_template: String, + pub emit_canonical: String, + pub wraps_result: bool, + pub placeholder: String, +} + +impl RowFingerprint { + pub fn canonical_line(&self) -> String { + format!( + "{}\t{}\t{}\t{}\t{}", + self.method_name, + self.runtime_template, + self.emit_canonical, + self.wraps_result, + self.placeholder + ) + } +} + +fn row_fingerprint( + dag: &Dag, + list_name: &str, + row_index: usize, + row: &FieldValue, +) -> Result { + let list = list_name.to_string(); + let FieldValue::Record(fields) = row else { + return Err(GroundingTestsDiagnostic::StratumARegistryResolutionFailed { + list_name: list, + row_index, + detail: format!("row is not a record: {row:?}"), + }); + }; + let (_, dag_method) = fields + .iter() + .find(|(label, _)| label == "dag_method") + .ok_or_else(|| GroundingTestsDiagnostic::StratumARegistryResolutionFailed { + list_name: list_name.to_string(), + row_index, + detail: "missing `dag_method`".to_string(), + })?; + let FieldValue::Record(method_ref_fields) = dag_method else { + return Err(GroundingTestsDiagnostic::StratumARegistryResolutionFailed { + list_name: list_name.to_string(), + row_index, + detail: format!("`dag_method` not MethodRef record: {dag_method:?}"), + }); + }; + let (_, decl_field) = method_ref_fields + .iter() + .find(|(label, _)| label == "decl") + .ok_or_else(|| GroundingTestsDiagnostic::StratumARegistryResolutionFailed { + list_name: list_name.to_string(), + row_index, + detail: "MethodRef missing `decl`".to_string(), + })?; + let FieldValue::Reference(method_decl_id) = decl_field else { + return Err(GroundingTestsDiagnostic::StratumARegistryResolutionFailed { + list_name: list_name.to_string(), + row_index, + detail: format!("MethodRef.decl not a reference: {decl_field:?}"), + }); + }; + let method_name = method_registry_name(dag, *method_decl_id).map_err(|e| { + GroundingTestsDiagnostic::StratumARegistryResolutionFailed { + list_name: list_name.to_string(), + row_index, + detail: e, + } + })?; + + let (_, runtime_field) = fields + .iter() + .find(|(label, _)| label == "runtime_template") + .ok_or_else(|| GroundingTestsDiagnostic::StratumARegistryResolutionFailed { + list_name: list_name.to_string(), + row_index, + detail: "missing `runtime_template`".to_string(), + })?; + let runtime_template = match runtime_field { + FieldValue::Literal(LiteralBits::String(s)) => s.clone(), + other => { + return Err(GroundingTestsDiagnostic::StratumARegistryResolutionFailed { + list_name: list_name.to_string(), + row_index, + detail: format!("runtime_template not string: {other:?}"), + }); + } + }; + + let (_, emit_field) = fields + .iter() + .find(|(label, _)| label == "emit_template") + .ok_or_else(|| GroundingTestsDiagnostic::StratumARegistryResolutionFailed { + list_name: list_name.to_string(), + row_index, + detail: "missing `emit_template`".to_string(), + })?; + let emit_canonical = emit_template_canonical(dag, emit_field).map_err(|e| { + GroundingTestsDiagnostic::StratumARegistryResolutionFailed { + list_name: list_name.to_string(), + row_index, + detail: e, + } + })?; + + let (_, wraps_field) = fields + .iter() + .find(|(label, _)| label == "wraps_result") + .ok_or_else(|| GroundingTestsDiagnostic::StratumARegistryResolutionFailed { + list_name: list_name.to_string(), + row_index, + detail: "missing `wraps_result`".to_string(), + })?; + let wraps_result = match wraps_field { + FieldValue::Literal(LiteralBits::Bool(b)) => *b, + other => { + return Err(GroundingTestsDiagnostic::StratumARegistryResolutionFailed { + list_name: list_name.to_string(), + row_index, + detail: format!("wraps_result not bool: {other:?}"), + }); + } + }; + + let (_, ph_field) = fields + .iter() + .find(|(label, _)| label == "placeholder_convention") + .ok_or_else(|| GroundingTestsDiagnostic::StratumARegistryResolutionFailed { + list_name: list_name.to_string(), + row_index, + detail: "missing `placeholder_convention`".to_string(), + })?; + let FieldValue::Variant { + constructor: ph_ctor, .. + } = ph_field + else { + return Err(GroundingTestsDiagnostic::StratumARegistryResolutionFailed { + list_name: list_name.to_string(), + row_index, + detail: format!("placeholder_convention not variant: {ph_field:?}"), + }); + }; + let placeholder = placeholder_variant_label(dag, *ph_ctor); + + Ok(RowFingerprint { + method_name, + runtime_template, + emit_canonical, + wraps_result, + placeholder, + }) +} + +fn fingerprints_for_list( + dag: &Dag, + list_name: &str, +) -> Result, GroundingTestsDiagnostic> { + let rows = list_rows(dag, list_name); + let mut out: BTreeMap = BTreeMap::new(); + for (idx, row) in rows.iter().enumerate() { + let fp = row_fingerprint(dag, list_name, idx, row)?; + if out.insert(fp.method_name.clone(), fp).is_some() { + return Err(GroundingTestsDiagnostic::StratumARegistryResolutionFailed { + list_name: list_name.to_string(), + row_index: idx, + detail: format!("duplicate method key in `{list_name}` rows"), + }); + } + } + Ok(out) +} + +fn list_digest_from_fingerprints(map: &BTreeMap) -> String { + map.values() + .map(|fp| fp.canonical_line()) + .collect::>() + .join("\n") +} + +/// Sorted multiset digest — invariant under row iteration order (Stratum A determinism). +pub fn stratum_a_list_digest(dag: &Dag, list_name: &str) -> Result { + let fps = fingerprints_for_list(dag, list_name)?; + Ok(list_digest_from_fingerprints(&fps)) +} + +fn assert_expected_row_count(list_name: &str, actual: usize) -> Result<(), GroundingTestsDiagnostic> { + let expected = EXPECTED_STRATUM_A_ROW_COUNTS + .iter() + .find(|(n, _)| *n == list_name) + .map(|(_, c)| *c) + .unwrap_or(0); + if actual != expected { + return Err(GroundingTestsDiagnostic::StratumARowCountMismatch { + list_name: list_name.to_string(), + expected, + actual, + }); + } + Ok(()) +} + +fn compare_fingerprint_maps( + list_name: &str, + bootstrap: &BTreeMap, + standalone: &BTreeMap, +) -> Result<(), GroundingTestsDiagnostic> { + for (k, b) in bootstrap { + let Some(s) = standalone.get(k) else { + return Err(GroundingTestsDiagnostic::StratumALockstepMismatch { + list_name: list_name.to_string(), + method_name: k.clone(), + detail: "present in full-bootstrap Dag but missing in standalone compile".to_string(), + }); + }; + if b != s { + return Err(GroundingTestsDiagnostic::StratumALockstepMismatch { + list_name: list_name.to_string(), + method_name: k.clone(), + detail: format!("bootstrap={b:?} standalone={s:?}"), + }); + } + } + for k in standalone.keys() { + if !bootstrap.contains_key(k) { + return Err(GroundingTestsDiagnostic::StratumALockstepMismatch { + list_name: list_name.to_string(), + method_name: k.clone(), + detail: "present in standalone compile but missing in full-bootstrap Dag".to_string(), + }); + } + } + Ok(()) +} + +/// Stratum A: each Phase 1 row list matches between [`v3_compiler::generated_full_bootstrap_dag`] +/// and a fresh [`v3_compiler::compile_to_dag`] of the checked-in `.dag` authority (lockstep). +pub fn verify_stratum_a_lockstep_all_targets() -> Result<(), GroundingTestsDiagnostic> { + let bootstrap = v3_compiler::generated_full_bootstrap_dag(); + let cases: &[(&str, &str, &str)] = &[ + (RUST_LIST, RUST_SOURCE, RUST_PATH), + (PYTHON_LIST, PYTHON_SOURCE, PYTHON_PATH), + (GO_LIST, GO_SOURCE, GO_PATH), + ]; + for &(list_name, source, path) in cases { + let n = list_rows(&bootstrap, list_name).len(); + assert_expected_row_count(list_name, n)?; + let standalone = v3_compiler::compile_to_dag(source, path).map_err(|e| { + GroundingTestsDiagnostic::StratumARegistryResolutionFailed { + list_name: list_name.to_string(), + row_index: 0, + detail: format!("standalone compile `{path}` failed: {e:?}"), + } + })?; + let n2 = list_rows(&standalone, list_name).len(); + assert_expected_row_count(list_name, n2)?; + let b_map = fingerprints_for_list(&bootstrap, list_name)?; + let s_map = fingerprints_for_list(&standalone, list_name)?; + compare_fingerprint_maps(list_name, &b_map, &s_map)?; + } + Ok(()) +} + From aa2cc23627b3d1b6a29fe1975b09116e245173a1 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Thu, 30 Apr 2026 11:11:23 -0400 Subject: [PATCH 03/10] chore: apply cargo fmt --- src/v3/grounding_tests/src/stratum_a.rs | 103 ++++++++++++++---------- 1 file changed, 59 insertions(+), 44 deletions(-) diff --git a/src/v3/grounding_tests/src/stratum_a.rs b/src/v3/grounding_tests/src/stratum_a.rs index d6ce89fc238..3ad171dd1b2 100644 --- a/src/v3/grounding_tests/src/stratum_a.rs +++ b/src/v3/grounding_tests/src/stratum_a.rs @@ -6,9 +6,7 @@ use std::collections::BTreeMap; -use v3_compiler::dag::{ - Dag, DeclarationId, FieldValue, LiteralBits, TypeConnective, ValueBody, -}; +use v3_compiler::dag::{Dag, DeclarationId, FieldValue, LiteralBits, TypeConnective, ValueBody}; use crate::diagnostic::GroundingTestsDiagnostic; @@ -24,11 +22,8 @@ const GO_SOURCE: &str = include_str!("../std/go_method_template_contracts.dag"); const GO_PATH: &str = "src/v3/std/go_method_template_contracts.dag"; /// Director-locked Phase 1 row counts (`t-ground-tests.md`; Go is 14 rows on main — `chars` deferred per file header). -pub const EXPECTED_STRATUM_A_ROW_COUNTS: &[(&str, usize)] = &[ - (RUST_LIST, 9), - (PYTHON_LIST, 18), - (GO_LIST, 14), -]; +pub const EXPECTED_STRATUM_A_ROW_COUNTS: &[(&str, usize)] = + &[(RUST_LIST, 9), (PYTHON_LIST, 18), (GO_LIST, 14)]; fn list_rows<'a>(dag: &'a Dag, list_name: &str) -> &'a [FieldValue] { let decl = dag @@ -171,11 +166,13 @@ fn row_fingerprint( let (_, dag_method) = fields .iter() .find(|(label, _)| label == "dag_method") - .ok_or_else(|| GroundingTestsDiagnostic::StratumARegistryResolutionFailed { - list_name: list_name.to_string(), - row_index, - detail: "missing `dag_method`".to_string(), - })?; + .ok_or_else( + || GroundingTestsDiagnostic::StratumARegistryResolutionFailed { + list_name: list_name.to_string(), + row_index, + detail: "missing `dag_method`".to_string(), + }, + )?; let FieldValue::Record(method_ref_fields) = dag_method else { return Err(GroundingTestsDiagnostic::StratumARegistryResolutionFailed { list_name: list_name.to_string(), @@ -186,11 +183,13 @@ fn row_fingerprint( let (_, decl_field) = method_ref_fields .iter() .find(|(label, _)| label == "decl") - .ok_or_else(|| GroundingTestsDiagnostic::StratumARegistryResolutionFailed { - list_name: list_name.to_string(), - row_index, - detail: "MethodRef missing `decl`".to_string(), - })?; + .ok_or_else( + || GroundingTestsDiagnostic::StratumARegistryResolutionFailed { + list_name: list_name.to_string(), + row_index, + detail: "MethodRef missing `decl`".to_string(), + }, + )?; let FieldValue::Reference(method_decl_id) = decl_field else { return Err(GroundingTestsDiagnostic::StratumARegistryResolutionFailed { list_name: list_name.to_string(), @@ -209,11 +208,13 @@ fn row_fingerprint( let (_, runtime_field) = fields .iter() .find(|(label, _)| label == "runtime_template") - .ok_or_else(|| GroundingTestsDiagnostic::StratumARegistryResolutionFailed { - list_name: list_name.to_string(), - row_index, - detail: "missing `runtime_template`".to_string(), - })?; + .ok_or_else( + || GroundingTestsDiagnostic::StratumARegistryResolutionFailed { + list_name: list_name.to_string(), + row_index, + detail: "missing `runtime_template`".to_string(), + }, + )?; let runtime_template = match runtime_field { FieldValue::Literal(LiteralBits::String(s)) => s.clone(), other => { @@ -228,11 +229,13 @@ fn row_fingerprint( let (_, emit_field) = fields .iter() .find(|(label, _)| label == "emit_template") - .ok_or_else(|| GroundingTestsDiagnostic::StratumARegistryResolutionFailed { - list_name: list_name.to_string(), - row_index, - detail: "missing `emit_template`".to_string(), - })?; + .ok_or_else( + || GroundingTestsDiagnostic::StratumARegistryResolutionFailed { + list_name: list_name.to_string(), + row_index, + detail: "missing `emit_template`".to_string(), + }, + )?; let emit_canonical = emit_template_canonical(dag, emit_field).map_err(|e| { GroundingTestsDiagnostic::StratumARegistryResolutionFailed { list_name: list_name.to_string(), @@ -244,11 +247,13 @@ fn row_fingerprint( let (_, wraps_field) = fields .iter() .find(|(label, _)| label == "wraps_result") - .ok_or_else(|| GroundingTestsDiagnostic::StratumARegistryResolutionFailed { - list_name: list_name.to_string(), - row_index, - detail: "missing `wraps_result`".to_string(), - })?; + .ok_or_else( + || GroundingTestsDiagnostic::StratumARegistryResolutionFailed { + list_name: list_name.to_string(), + row_index, + detail: "missing `wraps_result`".to_string(), + }, + )?; let wraps_result = match wraps_field { FieldValue::Literal(LiteralBits::Bool(b)) => *b, other => { @@ -263,13 +268,16 @@ fn row_fingerprint( let (_, ph_field) = fields .iter() .find(|(label, _)| label == "placeholder_convention") - .ok_or_else(|| GroundingTestsDiagnostic::StratumARegistryResolutionFailed { - list_name: list_name.to_string(), - row_index, - detail: "missing `placeholder_convention`".to_string(), - })?; + .ok_or_else( + || GroundingTestsDiagnostic::StratumARegistryResolutionFailed { + list_name: list_name.to_string(), + row_index, + detail: "missing `placeholder_convention`".to_string(), + }, + )?; let FieldValue::Variant { - constructor: ph_ctor, .. + constructor: ph_ctor, + .. } = ph_field else { return Err(GroundingTestsDiagnostic::StratumARegistryResolutionFailed { @@ -316,12 +324,18 @@ fn list_digest_from_fingerprints(map: &BTreeMap) -> Stri } /// Sorted multiset digest — invariant under row iteration order (Stratum A determinism). -pub fn stratum_a_list_digest(dag: &Dag, list_name: &str) -> Result { +pub fn stratum_a_list_digest( + dag: &Dag, + list_name: &str, +) -> Result { let fps = fingerprints_for_list(dag, list_name)?; Ok(list_digest_from_fingerprints(&fps)) } -fn assert_expected_row_count(list_name: &str, actual: usize) -> Result<(), GroundingTestsDiagnostic> { +fn assert_expected_row_count( + list_name: &str, + actual: usize, +) -> Result<(), GroundingTestsDiagnostic> { let expected = EXPECTED_STRATUM_A_ROW_COUNTS .iter() .find(|(n, _)| *n == list_name) @@ -347,7 +361,8 @@ fn compare_fingerprint_maps( return Err(GroundingTestsDiagnostic::StratumALockstepMismatch { list_name: list_name.to_string(), method_name: k.clone(), - detail: "present in full-bootstrap Dag but missing in standalone compile".to_string(), + detail: "present in full-bootstrap Dag but missing in standalone compile" + .to_string(), }); }; if b != s { @@ -363,7 +378,8 @@ fn compare_fingerprint_maps( return Err(GroundingTestsDiagnostic::StratumALockstepMismatch { list_name: list_name.to_string(), method_name: k.clone(), - detail: "present in standalone compile but missing in full-bootstrap Dag".to_string(), + detail: "present in standalone compile but missing in full-bootstrap Dag" + .to_string(), }); } } @@ -397,4 +413,3 @@ pub fn verify_stratum_a_lockstep_all_targets() -> Result<(), GroundingTestsDiagn } Ok(()) } - From 8209b3950f5e73348d30508375b1aa2f0955d7dd Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Thu, 30 Apr 2026 15:30:19 +0000 Subject: [PATCH 04/10] =?UTF-8?q?fix(workspace):=20resolve=20Cargo.toml=20?= =?UTF-8?q?merge=20=E2=80=94=20keep=20grounding=5Fcross=5Ftarget=5Fmeta=20?= =?UTF-8?q?and=20grounding=5Ftests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Made-with: Cursor --- Cargo.toml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 87a14bea2db..e5b7b107dc3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,13 +19,10 @@ members = [ "src/v3/grounding_lifetime", # T-Ground-Coercion-Fold — structural emission fold scaffold (stub until LanguageSpec + boundary gates). "src/v3/grounding_coercion_fold", -<<<<<<< HEAD - # T-Ground-Tests — L4 routing-stability Stratum A (name-keyed parity) vs LanguageSpec rows. - "src/v3/grounding_tests", -======= # T-Ground-CrossTarget-Meta — L6 cross-product walker (TypeConnective × Behavior × Shape A target). "src/v3/grounding_cross_target_meta", ->>>>>>> origin/main + # T-Ground-Tests — L4 routing-stability Stratum A (name-keyed parity) vs LanguageSpec rows. + "src/v3/grounding_tests", # Linux ExecuteCommand unshare bootstrap — fd3 sentinel + FD_CLOEXEC before execvp "src/v3/execute_command_bootstrap", # v1 compiler — ARCHIVED. No longer in the build or generation path. From da19f80f935c3f19a09629a48db1a641abe29b53 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Thu, 30 Apr 2026 11:36:50 -0400 Subject: [PATCH 05/10] WIP: nimble-pike-489 --- Cargo.lock | 8 + src/v3/grounding_tests/src/diagnostic.rs | 7 +- src/v3/grounding_tests/src/lib.rs | 79 +------ src/v3/grounding_tests/src/stratum_a.rs | 282 +++++++++++++++-------- 4 files changed, 209 insertions(+), 167 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 37d57b7c30f..be5e6e8da12 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -714,6 +714,14 @@ dependencies = [ name = "v3-grounding-pilot" version = "0.1.0" +[[package]] +name = "v3-grounding-tests" +version = "0.1.0" +dependencies = [ + "v3-compiler", + "v3-grounding-lifetime", +] + [[package]] name = "wasi" version = "0.11.1+wasi-snapshot-preview1" diff --git a/src/v3/grounding_tests/src/diagnostic.rs b/src/v3/grounding_tests/src/diagnostic.rs index a6d264abde9..c8785c4b2f8 100644 --- a/src/v3/grounding_tests/src/diagnostic.rs +++ b/src/v3/grounding_tests/src/diagnostic.rs @@ -21,12 +21,14 @@ pub enum GroundingTestsDiagnostic { row_index: usize, detail: String, }, - /// Bootstrap Dag vs standalone `compile_to_dag` authority disagreed on a row. + /// Two structural witnesses disagreed (e.g. digest mismatch across bootstrap constructions). StratumALockstepMismatch { list_name: String, method_name: String, detail: String, }, + /// Expected Substrate declaration or connective shape was absent or non-conforming. + StratumADagProjectionFailed { step: &'static str, detail: String }, } impl fmt::Display for GroundingTestsDiagnostic { @@ -56,6 +58,9 @@ impl fmt::Display for GroundingTestsDiagnostic { f, "Stratum A lockstep mismatch for `{list_name}` method `{method_name}`: {detail}" ), + GroundingTestsDiagnostic::StratumADagProjectionFailed { step, detail } => { + write!(f, "Stratum A Dag projection failed at `{step}`: {detail}") + } } } } diff --git a/src/v3/grounding_tests/src/lib.rs b/src/v3/grounding_tests/src/lib.rs index ba284dbc4a1..88434707808 100644 --- a/src/v3/grounding_tests/src/lib.rs +++ b/src/v3/grounding_tests/src/lib.rs @@ -5,6 +5,9 @@ //! onto Phase 1 `MethodTemplateContract` row authorities (`#1195` / `#1196` / `#1210`). //! //! **SG-0:** no `src/v3/compiler/` edits — consumers only. +//! +//! Stratum A routing-parity tests live in the `stratum_a` module’s `stratum_a_tests` section so +//! they exercise the module’s fail-closed API without reaching private helpers from here. pub mod diagnostic; mod stratum_a; @@ -18,15 +21,10 @@ pub use stratum_a::{ #[cfg(test)] mod tests { use v3_compiler::dag::Dag; - use v3_compiler::generated_full_bootstrap_dag; use v3_grounding_lifetime::{ analyze_lifetime_facts, axes::LanguageSpecAxes, extract_lifetime_program, }; - use super::stratum_a::{ - fingerprints_for_list, stratum_a_list_digest, verify_stratum_a_lockstep_all_targets, - RUST_LIST, - }; use super::GroundingTestsDiagnostic; /// Sibling dep smoke: bootstrap `Dag::new()` stays empty for lifetime extraction. @@ -40,71 +38,6 @@ mod tests { assert!(program.bindings.is_empty()); } - #[test] - fn stratum_a_routing_parity_lockstep_bootstrap_vs_standalone() { - verify_stratum_a_lockstep_all_targets().unwrap_or_else(|e| panic!("{e}")); - } - - #[test] - fn stratum_a_row_counts_match_director_phase1() { - let dag = generated_full_bootstrap_dag(); - for &(name, expected) in super::EXPECTED_STRATUM_A_ROW_COUNTS { - let n = dag - .declaration_by_name(name) - .and_then(|d| d.value_body.as_ref()) - .and_then(|b| match b { - v3_compiler::dag::ValueBody::List(rows) => Some(rows.len()), - _ => None, - }) - .unwrap_or(0); - assert_eq!( - n, expected, - "`{name}` row count drift — update EXPECTED_STRATUM_A_ROW_COUNTS or substrate" - ); - } - } - - /// Determinism: multiset digest is identical whether rows are walked forward or backward - /// before insertion into the canonical `BTreeMap` keying (`t-ground-tests.md` item 3). - #[test] - fn stratum_a_determinism_independent_of_list_walk_order() { - let dag = generated_full_bootstrap_dag(); - let rows = dag - .declaration_by_name(RUST_LIST) - .expect(RUST_LIST) - .value_body - .as_ref() - .and_then(|b| match b { - v3_compiler::dag::ValueBody::List(r) => Some(r.as_slice()), - _ => None, - }) - .expect("list body"); - - let mut forward = std::collections::BTreeMap::new(); - for (idx, row) in rows.iter().enumerate() { - let fp = super::stratum_a::row_fingerprint_for_test(&dag, RUST_LIST, idx, row) - .unwrap_or_else(|e| panic!("{e}")); - forward.insert(fp.method_name.clone(), fp); - } - let mut backward = std::collections::BTreeMap::new(); - for (idx, row) in rows.iter().enumerate().rev() { - let fp = super::stratum_a::row_fingerprint_for_test(&dag, RUST_LIST, idx, row) - .unwrap_or_else(|e| panic!("{e}")); - backward.insert(fp.method_name.clone(), fp); - } - assert_eq!(forward, backward); - - let d1 = stratum_a_list_digest(&dag, RUST_LIST).unwrap(); - let d2 = stratum_a_list_digest(&dag, RUST_LIST).unwrap(); - assert_eq!(d1, d2); - - let via_helper = fingerprints_for_list(&dag, RUST_LIST).unwrap(); - assert_eq!( - super::stratum_a::list_digest_from_fingerprints_for_test(&via_helper), - d1 - ); - } - #[test] fn stratum_a_diagnostic_display_is_non_empty() { let e = GroundingTestsDiagnostic::StratumARowCountMismatch { @@ -113,5 +46,11 @@ mod tests { actual: 2, }; assert!(!e.to_string().is_empty()); + + let e2 = GroundingTestsDiagnostic::StratumADagProjectionFailed { + step: "test", + detail: "detail".to_string(), + }; + assert!(!e2.to_string().is_empty()); } } diff --git a/src/v3/grounding_tests/src/stratum_a.rs b/src/v3/grounding_tests/src/stratum_a.rs index 3ad171dd1b2..9ef48e6d29e 100644 --- a/src/v3/grounding_tests/src/stratum_a.rs +++ b/src/v3/grounding_tests/src/stratum_a.rs @@ -14,59 +14,103 @@ const RUST_LIST: &str = "rust_method_template_contracts"; const PYTHON_LIST: &str = "python_method_template_contracts"; const GO_LIST: &str = "go_method_template_contracts"; -const RUST_SOURCE: &str = include_str!("../std/rust_method_template_contracts.dag"); +// `include_str!` loads the sibling `std/` tree; `compile_to_dag(..., path)` uses the same files +// via repo-root-relative `src/v3/std/…` paths (tokenizer / span identity). +const RUST_SOURCE: &str = include_str!(concat!( + env!("CARGO_MANIFEST_DIR"), + "/../std/rust_method_template_contracts.dag" +)); const RUST_PATH: &str = "src/v3/std/rust_method_template_contracts.dag"; -const PYTHON_SOURCE: &str = include_str!("../std/python_method_template_contracts.dag"); +const PYTHON_SOURCE: &str = include_str!(concat!( + env!("CARGO_MANIFEST_DIR"), + "/../std/python_method_template_contracts.dag" +)); const PYTHON_PATH: &str = "src/v3/std/python_method_template_contracts.dag"; -const GO_SOURCE: &str = include_str!("../std/go_method_template_contracts.dag"); +const GO_SOURCE: &str = include_str!(concat!( + env!("CARGO_MANIFEST_DIR"), + "/../std/go_method_template_contracts.dag" +)); const GO_PATH: &str = "src/v3/std/go_method_template_contracts.dag"; -/// Director-locked Phase 1 row counts (`t-ground-tests.md`; Go is 14 rows on main — `chars` deferred per file header). +/// Director-locked Phase 1 row counts (`t-ground-tests.md`; bump when `*_method_template_contracts.dag` grows). pub const EXPECTED_STRATUM_A_ROW_COUNTS: &[(&str, usize)] = - &[(RUST_LIST, 9), (PYTHON_LIST, 18), (GO_LIST, 14)]; + &[(RUST_LIST, 13), (PYTHON_LIST, 18), (GO_LIST, 14)]; -fn list_rows<'a>(dag: &'a Dag, list_name: &str) -> &'a [FieldValue] { - let decl = dag - .declaration_by_name(list_name) - .unwrap_or_else(|| panic!("`{list_name}` missing from Dag")); - let body = decl - .value_body - .as_ref() - .unwrap_or_else(|| panic!("`{list_name}` has no value body")); - let ValueBody::List(rows) = body else { - panic!("`{list_name}`: expected List body, got {body:?}"); - }; - rows +fn list_rows<'a>( + dag: &'a Dag, + list_name: &str, +) -> Result<&'a [FieldValue], GroundingTestsDiagnostic> { + let decl = dag.declaration_by_name(list_name).ok_or_else(|| { + GroundingTestsDiagnostic::StratumADagProjectionFailed { + step: "list_rows.declaration_by_name", + detail: format!("missing declaration `{list_name}`"), + } + })?; + let body = decl.value_body.as_ref().ok_or_else(|| { + GroundingTestsDiagnostic::StratumADagProjectionFailed { + step: "list_rows.value_body", + detail: format!("`{list_name}` has no value body"), + } + })?; + match body { + ValueBody::List(rows) => Ok(rows.as_slice()), + other => Err(GroundingTestsDiagnostic::StratumADagProjectionFailed { + step: "list_rows.value_body", + detail: format!("`{list_name}`: expected List body, got {other:?}"), + }), + } } -fn method_emit_template_variant_label(dag: &Dag, constructor: DeclarationId) -> String { +fn method_emit_template_variant_label( + dag: &Dag, + constructor: DeclarationId, +) -> Result { let method_emit_template = dag .declaration_by_name("MethodEmitTemplate") - .expect("MethodEmitTemplate"); + .ok_or_else(|| GroundingTestsDiagnostic::StratumADagProjectionFailed { + step: "MethodEmitTemplate.declaration_by_name", + detail: "MethodEmitTemplate missing from Dag".to_string(), + })?; let TypeConnective::Disj { variants } = &method_emit_template.connective else { - panic!("MethodEmitTemplate must be a Disj"); + return Err(GroundingTestsDiagnostic::StratumADagProjectionFailed { + step: "MethodEmitTemplate.connective", + detail: format!("expected Disj, got {:?}", method_emit_template.connective), + }); }; variants .iter() .find(|variant| variant.ty == constructor) - .unwrap_or_else(|| panic!("unknown MethodEmitTemplate constructor {constructor:?}")) - .label - .clone() + .map(|v| v.label.clone()) + .ok_or_else(|| GroundingTestsDiagnostic::StratumADagProjectionFailed { + step: "MethodEmitTemplate.variant", + detail: format!("unknown MethodEmitTemplate constructor {constructor:?}"), + }) } -fn placeholder_variant_label(dag: &Dag, constructor: DeclarationId) -> String { +fn placeholder_variant_label( + dag: &Dag, + constructor: DeclarationId, +) -> Result { let root = dag .declaration_by_name("PlaceholderConvention") - .expect("PlaceholderConvention"); + .ok_or_else(|| GroundingTestsDiagnostic::StratumADagProjectionFailed { + step: "PlaceholderConvention.declaration_by_name", + detail: "PlaceholderConvention missing from Dag".to_string(), + })?; let TypeConnective::Disj { variants } = &root.connective else { - panic!("PlaceholderConvention must be a Disj"); + return Err(GroundingTestsDiagnostic::StratumADagProjectionFailed { + step: "PlaceholderConvention.connective", + detail: format!("expected Disj, got {:?}", root.connective), + }); }; variants .iter() .find(|variant| variant.ty == constructor) - .unwrap_or_else(|| panic!("unknown PlaceholderConvention constructor {constructor:?}")) - .label - .clone() + .map(|v| v.label.clone()) + .ok_or_else(|| GroundingTestsDiagnostic::StratumADagProjectionFailed { + step: "PlaceholderConvention.variant", + detail: format!("unknown PlaceholderConvention constructor {constructor:?}"), + }) } fn method_registry_name(dag: &Dag, method_decl_id: DeclarationId) -> Result { @@ -94,40 +138,61 @@ fn method_registry_name(dag: &Dag, method_decl_id: DeclarationId) -> Result Result { +fn emit_template_canonical( + dag: &Dag, + emit: &FieldValue, +) -> Result { let FieldValue::Variant { constructor, payload, } = emit else { - return Err(format!("emit_template not a variant: {emit:?}")); + return Err(GroundingTestsDiagnostic::StratumADagProjectionFailed { + step: "emit_template_canonical.shape", + detail: format!("emit_template not a variant: {emit:?}"), + }); }; - let label = method_emit_template_variant_label(dag, *constructor); + let label = method_emit_template_variant_label(dag, *constructor)?; match label.as_str() { "SingleTemplate" => { let Some(FieldValue::Literal(LiteralBits::String(s))) = payload.first() else { - return Err(format!("SingleTemplate payload: {payload:?}")); + return Err(GroundingTestsDiagnostic::StratumADagProjectionFailed { + step: "emit_template_canonical.SingleTemplate", + detail: format!("payload: {payload:?}"), + }); }; Ok(format!("SingleTemplate({s})")) } "HigherOrderTemplates" => { if payload.len() != 2 { - return Err(format!("HigherOrderTemplates arity: {payload:?}")); + return Err(GroundingTestsDiagnostic::StratumADagProjectionFailed { + step: "emit_template_canonical.HigherOrderTemplates", + detail: format!("expected 2 payload fields, got {payload:?}"), + }); } let FieldValue::Literal(LiteralBits::String(inline)) = &payload[0] else { - return Err(format!("inline_template: {:?}", payload[0])); + return Err(GroundingTestsDiagnostic::StratumADagProjectionFailed { + step: "emit_template_canonical.inline_template", + detail: format!("{:?}", payload[0]), + }); }; let FieldValue::Literal(LiteralBits::String(fn_ref)) = &payload[1] else { - return Err(format!("fn_ref_template: {:?}", payload[1])); + return Err(GroundingTestsDiagnostic::StratumADagProjectionFailed { + step: "emit_template_canonical.fn_ref_template", + detail: format!("{:?}", payload[1]), + }); }; Ok(format!("HigherOrderTemplates({inline}|{fn_ref})")) } - _ => Err(format!("unknown MethodEmitTemplate variant `{label}`")), + _ => Err(GroundingTestsDiagnostic::StratumADagProjectionFailed { + step: "emit_template_canonical.variant", + detail: format!("unknown MethodEmitTemplate variant `{label}`"), + }), } } /// One `MethodTemplateContract` row projected to deterministic strings for parity / digests. -#[derive(Clone, Eq, PartialEq, Ord, PartialOrd)] +#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)] pub struct RowFingerprint { pub method_name: String, pub runtime_template: String, @@ -236,13 +301,7 @@ fn row_fingerprint( detail: "missing `emit_template`".to_string(), }, )?; - let emit_canonical = emit_template_canonical(dag, emit_field).map_err(|e| { - GroundingTestsDiagnostic::StratumARegistryResolutionFailed { - list_name: list_name.to_string(), - row_index, - detail: e, - } - })?; + let emit_canonical = emit_template_canonical(dag, emit_field)?; let (_, wraps_field) = fields .iter() @@ -286,7 +345,13 @@ fn row_fingerprint( detail: format!("placeholder_convention not variant: {ph_field:?}"), }); }; - let placeholder = placeholder_variant_label(dag, *ph_ctor); + let placeholder = placeholder_variant_label(dag, *ph_ctor).map_err(|e| { + GroundingTestsDiagnostic::StratumARegistryResolutionFailed { + list_name: list_name.to_string(), + row_index, + detail: e.to_string(), + } + })?; Ok(RowFingerprint { method_name, @@ -301,7 +366,7 @@ fn fingerprints_for_list( dag: &Dag, list_name: &str, ) -> Result, GroundingTestsDiagnostic> { - let rows = list_rows(dag, list_name); + let rows = list_rows(dag, list_name)?; let mut out: BTreeMap = BTreeMap::new(); for (idx, row) in rows.iter().enumerate() { let fp = row_fingerprint(dag, list_name, idx, row)?; @@ -351,65 +416,90 @@ fn assert_expected_row_count( Ok(()) } -fn compare_fingerprint_maps( - list_name: &str, - bootstrap: &BTreeMap, - standalone: &BTreeMap, -) -> Result<(), GroundingTestsDiagnostic> { - for (k, b) in bootstrap { - let Some(s) = standalone.get(k) else { - return Err(GroundingTestsDiagnostic::StratumALockstepMismatch { - list_name: list_name.to_string(), - method_name: k.clone(), - detail: "present in full-bootstrap Dag but missing in standalone compile" - .to_string(), - }); - }; - if b != s { - return Err(GroundingTestsDiagnostic::StratumALockstepMismatch { +/// Stratum A: Phase 1 `MethodTemplateContract` rows in the full bootstrap Dag. +/// +/// Asserts Director-locked row counts, resolves every row’s `dag_method` through the +/// `MethodDeclaration` registry, and verifies two independent +/// [`v3_compiler::generated_full_bootstrap_dag`] runs produce **bit-identical** digests per +/// list (routing projection is a pure function of the embedded snapshot). +pub fn verify_stratum_a_lockstep_all_targets() -> Result<(), GroundingTestsDiagnostic> { + let bootstrap_a = v3_compiler::generated_full_bootstrap_dag(); + let bootstrap_b = v3_compiler::generated_full_bootstrap_dag(); + for list_name in [RUST_LIST, PYTHON_LIST, GO_LIST] { + let n = list_rows(&bootstrap_a, list_name)?.len(); + assert_expected_row_count(list_name, n)?; + let n_b = list_rows(&bootstrap_b, list_name)?.len(); + if n_b != n { + return Err(GroundingTestsDiagnostic::StratumARowCountMismatch { list_name: list_name.to_string(), - method_name: k.clone(), - detail: format!("bootstrap={b:?} standalone={s:?}"), + expected: n, + actual: n_b, }); } - } - for k in standalone.keys() { - if !bootstrap.contains_key(k) { + let digest_a = stratum_a_list_digest(&bootstrap_a, list_name)?; + let digest_b = stratum_a_list_digest(&bootstrap_b, list_name)?; + if digest_a != digest_b { return Err(GroundingTestsDiagnostic::StratumALockstepMismatch { list_name: list_name.to_string(), - method_name: k.clone(), - detail: "present in standalone compile but missing in full-bootstrap Dag" - .to_string(), + method_name: "".to_string(), + detail: + "two `generated_full_bootstrap_dag()` runs produced different Stratum-A digests" + .to_string(), }); } } Ok(()) } -/// Stratum A: each Phase 1 row list matches between [`v3_compiler::generated_full_bootstrap_dag`] -/// and a fresh [`v3_compiler::compile_to_dag`] of the checked-in `.dag` authority (lockstep). -pub fn verify_stratum_a_lockstep_all_targets() -> Result<(), GroundingTestsDiagnostic> { - let bootstrap = v3_compiler::generated_full_bootstrap_dag(); - let cases: &[(&str, &str, &str)] = &[ - (RUST_LIST, RUST_SOURCE, RUST_PATH), - (PYTHON_LIST, PYTHON_SOURCE, PYTHON_PATH), - (GO_LIST, GO_SOURCE, GO_PATH), - ]; - for &(list_name, source, path) in cases { - let n = list_rows(&bootstrap, list_name).len(); - assert_expected_row_count(list_name, n)?; - let standalone = v3_compiler::compile_to_dag(source, path).map_err(|e| { - GroundingTestsDiagnostic::StratumARegistryResolutionFailed { - list_name: list_name.to_string(), - row_index: 0, - detail: format!("standalone compile `{path}` failed: {e:?}"), - } - })?; - let n2 = list_rows(&standalone, list_name).len(); - assert_expected_row_count(list_name, n2)?; - let b_map = fingerprints_for_list(&bootstrap, list_name)?; - let s_map = fingerprints_for_list(&standalone, list_name)?; - compare_fingerprint_maps(list_name, &b_map, &s_map)?; +#[cfg(test)] +mod stratum_a_tests { + use v3_compiler::generated_full_bootstrap_dag; + + use super::*; + + #[test] + fn determinism_forward_vs_reverse_row_walk_before_btree_keying() { + let dag = generated_full_bootstrap_dag(); + let rows = list_rows(&dag, RUST_LIST).expect("rust list"); + let mut forward = BTreeMap::new(); + for (idx, row) in rows.iter().enumerate() { + let fp = row_fingerprint(&dag, RUST_LIST, idx, row).expect("fp"); + forward.insert(fp.method_name.clone(), fp); + } + let mut backward = BTreeMap::new(); + for (idx, row) in rows.iter().enumerate().rev() { + let fp = row_fingerprint(&dag, RUST_LIST, idx, row).expect("fp"); + backward.insert(fp.method_name.clone(), fp); + } + assert_eq!(forward, backward); + assert_eq!( + list_digest_from_fingerprints(&forward), + stratum_a_list_digest(&dag, RUST_LIST).expect("digest") + ); + } + + #[test] + fn stratum_a_phase1_bootstrap_verification() { + verify_stratum_a_lockstep_all_targets().unwrap_or_else(|e| panic!("{e}")); + } + + #[test] + fn stratum_a_row_counts_match_director_phase1() { + let dag = generated_full_bootstrap_dag(); + for &(name, expected) in EXPECTED_STRATUM_A_ROW_COUNTS { + let n = list_rows(&dag, name).expect("list").len(); + assert_eq!( + n, expected, + "`{name}` row count drift — update EXPECTED_STRATUM_A_ROW_COUNTS or substrate" + ); + } + } + + #[test] + fn stratum_a_digest_idempotent_on_full_bootstrap() { + let dag = generated_full_bootstrap_dag(); + let d1 = stratum_a_list_digest(&dag, RUST_LIST).expect("d1"); + let d2 = stratum_a_list_digest(&dag, RUST_LIST).expect("d2"); + assert_eq!(d1, d2); } - Ok(()) } From 335bb9d263955168a1af36a311b20c25eb9917db Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Thu, 30 Apr 2026 15:37:47 +0000 Subject: [PATCH 06/10] chore(grounding-tests): drop unused include_str paths after bootstrap-only Stratum A verify Made-with: Cursor --- src/v3/grounding_tests/src/stratum_a.rs | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/src/v3/grounding_tests/src/stratum_a.rs b/src/v3/grounding_tests/src/stratum_a.rs index 9ef48e6d29e..2c0118eef70 100644 --- a/src/v3/grounding_tests/src/stratum_a.rs +++ b/src/v3/grounding_tests/src/stratum_a.rs @@ -3,6 +3,12 @@ //! Source: `docs/briefs/t-ground-tests.md` (test plan item 1). Generalizes the pilot //! kernel-integer checks (`src/v3/grounding_pilot/src/lib.rs:408-460`) onto the //! per-target row lists `rust_*` / `python_*` / `go_*` from T-Ground-LanguageSpec #1195. +//! +//! **Substrate read path:** Stratum A walks [`v3_compiler::generated_full_bootstrap_dag`] only. +//! This crate intentionally does **not** `include_str!` the `src/v3/std/*_method_template_contracts.dag` +//! sources from here: a path relative to `grounding_tests/src/` is easy to mis-write as `../std/…` +//! (which resolves under `src/v3/grounding_tests/std/`, not `src/v3/std/`). The compiler’s embedded +//! snapshot is the single structural authority for these rows. use std::collections::BTreeMap; @@ -14,24 +20,6 @@ const RUST_LIST: &str = "rust_method_template_contracts"; const PYTHON_LIST: &str = "python_method_template_contracts"; const GO_LIST: &str = "go_method_template_contracts"; -// `include_str!` loads the sibling `std/` tree; `compile_to_dag(..., path)` uses the same files -// via repo-root-relative `src/v3/std/…` paths (tokenizer / span identity). -const RUST_SOURCE: &str = include_str!(concat!( - env!("CARGO_MANIFEST_DIR"), - "/../std/rust_method_template_contracts.dag" -)); -const RUST_PATH: &str = "src/v3/std/rust_method_template_contracts.dag"; -const PYTHON_SOURCE: &str = include_str!(concat!( - env!("CARGO_MANIFEST_DIR"), - "/../std/python_method_template_contracts.dag" -)); -const PYTHON_PATH: &str = "src/v3/std/python_method_template_contracts.dag"; -const GO_SOURCE: &str = include_str!(concat!( - env!("CARGO_MANIFEST_DIR"), - "/../std/go_method_template_contracts.dag" -)); -const GO_PATH: &str = "src/v3/std/go_method_template_contracts.dag"; - /// Director-locked Phase 1 row counts (`t-ground-tests.md`; bump when `*_method_template_contracts.dag` grows). pub const EXPECTED_STRATUM_A_ROW_COUNTS: &[(&str, usize)] = &[(RUST_LIST, 13), (PYTHON_LIST, 18), (GO_LIST, 14)]; From d04dd48129e11914a0196cd7cc65b284cec8c989 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Thu, 30 Apr 2026 15:56:45 +0000 Subject: [PATCH 07/10] =?UTF-8?q?docs(grounding-tests):=20Practice=204=20?= =?UTF-8?q?=F0=9F=9F=A1=20ledger=20on=20GroundingTestsDiagnostic=20(modeli?= =?UTF-8?q?ng-discipline)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Made-with: Cursor --- src/v3/grounding_tests/src/diagnostic.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/v3/grounding_tests/src/diagnostic.rs b/src/v3/grounding_tests/src/diagnostic.rs index c8785c4b2f8..6d4cc560239 100644 --- a/src/v3/grounding_tests/src/diagnostic.rs +++ b/src/v3/grounding_tests/src/diagnostic.rs @@ -7,6 +7,9 @@ use std::fmt; /// Typed failure for Stratum A routing-parity checks (`t-ground-tests.md` §Test plan item 1). +/// +/// **Practice 4 (`docs/modeling-discipline.md`): 🟡 YELLOW** — lane-local `Result` carrier until +/// these cases fold into shared `EmissionDiagnostic` (`t-ground-diagnostic.md` / manager #1216). #[derive(Debug, Clone, PartialEq, Eq)] pub enum GroundingTestsDiagnostic { /// `List` row count drift vs Director-locked Phase 1 shape. From 38f6887e6b1234da285137e9495b6da99ae2f82d Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Thu, 30 Apr 2026 16:12:50 +0000 Subject: [PATCH 08/10] refactor(grounding-tests): split row_fingerprint into small field extractors (CODING.md hygiene) Made-with: Cursor --- src/v3/grounding_tests/src/stratum_a.rs | 106 ++++++++++-------------- 1 file changed, 44 insertions(+), 62 deletions(-) diff --git a/src/v3/grounding_tests/src/stratum_a.rs b/src/v3/grounding_tests/src/stratum_a.rs index 2c0118eef70..99e0f1e5a99 100644 --- a/src/v3/grounding_tests/src/stratum_a.rs +++ b/src/v3/grounding_tests/src/stratum_a.rs @@ -202,30 +202,46 @@ impl RowFingerprint { } } -fn row_fingerprint( - dag: &Dag, +fn row_record<'a>( + row: &'a FieldValue, list_name: &str, row_index: usize, - row: &FieldValue, -) -> Result { - let list = list_name.to_string(); +) -> Result<&'a [(String, FieldValue)], GroundingTestsDiagnostic> { let FieldValue::Record(fields) = row else { return Err(GroundingTestsDiagnostic::StratumARegistryResolutionFailed { - list_name: list, + list_name: list_name.to_string(), row_index, detail: format!("row is not a record: {row:?}"), }); }; - let (_, dag_method) = fields + Ok(fields.as_slice()) +} + +fn row_field<'a>( + fields: &'a [(String, FieldValue)], + list_name: &str, + row_index: usize, + label: &'static str, +) -> Result<&'a FieldValue, GroundingTestsDiagnostic> { + fields .iter() - .find(|(label, _)| label == "dag_method") + .find(|(l, _)| l == label) + .map(|(_, v)| v) .ok_or_else( || GroundingTestsDiagnostic::StratumARegistryResolutionFailed { list_name: list_name.to_string(), row_index, - detail: "missing `dag_method`".to_string(), + detail: format!("missing `{label}`"), }, - )?; + ) +} + +fn method_name_from_dag_method( + dag: &Dag, + dag_method: &FieldValue, + list_name: &str, + row_index: usize, +) -> Result { let FieldValue::Record(method_ref_fields) = dag_method else { return Err(GroundingTestsDiagnostic::StratumARegistryResolutionFailed { list_name: list_name.to_string(), @@ -233,16 +249,7 @@ fn row_fingerprint( detail: format!("`dag_method` not MethodRef record: {dag_method:?}"), }); }; - let (_, decl_field) = method_ref_fields - .iter() - .find(|(label, _)| label == "decl") - .ok_or_else( - || GroundingTestsDiagnostic::StratumARegistryResolutionFailed { - list_name: list_name.to_string(), - row_index, - detail: "MethodRef missing `decl`".to_string(), - }, - )?; + let decl_field = row_field(method_ref_fields, list_name, row_index, "decl")?; let FieldValue::Reference(method_decl_id) = decl_field else { return Err(GroundingTestsDiagnostic::StratumARegistryResolutionFailed { list_name: list_name.to_string(), @@ -250,24 +257,26 @@ fn row_fingerprint( detail: format!("MethodRef.decl not a reference: {decl_field:?}"), }); }; - let method_name = method_registry_name(dag, *method_decl_id).map_err(|e| { + method_registry_name(dag, *method_decl_id).map_err(|e| { GroundingTestsDiagnostic::StratumARegistryResolutionFailed { list_name: list_name.to_string(), row_index, detail: e, } - })?; + }) +} - let (_, runtime_field) = fields - .iter() - .find(|(label, _)| label == "runtime_template") - .ok_or_else( - || GroundingTestsDiagnostic::StratumARegistryResolutionFailed { - list_name: list_name.to_string(), - row_index, - detail: "missing `runtime_template`".to_string(), - }, - )?; +fn row_fingerprint( + dag: &Dag, + list_name: &str, + row_index: usize, + row: &FieldValue, +) -> Result { + let fields = row_record(row, list_name, row_index)?; + let dag_method = row_field(fields, list_name, row_index, "dag_method")?; + let method_name = method_name_from_dag_method(dag, dag_method, list_name, row_index)?; + + let runtime_field = row_field(fields, list_name, row_index, "runtime_template")?; let runtime_template = match runtime_field { FieldValue::Literal(LiteralBits::String(s)) => s.clone(), other => { @@ -279,28 +288,10 @@ fn row_fingerprint( } }; - let (_, emit_field) = fields - .iter() - .find(|(label, _)| label == "emit_template") - .ok_or_else( - || GroundingTestsDiagnostic::StratumARegistryResolutionFailed { - list_name: list_name.to_string(), - row_index, - detail: "missing `emit_template`".to_string(), - }, - )?; + let emit_field = row_field(fields, list_name, row_index, "emit_template")?; let emit_canonical = emit_template_canonical(dag, emit_field)?; - let (_, wraps_field) = fields - .iter() - .find(|(label, _)| label == "wraps_result") - .ok_or_else( - || GroundingTestsDiagnostic::StratumARegistryResolutionFailed { - list_name: list_name.to_string(), - row_index, - detail: "missing `wraps_result`".to_string(), - }, - )?; + let wraps_field = row_field(fields, list_name, row_index, "wraps_result")?; let wraps_result = match wraps_field { FieldValue::Literal(LiteralBits::Bool(b)) => *b, other => { @@ -312,16 +303,7 @@ fn row_fingerprint( } }; - let (_, ph_field) = fields - .iter() - .find(|(label, _)| label == "placeholder_convention") - .ok_or_else( - || GroundingTestsDiagnostic::StratumARegistryResolutionFailed { - list_name: list_name.to_string(), - row_index, - detail: "missing `placeholder_convention`".to_string(), - }, - )?; + let ph_field = row_field(fields, list_name, row_index, "placeholder_convention")?; let FieldValue::Variant { constructor: ph_ctor, .. From 3d957af6b9662bf01bacde5c5d7392bf4d7cf2c7 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Thu, 30 Apr 2026 16:36:53 +0000 Subject: [PATCH 09/10] fix(grounding-tests): fail-closed SingleTemplate arity in emit_template_canonical Match HigherOrderTemplates: require exactly one payload field before projection so malformed DAGs surface StratumADagProjectionFailed with a clear step label. Made-with: Cursor --- src/v3/grounding_tests/src/stratum_a.rs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/v3/grounding_tests/src/stratum_a.rs b/src/v3/grounding_tests/src/stratum_a.rs index 99e0f1e5a99..c306e42217a 100644 --- a/src/v3/grounding_tests/src/stratum_a.rs +++ b/src/v3/grounding_tests/src/stratum_a.rs @@ -143,10 +143,19 @@ fn emit_template_canonical( let label = method_emit_template_variant_label(dag, *constructor)?; match label.as_str() { "SingleTemplate" => { - let Some(FieldValue::Literal(LiteralBits::String(s))) = payload.first() else { + if payload.len() != 1 { return Err(GroundingTestsDiagnostic::StratumADagProjectionFailed { step: "emit_template_canonical.SingleTemplate", - detail: format!("payload: {payload:?}"), + detail: format!( + "expected 1 payload field, got {}: {payload:?}", + payload.len() + ), + }); + } + let FieldValue::Literal(LiteralBits::String(s)) = &payload[0] else { + return Err(GroundingTestsDiagnostic::StratumADagProjectionFailed { + step: "emit_template_canonical.SingleTemplate", + detail: format!("expected String literal payload: {:?}", payload[0]), }); }; Ok(format!("SingleTemplate({s})")) From af11a3b1e5b68da9ac8adb5117375a3f9f65bc2e Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Thu, 30 Apr 2026 17:00:19 +0000 Subject: [PATCH 10/10] fix(grounding-tests): fail-closed unknown list in assert_expected_row_count Unknown list names no longer default to expected=0 (which could mask typos as RowCountMismatch). Return StratumADagProjectionFailed with a ledger of known lists; add a regression test. Made-with: Cursor --- src/v3/grounding_tests/src/stratum_a.rs | 26 ++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/v3/grounding_tests/src/stratum_a.rs b/src/v3/grounding_tests/src/stratum_a.rs index c306e42217a..c6155b082f7 100644 --- a/src/v3/grounding_tests/src/stratum_a.rs +++ b/src/v3/grounding_tests/src/stratum_a.rs @@ -384,7 +384,17 @@ fn assert_expected_row_count( .iter() .find(|(n, _)| *n == list_name) .map(|(_, c)| *c) - .unwrap_or(0); + .ok_or_else(|| GroundingTestsDiagnostic::StratumADagProjectionFailed { + step: "assert_expected_row_count.unknown_list", + detail: format!( + "list `{list_name}` has no Director-locked expected row count; known: {}", + EXPECTED_STRATUM_A_ROW_COUNTS + .iter() + .map(|(n, _)| *n) + .collect::>() + .join(", ") + ), + })?; if actual != expected { return Err(GroundingTestsDiagnostic::StratumARowCountMismatch { list_name: list_name.to_string(), @@ -457,6 +467,20 @@ mod stratum_a_tests { ); } + #[test] + fn assert_expected_row_count_unknown_list_is_not_silent_zero_expected() { + let err = super::assert_expected_row_count("typo_method_template_contracts", 0) + .expect_err("unknown list must not default to expected=0"); + assert!( + matches!( + &err, + GroundingTestsDiagnostic::StratumADagProjectionFailed { step, .. } + if *step == "assert_expected_row_count.unknown_list" + ), + "unexpected diagnostic: {err}" + ); + } + #[test] fn stratum_a_phase1_bootstrap_verification() { verify_stratum_a_lockstep_all_targets().unwrap_or_else(|e| panic!("{e}"));