From 775148f28b48c808136074ca91de5a709ac654aa Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Fri, 9 Oct 2026 15:35:22 +0200 Subject: [PATCH 1/2] RulingAuthority gains Mind: a ruling the Mind's Self makes is its own word Additive unit-enum variant; schemas regenerated by derivation. The query alias domain now derives from the enum (unit_enums! exposes NAMES), so the second authority list in huginn-mind rows.rs is gone. Co-Authored-By: Claude Opus 5.5 --- crates/eureka-pipeline/src/lib.rs | 41 ++++++++++++++++++- crates/huginn-mind/src/admission.rs | 11 +++++ crates/huginn-mind/src/query.rs | 15 +++++++ crates/huginn-mind/src/rows.rs | 5 +-- .../epiphany.pipeline.ruling.v2.schema.json | 3 +- .../huginn.mind_request.v1.schema.json | 3 +- .../huginn.mind_response.v1.schema.json | 3 +- 7 files changed, 74 insertions(+), 7 deletions(-) diff --git a/crates/eureka-pipeline/src/lib.rs b/crates/eureka-pipeline/src/lib.rs index 056c9ae..6164288 100644 --- a/crates/eureka-pipeline/src/lib.rs +++ b/crates/eureka-pipeline/src/lib.rs @@ -439,6 +439,11 @@ macro_rules! unit_enums { ($($name:ident { $($variant:ident),* $(,)? })*) => {$( #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, JsonSchema)] pub enum $name { $($variant),* } + impl $name { + /// The variants' spellings, in declaration order: the names the wire and + /// `Debug` use. + pub const NAMES: &'static [&'static str] = &[$(stringify!($variant)),*]; + } impl Bounded for $name { fn validate(&self, _field: &str) -> Result<(), PipelineRefusal> { Ok(()) @@ -472,7 +477,7 @@ unit_enums! { ClaimOutcome { Holds, Falsified, Unproven } FindingConfidence { Confirmed, Plausible } FindingSeverity { Blocker, High, Medium, Low } - RulingAuthority { Operator, Standing, Defaulted } + RulingAuthority { Operator, Standing, Defaulted, Mind } FindingOrigin { Introduced, PreExisting } } @@ -1269,6 +1274,39 @@ mod tests { ruling } + /// A ruling the Mind's own Self made: authority Mind, no operator quote, + /// no question answered. Built from the ruling sample, not inserted into + /// `samples()`, whose tests pick kinds by index. + fn mind_ruling() -> PipelineRuling { + let mut ruling = ruling_sample(); + (ruling.authority, ruling.operator_quote, ruling.answers, ruling.choice) = (RulingAuthority::Mind, None, None, None); + ruling + } + + #[test] + fn a_ruling_with_authority_mind_keys_encodes_and_round_trips_like_any_ruling() -> Result<()> { + let cache = schema_cache()?; + let document = PipelineDocument::Ruling(mind_ruling()); + document.validate()?; + let envelope = document.prepare(&cache)?; + assert!(envelope.key.starts_with("eureka-state:ruling:"), "{}", envelope.key); + assert_eq!(PipelineDocument::decode(&envelope)?, document); + let json = serde_json::to_value(&document)?; + assert_eq!(json["value"]["authority"], "Mind"); + assert_eq!(serde_json::from_value::(json)?, document); + let packed = rmp_serde::to_vec_named(&document)?; + assert_eq!(rmp_serde::from_slice::(&packed)?, document); + let standing = PipelineDocument::Ruling(ruling_sample()); + assert_ne!(standing.prepare(&cache)?.payload, envelope.payload, "Mind is not a spelling of another authority"); + Ok(()) + } + + #[test] + fn unit_enum_names_are_the_variants_in_declaration_order() { + assert_eq!(RulingAuthority::NAMES, ["Operator", "Standing", "Defaulted", "Mind"]); + assert_eq!(FindingOrigin::NAMES, ["Introduced", "PreExisting"]); + } + fn report_sample() -> PipelineCutReport { let PipelineDocument::CutReport(report) = samples().remove(5).0 else { unreachable!() }; report @@ -1340,6 +1378,7 @@ mod tests { f.severity = severity; out.push((format!("finding.severity-{name}"), D::Finding(f))); } + out.push(("ruling.authority-mind".into(), D::Ruling(mind_ruling()))); out } diff --git a/crates/huginn-mind/src/admission.rs b/crates/huginn-mind/src/admission.rs index fddb88d..faaab29 100644 --- a/crates/huginn-mind/src/admission.rs +++ b/crates/huginn-mind/src/admission.rs @@ -1859,10 +1859,21 @@ mod tests { let mut quoted = ruling("R1"); quoted.operator_quote = Some("all recommendations, go ahead".into()); assert_eq!(refusal(admit(&mut mind, vec![D::Ruling(quoted.clone())])), MindRefusal::QuoteWithoutOperator { ruling: id("ruling", "R1") }); + quoted.authority = RulingAuthority::Mind; + assert_eq!(refusal(admit(&mut mind, vec![D::Ruling(quoted.clone())])), MindRefusal::QuoteWithoutOperator { ruling: id("ruling", "R1") }); + assert!(mind.get(K::Ruling, &id("ruling", "R1")).unwrap().is_none(), "a refused batch leaves the store unchanged"); quoted.authority = RulingAuthority::Operator; committed(admit(&mut mind, vec![D::Ruling(quoted)])); } + #[test] + fn a_mind_ruling_without_a_quote_is_admitted() { + let mut mind = seeded(); + let mut own = ruling("R1"); + own.authority = RulingAuthority::Mind; + committed(admit(&mut mind, vec![D::Ruling(own)])); + } + #[test] fn a_revision_requires_its_predecessors_supersession_in_the_batch() { let mut mind = seeded(); diff --git a/crates/huginn-mind/src/query.rs b/crates/huginn-mind/src/query.rs index c71afd8..d96c0d9 100644 --- a/crates/huginn-mind/src/query.rs +++ b/crates/huginn-mind/src/query.rs @@ -1219,6 +1219,21 @@ mod tests { } } + /// The alias domain and the enum agree: a Mind-authority ruling is + /// selectable by its own word, and only by it. + #[test] + fn a_query_for_mind_authority_is_answered() { + let mut mind = seeded(); + let mut own = ruling("R1"); + own.authority = RulingAuthority::Mind; + committed(admit(&mut mind, vec![D::Ruling(own), D::Ruling(ruling("R2"))])); + let by = |word: &str| mind.query(&with(of_kinds(&[K::Ruling]), any_of("authority", &[word]))).unwrap(); + assert_eq!(by("Mind").matched, 1); + assert_eq!(by("Standing").matched, 1); + let misspelt = with(of_kinds(&[K::Ruling]), any_of("authority", &["Minds"])); + assert!(matches!(refused(&mind, &misspelt), MindRefusal::SelectionInvalid { value: Some(v), .. } if v == "Minds")); + } + #[test] fn an_unknown_schema_or_malformed_key_is_refused() { let mind = worked(); diff --git a/crates/huginn-mind/src/rows.rs b/crates/huginn-mind/src/rows.rs index e34623f..6e3a19d 100644 --- a/crates/huginn-mind/src/rows.rs +++ b/crates/huginn-mind/src/rows.rs @@ -12,7 +12,7 @@ use std::collections::BTreeMap; use cultnet_rs::{Row, RowSet, Selection}; -use eureka_pipeline::{OrgRepo, Label, PipelineDocument, PipelineKind, PipelineRef, ResolutionOutcome, Slug}; +use eureka_pipeline::{OrgRepo, Label, PipelineDocument, PipelineKind, PipelineRef, ResolutionOutcome, RulingAuthority, Slug}; use crate::docs::{CitationRole, Held, kind_of_id, kind_of_type}; use crate::query::PipelineDocumentView; @@ -43,7 +43,6 @@ const FACULTIES: [&str; 7] = ["SelfFaculty", "Imagination", "Hands", "Soul", "Li const SEVERITIES: [&str; 4] = ["Blocker", "High", "Medium", "Low"]; const CONFIDENCES: [&str; 2] = ["Confirmed", "Plausible"]; const ORIGINS: [&str; 2] = ["Introduced", "PreExisting"]; -const AUTHORITIES: [&str; 3] = ["Operator", "Standing", "Defaulted"]; const CLAIM_OUTCOMES: [&str; 3] = ["Holds", "Falsified", "Unproven"]; const OUTCOMES: [&str; 6] = ["Superseded", "Answered", "Fixed", "Deferred", "Recorded", "Withdrawn"]; const BOOLEANS: [&str; 2] = ["true", "false"]; @@ -106,7 +105,7 @@ impl Alias { Self::Severity => Some(&SEVERITIES), Self::Confidence => Some(&CONFIDENCES), Self::Origin => Some(&ORIGINS), - Self::Authority => Some(&AUTHORITIES), + Self::Authority => Some(RulingAuthority::NAMES), Self::ClaimOutcome => Some(&CLAIM_OUTCOMES), Self::Outcome => Some(&OUTCOMES), Self::Root | Self::Repo | Self::Cut => None, diff --git a/schemas/cultnet/epiphany.pipeline.ruling.v2.schema.json b/schemas/cultnet/epiphany.pipeline.ruling.v2.schema.json index ce5720d..2439653 100644 --- a/schemas/cultnet/epiphany.pipeline.ruling.v2.schema.json +++ b/schemas/cultnet/epiphany.pipeline.ruling.v2.schema.json @@ -75,7 +75,8 @@ "enum": [ "Operator", "Standing", - "Defaulted" + "Defaulted", + "Mind" ], "type": "string" }, diff --git a/schemas/cultnet/huginn.mind_request.v1.schema.json b/schemas/cultnet/huginn.mind_request.v1.schema.json index 045cde5..322186b 100644 --- a/schemas/cultnet/huginn.mind_request.v1.schema.json +++ b/schemas/cultnet/huginn.mind_request.v1.schema.json @@ -1674,7 +1674,8 @@ "enum": [ "Operator", "Standing", - "Defaulted" + "Defaulted", + "Mind" ] }, "SemanticQuery": { diff --git a/schemas/cultnet/huginn.mind_response.v1.schema.json b/schemas/cultnet/huginn.mind_response.v1.schema.json index 91eeaa3..9896044 100644 --- a/schemas/cultnet/huginn.mind_response.v1.schema.json +++ b/schemas/cultnet/huginn.mind_response.v1.schema.json @@ -3702,7 +3702,8 @@ "enum": [ "Operator", "Standing", - "Defaulted" + "Defaulted", + "Mind" ] }, "Sha": { From 4a41ec947c25301f48e2bd7c50fe81c549396f19 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Fri, 9 Oct 2026 15:36:30 +0200 Subject: [PATCH 2/2] Golden: one new line for a Mind ruling; every existing line unchanged Derived with the ignored golden_dump, appended at the end of the corpus. Co-Authored-By: Claude Opus 5.5 --- crates/eureka-pipeline/golden/envelopes.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/eureka-pipeline/golden/envelopes.txt b/crates/eureka-pipeline/golden/envelopes.txt index 1ab31b1..b88d25e 100644 --- a/crates/eureka-pipeline/golden/envelopes.txt +++ b/crates/eureka-pipeline/golden/envelopes.txt @@ -38,3 +38,4 @@ verdict.claim-unproven-promise-none eureka-state:verdict:cut-3a.s2 epiphany.pipe finding.plausible-blocker-preexisting-kinds eureka-state:finding:cut-3a.s2.F4 epiphany.pipeline.finding.v2 EpiphanyPipelineFindingDocument 918da863616d706169676eac657572656b612d7374617465a776657264696374be657572656b612d73746174653a766572646963743a6375742d33612e7332a56c6162656ca24634a572616e676582a462617365a83566393832323864a468656164a83566393832323864aa636f6e666964656e6365a9506c61757369626c65a87365766572697479a7426c6f636b6572a5636c61696dd9214120646f74746564206c6162656c20636f6d706f7365732074776f206b6579732eaa696e76617269616e747391ab6d696e642d61646d697473a96c6f636174696f6e739183a470617468d9216372617465732f657572656b612d706970656c696e652f7372632f6c69622e7273a46c696e6501a8656e645f6c696e65c0b06661696c7572655f7363656e6172696fbc54776f20646f63756d656e747320636c61696d206f6e65206b65792ea865766964656e63659583a46b696e64a7436f6d6d616e64a76c6f6361746f72a178a6726573756c74a17983a46b696e64a84d75746174696f6ea76c6f6361746f72a178a6726573756c74a17983a46b696e64a550726f6265a76c6f6361746f72a178a6726573756c74a17983a46b696e64aa536f7572636552656164a76c6f6361746f72a178a6726573756c74a17983a46b696e64a743617074757265a76c6f6361746f72a178a6726573756c74a179aa707265636564656e747390a66f726967696eab5072654578697374696e67 finding.severity-medium eureka-state:finding:cut-3a.s2.F4 epiphany.pipeline.finding.v2 EpiphanyPipelineFindingDocument 918da863616d706169676eac657572656b612d7374617465a776657264696374be657572656b612d73746174653a766572646963743a6375742d33612e7332a56c6162656ca24634a572616e676582a462617365a83566393832323864a468656164a83566393832323864aa636f6e666964656e6365a9436f6e6669726d6564a87365766572697479a64d656469756da5636c61696dd9214120646f74746564206c6162656c20636f6d706f7365732074776f206b6579732eaa696e76617269616e747391ab6d696e642d61646d697473a96c6f636174696f6e739183a470617468d9216372617465732f657572656b612d706970656c696e652f7372632f6c69622e7273a46c696e6501a8656e645f6c696e6509b06661696c7572655f7363656e6172696fbc54776f20646f63756d656e747320636c61696d206f6e65206b65792ea865766964656e63659183a46b696e64a454657374a76c6f6361746f72aa636172676f2074657374a6726573756c74a26f6baa707265636564656e747390a66f726967696eaa496e74726f6475636564 finding.severity-low eureka-state:finding:cut-3a.s2.F4 epiphany.pipeline.finding.v2 EpiphanyPipelineFindingDocument 918da863616d706169676eac657572656b612d7374617465a776657264696374be657572656b612d73746174653a766572646963743a6375742d33612e7332a56c6162656ca24634a572616e676582a462617365a83566393832323864a468656164a83566393832323864aa636f6e666964656e6365a9436f6e6669726d6564a87365766572697479a34c6f77a5636c61696dd9214120646f74746564206c6162656c20636f6d706f7365732074776f206b6579732eaa696e76617269616e747391ab6d696e642d61646d697473a96c6f636174696f6e739183a470617468d9216372617465732f657572656b612d706970656c696e652f7372632f6c69622e7273a46c696e6501a8656e645f6c696e6509b06661696c7572655f7363656e6172696fbc54776f20646f63756d656e747320636c61696d206f6e65206b65792ea865766964656e63659183a46b696e64a454657374a76c6f6361746f72aa636172676f2074657374a6726573756c74a26f6baa707265636564656e747390a66f726967696eaa496e74726f6475636564 +ruling.authority-mind eureka-state:ruling:R8 epiphany.pipeline.ruling.v2 EpiphanyPipelineRulingDocument 918aa863616d706169676eac657572656b612d7374617465a56c6162656ca25238a57469746c65b9416e20696e7374616e6365206f776e7320697473206d696e64a7616e7377657273c0a663686f696365c0a672756c696e67ba416e20696e7374616e6365206f776e7320697473206d696e642eae6f70657261746f725f71756f7465c0a872756c65645f6f6eaa323032362d30392d3135aa707265636564656e74739185a47265706fb147616d6543756c742f4165746865726961a6636f6d6d6974d92861616161616161616161616161616161616161616161616161616161616161616161616161616161a46b696e64a652756c696e67a26964b363756c7463616368653a72756c696e673a5231ae7061796c6f61645f736861323536d94062626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262a9617574686f72697479a44d696e64