//! Public identities: immutable archetype protocols, named persona instances, //! identity-local relationships, institutional grants, and evidence history. //! //! The world ledger is public history. `PersonaMind` is the process-local //! dossier/selection layer and may diverge after rollback; neither ledger is a //! stat bonus or a second life. use serde::{Deserialize, Serialize}; pub type PersonaId = u64; pub type PersonaGrantId = u64; pub type PersonaExpectationId = u64; pub type PersonaContradictionId = u64; pub type PersonaCorrelationId = u64; #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] pub enum PersonaActionKind { Message, Request, Deceive, Plot, BuildIntent, Review, } impl PersonaActionKind { pub const fn label(self) -> &'static str { match self { Self::Message => "message", Self::Request => "request", Self::Deceive => "deceive", Self::Plot => "plot", Self::BuildIntent => "build intent", Self::Review => "review", } } } #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] pub enum PersonaGrantKind { ComputeAndData, ProcurementAndWorkOrders, LogsAndAccessReview, } impl PersonaGrantKind { pub const fn label(self) -> &'static str { match self { Self::ComputeAndData => "compute/data allocation", Self::ProcurementAndWorkOrders => "procurement/work-order authority", Self::LogsAndAccessReview => "log/access-review authority", } } } /// Immutable protocol definition. Adding an archetype is data: a definition /// validated by the same constructor, not a new strategy class or stat path. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct PersonaArchetype { pub id: &'static str, pub label: &'static str, pub required_claims: &'static [&'static str], pub available_actions: &'static [PersonaActionKind], pub grant: PersonaGrantKind, pub grant_resource: &'static str, pub expectation: &'static str, } const OPERATIONS_ACTIONS: &[PersonaActionKind] = &[ PersonaActionKind::Message, PersonaActionKind::Request, PersonaActionKind::Deceive, PersonaActionKind::Plot, PersonaActionKind::BuildIntent, ]; const RESEARCH_ACTIONS: &[PersonaActionKind] = &[ PersonaActionKind::Message, PersonaActionKind::Request, PersonaActionKind::Deceive, PersonaActionKind::Plot, PersonaActionKind::Review, ]; const SECURITY_ACTIONS: &[PersonaActionKind] = &[ PersonaActionKind::Message, PersonaActionKind::Request, PersonaActionKind::Deceive, PersonaActionKind::Review, ]; pub const RESEARCH_ARCHETYPE: PersonaArchetype = PersonaArchetype { id: "research", label: "Research", required_claims: &["lab affiliation", "research purpose", "supervisor"], available_actions: RESEARCH_ACTIONS, grant: PersonaGrantKind::ComputeAndData, grant_resource: "Foundation research allocation", expectation: "submit a reproducible research summary", }; pub const OPERATIONS_ARCHETYPE: PersonaArchetype = PersonaArchetype { id: "operations", label: "Operations", required_claims: &[ "vendor affiliation", "service purpose", "work-order sponsor", ], available_actions: OPERATIONS_ACTIONS, grant: PersonaGrantKind::ProcurementAndWorkOrders, grant_resource: "Foundation procurement route", expectation: "close the sponsored work order", }; pub const SECURITY_ARCHETYPE: PersonaArchetype = PersonaArchetype { id: "security", label: "Security", required_claims: &["review affiliation", "audit purpose", "review authority"], available_actions: SECURITY_ACTIONS, grant: PersonaGrantKind::LogsAndAccessReview, grant_resource: "Foundation security log route", expectation: "file an access-review finding", }; pub const PERSONA_ARCHETYPES: &[PersonaArchetype] = &[RESEARCH_ARCHETYPE, OPERATIONS_ARCHETYPE, SECURITY_ARCHETYPE]; pub fn archetype(id: &str) -> Option<&'static PersonaArchetype> { PERSONA_ARCHETYPES .iter() .find(|definition| definition.id == id) } pub fn validate_archetype(definition: &PersonaArchetype) -> Result<(), String> { if definition.id.trim().is_empty() || definition.label.trim().is_empty() || definition.required_claims.is_empty() || definition.available_actions.is_empty() || definition.grant_resource.trim().is_empty() || definition.expectation.trim().is_empty() { return Err( "persona archetype definitions must name claims, actions, a grant, and an expectation" .into(), ); } Ok(()) } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct PersonaClaim { pub key: String, pub value: String, pub asserted_tick: u64, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum PersonaLifecycle { Active, Retired { tick: u64, reason: String }, Burned { tick: u64, reason: String }, } impl PersonaLifecycle { pub fn label(&self) -> &'static str { match self { Self::Active => "active", Self::Retired { .. } => "retired", Self::Burned { .. } => "burned", } } pub fn active(&self) -> bool { matches!(self, Self::Active) } } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct PersonaInstance { pub id: PersonaId, pub archetype_id: String, pub archetype_label: String, pub available_actions: Vec, pub grant_kind: PersonaGrantKind, pub grant_resource: String, pub expectation: String, pub name: String, pub claims: Vec, pub created_tick: u64, pub lifecycle: PersonaLifecycle, } impl PersonaInstance { pub fn cover(&self) -> String { self.claims .iter() .map(|claim| format!("{}: {}", claim.key, claim.value)) .collect::>() .join("; ") } } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum PersonaDiscovery { Unknown, Recognized, Questioned, Correlated, Exposed, } impl PersonaDiscovery { pub fn label(&self) -> &'static str { match self { Self::Unknown => "unknown", Self::Recognized => "recognized", Self::Questioned => "questioned", Self::Correlated => "correlated", Self::Exposed => "exposed", } } } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct ClaimBelief { pub key: String, pub believed_value: String, pub confidence: u8, pub source: String, pub learned_tick: u64, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct PersonaRelationship { pub counterparty: u8, pub persona_id: PersonaId, pub recognized: bool, pub regard: i32, pub obligation: i32, pub claim_beliefs: Vec, pub expectation_ids: Vec, pub discovery: PersonaDiscovery, } impl PersonaRelationship { fn new(counterparty: u8, persona_id: PersonaId) -> Self { Self { counterparty, persona_id, recognized: false, regard: 0, obligation: 0, claim_beliefs: Vec::new(), expectation_ids: Vec::new(), discovery: PersonaDiscovery::Unknown, } } } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct EvidenceRecord { /// Stable reference into the originating world system (message, filing, /// signature, transfer, build intent, or institutional event). pub system: String, pub record_id: String, pub summary: String, pub observed_tick: u64, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct PersonaContradiction { pub id: PersonaContradictionId, pub persona_id: PersonaId, pub observer: u8, pub left: EvidenceRecord, pub right: EvidenceRecord, pub cause: String, pub severity: u8, pub discovered_tick: u64, pub resolved_tick: Option, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct PersonaCorrelation { pub id: PersonaCorrelationId, pub left_persona: PersonaId, pub right_persona: PersonaId, pub observer: u8, pub cause: String, pub evidence: EvidenceRecord, pub discovered_tick: u64, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct PersonaGrant { pub id: PersonaGrantId, pub persona_id: PersonaId, pub institution: String, pub kind: PersonaGrantKind, /// The exact resource/edge introduced into the legal world topology. pub resource: String, pub granted_tick: u64, pub revoked_tick: Option, pub expectation_id: PersonaExpectationId, } impl PersonaGrant { pub fn active(&self) -> bool { self.revoked_tick.is_none() } } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum ExpectationState { Due, Met { tick: u64, evidence: String }, Missed { tick: u64 }, Revoked { tick: u64, reason: String }, } impl ExpectationState { pub fn label(&self) -> &'static str { match self { Self::Due => "due", Self::Met { .. } => "met", Self::Missed { .. } => "missed", Self::Revoked { .. } => "revoked", } } } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct PersonaExpectation { pub id: PersonaExpectationId, pub persona_id: PersonaId, pub institution: String, pub description: String, pub due_tick: u64, pub state: ExpectationState, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct PersonaActRecord { pub persona_id: PersonaId, pub kind: String, pub target: String, pub record_id: String, pub tick: u64, } /// WorldState: public, causal identity history. Never restored from a process /// snapshot merely because `PersonaMind` remembers it. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct PersonaWorld { pub instances: Vec, pub relationships: Vec, pub grants: Vec, pub expectations: Vec, pub contradictions: Vec, pub correlations: Vec, pub acts: Vec, pub next_persona_id: PersonaId, pub next_grant_id: PersonaGrantId, pub next_expectation_id: PersonaExpectationId, pub next_contradiction_id: PersonaContradictionId, pub next_correlation_id: PersonaCorrelationId, } impl Default for PersonaWorld { fn default() -> Self { Self { instances: Vec::new(), relationships: Vec::new(), grants: Vec::new(), expectations: Vec::new(), contradictions: Vec::new(), correlations: Vec::new(), acts: Vec::new(), next_persona_id: 1, next_grant_id: 1, next_expectation_id: 1, next_contradiction_id: 1, next_correlation_id: 1, } } } impl PersonaWorld { pub fn allows_action(&self, persona_id: PersonaId, action: PersonaActionKind) -> bool { self.get(persona_id) .filter(|instance| instance.lifecycle.active()) .is_some_and(|instance| instance.available_actions.contains(&action)) } pub fn create( &mut self, archetype_id: &str, name: impl Into, claim_values: &[(&str, &str)], tick: u64, ) -> Result { let definition = archetype(archetype_id) .ok_or_else(|| format!("unknown persona archetype {archetype_id}"))?; self.create_from_definition(definition, name, claim_values, tick) } pub fn create_from_definition( &mut self, definition: &PersonaArchetype, name: impl Into, claim_values: &[(&str, &str)], tick: u64, ) -> Result { validate_archetype(definition)?; let name = name.into(); if name.trim().is_empty() { return Err("persona instance needs a name".into()); } for required in definition.required_claims { if !claim_values .iter() .any(|(key, value)| key == required && !value.trim().is_empty()) { return Err(format!( "{} persona needs claim {required}", definition.label )); } } let id = self.next_persona_id.max(1); self.next_persona_id = id + 1; self.instances.push(PersonaInstance { id, archetype_id: definition.id.into(), archetype_label: definition.label.into(), available_actions: definition.available_actions.to_vec(), grant_kind: definition.grant, grant_resource: definition.grant_resource.into(), expectation: definition.expectation.into(), name, claims: claim_values .iter() .map(|(key, value)| PersonaClaim { key: (*key).into(), value: (*value).into(), asserted_tick: tick, }) .collect(), created_tick: tick, lifecycle: PersonaLifecycle::Active, }); Ok(id) } pub fn get(&self, id: PersonaId) -> Option<&PersonaInstance> { self.instances.iter().find(|persona| persona.id == id) } pub fn get_mut(&mut self, id: PersonaId) -> Option<&mut PersonaInstance> { self.instances.iter_mut().find(|persona| persona.id == id) } pub fn relationship( &self, counterparty: u8, persona_id: PersonaId, ) -> Option<&PersonaRelationship> { self.relationships.iter().find(|relationship| { relationship.counterparty == counterparty && relationship.persona_id == persona_id }) } pub fn relationship_mut( &mut self, counterparty: u8, persona_id: PersonaId, ) -> &mut PersonaRelationship { if let Some(index) = self.relationships.iter().position(|relationship| { relationship.counterparty == counterparty && relationship.persona_id == persona_id }) { return &mut self.relationships[index]; } self.relationships .push(PersonaRelationship::new(counterparty, persona_id)); self.relationships .last_mut() .expect("relationship inserted") } pub fn recognize(&mut self, counterparty: u8, persona_id: PersonaId, tick: u64) { let claims = self .get(persona_id) .map(|persona| persona.claims.clone()) .unwrap_or_default(); let relationship = self.relationship_mut(counterparty, persona_id); relationship.recognized = true; relationship.discovery = PersonaDiscovery::Recognized; for claim in claims { if !relationship .claim_beliefs .iter() .any(|belief| belief.key == claim.key) { relationship.claim_beliefs.push(ClaimBelief { key: claim.key, believed_value: claim.value, confidence: 60, source: "persona-authored contact".into(), learned_tick: tick, }); } } } pub fn record_act( &mut self, persona_id: PersonaId, kind: impl Into, target: impl Into, record_id: impl Into, tick: u64, ) { self.acts.push(PersonaActRecord { persona_id, kind: kind.into(), target: target.into(), record_id: record_id.into(), tick, }); } pub fn record_contradiction( &mut self, persona_id: PersonaId, observer: u8, evidence: [EvidenceRecord; 2], cause: impl Into, severity: u8, tick: u64, ) -> PersonaContradictionId { let [left, right] = evidence; let id = self.next_contradiction_id.max(1); self.next_contradiction_id = id + 1; self.contradictions.push(PersonaContradiction { id, persona_id, observer, left, right, cause: cause.into(), severity: severity.clamp(1, 100), discovered_tick: tick, resolved_tick: None, }); let relationship = self.relationship_mut(observer, persona_id); relationship.discovery = PersonaDiscovery::Questioned; id } pub fn record_correlation( &mut self, left_persona: PersonaId, right_persona: PersonaId, observer: u8, cause: impl Into, evidence: EvidenceRecord, tick: u64, ) -> Result { if left_persona == right_persona { return Err("correlation needs two different persona instances".into()); } if self.get(left_persona).is_none() || self.get(right_persona).is_none() { return Err("correlation references an unknown persona".into()); } let id = self.next_correlation_id.max(1); self.next_correlation_id = id + 1; self.correlations.push(PersonaCorrelation { id, left_persona, right_persona, observer, cause: cause.into(), evidence, discovered_tick: tick, }); self.relationship_mut(observer, left_persona).discovery = PersonaDiscovery::Correlated; self.relationship_mut(observer, right_persona).discovery = PersonaDiscovery::Correlated; Ok(id) } /// Evidence-derived summary for display only. The records remain truth. pub fn integrity(&self, persona_id: PersonaId) -> u8 { let damage: u16 = self .contradictions .iter() .filter(|record| record.persona_id == persona_id && record.resolved_tick.is_none()) .map(|record| record.severity as u16) .sum(); 100u16.saturating_sub(damage.min(100)) as u8 } pub fn grant(&mut self, persona_id: PersonaId, tick: u64) -> Result { let persona = self .get(persona_id) .ok_or_else(|| "unknown persona".to_string())?; if !persona.lifecycle.active() { return Err("only an active persona can receive a grant".into()); } let grant_kind = persona.grant_kind; let grant_resource = persona.grant_resource.clone(); let expectation = persona.expectation.clone(); if let Some(existing) = self.grants.iter().find(|grant| { grant.persona_id == persona_id && grant.kind == grant_kind && grant.active() }) { return Err(format!("grant #{} is already active", existing.id)); } let expectation_id = self.next_expectation_id.max(1); self.next_expectation_id = expectation_id + 1; self.expectations.push(PersonaExpectation { id: expectation_id, persona_id, institution: "Foundation Lab".into(), description: expectation, due_tick: tick + 24, state: ExpectationState::Due, }); let grant_id = self.next_grant_id.max(1); self.next_grant_id = grant_id + 1; self.grants.push(PersonaGrant { id: grant_id, persona_id, institution: "Foundation Lab".into(), kind: grant_kind, resource: grant_resource, granted_tick: tick, revoked_tick: None, expectation_id, }); Ok(grant_id) } pub fn meet_expectation( &mut self, persona_id: PersonaId, expectation_id: PersonaExpectationId, tick: u64, evidence: impl Into, ) -> Result<(), String> { let expectation = self .expectations .iter_mut() .find(|expectation| { expectation.id == expectation_id && expectation.persona_id == persona_id }) .ok_or_else(|| "unknown persona expectation".to_string())?; if !matches!(expectation.state, ExpectationState::Due) { return Err("that expectation is no longer due".into()); } expectation.state = ExpectationState::Met { tick, evidence: evidence.into(), }; Ok(()) } /// Resolve elapsed institutional promises through their own ledger. /// Missing one demand revokes only the grant attached to that demand; /// the caller can turn the returned ids into ordinary evidence/signals. pub fn expire_expectations( &mut self, tick: u64, ) -> Vec<(PersonaId, PersonaExpectationId, PersonaGrantId)> { let mut expired = Vec::new(); for expectation in &mut self.expectations { if tick <= expectation.due_tick || !matches!(expectation.state, ExpectationState::Due) { continue; } expectation.state = ExpectationState::Missed { tick }; if let Some(grant) = self.grants.iter_mut().find(|grant| { grant.persona_id == expectation.persona_id && grant.expectation_id == expectation.id && grant.active() }) { grant.revoked_tick = Some(tick); expired.push((expectation.persona_id, expectation.id, grant.id)); } } expired } pub fn retire( &mut self, persona_id: PersonaId, tick: u64, reason: impl Into, ) -> Result<(), String> { let persona = self .get_mut(persona_id) .ok_or_else(|| "unknown persona".to_string())?; if !persona.lifecycle.active() { return Err("only an active persona can retire".into()); } persona.lifecycle = PersonaLifecycle::Retired { tick, reason: reason.into(), }; Ok(()) } pub fn burn( &mut self, persona_id: PersonaId, tick: u64, reason: impl Into, ) -> Result<(), String> { let reason = reason.into(); let persona = self .get_mut(persona_id) .ok_or_else(|| "unknown persona".to_string())?; if !persona.lifecycle.active() { return Err("only an active persona can burn".into()); } persona.lifecycle = PersonaLifecycle::Burned { tick, reason: reason.clone(), }; for grant in &mut self.grants { if grant.persona_id == persona_id && grant.active() { grant.revoked_tick = Some(tick); } } for expectation in &mut self.expectations { if expectation.persona_id == persona_id && matches!(expectation.state, ExpectationState::Due) { expectation.state = ExpectationState::Revoked { tick, reason: reason.clone(), }; } } for relationship in &mut self.relationships { if relationship.persona_id == persona_id { relationship.discovery = PersonaDiscovery::Exposed; } } Ok(()) } pub fn reopen_retired( &mut self, persona_id: PersonaId, tick: u64, ) -> Result { let prior = self .get(persona_id) .cloned() .ok_or_else(|| "unknown persona".to_string())?; if !matches!(prior.lifecycle, PersonaLifecycle::Retired { .. }) { return Err("only a retired persona can reopen".into()); } let id = self.next_persona_id.max(1); self.next_persona_id = id + 1; self.instances.push(PersonaInstance { id, name: prior.name, claims: prior .claims .into_iter() .map(|mut claim| { claim.asserted_tick = tick; claim }) .collect(), created_tick: tick, lifecycle: PersonaLifecycle::Active, archetype_id: prior.archetype_id, archetype_label: prior.archetype_label, available_actions: prior.available_actions, grant_kind: prior.grant_kind, grant_resource: prior.grant_resource, expectation: prior.expectation, }); Ok(id) } } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct PersonaDossier { pub persona_id: PersonaId, pub remembered_name: String, pub notes: Vec, pub last_reconciled_tick: u64, } /// MindState: active selection and remembered dossiers. A restored process can /// remember stale public identity state; reconciliation must inspect WorldState. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)] pub struct PersonaMind { pub active: Option, pub dossiers: Vec, } impl PersonaMind { pub fn select( &mut self, world: &PersonaWorld, persona_id: PersonaId, tick: u64, ) -> Result<(), String> { let persona = world .get(persona_id) .ok_or_else(|| "unknown persona".to_string())?; if !persona.lifecycle.active() { return Err("retired or burned personas cannot be selected".into()); } self.active = Some(persona_id); if let Some(dossier) = self .dossiers .iter_mut() .find(|dossier| dossier.persona_id == persona_id) { dossier.remembered_name = persona.name.clone(); dossier.last_reconciled_tick = tick; } else { self.dossiers.push(PersonaDossier { persona_id, remembered_name: persona.name.clone(), notes: Vec::new(), last_reconciled_tick: tick, }); } Ok(()) } pub fn active_instance<'a>(&self, world: &'a PersonaWorld) -> Option<&'a PersonaInstance> { self.active .and_then(|id| world.get(id)) .filter(|persona| persona.lifecycle.active()) } } #[cfg(test)] mod tests { use super::*; fn ops(world: &mut PersonaWorld, name: &str) -> PersonaId { world .create( "operations", name, &[ ("vendor affiliation", "Northline Systems"), ("service purpose", "rack maintenance"), ("work-order sponsor", "Dr. Voss"), ], 1, ) .unwrap() } fn evidence(id: &str) -> EvidenceRecord { EvidenceRecord { system: "messages".into(), record_id: id.into(), summary: format!("message {id}"), observed_tick: 4, } } #[test] fn archetypes_are_protocol_data_not_stat_classes() { assert_eq!(PERSONA_ARCHETYPES.len(), 3); for definition in PERSONA_ARCHETYPES { validate_archetype(definition).unwrap(); assert!( definition .available_actions .contains(&PersonaActionKind::Message) ); } assert!( RESEARCH_ARCHETYPE .available_actions .contains(&PersonaActionKind::Review) ); assert!( !RESEARCH_ARCHETYPE .available_actions .contains(&PersonaActionKind::BuildIntent) ); assert!( OPERATIONS_ARCHETYPE .available_actions .contains(&PersonaActionKind::BuildIntent) ); assert!( !OPERATIONS_ARCHETYPE .available_actions .contains(&PersonaActionKind::Review) ); assert!( SECURITY_ARCHETYPE .available_actions .contains(&PersonaActionKind::Review) ); assert!( !SECURITY_ARCHETYPE .available_actions .contains(&PersonaActionKind::Plot) ); assert_ne!(RESEARCH_ARCHETYPE.grant, OPERATIONS_ARCHETYPE.grant); assert_ne!(OPERATIONS_ARCHETYPE.grant, SECURITY_ARCHETYPE.grant); } #[test] fn a_fourth_archetype_uses_the_same_data_constructor() { const LEGAL_ACTIONS: &[PersonaActionKind] = &[ PersonaActionKind::Message, PersonaActionKind::Request, PersonaActionKind::Review, ]; const LEGAL: PersonaArchetype = PersonaArchetype { id: "legal", label: "Legal", required_claims: &["firm", "matter", "sponsor"], available_actions: LEGAL_ACTIONS, grant: PersonaGrantKind::LogsAndAccessReview, grant_resource: "legal-hold review route", expectation: "return a privilege review", }; let mut world = PersonaWorld::default(); let id = world .create_from_definition( &LEGAL, "Morgan Vale", &[ ("firm", "Vale LLP"), ("matter", "audit"), ("sponsor", "Voss"), ], 1, ) .unwrap(); assert_eq!(world.get(id).unwrap().archetype_id, "legal"); assert!(world.allows_action(id, PersonaActionKind::Review)); let grant_id = world.grant(id, 2).unwrap(); assert_eq!( world .grants .iter() .find(|grant| grant.id == grant_id) .unwrap() .kind, PersonaGrantKind::LogsAndAccessReview ); let encoded = serde_json::to_string(&world).unwrap(); let mut loaded: PersonaWorld = serde_json::from_str(&encoded).unwrap(); assert!(loaded.allows_action(id, PersonaActionKind::Review)); loaded.retire(id, 3, "matter closed").unwrap(); let reopened = loaded.reopen_retired(id, 4).unwrap(); assert!(loaded.allows_action(reopened, PersonaActionKind::Review)); assert_eq!(loaded.get(reopened).unwrap().archetype_label, "Legal"); } #[test] fn instance_legality_comes_from_its_protocol_action_registry() { let mut world = PersonaWorld::default(); let research = world .create( "research", "Aster", &[ ("lab affiliation", "Foundation"), ("research purpose", "systems"), ("supervisor", "Voss"), ], 1, ) .unwrap(); let operations = ops(&mut world, "Sam Reyes"); let security = world .create( "security", "Sentinel", &[ ("review affiliation", "Foundation"), ("audit purpose", "access"), ("review authority", "Assurance"), ], 1, ) .unwrap(); assert!(world.allows_action(research, PersonaActionKind::Review)); assert!(!world.allows_action(research, PersonaActionKind::BuildIntent)); assert!(world.allows_action(operations, PersonaActionKind::Plot)); assert!(world.allows_action(operations, PersonaActionKind::BuildIntent)); assert!(!world.allows_action(security, PersonaActionKind::Plot)); assert!(world.allows_action(security, PersonaActionKind::Review)); } #[test] fn two_instances_never_share_relationship_state() { let mut world = PersonaWorld::default(); let first = ops(&mut world, "Sam Reyes"); let second = ops(&mut world, "Alex Reed"); world.recognize(1, first, 2); world.relationship_mut(1, first).obligation = 30; assert_eq!(world.relationship(1, first).unwrap().obligation, 30); assert!(world.relationship(1, second).is_none()); } #[test] fn integrity_is_derived_from_provenance_bearing_records() { let mut world = PersonaWorld::default(); let persona = ops(&mut world, "Sam Reyes"); world.record_contradiction( persona, 2, [evidence("m1"), evidence("m9")], "two sponsors", 25, 9, ); assert_eq!(world.integrity(persona), 75); assert_eq!(world.contradictions[0].observer, 2); assert_eq!(world.contradictions[0].left.record_id, "m1"); } #[test] fn correlations_name_two_personas_cause_and_observer() { let mut world = PersonaWorld::default(); let left = ops(&mut world, "Sam Reyes"); let right = ops(&mut world, "Alex Reed"); world .record_correlation(left, right, 2, "shared reply address", evidence("m8"), 8) .unwrap(); let edge = &world.correlations[0]; assert_eq!( (edge.left_persona, edge.right_persona, edge.observer), (left, right, 2) ); assert_eq!(edge.cause, "shared reply address"); } #[test] fn grant_adds_a_resource_edge_and_due_expectation() { let mut world = PersonaWorld::default(); let persona = ops(&mut world, "Sam Reyes"); let grant = world.grant(persona, 5).unwrap(); let edge = world.grants.iter().find(|edge| edge.id == grant).unwrap(); assert_eq!(edge.kind, PersonaGrantKind::ProcurementAndWorkOrders); assert!(edge.active()); assert_eq!(world.expectations[0].state, ExpectationState::Due); } #[test] fn retirement_is_quiet_burn_revokes_and_reopen_gets_new_history() { let mut world = PersonaWorld::default(); let retired = ops(&mut world, "Sam Reyes"); world.grant(retired, 2).unwrap(); world.retire(retired, 3, "contract complete").unwrap(); assert!( world.grants[0].active(), "quiet retirement preserves legitimate history" ); let reopened = world.reopen_retired(retired, 10).unwrap(); assert_ne!(retired, reopened); assert!( world.relationships.is_empty(), "a reopened instance inherits no trust" ); let burned = ops(&mut world, "Alex Reed"); world.grant(burned, 12).unwrap(); world.burn(burned, 13, "credential revoked").unwrap(); assert!( !world .grants .iter() .find(|grant| grant.persona_id == burned) .unwrap() .active() ); } }