From 9673ab8c9ef970b24ef4e5167d8f6c7c44e06be9 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Tue, 29 Sep 2026 06:32:27 +0200 Subject: [PATCH] RS-3 3: pin the vocabulary tables through the substrate's trait Each kind declares exactly its aliases and roles, role targets are the field's typed kinds, and no alias is numeric. Found by cargo-mutants over the RS-3 diff: declared_on, declared_roles and is_numeric survived. Co-Authored-By: Claude Sonnet 5 --- crates/huginn-mind/src/rows.rs | 76 ++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/crates/huginn-mind/src/rows.rs b/crates/huginn-mind/src/rows.rs index 3a85b16..d25f53c 100644 --- a/crates/huginn-mind/src/rows.rs +++ b/crates/huginn-mind/src/rows.rs @@ -334,3 +334,79 @@ pub(crate) fn refuse_values(selection: &Selection) -> Result) -> Vec { + names.sort(); + names + } + + fn strings(names: &[&str]) -> Vec { + sorted(names.iter().map(|name| name.to_string()).collect()) + } + + /// The vocabulary's tables, read back through the substrate's trait: every + /// kind declares the three universal aliases and exactly the ones its + /// documents carry, and no alias is numeric, so a comparison is always + /// refused at the substrate's door. + #[test] + fn each_kind_declares_exactly_its_aliases_and_none_is_numeric() { + let table: [(K, &[&str]); 13] = [ + (K::Campaign, &["repo"]), + (K::Target, &[]), + (K::Question, &[]), + (K::Ruling, &["authority"]), + (K::CutSpec, &["repo", "cut"]), + (K::CutReport, &["repo", "cut"]), + (K::Verdict, &["cut", "claim_outcome"]), + (K::Finding, &["cut", "severity", "confidence", "origin"]), + (K::FollowUp, &["repo"]), + (K::Resolution, &["repo", "cut", "outcome"]), + (K::Instance, &[]), + (K::Stewardship, &["repo"]), + (K::HandOff, &["repo"]), + ]; + for (kind, extra) in table { + let want = [&["root", "in_force", "faculty"][..], extra].concat(); + assert_eq!(sorted(Vocabulary.declared_indexes(kind.type_id())), strings(&want), "{kind:?}"); + for alias in Alias::ALL { + assert!(!Vocabulary.is_numeric(kind.type_id(), alias.name()), "{kind:?}/{}", alias.name()); + } + } + } + + /// Each role is declared by the one kind whose field carries it, and its + /// targets are the kinds that field is typed to, "any" spelled as all + /// thirteen. + #[test] + fn each_kind_declares_the_roles_it_carries_and_their_targets() { + let table: [(K, &[&str]); 13] = [ + (K::Campaign, &[]), + (K::Target, &[]), + (K::Question, &["raised_in"]), + (K::Ruling, &["answers"]), + (K::CutSpec, &["rulings", "questions"]), + (K::CutReport, &["cut_spec", "forks"]), + (K::Verdict, &["cut_report", "findings"]), + (K::Finding, &["verdict"]), + (K::FollowUp, &["source"]), + (K::Resolution, &["subject", "superseded_by", "resolved_by", "deferred_to"]), + (K::Instance, &[]), + (K::Stewardship, &[]), + (K::HandOff, &["documents"]), + ]; + for (kind, roles) in table { + assert_eq!(sorted(Vocabulary.declared_roles(kind.type_id())), strings(roles), "{kind:?}"); + } + assert_eq!(Vocabulary.target_leaves("answers"), vec![K::Question.type_id().to_string()]); + assert_eq!(Vocabulary.target_leaves("rulings"), vec![K::Ruling.type_id().to_string()]); + assert_eq!(Vocabulary.target_leaves("subject").len(), 13); + assert!(Vocabulary.target_leaves("nonsense").is_empty()); + assert_eq!(Vocabulary.schema_name(K::Question.type_id()), Some(K::Question.type_id().to_string())); + assert_eq!(Vocabulary.schema_name("epiphany.pipeline.nonsense.v2"), None); + } +}