From 1c1466aa5bf4aa178a1c80ae1d41eb979b5e89a3 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Tue, 29 Sep 2026 10:08:37 +0200 Subject: [PATCH] Mind exposes its identity: the genesis receipt id The instance name is only a label; two stores may share it. The receipt of the first admission never changes and digests the first batch. Co-Authored-By: Claude Sonnet 5 --- crates/huginn-mind/src/admission.rs | 13 +++++++++++++ crates/huginn-mind/src/mind.rs | 10 ++++++++++ 2 files changed, 23 insertions(+) diff --git a/crates/huginn-mind/src/admission.rs b/crates/huginn-mind/src/admission.rs index 8ea8f8a..4b85cdd 100644 --- a/crates/huginn-mind/src/admission.rs +++ b/crates/huginn-mind/src/admission.rs @@ -680,6 +680,19 @@ mod tests { assert!(Mind::open_with(store, &slug(OTHER_INSTANCE)).is_err()); } + /// The identity the index labels its collection with: none before the + /// first write, then the first receipt's id and never a later one. + #[test] + fn the_genesis_receipt_id_is_the_first_admissions_and_stays_put() { + let mut mind = opened(MemoryStore::new(), INSTANCE); + assert_eq!(mind.genesis_receipt_id().unwrap(), None); + let (first, _) = committed(admit(&mut mind, vec![instance(INSTANCE), stewardship(INSTANCE, REPO)])); + assert_eq!(mind.genesis_receipt_id().unwrap(), Some(first.clone())); + let (second, _) = committed(admit(&mut mind, vec![campaign(&[REPO])])); + assert_ne!(first, second); + assert_eq!(mind.genesis_receipt_id().unwrap(), Some(first)); + } + #[test] fn admission_refuses_a_foreign_instance_whatever_the_transport() { let store = MemoryStore::new(); diff --git a/crates/huginn-mind/src/mind.rs b/crates/huginn-mind/src/mind.rs index 8deed10..ff5fbc2 100644 --- a/crates/huginn-mind/src/mind.rs +++ b/crates/huginn-mind/src/mind.rs @@ -245,6 +245,16 @@ impl Mind { .collect() } + /// The mind's own identity: the receipt id of its first admission + /// (ordinal 1), which admission never rewrites and which digests the + /// instance and the first batch's bytes. `None` for a mind that has not + /// been written yet. The instance name is only a label, since two stores + /// on two hosts may share it; this tells them apart unless their first + /// batches were byte-identical. + pub fn genesis_receipt_id(&self) -> Result, MindRefusal> { + Ok(self.receipts()?.into_iter().find(|receipt| receipt.ordinal == 1).map(|receipt| receipt.receipt_id)) + } + pub(crate) fn raw_envelope(&self, type_id: &str, key: &str) -> Option<&CultCacheEnvelope> { self.image.iter().find(|envelope| envelope.r#type == type_id && envelope.key == key) }