diff --git a/epiphany-core/src/agent_memory.rs b/epiphany-core/src/agent_memory.rs index c4885d9b..688f1f7c 100644 --- a/epiphany-core/src/agent_memory.rs +++ b/epiphany-core/src/agent_memory.rs @@ -1,19 +1,15 @@ -use anyhow::Context; use anyhow::Result; use anyhow::anyhow; use chrono::SecondsFormat; use chrono::Utc; use cultcache_rs::CultCache; -use cultcache_rs::CultSoaColumnValues; use cultcache_rs::DatabaseEntry; use cultcache_rs::SingleFileMessagePackBackingStore; -use cultcache_rs::SoaDocument; use serde::Deserialize; use serde::Serialize; use serde_json::Value; use sha2::{Digest, Sha256}; use std::collections::BTreeMap; -use std::fs; use std::path::Path; pub const AGENT_MEMORY_TYPE: &str = "epiphany.agent_memory"; @@ -83,10 +79,6 @@ pub struct AgentMemoryMindAdmissionReceipt { #[cultcache(key = 7)] pub resulting_source_hash: String, } -pub const AGENT_STATE_SOA_TYPE: &str = "epiphany.agent_state_soa"; -pub const AGENT_STATE_SOA_SCHEMA_VERSION: &str = "epiphany.agent_state_soa.v0"; -pub const AGENT_STATE_SOA_KEY: &str = "swarm"; - #[derive(Clone, Debug, PartialEq, Eq, DatabaseEntry)] #[cultcache( type = "epiphany.agent_memory_swarm_identity", @@ -318,132 +310,6 @@ pub struct EpiphanyAgentMemoryEntry { pub scenes: Vec, } -impl SoaDocument for EpiphanyAgentMemoryEntry { - fn soa_columns(rows: &[Self]) -> BTreeMap<&'static str, CultSoaColumnValues> { - let mut columns = BTreeMap::new(); - columns.insert( - "roleId", - CultSoaColumnValues::new( - rows.iter() - .map(|row| row.role_id.clone()) - .collect::>(), - ), - ); - columns.insert( - "agentId", - CultSoaColumnValues::new( - rows.iter() - .map(|row| row.agent.agent_id.clone()) - .collect::>(), - ), - ); - columns.insert( - "displayName", - CultSoaColumnValues::new( - rows.iter() - .map(|row| row.agent.identity.name.clone()) - .collect::>(), - ), - ); - columns.insert( - "profileKind", - CultSoaColumnValues::new( - rows.iter() - .map(|row| { - format!( - "{:?}", - organ_state_profile_for_role(&row.role_id).profile_kind - ) - }) - .collect::>(), - ), - ); - columns.insert( - "portableContract", - CultSoaColumnValues::new( - rows.iter() - .map(|row| organ_state_profile_for_role(&row.role_id).portable_contract) - .collect::>(), - ), - ); - columns.insert( - "semanticMemoryCount", - CultSoaColumnValues::new( - rows.iter() - .map(|row| row.agent.memories.semantic.len() as u32) - .collect::>(), - ), - ); - columns.insert( - "episodicMemoryCount", - CultSoaColumnValues::new( - rows.iter() - .map(|row| row.agent.memories.episodic.len() as u32) - .collect::>(), - ), - ); - columns.insert( - "relationshipMemoryCount", - CultSoaColumnValues::new( - rows.iter() - .map(|row| row.agent.memories.relationship_summaries.len() as u32) - .collect::>(), - ), - ); - columns.insert( - "goalCount", - CultSoaColumnValues::new( - rows.iter() - .map(|row| row.agent.goals.len() as u32) - .collect::>(), - ), - ); - columns.insert( - "valueCount", - CultSoaColumnValues::new( - rows.iter() - .map(|row| row.agent.canonical_state.values.len() as u32) - .collect::>(), - ), - ); - columns - } -} - -#[derive(Clone, Debug, PartialEq, DatabaseEntry)] -#[cultcache( - type = "epiphany.agent_state_soa", - schema = "EpiphanyAgentStateSoaEntry" -)] -pub struct EpiphanyAgentStateSoaEntry { - #[cultcache(key = 0)] - pub schema_version: String, - #[cultcache(key = 1)] - pub generated_at: String, - #[cultcache(key = 2)] - pub source_store: String, - #[cultcache(key = 3)] - pub role_ids: Vec, - #[cultcache(key = 4)] - pub agent_ids: Vec, - #[cultcache(key = 5)] - pub display_names: Vec, - #[cultcache(key = 6)] - pub profile_kinds: Vec, - #[cultcache(key = 7)] - pub portable_contracts: Vec, - #[cultcache(key = 8)] - pub semantic_memory_counts: Vec, - #[cultcache(key = 9)] - pub episodic_memory_counts: Vec, - #[cultcache(key = 10)] - pub relationship_memory_counts: Vec, - #[cultcache(key = 11)] - pub goal_counts: Vec, - #[cultcache(key = 12)] - pub value_counts: Vec, -} - #[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)] pub struct GhostlightWorld { pub world_id: String, @@ -609,19 +475,6 @@ pub struct GhostlightMemory { pub linked_relationship_id: Option, } -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -struct AgentMemoryProjection { - pub schema_version: String, - pub world: GhostlightWorld, - pub agents: Vec, - #[serde(default)] - pub relationships: Vec, - #[serde(default)] - pub events: Vec, - #[serde(default)] - pub scenes: Vec, -} - #[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct AgentSelfPatch { @@ -660,82 +513,6 @@ pub struct SelfPatchMemory { pub linked_relationship_id: Option, } -#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct AgentMemoryLifecycleOperation { - #[serde(default)] - pub agent_id: Option, - pub reason: String, - pub actions: Vec, - #[serde(flatten)] - pub extra: BTreeMap, -} - -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase", tag = "action")] -pub enum AgentMemoryLifecycleAction { - Revise { - bundle: AgentMemoryBundle, - #[serde(alias = "memoryId")] - memory_id: String, - #[serde(default, skip_serializing_if = "Option::is_none")] - summary: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] - salience: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] - confidence: Option, - reason: String, - }, - Retire { - bundle: AgentMemoryBundle, - #[serde(alias = "memoryId")] - memory_id: String, - reason: String, - }, - Crystallize { - #[serde(alias = "fromBundle")] - from_bundle: AgentMemoryBundle, - #[serde(alias = "toBundle")] - to_bundle: AgentMemoryBundle, - #[serde(alias = "memoryId")] - memory_id: String, - #[serde(alias = "newMemoryId")] - #[serde(default, skip_serializing_if = "Option::is_none")] - new_memory_id: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] - summary: Option, - reason: String, - }, - Prune { - bundle: AgentMemoryBundle, - #[serde(alias = "maxRecords")] - max_records: usize, - #[serde(alias = "minimumSalience")] - #[serde(default, skip_serializing_if = "Option::is_none")] - minimum_salience: Option, - reason: String, - }, - Merge { - bundle: AgentMemoryBundle, - #[serde(alias = "targetMemoryId")] - target_memory_id: String, - #[serde(alias = "sourceMemoryIds")] - source_memory_ids: Vec, - summary: String, - salience: f64, - confidence: f64, - reason: String, - }, -} - -#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "snake_case")] -pub enum AgentMemoryBundle { - Semantic, - Episodic, - RelationshipSummaries, -} - #[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct SelfPatchGoal { @@ -790,52 +567,8 @@ pub struct EpiphanyOrganStateProfile { pub notes: Vec, } -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct AgentCanonicalTraitSeed { - pub role_id: String, - pub group_name: String, - pub trait_name: String, - pub mean: f64, - pub plasticity: f64, - pub current_activation: f64, - #[serde(default, skip_serializing_if = "Option::is_none")] - pub source: Option, -} - pub const PERSONA_STATE_SCHEMA_VERSION: &str = "gamecult.persona_state.v0"; -pub fn migrate_agent_memory_json_dir_to_cultcache( - agent_dir: impl AsRef, - store_path: impl AsRef, -) -> Result { - let agent_dir = agent_dir.as_ref(); - let store_path = store_path.as_ref(); - let mut cache = agent_memory_cache(store_path)?; - require_agent_memory_migration_open(&mut cache, "JSON agent-memory import")?; - let mut migrated = Vec::new(); - for (role_id, expected_agent_id, filename) in ROLE_TARGETS { - let path = agent_dir.join(filename); - let raw = fs::read_to_string(&path) - .with_context(|| format!("failed to read {}", path.display()))?; - let projection: AgentMemoryProjection = serde_json::from_str(&raw) - .with_context(|| format!("failed to decode {}", path.display()))?; - let entry = entry_from_projection(role_id, expected_agent_id, projection) - .with_context(|| format!("invalid role memory {}", path.display()))?; - cache.put(*role_id, &entry)?; - migrated.push(serde_json::json!({ - "roleId": role_id, - "agentId": expected_agent_id, - "source": path, - })); - } - Ok(serde_json::json!({ - "ok": true, - "store": store_path, - "migrated": migrated, - })) -} - pub fn validate_agent_memory_store(store_path: impl AsRef) -> Result> { let store_path = store_path.as_ref(); let mut cache = agent_memory_cache(store_path)?; @@ -851,150 +584,6 @@ pub fn validate_agent_memory_store(store_path: impl AsRef) -> Result) -> Result { - let store_path = store_path.as_ref(); - let mut cache = agent_memory_cache(store_path)?; - require_agent_memory_migration_open(&mut cache, "agent-memory repair")?; - let mut repaired = Vec::new(); - - if let Some(mut modeling) = cache.get::("modeling")? - && modeling.agent.agent_id == "epiphany.proprioception" - { - modeling.agent.agent_id = "epiphany.modeling".to_string(); - if modeling.agent.identity.name == "Proprioception" { - modeling.agent.identity.name = "Modeling".to_string(); - } - if modeling - .agent - .identity - .roles - .iter() - .any(|role| role == "Proprioception") - { - modeling.agent.identity.roles = modeling - .agent - .identity - .roles - .into_iter() - .map(|role| { - if role == "Proprioception" { - "Modeling".to_string() - } else { - role - } - }) - .collect(); - } - cache.put("modeling".to_string(), &modeling)?; - repaired.push(serde_json::json!({ - "roleId": "modeling", - "repair": "renamed legacy epiphany.proprioception vessel to epiphany.modeling", - })); - } - - if let Some(mut modeling) = cache.get::("modeling")? { - let mut renamed = 0usize; - renamed += replace_deprecated_faculty_name(&mut modeling.agent.identity.public_description); - for note in &mut modeling.agent.identity.private_notes { - renamed += replace_deprecated_faculty_name(note); - } - for goal in &mut modeling.agent.goals { - renamed += replace_deprecated_faculty_name(&mut goal.description); - renamed += replace_deprecated_faculty_name(&mut goal.emotional_stake); - } - for memory in modeling - .agent - .memories - .semantic - .iter_mut() - .chain(modeling.agent.memories.episodic.iter_mut()) - .chain(modeling.agent.memories.relationship_summaries.iter_mut()) - { - renamed += replace_deprecated_faculty_name(&mut memory.summary); - } - if renamed > 0 { - cache.put("modeling".to_string(), &modeling)?; - repaired.push(serde_json::json!({ - "roleId": "modeling", - "repair": "replaced deprecated Proprioception prose with canonical Modeling doctrine", - "replacements": renamed, - })); - } - } - - if cache.get::("Persona")?.is_none() - && let Some(mut face) = cache.get::("face")? - { - face.role_id = "Persona".to_string(); - face.agent.agent_id = "epiphany.Persona".to_string(); - if face.agent.identity.name == "Face" { - face.agent.identity.name = "Persona".to_string(); - } - if face.agent.identity.roles.is_empty() { - face.agent.identity.roles.push("Persona".to_string()); - } - if !face - .agent - .identity - .roles - .iter() - .any(|role| role == "Persona") - { - face.agent.identity.roles.push("Persona".to_string()); - } - face.agent.identity.public_description = if face - .agent - .identity - .public_description - .trim() - .is_empty() - { - "Epiphany Persona is the public-facing project voice; Imagination projects context before speech and Mind interprets side effects after speech.".to_string() - } else { - face.agent.identity.public_description - }; - cache.put("Persona".to_string(), &face)?; - cache.delete::("face")?; - repaired.push(serde_json::json!({ - "roleId": "Persona", - "repair": "promoted legacy face memory entry to Persona role memory and removed obsolete face row", - })); - } - - if cache - .get::("reorientation")? - .is_some() - { - cache.delete::("reorientation")?; - repaired.push(serde_json::json!({ - "roleId": "reorientation", - "repair": "removed obsolete reorientation row; Continuity is protocol machinery, not a standing sub-agent identity", - })); - } - - let errors = validate_agent_memory_store(store_path)?; - let soa = refresh_agent_state_soa(store_path)?; - Ok(serde_json::json!({ - "ok": errors.is_empty(), - "store": store_path, - "repaired": repaired, - "errors": errors, - "soa": soa, - })) -} - -pub fn load_agent_memory_generation_witness( - store_path: impl AsRef, -) -> Result> { - let store_path = store_path.as_ref(); - if !store_path.exists() { - return Ok(None); - } - let mut cache = agent_memory_cache(store_path)?; - cache.pull_all_backing_stores()?; - cache.get(AGENT_MEMORY_GENERATION_WITNESS_LATEST_KEY) -} - pub fn agent_memory_semantic_projection_input( store_path: impl AsRef, ) -> Result { @@ -1270,17 +859,6 @@ fn commit_agent_memory_generation( Ok(witness) } -pub fn admit_legacy_agent_memory_generation( - store_path: impl AsRef, -) -> Result { - admit_initial_agent_memory_generation( - store_path.as_ref(), - "legacy-bootstrap-admission", - "Admit existing canonical Mind rows without changing their content.", - Vec::new(), - ) -} - fn admit_initial_agent_memory_generation( store_path: &Path, mutation_kind: &str, @@ -1457,14 +1035,6 @@ fn require_agent_memory_migration_open(cache: &mut CultCache, operation: &str) - Ok(()) } -fn replace_deprecated_faculty_name(value: &mut String) -> usize { - let count = value.matches("Proprioception").count(); - if count > 0 { - *value = value.replace("Proprioception", "Modeling"); - } - count -} - pub fn load_agent_memory_entry_for_role( store_path: impl AsRef, role_id: &str, @@ -1580,6 +1150,27 @@ pub fn review_agent_self_patch_document( agent_memory_review(role_id, target_agent_id, store_path, reasons, None) } +fn agent_memory_review( + role_id: &str, + target_agent_id: &str, + store_path: &Path, + reasons: Vec, + applied: Option, +) -> AgentMemoryReview { + AgentMemoryReview { + status: if reasons.is_empty() { + "accepted".to_string() + } else { + "rejected".to_string() + }, + target_agent_id: target_agent_id.to_string(), + target_role_id: role_id.to_string(), + target_store: store_path.display().to_string(), + reasons, + applied, + } +} + pub(crate) fn review_agent_self_patch_contract( expected_agent_id: &str, patch: &AgentSelfPatch, @@ -1717,863 +1308,6 @@ pub fn apply_agent_self_patch_document( Ok(review) } -pub fn review_agent_memory_lifecycle_operation( - role_id: &str, - operation: &AgentMemoryLifecycleOperation, - store_path: impl AsRef, -) -> AgentMemoryReview { - let store_path = store_path.as_ref(); - let target_agent_id = match agent_id_for_role(role_id) { - Ok(agent_id) => agent_id, - Err(reason) => { - return AgentMemoryReview { - status: "rejected".to_string(), - target_agent_id: String::new(), - target_role_id: role_id.to_string(), - target_store: store_path.display().to_string(), - reasons: vec![reason], - applied: None, - }; - } - }; - let reasons = review_memory_lifecycle_contract(target_agent_id, operation); - agent_memory_review(role_id, target_agent_id, store_path, reasons, None) -} - -pub fn apply_agent_memory_lifecycle_operation( - role_id: &str, - operation: AgentMemoryLifecycleOperation, - store_path: impl AsRef, -) -> Result { - let store_path = store_path.as_ref(); - let mut review = review_agent_memory_lifecycle_operation(role_id, &operation, store_path); - if review.status != "accepted" { - return Ok(review); - } - let mut cache = agent_memory_cache(store_path)?; - cache.pull_all_backing_stores()?; - let mut entry = cache - .get::(role_id)? - .ok_or_else(|| anyhow!("CultCache has no role memory entry for {role_id:?}"))?; - let (expected_generation, expected_source_hash) = agent_memory_source_head(&mut cache)?; - - let reason = operation.reason.clone(); - - for action in operation.actions { - apply_memory_lifecycle_action(&mut entry, action)?; - } - commit_agent_memory_generation( - store_path, - expected_generation, - &expected_source_hash, - role_id, - entry, - "lifecycle", - &reason, - )?; - review.applied = Some(true); - Ok(review) -} - -fn review_memory_lifecycle_contract( - expected_agent_id: &str, - operation: &AgentMemoryLifecycleOperation, -) -> Vec { - let mut reasons = Vec::new(); - if operation.agent_id.as_deref() != Some(expected_agent_id) { - reasons.push(format!( - "lifecycle agentId {:?} does not match this lane; expected {:?}", - operation.agent_id, expected_agent_id - )); - } - check_patch_text(&operation.reason, "lifecycle reason", &mut reasons, 800); - if operation.actions.is_empty() { - reasons.push("lifecycle operation must contain at least one action".to_string()); - } - if operation.actions.len() > 8 { - reasons.push("lifecycle operation may contain at most 8 actions".to_string()); - } - for key in operation.extra.keys() { - reasons.push(format!( - "lifecycle field {key:?} is not part of the bounded memory lifecycle contract" - )); - } - for (index, action) in operation.actions.iter().enumerate() { - review_memory_lifecycle_action(index, action, &mut reasons); - } - reasons -} - -fn review_memory_lifecycle_action( - index: usize, - action: &AgentMemoryLifecycleAction, - reasons: &mut Vec, -) { - match action { - AgentMemoryLifecycleAction::Revise { - memory_id, - summary, - salience, - confidence, - reason, - .. - } => { - review_memory_lifecycle_id(index, "memoryId", memory_id, reasons); - if summary.is_none() && salience.is_none() && confidence.is_none() { - reasons.push(format!( - "lifecycle actions[{index}] revise must change summary, salience, or confidence" - )); - } - if let Some(summary) = summary { - check_patch_text( - summary, - &format!("lifecycle actions[{index}].summary"), - reasons, - 800, - ); - } - if let Some(salience) = salience { - check_patch_unit( - *salience, - &format!("lifecycle actions[{index}].salience"), - reasons, - ); - } - if let Some(confidence) = confidence { - check_patch_unit( - *confidence, - &format!("lifecycle actions[{index}].confidence"), - reasons, - ); - } - check_patch_text( - reason, - &format!("lifecycle actions[{index}].reason"), - reasons, - 500, - ); - } - AgentMemoryLifecycleAction::Retire { - memory_id, reason, .. - } => { - review_memory_lifecycle_id(index, "memoryId", memory_id, reasons); - check_patch_text( - reason, - &format!("lifecycle actions[{index}].reason"), - reasons, - 500, - ); - } - AgentMemoryLifecycleAction::Crystallize { - memory_id, - new_memory_id, - summary, - reason, - .. - } => { - review_memory_lifecycle_id(index, "memoryId", memory_id, reasons); - if let Some(new_memory_id) = new_memory_id { - review_memory_lifecycle_id(index, "newMemoryId", new_memory_id, reasons); - } - if let Some(summary) = summary { - check_patch_text( - summary, - &format!("lifecycle actions[{index}].summary"), - reasons, - 800, - ); - } - check_patch_text( - reason, - &format!("lifecycle actions[{index}].reason"), - reasons, - 500, - ); - } - AgentMemoryLifecycleAction::Prune { - max_records, - minimum_salience, - reason, - .. - } => { - if *max_records == 0 || *max_records > 128 { - reasons.push(format!( - "lifecycle actions[{index}].maxRecords must be between 1 and 128" - )); - } - if let Some(minimum_salience) = minimum_salience { - check_patch_unit( - *minimum_salience, - &format!("lifecycle actions[{index}].minimumSalience"), - reasons, - ); - } - check_patch_text( - reason, - &format!("lifecycle actions[{index}].reason"), - reasons, - 500, - ); - } - AgentMemoryLifecycleAction::Merge { - target_memory_id, - source_memory_ids, - summary, - salience, - confidence, - reason, - .. - } => { - review_memory_lifecycle_id(index, "targetMemoryId", target_memory_id, reasons); - if source_memory_ids.len() < 2 || source_memory_ids.len() > 8 { - reasons.push(format!( - "lifecycle actions[{index}].sourceMemoryIds must contain 2 to 8 ids" - )); - } - for source_id in source_memory_ids { - review_memory_lifecycle_id(index, "sourceMemoryIds", source_id, reasons); - } - check_patch_text( - summary, - &format!("lifecycle actions[{index}].summary"), - reasons, - 800, - ); - check_patch_unit( - *salience, - &format!("lifecycle actions[{index}].salience"), - reasons, - ); - check_patch_unit( - *confidence, - &format!("lifecycle actions[{index}].confidence"), - reasons, - ); - check_patch_text( - reason, - &format!("lifecycle actions[{index}].reason"), - reasons, - 500, - ); - } - } -} - -fn review_memory_lifecycle_id(index: usize, field: &str, value: &str, reasons: &mut Vec) { - if !valid_identifier(value, "mem-") { - reasons.push(format!( - "lifecycle actions[{index}].{field} must start with 'mem-' and avoid whitespace" - )); - } -} - -fn apply_memory_lifecycle_action( - entry: &mut EpiphanyAgentMemoryEntry, - action: AgentMemoryLifecycleAction, -) -> Result<()> { - match action { - AgentMemoryLifecycleAction::Revise { - bundle, - memory_id, - summary, - salience, - confidence, - .. - } => { - let memory = memory_bundle_mut(entry, bundle) - .iter_mut() - .find(|memory| memory.memory_id == memory_id) - .ok_or_else(|| anyhow!("memory {memory_id:?} not found for revise"))?; - if let Some(summary) = summary { - memory.summary = summary; - } - if let Some(salience) = salience { - memory.salience = salience; - } - if let Some(confidence) = confidence { - memory.confidence = confidence; - } - } - AgentMemoryLifecycleAction::Retire { - bundle, memory_id, .. - } => { - let records = memory_bundle_mut(entry, bundle); - let before = records.len(); - records.retain(|memory| memory.memory_id != memory_id); - if records.len() == before { - return Err(anyhow!("memory {memory_id:?} not found for retire")); - } - } - AgentMemoryLifecycleAction::Crystallize { - from_bundle, - to_bundle, - memory_id, - new_memory_id, - summary, - .. - } => { - let source = memory_bundle_mut(entry, from_bundle) - .iter() - .find(|memory| memory.memory_id == memory_id) - .cloned() - .ok_or_else(|| anyhow!("memory {memory_id:?} not found for crystallize"))?; - let mut crystallized = source; - if let Some(new_memory_id) = new_memory_id { - crystallized.memory_id = new_memory_id; - } - if let Some(summary) = summary { - crystallized.summary = summary; - } - upsert_ghostlight_memory(memory_bundle_mut(entry, to_bundle), crystallized); - } - AgentMemoryLifecycleAction::Prune { - bundle, - max_records, - minimum_salience, - .. - } => { - let records = memory_bundle_mut(entry, bundle); - if let Some(minimum_salience) = minimum_salience { - records.retain(|memory| memory.salience >= minimum_salience); - } - records.sort_by(|left, right| { - right - .salience - .partial_cmp(&left.salience) - .unwrap_or(std::cmp::Ordering::Equal) - .then_with(|| { - right - .confidence - .partial_cmp(&left.confidence) - .unwrap_or(std::cmp::Ordering::Equal) - }) - .then_with(|| left.memory_id.cmp(&right.memory_id)) - }); - records.truncate(max_records); - } - AgentMemoryLifecycleAction::Merge { - bundle, - target_memory_id, - source_memory_ids, - summary, - salience, - confidence, - .. - } => { - let records = memory_bundle_mut(entry, bundle); - for source_id in &source_memory_ids { - if !records.iter().any(|memory| memory.memory_id == *source_id) { - return Err(anyhow!("memory {source_id:?} not found for merge")); - } - } - records.retain(|memory| { - !source_memory_ids - .iter() - .any(|source_id| source_id == &memory.memory_id) - }); - upsert_ghostlight_memory( - records, - GhostlightMemory { - memory_id: target_memory_id, - summary, - salience, - confidence, - linked_event_ids: None, - linked_relationship_id: None, - }, - ); - } - } - Ok(()) -} - -fn memory_bundle_mut( - entry: &mut EpiphanyAgentMemoryEntry, - bundle: AgentMemoryBundle, -) -> &mut Vec { - match bundle { - AgentMemoryBundle::Semantic => &mut entry.agent.memories.semantic, - AgentMemoryBundle::Episodic => &mut entry.agent.memories.episodic, - AgentMemoryBundle::RelationshipSummaries => { - &mut entry.agent.memories.relationship_summaries - } - } -} - -fn upsert_ghostlight_memory(records: &mut Vec, incoming: GhostlightMemory) { - if let Some(existing) = records - .iter_mut() - .find(|memory| memory.memory_id == incoming.memory_id) - { - *existing = incoming; - } else { - records.push(incoming); - } -} - -fn agent_memory_review( - role_id: &str, - target_agent_id: &str, - store_path: &Path, - reasons: Vec, - applied: Option, -) -> AgentMemoryReview { - AgentMemoryReview { - status: if reasons.is_empty() { - "accepted" - } else { - "rejected" - } - .to_string(), - target_agent_id: target_agent_id.to_string(), - target_role_id: role_id.to_string(), - target_store: store_path.display().to_string(), - reasons, - applied, - } -} - -pub fn apply_agent_canonical_trait_seeds( - seeds: &[AgentCanonicalTraitSeed], - store_path: impl AsRef, -) -> Result { - let store_path = store_path.as_ref(); - let mut cache = agent_memory_cache(store_path)?; - require_agent_memory_migration_open(&mut cache, "canonical trait seeding")?; - let mut applied = Vec::new(); - for seed in seeds { - if seed.trait_name.trim().is_empty() { - return Err(anyhow!( - "trait seed for role {:?} has an empty trait_name", - seed.role_id - )); - } - let vector = GhostlightTraitVector { - mean: clamp_unit(seed.mean), - plasticity: clamp_unit(seed.plasticity), - current_activation: clamp_unit(seed.current_activation), - }; - let mut entry = cache - .get::(&seed.role_id)? - .ok_or_else(|| anyhow!("CultCache has no role memory entry for {:?}", seed.role_id))?; - let group = canonical_group_mut(&mut entry.agent.canonical_state, &seed.group_name)?; - let previous = group.get(&seed.trait_name).cloned(); - if seed.trait_name != "baseline" { - group.remove("baseline"); - } - group.insert(seed.trait_name.clone(), vector.clone()); - cache.put(seed.role_id.clone(), &entry)?; - applied.push(serde_json::json!({ - "roleId": seed.role_id, - "groupName": seed.group_name, - "traitName": seed.trait_name, - "source": seed.source, - "previous": previous, - "vector": vector, - "status": "applied", - })); - } - Ok(serde_json::json!({ - "status": if applied.is_empty() { "no-seeds" } else { "applied" }, - "agentStore": store_path, - "applied": applied.len(), - "seeds": applied, - })) -} - -pub fn agent_memory_status(store_path: impl AsRef) -> Result { - let store_path = store_path.as_ref(); - if !store_path.exists() { - return Ok(serde_json::json!({ - "ok": false, - "store": store_path, - "present": false, - "entryType": AGENT_MEMORY_TYPE, - "roles": [], - })); - } - let mut cache = agent_memory_cache(store_path)?; - cache.pull_all_backing_stores()?; - let mut roles = Vec::new(); - for (role_id, expected_agent_id, _) in ROLE_TARGETS { - let entry = cache.get::(role_id)?; - roles.push(match entry { - Some(entry) => serde_json::json!({ - "roleId": role_id, - "agentId": entry.agent.agent_id, - "displayName": entry.agent.identity.name, - "organStateProfile": organ_state_profile_for_role(role_id), - "semanticMemories": entry.agent.memories.semantic.len(), - "episodicMemories": entry.agent.memories.episodic.len(), - "relationshipMemories": entry.agent.memories.relationship_summaries.len(), - "goals": entry.agent.goals.len(), - "values": entry.agent.canonical_state.values.len(), - }), - None => serde_json::json!({ - "roleId": role_id, - "agentId": expected_agent_id, - "missing": true, - }), - }); - } - let errors = validate_agent_memory_store(store_path)?; - let swarm_identity = cache - .get::(AGENT_MEMORY_SWARM_IDENTITY_KEY)? - .map(|identity| { - serde_json::json!({ - "schemaVersion": identity.schema_version, - "swarmId": identity.swarm_id, - }) - }); - let agent_state_soa = project_agent_state_soa_from_cache(store_path, &cache)?; - let persisted_agent_state_soa = cache - .get::(AGENT_STATE_SOA_KEY)? - .map(|entry| { - serde_json::json!({ - "schemaVersion": entry.schema_version, - "generatedAt": entry.generated_at, - "rowCount": entry.role_ids.len(), - "sourceStore": entry.source_store, - }) - }); - Ok(serde_json::json!({ - "ok": errors.is_empty(), - "store": store_path, - "present": true, - "entryType": AGENT_MEMORY_TYPE, - "swarmIdentityEntryType": AGENT_MEMORY_SWARM_IDENTITY_TYPE, - "swarmIdentity": swarm_identity, - "agentStateSoaEntryType": AGENT_STATE_SOA_TYPE, - "errors": errors, - "agentStateSoa": { - "schemaVersion": agent_state_soa.schema_version, - "rowCount": agent_state_soa.role_ids.len(), - "roleIds": agent_state_soa.role_ids, - "agentIds": agent_state_soa.agent_ids, - "displayNames": agent_state_soa.display_names, - "profileKinds": agent_state_soa.profile_kinds, - "portableContracts": agent_state_soa.portable_contracts, - "semanticMemoryCounts": agent_state_soa.semantic_memory_counts, - "episodicMemoryCounts": agent_state_soa.episodic_memory_counts, - "relationshipMemoryCounts": agent_state_soa.relationship_memory_counts, - "goalCounts": agent_state_soa.goal_counts, - "valueCounts": agent_state_soa.value_counts, - }, - "persistedAgentStateSoa": persisted_agent_state_soa, - "roles": roles, - })) -} - -pub fn refresh_agent_state_soa(store_path: impl AsRef) -> Result { - let store_path = store_path.as_ref(); - let mut cache = agent_memory_cache(store_path)?; - cache.pull_all_backing_stores()?; - let entry = project_agent_state_soa_from_cache(store_path, &cache)?; - validate_agent_state_soa_entry(&entry)?; - cache.put(AGENT_STATE_SOA_KEY, &entry)?; - Ok(serde_json::json!({ - "ok": true, - "store": store_path, - "entryType": AGENT_STATE_SOA_TYPE, - "key": AGENT_STATE_SOA_KEY, - "rowCount": entry.role_ids.len(), - "roleIds": entry.role_ids, - "agentIds": entry.agent_ids, - })) -} - -pub fn load_agent_state_soa_entry( - store_path: impl AsRef, -) -> Result> { - let store_path = store_path.as_ref(); - if !store_path.exists() { - return Ok(None); - } - let mut cache = agent_memory_cache(store_path)?; - cache.pull_all_backing_stores()?; - cache.get::(AGENT_STATE_SOA_KEY) -} - -fn project_agent_state_soa_from_cache( - store_path: &Path, - cache: &CultCache, -) -> Result { - let table = cache.soa::()?; - let entry = EpiphanyAgentStateSoaEntry { - schema_version: AGENT_STATE_SOA_SCHEMA_VERSION.to_string(), - generated_at: now_rfc3339(), - source_store: store_path.display().to_string(), - role_ids: table.column::("roleId")?.values().to_vec(), - agent_ids: table.column::("agentId")?.values().to_vec(), - display_names: table.column::("displayName")?.values().to_vec(), - profile_kinds: table.column::("profileKind")?.values().to_vec(), - portable_contracts: table - .column::("portableContract")? - .values() - .to_vec(), - semantic_memory_counts: table - .column::("semanticMemoryCount")? - .values() - .to_vec(), - episodic_memory_counts: table - .column::("episodicMemoryCount")? - .values() - .to_vec(), - relationship_memory_counts: table - .column::("relationshipMemoryCount")? - .values() - .to_vec(), - goal_counts: table.column::("goalCount")?.values().to_vec(), - value_counts: table.column::("valueCount")?.values().to_vec(), - }; - validate_agent_state_soa_entry(&entry)?; - Ok(entry) -} - -fn validate_agent_state_soa_entry(entry: &EpiphanyAgentStateSoaEntry) -> Result<()> { - if entry.schema_version != AGENT_STATE_SOA_SCHEMA_VERSION { - return Err(anyhow!( - "agent state SoA schema_version must be {:?}", - AGENT_STATE_SOA_SCHEMA_VERSION - )); - } - let len = entry.role_ids.len(); - for (name, candidate) in [ - ("agentIds", entry.agent_ids.len()), - ("displayNames", entry.display_names.len()), - ("profileKinds", entry.profile_kinds.len()), - ("portableContracts", entry.portable_contracts.len()), - ("semanticMemoryCounts", entry.semantic_memory_counts.len()), - ("episodicMemoryCounts", entry.episodic_memory_counts.len()), - ( - "relationshipMemoryCounts", - entry.relationship_memory_counts.len(), - ), - ("goalCounts", entry.goal_counts.len()), - ("valueCounts", entry.value_counts.len()), - ] { - if candidate != len { - return Err(anyhow!( - "agent state SoA column {name} has length {candidate}, expected {len}" - )); - } - } - Ok(()) -} - -pub fn project_persona_state_for_role( - store_path: impl AsRef, - role_id: &str, -) -> Result { - let store_path = store_path.as_ref(); - let profile = organ_state_profile_for_role(role_id); - if profile.profile_kind != EpiphanyOrganStateProfileKind::Persona { - return Err(anyhow!( - "{role_id:?} is {:?}, not persona; use epiphany.work_organ_state.v0 for work organs", - profile.profile_kind - )); - } - let entry = load_agent_memory_entry_for_role(store_path, role_id)? - .ok_or_else(|| anyhow!("CultCache has no role memory entry for {role_id:?}"))?; - Ok(project_persona_state_from_entry(&entry, store_path)) -} - -pub fn project_persona_state_from_entry( - entry: &EpiphanyAgentMemoryEntry, - store_path: &Path, -) -> Value { - let exported_at = now_rfc3339(); - let persona_id = entry.agent.agent_id.clone(); - let repo_name = "EpiphanyAgent"; - serde_json::json!({ - "schemaVersion": PERSONA_STATE_SCHEMA_VERSION, - "provenance": { - "sourceSystem": "epiphany", - "sourceDocumentId": format!("{}#{}", store_path.display(), entry.role_id), - "sourceUpdatedAt": exported_at, - "exportedAt": exported_at, - "authority": "projection", - }, - "personaId": persona_id, - "publicName": &entry.agent.identity.name, - "publicDescription": &entry.agent.identity.public_description, - "presentation": { - "voiceSummary": &entry.agent.identity.public_description, - "defaultRenderer": "chat", - "homeContext": { - "kind": "repo", - "id": repo_name, - "label": repo_name, - }, - "jurisdiction": entry - .agent - .identity - .roles - .iter() - .map(String::as_str) - .collect::>() - .join(", "), - "publicHandles": [], - }, - "privateNotes": &entry.agent.identity.private_notes, - "values": entry.agent.canonical_state.values.iter().map(persona_value).collect::>(), - "activationProfile": { - "underlyingOrganization": persona_trait_map(&entry.agent.canonical_state.underlying_organization), - "stableDispositions": persona_trait_map(&entry.agent.canonical_state.stable_dispositions), - "behavioralDimensions": persona_trait_map(&entry.agent.canonical_state.behavioral_dimensions), - "presentationStrategy": persona_trait_map(&entry.agent.canonical_state.presentation_strategy), - "voiceStyle": persona_trait_map(&entry.agent.canonical_state.voice_style), - "situationalState": persona_trait_map(&entry.agent.canonical_state.situational_state), - }, - "thoughtMemory": { - "shortTerm": [], - "memories": entry - .agent - .memories - .semantic - .iter() - .chain(entry.agent.memories.episodic.iter()) - .chain(entry.agent.memories.relationship_summaries.iter()) - .map(|memory| persona_memory_thought(memory, &persona_id, &exported_at)) - .collect::>(), - "incubation": [], - }, - "agencyPressure": { - "pressures": entry.agent.goals.iter().map(|goal| persona_goal_thought(goal, &persona_id, &exported_at)).collect::>(), - }, - "candidateActions": { - "actions": [], - }, - "voidbotProjection": { - "candidateInterventions": [], - }, - "affect": { - "needs": [], - "socialBonds": [], - "statusReads": entry.agent.perceived_state_overlays.iter().map(|overlay| persona_status_read(overlay, &persona_id, &exported_at)).collect::>(), - "moodDimensions": [], - "socialBiases": [], - "doctrineStances": entry.agent.canonical_state.values.iter().map(|value| persona_doctrine_stance(value, &persona_id, &exported_at)).collect::>(), - }, - "updatedAt": exported_at, - }) -} - -fn persona_trait_map(vectors: &BTreeMap) -> Value { - let mut map = serde_json::Map::new(); - for (name, vector) in vectors { - map.insert( - name.clone(), - serde_json::json!({ - "mean": vector.mean, - "plasticity": vector.plasticity, - "currentActivation": vector.current_activation, - }), - ); - } - Value::Object(map) -} - -fn persona_value(value: &GhostlightValue) -> Value { - serde_json::json!({ - "id": value.value_id, - "label": value.label, - "priority": value.priority, - "summary": if value.unforgivable_if_betrayed { - "Protected value; betrayal is marked as unforgivable in local Persona state." - } else { - "Value projected from local Persona organ memory." - }, - }) -} - -fn persona_target(kind: &str, id: &str, label: &str) -> Value { - serde_json::json!({ - "kind": kind, - "id": id, - "label": label, - }) -} - -fn persona_memory_thought(memory: &GhostlightMemory, persona_id: &str, timestamp: &str) -> Value { - serde_json::json!({ - "id": memory.memory_id, - "status": "crystallized", - "target": persona_target("self", persona_id, persona_id), - "summary": memory.summary, - "claim": memory.summary, - "tension": "Projected from local Persona memory; consumers should preserve provenance and avoid treating this as omniscient project truth.", - "actionImplication": "Use as Persona memory pressure, not as direct action authority.", - "intensity": memory.salience, - "valence": 0, - "createdAt": timestamp, - "updatedAt": timestamp, - "tags": ["epiphany", "Persona-memory"], - }) -} - -fn persona_goal_thought(goal: &GhostlightGoal, persona_id: &str, timestamp: &str) -> Value { - serde_json::json!({ - "id": goal.goal_id, - "status": persona_goal_status(&goal.status), - "target": persona_target("self", persona_id, persona_id), - "summary": goal.description, - "claim": goal.description, - "tension": goal.emotional_stake, - "actionImplication": "This goal may create Persona agency pressure, but action still requires the caller's review path.", - "intensity": goal.priority, - "createdAt": timestamp, - "updatedAt": timestamp, - "tags": ["epiphany", "Persona-goal", &goal.scope], - }) -} - -fn persona_status_read( - overlay: &GhostlightPerceivedStateOverlay, - persona_id: &str, - timestamp: &str, -) -> Value { - serde_json::json!({ - "id": overlay.overlay_id, - "status": "active", - "target": persona_target("self", persona_id, &overlay.label), - "statusKind": "uncertainty", - "summary": overlay.summary, - "confidence": overlay.confidence, - "intensity": overlay.salience, - "valence": 0, - "updatedAt": timestamp, - "extensions": { - "source": overlay.source, - "linkedMemoryIds": overlay.linked_memory_ids, - }, - }) -} - -fn persona_doctrine_stance(value: &GhostlightValue, persona_id: &str, timestamp: &str) -> Value { - serde_json::json!({ - "id": format!("stance-{}", value.value_id), - "status": "active", - "target": persona_target("self", persona_id, persona_id), - "stanceKind": "aligned", - "principle": value.label, - "summary": value.label, - "actionImplication": "Let this value bend Persona speech and interpretation without granting automatic action authority.", - "intensity": value.priority, - "updatedAt": timestamp, - }) -} - -fn persona_goal_status(status: &str) -> &'static str { - match status { - "active" => "active", - "blocked" | "dormant" => "cooling", - "resolved" => "resolved", - "abandoned" => "retired", - _ => "draft", - } -} - fn now_rfc3339() -> String { Utc::now().to_rfc3339_opts(SecondsFormat::Secs, true) } @@ -2621,45 +1355,6 @@ pub fn organ_state_profile_for_role(role_id: &str) -> EpiphanyOrganStateProfile } } -pub fn project_agent_memory_to_json_dir( - store_path: impl AsRef, - output_dir: impl AsRef, -) -> Result { - let store_path = store_path.as_ref(); - let output_dir = output_dir.as_ref(); - let mut cache = agent_memory_cache(store_path)?; - cache.pull_all_backing_stores()?; - fs::create_dir_all(output_dir) - .with_context(|| format!("failed to create {}", output_dir.display()))?; - let mut projected = Vec::new(); - for (role_id, _, filename) in ROLE_TARGETS { - let entry = cache - .get::(role_id)? - .ok_or_else(|| anyhow!("missing role memory entry for {role_id:?}"))?; - let projection = AgentMemoryProjection { - schema_version: entry.schema_version, - world: entry.world, - agents: vec![entry.agent], - relationships: entry.relationships, - events: entry.events, - scenes: entry.scenes, - }; - let path = output_dir.join(filename); - fs::write( - &path, - format!("{}\n", serde_json::to_string_pretty(&projection)?), - ) - .with_context(|| format!("failed to write {}", path.display()))?; - projected.push(serde_json::json!({"roleId": role_id, "path": path})); - } - Ok(serde_json::json!({ - "ok": true, - "store": store_path, - "outputDir": output_dir, - "projected": projected, - })) -} - fn agent_memory_cache(store_path: &Path) -> Result { let mut cache = CultCache::new(); cache.register_entry_type::()?; @@ -2675,64 +1370,10 @@ fn agent_memory_cache(store_path: &Path) -> Result { cache.register_entry_type::()?; cache.register_entry_type::()?; cache.register_entry_type::()?; - cache.register_entry_type::()?; cache.add_generic_backing_store(SingleFileMessagePackBackingStore::new(store_path)); Ok(cache) } -fn canonical_group_mut<'a>( - state: &'a mut GhostlightCanonicalState, - group_name: &str, -) -> Result<&'a mut BTreeMap> { - match group_name { - "underlying_organization" => Ok(&mut state.underlying_organization), - "stable_dispositions" => Ok(&mut state.stable_dispositions), - "behavioral_dimensions" => Ok(&mut state.behavioral_dimensions), - "presentation_strategy" => Ok(&mut state.presentation_strategy), - "voice_style" => Ok(&mut state.voice_style), - "situational_state" => Ok(&mut state.situational_state), - other => Err(anyhow!( - "unknown canonical_state group {:?}; expected one of the six Ghostlight trait bundles", - other - )), - } -} - -fn entry_from_projection( - role_id: &str, - expected_agent_id: &str, - projection: AgentMemoryProjection, -) -> Result { - if projection.schema_version != AGENT_MEMORY_SCHEMA_VERSION { - return Err(anyhow!( - "schema_version must be {:?}", - AGENT_MEMORY_SCHEMA_VERSION - )); - } - if projection.agents.len() != 1 { - return Err(anyhow!( - "role memory projection must contain exactly one agent" - )); - } - let agent = projection.agents.into_iter().next().expect("checked len"); - if agent.agent_id != expected_agent_id { - return Err(anyhow!( - "agent_id {:?} does not match expected {:?}", - agent.agent_id, - expected_agent_id - )); - } - Ok(EpiphanyAgentMemoryEntry { - schema_version: projection.schema_version, - role_id: role_id.to_string(), - world: projection.world, - agent, - relationships: projection.relationships, - events: projection.events, - scenes: projection.scenes, - }) -} - fn validate_agent_entry(entry: &EpiphanyAgentMemoryEntry, expected_agent_id: &str) -> Vec { let mut errors = Vec::new(); if entry.schema_version != AGENT_MEMORY_SCHEMA_VERSION { @@ -3007,14 +1648,6 @@ fn check_unit(value: f64, path: &str, errors: &mut Vec) { } } -fn clamp_unit(value: f64) -> f64 { - if !value.is_finite() { - 0.5 - } else { - value.clamp(0.0, 1.0) - } -} - fn agent_id_for_role(role_id: &str) -> std::result::Result<&'static str, String> { ROLE_TARGETS .iter() @@ -3316,659 +1949,19 @@ mod tests { use tempfile::tempdir; #[test] - fn fresh_agent_memory_initializes_all_organs_and_replays_exactly() -> Result<()> { + fn fresh_store_initialization_is_exact_and_idempotent() -> Result<()> { let temp = tempdir()?; let store = temp.path().join("fresh-agent-memory.cc"); let witness = initialize_fresh_agent_memory_store(&store, "fresh-swarm")?; assert_eq!(witness.generation, 1); - assert_eq!(witness.previous_generation, 0); - assert_eq!(witness.mutation_kind, "fresh-bootstrap-admission"); assert_eq!(witness.changed_role_ids.len(), ROLE_TARGETS.len()); assert_eq!( initialize_fresh_agent_memory_store(&store, "fresh-swarm")?, witness ); - - let mut cache = agent_memory_cache(&store)?; - cache.pull_all_backing_stores()?; - for (role_id, agent_id, _) in ROLE_TARGETS { - let entry = cache - .get::(role_id)? - .expect("fresh role entry"); - assert_eq!(entry.agent.agent_id, *agent_id); - assert!(validate_agent_entry(&entry, agent_id).is_empty()); - } Ok(()) } - - #[test] - fn legacy_mind_admission_preserves_rows_and_adds_exact_generation_pressure() -> Result<()> { - let temp = tempdir()?; - let agent_dir = temp.path().join("agents-legacy-admission"); - fs::create_dir_all(&agent_dir)?; - for (role_id, agent_id, filename) in ROLE_TARGETS { - fs::write( - agent_dir.join(filename), - sample_agent_json(agent_id, &format!("Agent {role_id}")), - )?; - } - let store = temp.path().join("legacy-admission.msgpack"); - migrate_agent_memory_json_dir_to_cultcache(&agent_dir, &store)?; - ensure_agent_memory_swarm_identity(&store, "legacy-admission-swarm")?; - let mut before_cache = agent_memory_cache(&store)?; - before_cache.pull_all_backing_stores()?; - let before = before_cache - .snapshot_envelopes() - .into_iter() - .filter(|envelope| envelope.r#type == AGENT_MEMORY_TYPE) - .collect::>(); - - let witness = admit_legacy_agent_memory_generation(&store)?; - assert_eq!(witness.generation, 1); - assert_eq!(witness.previous_generation, 0); - assert_eq!(witness.mutation_kind, "legacy-bootstrap-admission"); - assert!(witness.changed_role_ids.is_empty()); - assert_eq!(admit_legacy_agent_memory_generation(&store)?, witness); - - let mut cache = agent_memory_cache(&store)?; - cache.pull_all_backing_stores()?; - let after = cache - .snapshot_envelopes() - .into_iter() - .filter(|envelope| envelope.r#type == AGENT_MEMORY_TYPE) - .collect::>(); - assert_eq!(after, before); - let receipt = cache - .get::(&witness.authority_receipt_id)? - .expect("legacy admission receipt"); - assert_eq!(receipt.resulting_source_hash, witness.source_hash); - let obligations = cache - .get_all::()? - .into_iter() - .filter(|obligation| obligation.source_commit_id == witness.witness_id) - .collect::>(); - assert_eq!(obligations.len(), 1); - assert_eq!(obligations[0].partition, "mind"); - assert_eq!(obligations[0].source_generation, 1); - assert!(!obligations[0].source_model_hash.is_empty()); - let scope_id = format!("{}:{}", obligations[0].swarm_id, obligations[0].partition); - cache.put( - &scope_id, - &crate::MemorySemanticProjectionRetentionHead { - schema_version: crate::MEMORY_SEMANTIC_PROJECTION_RETENTION_HEAD_SCHEMA_VERSION - .to_string(), - scope_id: scope_id.clone(), - revision: 1, - retired_generation_count: 1, - retired_envelope_count: 1, - retired_type_counts: BTreeMap::from([("test.retired".to_string(), 1)]), - retired_status_counts: BTreeMap::from([("failed".to_string(), 1)]), - retired_chain_digest: format!("sha256:{}", "0".repeat(64)), - retained_at: "2026-07-15T00:00:01Z".to_string(), - private_state_exposed: false, - }, - )?; - let input = agent_memory_semantic_projection_input(&store)?; - assert_eq!(input.obligation, obligations[0]); - assert_eq!(input.snapshot.model_revision, 1); - assert_eq!(input.authority.envelopes.len(), ROLE_TARGETS.len() + 2); - assert!(repair_agent_memory_store(&store).is_err()); - Ok(()) - } - - #[test] - fn concurrent_mind_commits_have_one_generation_and_no_partial_companions() -> Result<()> { - let temp = tempdir()?; - let agent_dir = temp.path().join("agents-concurrent"); - fs::create_dir_all(&agent_dir)?; - for (role_id, agent_id, filename) in ROLE_TARGETS { - fs::write( - agent_dir.join(filename), - sample_agent_json(agent_id, &format!("Agent {role_id}")), - )?; - } - let store = temp.path().join("concurrent.msgpack"); - migrate_agent_memory_json_dir_to_cultcache(&agent_dir, &store)?; - ensure_agent_memory_swarm_identity(&store, "concurrent-swarm")?; - let mut cache = agent_memory_cache(&store)?; - cache.pull_all_backing_stores()?; - let (generation, source_hash) = agent_memory_source_head(&mut cache)?; - let base = cache.get_required::("modeling")?; - let barrier = std::sync::Arc::new(std::sync::Barrier::new(3)); - let mut joins = Vec::new(); - for suffix in ["alpha", "beta"] { - let mut next = base.clone(); - next.agent.identity.public_description = format!( - "Modeling generation {suffix} preserves a distinct concurrent candidate truth." - ); - let barrier = barrier.clone(); - let store = store.clone(); - let source_hash = source_hash.clone(); - joins.push(std::thread::spawn(move || { - barrier.wait(); - commit_agent_memory_generation( - &store, - generation, - &source_hash, - "modeling", - next, - "self_patch", - &format!("Concurrent {suffix} admission candidate"), - ) - })); - } - barrier.wait(); - let results = joins - .into_iter() - .map(|join| join.join().expect("Mind commit worker panicked")) - .collect::>(); - assert_eq!(results.iter().filter(|result| result.is_ok()).count(), 1); - assert_eq!(results.iter().filter(|result| result.is_err()).count(), 1); - let witness = load_agent_memory_generation_witness(&store)?.expect("generation witness"); - assert_eq!(witness.generation, 1); - let mut cache = agent_memory_cache(&store)?; - cache.pull_all_backing_stores()?; - let receipt = - cache.get::(&witness.authority_receipt_id)?; - assert!(receipt.is_some()); - let obligations = cache - .get_all::()? - .into_iter() - .filter(|obligation| obligation.source_commit_id == witness.witness_id) - .collect::>(); - assert_eq!(obligations.len(), 1); - let obligation = &obligations[0]; - assert_eq!(obligation.source_generation, witness.generation); - assert_eq!(obligation.partition, "mind"); - assert_eq!(obligation.swarm_id, witness.swarm_id); - let admitted = cache.get_required::("modeling")?; - assert!(write_agent_memory_entry_for_role_migration(&store, &admitted).is_err()); - assert!(repair_agent_memory_store(&store).is_err()); - assert!( - apply_agent_canonical_trait_seeds( - &[AgentCanonicalTraitSeed { - role_id: "coordinator".to_string(), - group_name: "underlying_organization".to_string(), - trait_name: "forbidden_after_admission".to_string(), - mean: 0.5, - plasticity: 0.5, - current_activation: 0.5, - source: Some("hostile bypass test".to_string()), - }], - &store, - ) - .is_err() - ); - - let mut corrupted = admitted; - corrupted - .agent - .identity - .public_description - .push_str(" corrupt"); - cache.put("modeling", &corrupted)?; - assert!(agent_memory_source_head(&mut cache).is_err()); - Ok(()) - } - - #[test] - fn role_memory_migrates_reviews_and_applies_native_patch() -> Result<()> { - let temp = tempdir()?; - let agent_dir = temp.path().join("agents"); - fs::create_dir_all(&agent_dir)?; - fs::write( - agent_dir.join("modeling.agent-state.json"), - sample_agent_json("epiphany.modeling", "Modeling"), - )?; - for (role_id, agent_id, filename) in ROLE_TARGETS { - if *role_id == "modeling" { - continue; - } - fs::write( - agent_dir.join(filename), - sample_agent_json(agent_id, &format!("Agent {role_id}")), - )?; - } - let store = temp.path().join("agents.msgpack"); - migrate_agent_memory_json_dir_to_cultcache(&agent_dir, &store)?; - ensure_agent_memory_swarm_identity(&store, "test-swarm")?; - assert!(validate_agent_memory_store(&store)?.is_empty()); - - let patch: AgentSelfPatch = serde_json::from_value(serde_json::json!({ - "agentId": "epiphany.modeling", - "reason": "Modeling should remember accepted graph growth must stay source-grounded.", - "semanticMemories": [{ - "memoryId": "mem-body-native-source-grounding", - "summary": "Native role memory patches update typed CultCache organ state rather than JSON dossier files.", - "salience": 0.74, - "confidence": 0.86 - }] - }))?; - let review = review_agent_self_patch_document("modeling", &patch, &store); - assert_eq!(review.status, "accepted"); - let applied = apply_agent_self_patch_document("modeling", patch, &store)?; - assert_eq!(applied.status, "accepted"); - assert_eq!(applied.applied, Some(true)); - - let mut cache = agent_memory_cache(&store)?; - cache.pull_all_backing_stores()?; - let modeling = cache.get_required::("modeling")?; - assert!( - modeling - .agent - .memories - .semantic - .iter() - .any(|memory| memory.memory_id == "mem-body-native-source-grounding") - ); - Ok(()) - } - - #[test] - fn memory_lifecycle_operation_revises_crystallizes_merges_retires_and_prunes() -> Result<()> { - let temp = tempdir()?; - let agent_dir = temp.path().join("agents"); - fs::create_dir_all(&agent_dir)?; - for (role_id, agent_id, filename) in ROLE_TARGETS { - fs::write( - agent_dir.join(filename), - sample_agent_json(agent_id, &format!("Agent {role_id}")), - )?; - } - let store = temp.path().join("agents.msgpack"); - migrate_agent_memory_json_dir_to_cultcache(&agent_dir, &store)?; - ensure_agent_memory_swarm_identity(&store, "test-swarm")?; - - let patch: AgentSelfPatch = serde_json::from_value(serde_json::json!({ - "agentId": "epiphany.modeling", - "reason": "Seed memories so the lifecycle operation can prove maintenance without identity edits.", - "semanticMemories": [ - { - "memoryId": "mem-lifecycle-alpha", - "summary": "Alpha memory starts as short doctrine needing revision.", - "salience": 0.42, - "confidence": 0.7 - }, - { - "memoryId": "mem-lifecycle-beta", - "summary": "Beta support should merge with gamma.", - "salience": 0.31, - "confidence": 0.72 - }, - { - "memoryId": "mem-lifecycle-gamma", - "summary": "Gamma support should merge with beta.", - "salience": 0.33, - "confidence": 0.74 - } - ] - }))?; - assert_eq!( - apply_agent_self_patch_document("modeling", patch, &store)?.status, - "accepted" - ); - - let operation = AgentMemoryLifecycleOperation { - agent_id: Some("epiphany.modeling".to_string()), - reason: "Sleep maintenance is revising, crystallizing, merging, retiring, and pruning bounded role memory.".to_string(), - actions: vec![ - AgentMemoryLifecycleAction::Revise { - bundle: AgentMemoryBundle::Semantic, - memory_id: "mem-lifecycle-alpha".to_string(), - summary: Some("Alpha memory was revised before crystallization.".to_string()), - salience: Some(0.81), - confidence: None, - reason: "The old wording was too weak for durable modeling doctrine.".to_string(), - }, - AgentMemoryLifecycleAction::Crystallize { - from_bundle: AgentMemoryBundle::Semantic, - to_bundle: AgentMemoryBundle::Episodic, - memory_id: "mem-lifecycle-alpha".to_string(), - new_memory_id: Some("mem-lifecycle-alpha-episode".to_string()), - summary: Some("Alpha became an episodic scar from sleep maintenance.".to_string()), - reason: "The revised pressure should leave a separate episodic witness.".to_string(), - }, - AgentMemoryLifecycleAction::Merge { - bundle: AgentMemoryBundle::Semantic, - target_memory_id: "mem-lifecycle-merged".to_string(), - source_memory_ids: vec![ - "mem-lifecycle-beta".to_string(), - "mem-lifecycle-gamma".to_string(), - ], - summary: "Beta and gamma collapsed into one support memory.".to_string(), - salience: 0.77, - confidence: 0.82, - reason: "Duplicate support belongs in one clearer memory.".to_string(), - }, - AgentMemoryLifecycleAction::Retire { - bundle: AgentMemoryBundle::Semantic, - memory_id: "mem-lifecycle-alpha".to_string(), - reason: "The revised alpha pressure was crystallized and should leave semantic room.".to_string(), - }, - AgentMemoryLifecycleAction::Prune { - bundle: AgentMemoryBundle::Semantic, - max_records: 1, - minimum_salience: Some(0.5), - reason: "Keep only strong semantic residue after merge.".to_string(), - }, - ], - extra: BTreeMap::new(), - }; - - let review = review_agent_memory_lifecycle_operation("modeling", &operation, &store); - assert_eq!(review.status, "accepted"); - let applied = apply_agent_memory_lifecycle_operation("modeling", operation, &store)?; - assert_eq!(applied.status, "accepted"); - assert_eq!(applied.applied, Some(true)); - - let mut cache = agent_memory_cache(&store)?; - cache.pull_all_backing_stores()?; - let modeling = cache.get_required::("modeling")?; - assert_eq!(modeling.agent.memories.semantic.len(), 1); - assert_eq!( - modeling.agent.memories.semantic[0].memory_id, - "mem-lifecycle-merged" - ); - assert!( - modeling - .agent - .memories - .episodic - .iter() - .any(|memory| memory.memory_id == "mem-lifecycle-alpha-episode") - ); - assert!(validate_agent_memory_store(&store)?.is_empty()); - Ok(()) - } - - #[test] - fn canonical_trait_seeds_replace_baseline_vectors() -> Result<()> { - let temp = tempdir()?; - let agent_dir = temp.path().join("agents"); - fs::create_dir_all(&agent_dir)?; - for (role_id, agent_id, filename) in ROLE_TARGETS { - fs::write( - agent_dir.join(filename), - sample_agent_json(agent_id, &format!("Agent {role_id}")), - )?; - } - let store = temp.path().join("agents.msgpack"); - migrate_agent_memory_json_dir_to_cultcache(&agent_dir, &store)?; - ensure_agent_memory_swarm_identity(&store, "test-swarm")?; - - let applied = apply_agent_canonical_trait_seeds( - &[AgentCanonicalTraitSeed { - role_id: "coordinator".to_string(), - group_name: "underlying_organization".to_string(), - trait_name: "routing_discipline".to_string(), - mean: 0.92, - plasticity: 0.22, - current_activation: 0.9, - source: Some("startup personality projection smoke".to_string()), - }], - &store, - )?; - assert_eq!(applied["applied"], 1); - - let mut cache = agent_memory_cache(&store)?; - cache.pull_all_backing_stores()?; - let entry = cache.get_required::("coordinator")?; - assert!( - !entry - .agent - .canonical_state - .underlying_organization - .contains_key("baseline") - ); - assert_eq!( - entry - .agent - .canonical_state - .underlying_organization - .get("routing_discipline") - .map(|vector| vector.mean), - Some(0.92) - ); - Ok(()) - } - - #[test] - fn organ_state_profiles_distinguish_persona_from_lane_organs() { - let persona = organ_state_profile_for_role("Persona"); - assert_eq!(persona.profile_kind, EpiphanyOrganStateProfileKind::Persona); - assert_eq!(persona.state_density, "persona_grade"); - assert_eq!(persona.portable_contract, "gamecult.persona_state.v0"); - assert_eq!(persona.affect_model, "persona_affect_allowed_and_expected"); - - let hands = organ_state_profile_for_role("implementation"); - assert_eq!(hands.profile_kind, EpiphanyOrganStateProfileKind::WorkOrgan); - assert_eq!(hands.state_density, "lean_work_organ"); - assert_eq!(hands.portable_contract, "epiphany.work_organ_state.v0"); - assert_eq!(hands.affect_model, "no_affect_or_persona_machinery"); - } - - #[test] - fn agent_memory_projects_swarm_state_as_soa_columns() -> Result<()> { - let temp = tempdir()?; - let agent_dir = temp.path().join("agents"); - fs::create_dir_all(&agent_dir)?; - for (role_id, agent_id, filename) in ROLE_TARGETS { - fs::write( - agent_dir.join(filename), - sample_agent_json(agent_id, &format!("Agent {role_id}")), - )?; - } - let store = temp.path().join("agents.msgpack"); - migrate_agent_memory_json_dir_to_cultcache(&agent_dir, &store)?; - ensure_agent_memory_swarm_identity(&store, "test-swarm")?; - - let refreshed = refresh_agent_state_soa(&store)?; - assert_eq!(refreshed["rowCount"], 7); - - let mut cache = agent_memory_cache(&store)?; - cache.pull_all_backing_stores()?; - let entry = cache.get_required::(AGENT_STATE_SOA_KEY)?; - assert_eq!(entry.schema_version, AGENT_STATE_SOA_SCHEMA_VERSION); - assert_eq!(entry.role_ids.len(), 7); - assert!(entry.role_ids.iter().any(|role| role == "Persona")); - assert!( - entry - .agent_ids - .iter() - .any(|agent_id| agent_id == "epiphany.Persona") - ); - assert_eq!(entry.role_ids.len(), entry.semantic_memory_counts.len()); - assert!( - entry - .portable_contracts - .iter() - .any(|contract| contract == "gamecult.persona_state.v0") - ); - Ok(()) - } - - #[test] - fn repair_agent_memory_store_promotes_legacy_face_and_modeling_vessel() -> Result<()> { - let temp = tempdir()?; - let agent_dir = temp.path().join("agents"); - fs::create_dir_all(&agent_dir)?; - for (role_id, agent_id, filename) in ROLE_TARGETS { - if *role_id == "Persona" { - continue; - } - let actual_agent_id = if *role_id == "modeling" { - "epiphany.proprioception" - } else { - agent_id - }; - fs::write( - agent_dir.join(filename), - sample_agent_json(actual_agent_id, &format!("Agent {role_id}")), - )?; - } - fs::write( - agent_dir.join("face.agent-state.json"), - sample_agent_json("epiphany.face", "Face"), - )?; - - let store = temp.path().join("agents.msgpack"); - let mut cache = agent_memory_cache(&store)?; - for (role_id, agent_id, filename) in ROLE_TARGETS { - if *role_id == "Persona" { - continue; - } - let raw = fs::read_to_string(agent_dir.join(filename))?; - let projection: AgentMemoryProjection = serde_json::from_str(&raw)?; - let expected_agent_id = if *role_id == "modeling" { - "epiphany.proprioception" - } else { - agent_id - }; - let mut entry = entry_from_projection(role_id, expected_agent_id, projection)?; - if *role_id == "modeling" { - entry.agent.identity.public_description = - "Proprioception models source-grounded anatomy.".to_string(); - entry.agent.goals[0].emotional_stake = - "If Proprioception lies, Hands cuts blind.".to_string(); - entry.agent.memories.semantic.push(GhostlightMemory { - memory_id: "legacy-proprioception-prose".to_string(), - summary: "Proprioception leaves a verified trail.".to_string(), - salience: 0.8, - confidence: 0.8, - linked_event_ids: None, - linked_relationship_id: None, - }); - } - cache.put((*role_id).to_string(), &entry)?; - } - let raw = fs::read_to_string(agent_dir.join("face.agent-state.json"))?; - let projection: AgentMemoryProjection = serde_json::from_str(&raw)?; - let face = entry_from_projection("face", "epiphany.face", projection)?; - cache.put("face".to_string(), &face)?; - let projection: AgentMemoryProjection = - serde_json::from_str(&sample_agent_json("epiphany.life", "Life"))?; - let reorientation = entry_from_projection("reorientation", "epiphany.life", projection)?; - cache.put("reorientation".to_string(), &reorientation)?; - - let errors = validate_agent_memory_store(&store)?; - assert!(errors.iter().any(|error| error.contains("proprioception"))); - assert!( - errors - .iter() - .any(|error| error.contains("Persona: missing")) - ); - - let repaired = repair_agent_memory_store(&store)?; - assert_eq!(repaired["ok"], true); - assert!(validate_agent_memory_store(&store)?.is_empty()); - let refreshed = refresh_agent_state_soa(&store)?; - assert_eq!(refreshed["rowCount"], 7); - - let mut cache = agent_memory_cache(&store)?; - cache.pull_all_backing_stores()?; - let modeling = cache.get_required::("modeling")?; - assert_eq!(modeling.agent.agent_id, "epiphany.modeling"); - assert!( - !modeling - .agent - .identity - .public_description - .contains("Proprioception") - ); - assert!( - modeling - .agent - .goals - .iter() - .all(|goal| !goal.emotional_stake.contains("Proprioception")) - ); - assert!( - modeling - .agent - .memories - .semantic - .iter() - .all(|memory| { !memory.summary.contains("Proprioception") }) - ); - let persona = cache.get_required::("Persona")?; - assert_eq!(persona.role_id, "Persona"); - assert_eq!(persona.agent.agent_id, "epiphany.Persona"); - assert!( - persona - .agent - .identity - .roles - .iter() - .any(|role| role == "Persona") - ); - assert!( - cache - .get::("reorientation")? - .is_none() - ); - Ok(()) - } - - fn sample_agent_json(agent_id: &str, name: &str) -> String { - serde_json::json!({ - "schema_version": AGENT_MEMORY_SCHEMA_VERSION, - "world": { - "world_id": "epiphany-agent-memory", - "setting": "Epiphany local harness role memory", - "time": {"label": "standing memory"}, - "canon_context": ["Organ-state records preserve lane identity."] - }, - "agents": [{ - "agent_id": agent_id, - "identity": { - "name": name, - "roles": ["test"], - "origin": "test lane", - "public_description": "Test role memory.", - "private_notes": [] - }, - "canonical_state": { - "underlying_organization": {"test": {"mean": 0.5, "plasticity": 0.5, "current_activation": 0.5}}, - "stable_dispositions": {"test": {"mean": 0.5, "plasticity": 0.5, "current_activation": 0.5}}, - "behavioral_dimensions": {"test": {"mean": 0.5, "plasticity": 0.5, "current_activation": 0.5}}, - "presentation_strategy": {"test": {"mean": 0.5, "plasticity": 0.5, "current_activation": 0.5}}, - "voice_style": {"test": {"mean": 0.5, "plasticity": 0.5, "current_activation": 0.5}}, - "situational_state": {"test": {"mean": 0.5, "plasticity": 0.5, "current_activation": 0.5}}, - "values": [{ - "value_id": "value-test", - "label": "Test value.", - "priority": 0.5, - "unforgivable_if_betrayed": false - }] - }, - "goals": [{ - "goal_id": "goal-test", - "description": "Keep the test role valid.", - "scope": "life", - "priority": 0.5, - "emotional_stake": "Broken memory makes broken agents.", - "blockers": [], - "status": "active" - }], - "memories": { - "episodic": [], - "semantic": [{ - "memory_id": "mem-test", - "summary": "Test memory.", - "salience": 0.5, - "confidence": 0.5 - }], - "relationship_summaries": [] - }, - "perceived_state_overlays": [] - }], - "relationships": [], - "events": [], - "scenes": [] - }) - .to_string() - } } #[test] fn immutable_swarm_identity_separates_stores_and_refuses_collision() -> Result<()> { diff --git a/epiphany-core/src/cultmesh_integration.rs b/epiphany-core/src/cultmesh_integration.rs index 246db509..30219912 100644 --- a/epiphany-core/src/cultmesh_integration.rs +++ b/epiphany-core/src/cultmesh_integration.rs @@ -1,4 +1,3 @@ -use crate::EpiphanyAgentStateSoaEntry; use crate::default_continuity_cultnet_contracts; use crate::default_eyes_cultnet_contracts; use crate::default_hands_cultnet_contracts; @@ -30,10 +29,8 @@ use chrono::DateTime; use chrono::FixedOffset; use chrono::Utc; use cultcache_rs::CacheBackingStore; -use cultcache_rs::CultSoaColumnValues; use cultcache_rs::DatabaseEntry; use cultcache_rs::SingleFileMessagePackBackingStore; -use cultcache_rs::SoaDocument; use cultmesh_rs::CultMesh; use cultmesh_rs::CultMeshNode; use cultmesh_rs::CultMeshNodeOptions; @@ -87,12 +84,6 @@ pub const EPIPHANY_CULTMESH_WORK_LOOP_TELEMETRY_SCHEMA_VERSION: &str = "epiphany.cultmesh.work_loop_telemetry.v0"; pub const EPIPHANY_CULTMESH_WORK_LOOP_TELEMETRY_LATEST_KEY: &str = "epiphany-internal/work-loop-telemetry/latest"; -pub const EPIPHANY_CULTMESH_AGENT_STATE_SOA_SUMMARY_TYPE: &str = - "epiphany.cultmesh.agent_state_soa_summary"; -pub const EPIPHANY_CULTMESH_AGENT_STATE_SOA_SUMMARY_SCHEMA_VERSION: &str = - "epiphany.cultmesh.agent_state_soa_summary.v0"; -pub const EPIPHANY_CULTMESH_AGENT_STATE_SOA_SUMMARY_LATEST_KEY: &str = - "epiphany-local/agent-state-soa-summary/latest"; pub const EPIPHANY_CULTMESH_REPO_WORK_OVERVIEW_TYPE: &str = "epiphany.cultmesh.repo_work_overview"; pub const EPIPHANY_CULTMESH_REPO_WORK_OVERVIEW_SCHEMA_VERSION: &str = "epiphany.cultmesh.repo_work_overview.v0"; @@ -681,95 +672,6 @@ pub struct EpiphanyCultMeshWorkLoopTelemetryEntry { pub verification_assertions: Vec, } -#[derive(Clone, Debug, PartialEq, Eq, DatabaseEntry)] -#[cultcache( - type = "epiphany.cultmesh.agent_state_soa_summary", - schema = "EpiphanyCultMeshAgentStateSoaSummaryEntry" -)] -pub struct EpiphanyCultMeshAgentStateSoaSummaryEntry { - #[cultcache(key = 0)] - pub schema_version: String, - #[cultcache(key = 1)] - pub runtime_id: String, - #[cultcache(key = 2)] - pub verse_id: String, - #[cultcache(key = 3)] - pub summary_id: String, - #[cultcache(key = 4)] - pub generated_at: String, - #[cultcache(key = 5)] - pub source_store: String, - #[cultcache(key = 6)] - pub row_count: u32, - #[cultcache(key = 7)] - pub role_ids: Vec, - #[cultcache(key = 8)] - pub agent_ids: Vec, - #[cultcache(key = 9)] - pub display_names: Vec, - #[cultcache(key = 10)] - pub profile_kinds: Vec, - #[cultcache(key = 11)] - pub portable_contracts: Vec, - #[cultcache(key = 12)] - pub semantic_memory_counts: Vec, - #[cultcache(key = 13)] - pub episodic_memory_counts: Vec, - #[cultcache(key = 14)] - pub relationship_memory_counts: Vec, - #[cultcache(key = 15)] - pub goal_counts: Vec, - #[cultcache(key = 16)] - pub value_counts: Vec, - #[cultcache(key = 17)] - pub private_state_exposed: bool, - #[cultcache(key = 18)] - pub notes: Vec, -} - -impl SoaDocument for EpiphanyCultMeshAgentStateSoaSummaryEntry { - fn soa_columns(rows: &[Self]) -> std::collections::BTreeMap<&'static str, CultSoaColumnValues> { - let mut columns = std::collections::BTreeMap::new(); - columns.insert( - "summaryId", - CultSoaColumnValues::new( - rows.iter() - .map(|row| row.summary_id.clone()) - .collect::>(), - ), - ); - columns.insert( - "rowCount", - CultSoaColumnValues::new(rows.iter().map(|row| row.row_count).collect::>()), - ); - columns.insert( - "privateStateExposed", - CultSoaColumnValues::new( - rows.iter() - .map(|row| row.private_state_exposed) - .collect::>(), - ), - ); - columns.insert( - "roleIds", - CultSoaColumnValues::new( - rows.iter() - .map(|row| row.role_ids.clone()) - .collect::>(), - ), - ); - columns.insert( - "portableContracts", - CultSoaColumnValues::new( - rows.iter() - .map(|row| row.portable_contracts.clone()) - .collect::>(), - ), - ); - columns - } -} - #[derive(Clone, Debug, PartialEq, Eq, DatabaseEntry)] #[cultcache( type = "epiphany.cultmesh.repo_work_overview", @@ -2439,7 +2341,6 @@ pub struct EpiphanyLocalVerseContext { pub latest_hands_action_gate: Option, pub latest_role_review_event: Option, pub latest_work_loop_summary: Option, - pub latest_agent_state_soa_summary: Option, pub latest_repo_work_overview: Option, pub latest_repo_work_map_entry: Option, pub contract_summaries: Vec, @@ -2488,7 +2389,6 @@ cultmesh_documents!(EpiphanyCultMeshDocuments { EpiphanyCultMeshHandsActionGateEntry => EPIPHANY_CULTMESH_HANDS_ACTION_GATE_SCHEMA_VERSION, EpiphanyCultMeshRoleReviewEventEntry => EPIPHANY_CULTMESH_ROLE_REVIEW_EVENT_SCHEMA_VERSION, EpiphanyCultMeshWorkLoopTelemetryEntry => EPIPHANY_CULTMESH_WORK_LOOP_TELEMETRY_SCHEMA_VERSION, - EpiphanyCultMeshAgentStateSoaSummaryEntry => EPIPHANY_CULTMESH_AGENT_STATE_SOA_SUMMARY_SCHEMA_VERSION, EpiphanyCultMeshRepoWorkOverviewEntry => EPIPHANY_CULTMESH_REPO_WORK_OVERVIEW_SCHEMA_VERSION, EpiphanyCultMeshRepoWorkReadinessEntry => EPIPHANY_CULTMESH_REPO_WORK_READINESS_SCHEMA_VERSION, EpiphanyCultMeshRepoWorkMapEntry => EPIPHANY_CULTMESH_REPO_WORK_MAP_ENTRY_SCHEMA_VERSION, @@ -5939,136 +5839,6 @@ pub fn epiphany_local_verse_work_loop_summary( } } -pub fn epiphany_cultmesh_agent_state_soa_summary_from_entry( - runtime_id: impl Into, - summary_id: impl Into, - soa: &EpiphanyAgentStateSoaEntry, -) -> EpiphanyCultMeshAgentStateSoaSummaryEntry { - EpiphanyCultMeshAgentStateSoaSummaryEntry { - schema_version: EPIPHANY_CULTMESH_AGENT_STATE_SOA_SUMMARY_SCHEMA_VERSION.to_string(), - runtime_id: runtime_id.into(), - verse_id: EPIPHANY_CULTMESH_LOCAL_AREA_VERSE_ID.to_string(), - summary_id: summary_id.into(), - generated_at: soa.generated_at.clone(), - source_store: soa.source_store.clone(), - row_count: soa.role_ids.len() as u32, - role_ids: soa.role_ids.clone(), - agent_ids: soa.agent_ids.clone(), - display_names: soa.display_names.clone(), - profile_kinds: soa.profile_kinds.clone(), - portable_contracts: soa.portable_contracts.clone(), - semantic_memory_counts: soa.semantic_memory_counts.clone(), - episodic_memory_counts: soa.episodic_memory_counts.clone(), - relationship_memory_counts: soa.relationship_memory_counts.clone(), - goal_counts: soa.goal_counts.clone(), - value_counts: soa.value_counts.clone(), - private_state_exposed: false, - notes: vec![ - "Summary mirrors persisted epiphany.agent_state_soa.v0 column shape for local Verse discovery; agent memory text remains in the agent-memory store.".to_string(), - "CultMesh carries row/column topology for Odin, Eve, and prompt assembly without becoming the agent-memory owner.".to_string(), - ], - } -} - -pub fn write_epiphany_cultmesh_agent_state_soa_summary( - store_path: impl AsRef, - summary: EpiphanyCultMeshAgentStateSoaSummaryEntry, -) -> Result { - validate_agent_state_soa_summary(&summary)?; - let mut node = open_epiphany_cultmesh_node(&store_path, summary.runtime_id.clone())?; - let written = node.put(summary.summary_id.clone(), &summary)?; - let current_latest = node.get::( - EPIPHANY_CULTMESH_AGENT_STATE_SOA_SUMMARY_LATEST_KEY, - )?; - if current_latest.as_ref().is_none_or(|current| { - agent_state_soa_generation_key(&written) >= agent_state_soa_generation_key(current) - }) { - node.put( - EPIPHANY_CULTMESH_AGENT_STATE_SOA_SUMMARY_LATEST_KEY, - &written, - )?; - } - node.flush()?; - Ok(written) -} - -pub fn load_latest_epiphany_cultmesh_agent_state_soa_summary( - store_path: impl AsRef, - runtime_id: impl Into, -) -> Result> { - let node = open_epiphany_cultmesh_node(store_path, runtime_id)?; - node.get(EPIPHANY_CULTMESH_AGENT_STATE_SOA_SUMMARY_LATEST_KEY) -} - -fn validate_agent_state_soa_summary( - summary: &EpiphanyCultMeshAgentStateSoaSummaryEntry, -) -> Result<()> { - if summary.private_state_exposed { - return Err(anyhow!( - "agent state SoA summaries must not expose private state" - )); - } - if summary.schema_version != EPIPHANY_CULTMESH_AGENT_STATE_SOA_SUMMARY_SCHEMA_VERSION { - return Err(anyhow!( - "agent state SoA summary schema_version must be {:?}", - EPIPHANY_CULTMESH_AGENT_STATE_SOA_SUMMARY_SCHEMA_VERSION - )); - } - if summary.verse_id != EPIPHANY_CULTMESH_LOCAL_AREA_VERSE_ID { - return Err(anyhow!( - "agent state SoA summary belongs in the local-area Verse" - )); - } - DateTime::parse_from_rfc3339(&summary.generated_at) - .map_err(|error| anyhow!("agent state SoA summary has invalid generated_at: {error}"))?; - let len = summary.role_ids.len(); - if summary.row_count as usize != len { - return Err(anyhow!( - "agent state SoA summary row_count is {}, expected {}", - summary.row_count, - len - )); - } - for (name, candidate) in [ - ("agentIds", summary.agent_ids.len()), - ("displayNames", summary.display_names.len()), - ("profileKinds", summary.profile_kinds.len()), - ("portableContracts", summary.portable_contracts.len()), - ("semanticMemoryCounts", summary.semantic_memory_counts.len()), - ("episodicMemoryCounts", summary.episodic_memory_counts.len()), - ( - "relationshipMemoryCounts", - summary.relationship_memory_counts.len(), - ), - ("goalCounts", summary.goal_counts.len()), - ("valueCounts", summary.value_counts.len()), - ] { - if candidate != len { - return Err(anyhow!( - "agent state SoA summary column {name} has length {candidate}, expected {len}" - )); - } - } - if summary - .role_ids - .iter() - .any(|role_id| role_id.trim().is_empty()) - { - return Err(anyhow!("agent state SoA summary contains an empty role id")); - } - Ok(()) -} - -fn agent_state_soa_generation_key( - summary: &EpiphanyCultMeshAgentStateSoaSummaryEntry, -) -> (DateTime, &str) { - ( - DateTime::parse_from_rfc3339(&summary.generated_at) - .expect("validated agent state SoA generation timestamp"), - summary.summary_id.as_str(), - ) -} - pub fn load_latest_epiphany_cultmesh_repo_work_overview( store_path: impl AsRef, runtime_id: impl Into, @@ -6375,8 +6145,6 @@ pub fn query_epiphany_local_verse_context( )? .as_ref() .map(epiphany_local_verse_work_loop_summary), - latest_agent_state_soa_summary: node - .get(EPIPHANY_CULTMESH_AGENT_STATE_SOA_SUMMARY_LATEST_KEY)?, latest_repo_work_overview: node.get(EPIPHANY_CULTMESH_REPO_WORK_OVERVIEW_LATEST_KEY)?, latest_repo_work_map_entry: node.get(EPIPHANY_CULTMESH_REPO_WORK_MAP_ENTRY_LATEST_KEY)?, contract_summaries, @@ -9485,124 +9253,6 @@ mod tests { Ok(()) } - #[test] - fn agent_state_soa_summary_mirrors_agent_store_shape_without_memory_text() -> Result<()> { - let temp = tempfile::tempdir()?; - let store = temp.path().join("epiphany-local-verse.ccmp"); - let soa = EpiphanyAgentStateSoaEntry { - schema_version: "epiphany.agent_state_soa.v0".to_string(), - generated_at: "2026-06-18T00:00:00Z".to_string(), - source_store: "state/agents.msgpack".to_string(), - role_ids: vec!["Persona".to_string(), "implementation".to_string()], - agent_ids: vec!["epiphany.Persona".to_string(), "epiphany.hands".to_string()], - display_names: vec!["Persona".to_string(), "Hands".to_string()], - profile_kinds: vec!["Persona".to_string(), "WorkOrgan".to_string()], - portable_contracts: vec![ - "gamecult.persona_state.v0".to_string(), - "epiphany.work_organ_state.v0".to_string(), - ], - semantic_memory_counts: vec![3, 2], - episodic_memory_counts: vec![1, 0], - relationship_memory_counts: vec![2, 0], - goal_counts: vec![1, 1], - value_counts: vec![4, 3], - }; - let summary = epiphany_cultmesh_agent_state_soa_summary_from_entry( - "epiphany-test", - "agent-state-soa-summary-test", - &soa, - ); - write_epiphany_cultmesh_agent_state_soa_summary(&store, summary.clone())?; - assert_eq!( - load_latest_epiphany_cultmesh_agent_state_soa_summary(&store, "epiphany-test")?, - Some(summary.clone()) - ); - let context = query_epiphany_local_verse_context(&store, "epiphany-test")?; - assert_eq!( - context.latest_agent_state_soa_summary, - Some(summary.clone()) - ); - let node = open_epiphany_cultmesh_node(&store, "epiphany-test")?; - assert!( - node.documents() - .binding(EPIPHANY_CULTMESH_AGENT_STATE_SOA_SUMMARY_TYPE) - .is_some() - ); - let summary_table = node.soa::()?; - assert_eq!(summary_table.len(), 2); - assert!( - summary_table - .column::("summaryId")? - .values() - .contains(&"agent-state-soa-summary-test".to_string()) - ); - assert!( - summary_table - .column::("rowCount")? - .values() - .contains(&2) - ); - assert!( - summary_table - .column::("privateStateExposed")? - .values() - .iter() - .all(|exposed| !exposed) - ); - assert!( - summary_table - .column::>("portableContracts")? - .values() - .iter() - .any(|contracts| contracts.contains(&"gamecult.persona_state.v0".to_string())) - ); - let serialized = serde_json::to_string(&summary)?; - assert!(serialized.contains("gamecult.persona_state.v0")); - assert!(!serialized.contains("privateNotes")); - assert!(!summary.private_state_exposed); - - let mut newer = summary.clone(); - newer.summary_id = "agent-state-soa-summary-newer".to_string(); - newer.generated_at = "2026-06-18T01:00:00Z".to_string(); - write_epiphany_cultmesh_agent_state_soa_summary(&store, newer.clone())?; - - let mut delayed = summary.clone(); - delayed.summary_id = "agent-state-soa-summary-delayed".to_string(); - delayed.generated_at = "2026-06-17T23:00:00Z".to_string(); - write_epiphany_cultmesh_agent_state_soa_summary(&store, delayed)?; - assert_eq!( - load_latest_epiphany_cultmesh_agent_state_soa_summary(&store, "epiphany-test")?, - Some(newer) - ); - - let mut invalid_time = summary.clone(); - invalid_time.summary_id = "agent-state-soa-summary-invalid".to_string(); - invalid_time.generated_at = "not-a-time".to_string(); - let err = write_epiphany_cultmesh_agent_state_soa_summary(&store, invalid_time) - .expect_err("invalid agent summary time must be refused"); - assert!(err.to_string().contains("invalid generated_at")); - - let mut wrong_verse = summary.clone(); - wrong_verse.summary_id = "agent-state-soa-summary-wrong-verse".to_string(); - wrong_verse.verse_id = EPIPHANY_CULTMESH_INTERNAL_VERSE_ID.to_string(); - let err = write_epiphany_cultmesh_agent_state_soa_summary(&store, wrong_verse) - .expect_err("agent summary outside local-area Verse must be refused"); - assert!(err.to_string().contains("local-area Verse")); - - let mut leaked = summary.clone(); - leaked.private_state_exposed = true; - let err = write_epiphany_cultmesh_agent_state_soa_summary(&store, leaked) - .expect_err("private-state agent summaries must be refused"); - assert!(err.to_string().contains("must not expose private state")); - - let mut drifted = summary; - drifted.agent_ids.pop(); - let err = write_epiphany_cultmesh_agent_state_soa_summary(&store, drifted) - .expect_err("column length drift must be refused"); - assert!(err.to_string().contains("column agentIds has length")); - Ok(()) - } - #[test] fn builtin_verse_policies_keep_public_and_private_boundaries_apart() -> Result<()> { let temp = tempfile::tempdir()?; diff --git a/epiphany-core/src/lib.rs b/epiphany-core/src/lib.rs index 3756f42a..cfc21e2d 100644 --- a/epiphany-core/src/lib.rs +++ b/epiphany-core/src/lib.rs @@ -114,17 +114,12 @@ pub use agent_memory::AGENT_MEMORY_MIND_ADMISSION_TYPE; pub use agent_memory::AGENT_MEMORY_SWARM_IDENTITY_KEY; pub use agent_memory::AGENT_MEMORY_SWARM_IDENTITY_SCHEMA_VERSION; pub use agent_memory::AGENT_MEMORY_SWARM_IDENTITY_TYPE; -pub use agent_memory::AgentCanonicalTraitSeed; -pub use agent_memory::AgentMemoryBundle; pub use agent_memory::AgentMemoryGenerationWitness; -pub use agent_memory::AgentMemoryLifecycleAction; -pub use agent_memory::AgentMemoryLifecycleOperation; pub use agent_memory::AgentMemoryMindAdmissionReceipt; pub use agent_memory::AgentMemoryReview; pub use agent_memory::AgentMemorySwarmIdentity; pub use agent_memory::AgentSelfPatch; pub use agent_memory::EpiphanyAgentMemoryEntry; -pub use agent_memory::EpiphanyAgentStateSoaEntry; pub use agent_memory::EpiphanyOrganStateProfile; pub use agent_memory::EpiphanyOrganStateProfileKind; pub use agent_memory::GhostlightAgent; @@ -141,28 +136,15 @@ pub use agent_memory::GhostlightTraitVector; pub use agent_memory::GhostlightValue; pub use agent_memory::GhostlightWorld; pub use agent_memory::PERSONA_STATE_SCHEMA_VERSION; -pub use agent_memory::admit_legacy_agent_memory_generation; pub use agent_memory::agent_memory_role_ids; pub use agent_memory::agent_memory_semantic_projection_input; -pub use agent_memory::agent_memory_status; -pub use agent_memory::apply_agent_canonical_trait_seeds; -pub use agent_memory::apply_agent_memory_lifecycle_operation; pub use agent_memory::apply_agent_self_patch; pub use agent_memory::apply_agent_self_patch_document; pub use agent_memory::ensure_agent_memory_swarm_identity; pub use agent_memory::initialize_fresh_agent_memory_store; pub use agent_memory::load_agent_memory_entry_for_role; -pub use agent_memory::load_agent_memory_generation_witness; pub use agent_memory::load_agent_memory_swarm_identity; -pub use agent_memory::load_agent_state_soa_entry; -pub use agent_memory::migrate_agent_memory_json_dir_to_cultcache; pub use agent_memory::organ_state_profile_for_role; -pub use agent_memory::project_agent_memory_to_json_dir; -pub use agent_memory::project_persona_state_for_role; -pub use agent_memory::project_persona_state_from_entry; -pub use agent_memory::refresh_agent_state_soa; -pub use agent_memory::repair_agent_memory_store; -pub use agent_memory::review_agent_memory_lifecycle_operation; pub use agent_memory::review_agent_self_patch; pub use agent_memory::review_agent_self_patch_document; pub use agent_memory::validate_agent_memory_store; @@ -205,9 +187,6 @@ pub use cultcache_rs::RedbMessagePackBackingStore; pub use cultcache_rs::SingleFileMessagePackBackingStore; pub use cultmesh_integration::EPIPHANY_CANONICAL_SWARM_BRAKE_ID; pub use cultmesh_integration::EPIPHANY_CANONICAL_SWARM_BRAKE_OWNER; -pub use cultmesh_integration::EPIPHANY_CULTMESH_AGENT_STATE_SOA_SUMMARY_LATEST_KEY; -pub use cultmesh_integration::EPIPHANY_CULTMESH_AGENT_STATE_SOA_SUMMARY_SCHEMA_VERSION; -pub use cultmesh_integration::EPIPHANY_CULTMESH_AGENT_STATE_SOA_SUMMARY_TYPE; pub use cultmesh_integration::EPIPHANY_CULTMESH_BIFROST_ARTIFACT_ACCEPTANCE_RECEIPT_ARRIVAL_LATEST_KEY; pub use cultmesh_integration::EPIPHANY_CULTMESH_BIFROST_ARTIFACT_ACCEPTANCE_RECEIPT_SCHEMA_VERSION; pub use cultmesh_integration::EPIPHANY_CULTMESH_BIFROST_ARTIFACT_ACCEPTANCE_RECEIPT_TYPE; @@ -326,7 +305,6 @@ pub use cultmesh_integration::EPIPHANY_CULTMESH_WORK_LOOP_TELEMETRY_SCHEMA_VERSI pub use cultmesh_integration::EPIPHANY_CULTMESH_WORK_LOOP_TELEMETRY_TYPE; pub use cultmesh_integration::EPIPHANY_WORKSPACE_COVERAGE_PROJECTOR_DAEMON_ID; pub use cultmesh_integration::EPIPHANY_WORKSPACE_COVERAGE_PROJECTOR_SERVICE_ID; -pub use cultmesh_integration::EpiphanyCultMeshAgentStateSoaSummaryEntry; pub use cultmesh_integration::EpiphanyCultMeshBifrostArtifactAcceptanceReceiptEntry; pub use cultmesh_integration::EpiphanyCultMeshBifrostBodyChangePublicationIntentEntry; pub use cultmesh_integration::EpiphanyCultMeshBifrostBodyChangePublicationReceiptEntry; @@ -385,7 +363,6 @@ pub use cultmesh_integration::canonical_epiphany_swarm_brake_protected_surfaces; pub use cultmesh_integration::default_epiphany_cultmesh_swarm_brake; pub use cultmesh_integration::engage_epiphany_cultmesh_swarm_brake; pub use cultmesh_integration::epiphany_cluster_service_execution_audit_report; -pub use cultmesh_integration::epiphany_cultmesh_agent_state_soa_summary_from_entry; #[cfg(test)] pub use cultmesh_integration::epiphany_cultmesh_bifrost_artifact_acceptance_receipt_for_map_entry; pub use cultmesh_integration::epiphany_cultmesh_bifrost_body_change_publication_intent; @@ -456,7 +433,6 @@ pub use cultmesh_integration::load_epiphany_cultmesh_repo_work_readiness_reports pub use cultmesh_integration::load_epiphany_cultmesh_semantic_projection_health; pub use cultmesh_integration::load_epiphany_cultmesh_status; pub use cultmesh_integration::load_epiphany_cultmesh_swarm_brake; -pub use cultmesh_integration::load_latest_epiphany_cultmesh_agent_state_soa_summary; pub use cultmesh_integration::load_latest_epiphany_cultmesh_coordinator_run_receipt; pub use cultmesh_integration::load_latest_epiphany_cultmesh_daemon_heartbeat; pub use cultmesh_integration::load_latest_epiphany_cultmesh_daemon_poke_intent; @@ -487,7 +463,6 @@ pub use cultmesh_integration::release_epiphany_cultmesh_swarm_brake; pub use cultmesh_integration::retire_epiphany_cultmesh_legacy_provider_documents; pub use cultmesh_integration::retire_epiphany_cultmesh_operator_status_documents; pub use cultmesh_integration::seed_epiphany_local_verse_context; -pub use cultmesh_integration::write_epiphany_cultmesh_agent_state_soa_summary; #[cfg(test)] pub use cultmesh_integration::write_epiphany_cultmesh_bifrost_artifact_acceptance_receipt; pub use cultmesh_integration::write_epiphany_cultmesh_bifrost_body_change_publication_intent; diff --git a/epiphany-core/src/mind_documents.rs b/epiphany-core/src/mind_documents.rs index 740c35f2..ec6d291c 100644 --- a/epiphany-core/src/mind_documents.rs +++ b/epiphany-core/src/mind_documents.rs @@ -351,6 +351,7 @@ pub struct EpiphanyMindView { pub invariants: Vec, pub observations: Vec, pub evidence: Vec, + pub persona_memories: Vec, pub verification_audits: Vec, pub reorientation_decisions: Vec, pub reorientation_failures: Vec, @@ -588,6 +589,10 @@ pub fn assemble_mind_view(store_path: impl AsRef) -> Result(&cache, |value| value.value)?; let mut evidence = values::(&cache, |value| value.value)?; + let mut persona_memories = cache.get_all::()?; + for memory in &persona_memories { + memory.validate()?; + } let mut verification_audits = cache.get_all::()?; let mut reorientation_decisions = cache.get_all::()?; @@ -638,6 +643,7 @@ pub fn assemble_mind_view(store_path: impl AsRef) -> Result) -> Result()?; assert_eq!(first.len(), 1); - let memories = cache.get_all::()?; + let mut memories = cache.get_all::()?; + memories.sort_by(|left, right| left.memory_id.cmp(&right.memory_id)); assert_eq!(memories.len(), 2); assert!(memories.iter().all(|memory| { memory.agent_id == request.agent_id && memory.decision_context_id == document.decision_context_id })); + let view = crate::assemble_mind_view(&runtime)?; + assert_eq!(view.persona_memories, memories); + assert!(view.persona_memories.iter().all(|memory| { + view.source_documents.iter().any(|source| { + source.document_type == crate::EpiphanyMindPersonaMemoryDocument::TYPE + && source.document_key == memory.memory_id + }) + })); assert_eq!( admit_persona_state_notes( diff --git a/epiphany-core/src/persona_turn.rs b/epiphany-core/src/persona_turn.rs index bee322da..1cae4b5b 100644 --- a/epiphany-core/src/persona_turn.rs +++ b/epiphany-core/src/persona_turn.rs @@ -1,4 +1,4 @@ -use crate::EpiphanyAgentMemoryEntry; +use crate::EpiphanyMindPersonaMemoryDocument; use crate::EpiphanyOrganDependency; use crate::HeartbeatPendingMention; use crate::default_organ_dependencies_for; @@ -239,7 +239,7 @@ pub struct PersonaSocialAffordance { pub struct PersonaProjectorInput { pub identity: PersonaIdentity, #[serde(default)] - pub memory: Option, + pub memories: Vec, #[serde(default)] pub semantic_memory_recall: String, #[serde(default)] @@ -283,11 +283,7 @@ pub fn build_persona_projector_prompt_with_transcript( input: &PersonaProjectorInput, transcript: &[PersonaTranscriptMessage], ) -> String { - let memory = input - .memory - .as_ref() - .map(render_memory_packet) - .unwrap_or_else(|| "No durable Persona memory entry is loaded.".to_string()); + let memory = render_memory_packet(&input.memories); let dependencies = if input.organ_dependencies.is_empty() { vec![default_organ_dependencies_for("Persona")] } else { @@ -603,38 +599,20 @@ fn render_identity(identity: &PersonaIdentity) -> String { ) } -fn render_memory_packet(memory: &EpiphanyAgentMemoryEntry) -> String { - let values = memory - .agent - .canonical_state - .values +fn render_memory_packet(memories: &[EpiphanyMindPersonaMemoryDocument]) -> String { + if memories.is_empty() { + return "- no durable Persona memories".to_string(); + } + memories .iter() - .map(|value| format!("- {} ({:.2})", value.label, value.priority)) + .map(|memory| { + format!( + "- {} [{}; salience {:.2}; confidence {:.2}]", + memory.summary, memory.memory_kind, memory.salience, memory.confidence + ) + }) .collect::>() - .join("\n"); - let goals = memory - .agent - .goals - .iter() - .map(|goal| format!("- {} [{}]", goal.description, goal.status)) - .collect::>() - .join("\n"); - let notes = memory - .agent - .identity - .private_notes - .iter() - .map(|note| format!("- {note}")) - .collect::>() - .join("\n"); - format!( - "Identity: {}\nPublic description: {}\nPrivate notes:\n{}\nValues:\n{}\nGoals:\n{}", - memory.agent.identity.name, - memory.agent.identity.public_description, - fallback(¬es, "- none"), - fallback(&values, "- none"), - fallback(&goals, "- none"), - ) + .join("\n") } fn render_pending_mentions(mentions: &[HeartbeatPendingMention]) -> String { diff --git a/epiphany-core/src/prompt_context.rs b/epiphany-core/src/prompt_context.rs index c21b4187..48f652e3 100644 --- a/epiphany-core/src/prompt_context.rs +++ b/epiphany-core/src/prompt_context.rs @@ -103,53 +103,6 @@ pub fn render_epiphany_prompt_context(input: &EpiphanyPromptContextInput) -> Str ); } - if let Some(summary) = input.local_verse.latest_agent_state_soa_summary.as_ref() { - lines.push("## Agent State SoA".to_string()); - lines.push(format!( - "- Summary `{}`: rows={}, source={}, private_state_exposed={}", - summary.summary_id, - summary.row_count, - summary.source_store, - summary.private_state_exposed - )); - for index in 0..summary.role_ids.len().min(7) { - lines.push(format!( - "- `{}` / `{}`: profile={}, contract={}, semantic={}, episodic={}, relationship={}, goals={}, values={}", - summary.role_ids[index], - summary.agent_ids.get(index).cloned().unwrap_or_default(), - summary.profile_kinds.get(index).cloned().unwrap_or_default(), - summary - .portable_contracts - .get(index) - .cloned() - .unwrap_or_default(), - summary - .semantic_memory_counts - .get(index) - .copied() - .unwrap_or_default(), - summary - .episodic_memory_counts - .get(index) - .copied() - .unwrap_or_default(), - summary - .relationship_memory_counts - .get(index) - .copied() - .unwrap_or_default(), - summary.goal_counts.get(index).copied().unwrap_or_default(), - summary.value_counts.get(index).copied().unwrap_or_default() - )); - } - push_omitted_count( - &mut lines, - summary.role_ids.len(), - 7, - "agent state SoA rows", - ); - } - if let Some(intent) = input .local_verse .latest_daemon_tool_invocation_intent @@ -404,15 +357,12 @@ mod tests { use super::*; use crate::EPIPHANY_CULTMESH_INTERNAL_VERSE_ID; use crate::EPIPHANY_CULTMESH_WORK_LOOP_TELEMETRY_SCHEMA_VERSION; - use crate::EpiphanyAgentStateSoaEntry; use crate::EpiphanyCultMeshWorkLoopTelemetryEntry; - use crate::epiphany_cultmesh_agent_state_soa_summary_from_entry; use crate::epiphany_cultmesh_bifrost_body_change_publication_intent; use crate::epiphany_cultmesh_bifrost_body_change_publication_receipt_for_intent; use crate::epiphany_cultmesh_bifrost_github_publication_receipt_for_publication; use crate::query_epiphany_local_verse_context; use crate::seed_epiphany_local_verse_context; - use crate::write_epiphany_cultmesh_agent_state_soa_summary; use crate::write_epiphany_cultmesh_bifrost_body_change_publication_intent; use crate::write_epiphany_cultmesh_bifrost_body_change_publication_receipt; use crate::write_epiphany_cultmesh_bifrost_github_publication_receipt; @@ -520,30 +470,6 @@ mod tests { verification_assertions: vec!["prompt renders digest only".to_string()], }, )?; - let agent_state_soa = EpiphanyAgentStateSoaEntry { - schema_version: "epiphany.agent_state_soa.v0".to_string(), - generated_at: "2026-06-18T00:00:00Z".to_string(), - source_store: "state/agents.msgpack".to_string(), - role_ids: vec!["Persona".to_string(), "implementation".to_string()], - agent_ids: vec!["epiphany.Persona".to_string(), "epiphany.hands".to_string()], - display_names: vec!["Persona".to_string(), "Hands".to_string()], - profile_kinds: vec!["Persona".to_string(), "WorkOrgan".to_string()], - portable_contracts: vec![ - "gamecult.persona_state.v0".to_string(), - "epiphany.work_organ_state.v0".to_string(), - ], - semantic_memory_counts: vec![3, 2], - episodic_memory_counts: vec![1, 0], - relationship_memory_counts: vec![2, 0], - goal_counts: vec![1, 1], - value_counts: vec![4, 3], - }; - let agent_state_summary = epiphany_cultmesh_agent_state_soa_summary_from_entry( - "epiphany-test", - "agent-state-soa-summary-prompt-test", - &agent_state_soa, - ); - write_epiphany_cultmesh_agent_state_soa_summary(&store, agent_state_summary)?; let local_verse = query_epiphany_local_verse_context(&store, "epiphany-test")?; let memory_context = EpiphanyMemoryContextPacket { id: "memctx-test".to_string(), @@ -596,10 +522,6 @@ mod tests { assert!(rendered.contains("private_state_exposed=false")); assert!(!rendered.contains("inspectCompactSurface")); assert!(!rendered.contains("watchTypedReceipts")); - assert!(rendered.contains("Agent State SoA")); - assert!(rendered.contains("agent-state-soa-summary-prompt-test")); - assert!(rendered.contains("gamecult.persona_state.v0")); - assert!(rendered.contains("epiphany.work_organ_state.v0")); assert!(rendered.contains("Bifrost Publication Gate")); assert!(rendered.contains("bifrost-publication-intent-prompt-test")); assert!(rendered.contains("github_receipt=github-publication-prompt-test")); diff --git a/epiphany-core/src/resident_self.rs b/epiphany-core/src/resident_self.rs index b1a32f28..eae59419 100644 --- a/epiphany-core/src/resident_self.rs +++ b/epiphany-core/src/resident_self.rs @@ -805,206 +805,6 @@ pub fn resident_prepared_launch_thread_id(prepared: &ResidentSelfPreparedLaunch) prepared_coordinator_thread_id(&prepared.argv) } -#[cfg(test)] -fn reconcile_resident_self( - state: &mut ResidentSelfState, - policy: &ResidentSelfPolicy, - ports: &mut impl ResidentSelfPorts, - now_millis: u64, - wake: Option, -) -> Result<(ResidentSelfOutcome, ResidentSelfRuntimeReceipt)> { - policy.validate()?; - let receipt_revision = state.revision + 1; - let receipt = |status: &str, - reason: &str, - turn_id: Option, - coordinator_receipt_id: Option, - process_id: Option| { - ResidentSelfRuntimeReceipt { - schema_version: RESIDENT_SELF_RUNTIME_RECEIPT_SCHEMA_VERSION.into(), - receipt_id: format!("resident-self-{now_millis}-{receipt_revision}"), - occurred_at_millis: now_millis, - status: status.into(), - reason: reason.into(), - turn_id, - coordinator_receipt_id, - process_id, - private_state_exposed: false, - } - }; - if ports.brake_engaged()? && state.active_turn.is_none() { - return Ok(( - ResidentSelfOutcome::Braked, - receipt( - "braked", - "local Verse swarm brake is engaged", - state.active_turn.as_ref().map(|v| v.turn_id.clone()), - None, - state.active_turn.as_ref().map(|v| v.process_id), - ), - )); - } - if let Some(active) = state.active_turn.clone() { - if ports.brake_engaged()? { - match ports.observe_child(&active)? { - ChildObservation::Running => { - ports.request_child_stop(&active)?; - return Ok(( - ResidentSelfOutcome::Draining, - receipt( - "draining", - "local Verse brake is engaged; exact active coordinator stop requested and lease remains active until termination is proven", - Some(active.turn_id), - None, - Some(active.process_id), - ), - )); - } - ChildObservation::Exited(_) | ChildObservation::Missing => { - // Continue into terminal reconciliation. A brake does not erase receipt obligations. - } - } - } - match ports.observe_child(&active)? { - ChildObservation::Running => { - return Ok(( - ResidentSelfOutcome::Running, - receipt( - "running", - "bounded coordinator turn remains active", - Some(active.turn_id), - None, - Some(active.process_id), - ), - )); - } - ChildObservation::Exited(0) => { - if let Some(coordinator_receipt_id) = - ports.coordinator_receipt_since(&active.turn_id, active.started_at_millis)? - { - state.active_turn = None; - state.last_coordinator_receipt_id = Some(coordinator_receipt_id.clone()); - state.next_eligible_at_millis = - now_millis.saturating_add(policy.cooldown_seconds * 1000); - state.consecutive_failures = 0; - state.revision += 1; - return Ok(( - ResidentSelfOutcome::Completed, - receipt( - "completed", - "coordinator exited successfully with typed run receipt", - Some(active.turn_id), - Some(coordinator_receipt_id), - Some(active.process_id), - ), - )); - } - state.active_turn = None; - state.consecutive_failures += 1; - state.next_eligible_at_millis = - now_millis.saturating_add(policy.failure_backoff_seconds * 1000); - state.revision += 1; - return Ok(( - ResidentSelfOutcome::Failed, - receipt( - "failed", - "coordinator exited zero without a typed run receipt", - Some(active.turn_id), - None, - Some(active.process_id), - ), - )); - } - ChildObservation::Exited(code) => { - state.active_turn = None; - state.consecutive_failures += 1; - state.next_eligible_at_millis = - now_millis.saturating_add(policy.failure_backoff_seconds * 1000); - state.revision += 1; - return Ok(( - ResidentSelfOutcome::Failed, - receipt( - "failed", - &format!("coordinator exited with status {code}"), - Some(active.turn_id), - None, - Some(active.process_id), - ), - )); - } - ChildObservation::Missing => { - state.active_turn = None; - state.consecutive_failures += 1; - state.next_eligible_at_millis = - now_millis.saturating_add(policy.failure_backoff_seconds * 1000); - state.revision += 1; - return Ok(( - ResidentSelfOutcome::Failed, - receipt( - "failed", - "coordinator process disappeared without a typed run receipt", - Some(active.turn_id), - None, - Some(active.process_id), - ), - )); - } - } - } - if now_millis < state.next_eligible_at_millis || wake.is_none() { - return Ok(( - ResidentSelfOutcome::Sleeping, - receipt( - "sleeping", - if wake.is_none() { - "no explicit or existing idle wake is pending; proactive Body and feedback wake derivation is not implemented" - } else { - "cooldown or failure backoff remains active" - }, - None, - None, - None, - ), - )); - } - let wake = wake.expect("checked above"); - let turn_id = format!("resident-self-turn-{now_millis}-{}", state.revision + 1); - let launch = CoordinatorLaunch { - turn_id: turn_id.clone(), - wake: wake.clone(), - argv: coordinator_argv(policy, &policy.release_runtime_id, &turn_id, None, &wake), - }; - let process = ports.launch_coordinator(&launch)?; - let process_id = process.process_id; - state.active_turn = Some(ResidentSelfTurnLease { - turn_id: turn_id.clone(), - wake, - process_id, - process_creation_token: process.process_creation_token, - process_executable_path: process.process_executable_path, - started_at_millis: now_millis, - grant_id: String::new(), - launch_digest: String::new(), - policy_digest: String::new(), - argv_digest: String::new(), - objective_digest: String::new(), - release_commit: policy.release_commit.clone(), - release_manifest_digest: policy.release_manifest_digest.clone(), - coordinator_executable_digest: String::new(), - }); - state.revision += 1; - Ok(( - ResidentSelfOutcome::Launched, - receipt( - "launched", - "one bounded coordinator turn admitted", - Some(turn_id), - None, - Some(process_id), - ), - )) -} - fn state_cache(path: &Path) -> Result { let mut cache = CultCache::new(); cache.register_entry_type::()?; @@ -2565,26 +2365,6 @@ pub fn validate_resident_self_prepared_objective( )) } -#[cfg(test)] -fn complete_resident_self_turn( - path: &Path, - lease: &ResidentSelfTurnLease, - coordinator: &crate::EpiphanyCoordinatorRunReceipt, - now_millis: u64, - cooldown_seconds: u64, -) -> Result { - validate_resident_self_coordinator_receipt(lease, coordinator)?; - complete_resident_self_turn_with_terminal( - path, - lease, - &coordinator.receipt_id, - &coordinator.status, - now_millis, - cooldown_seconds, - false, - ) -} - pub fn validate_resident_self_coordinator_receipt( lease: &ResidentSelfTurnLease, coordinator: &crate::EpiphanyCoordinatorRunReceipt, diff --git a/epiphany-core/src/runtime_spine.rs b/epiphany-core/src/runtime_spine.rs index eb901367..36c4086a 100644 --- a/epiphany-core/src/runtime_spine.rs +++ b/epiphany-core/src/runtime_spine.rs @@ -11181,34 +11181,6 @@ pub fn runtime_soul_verdict_receipt( cache.get::(receipt_id) } -// Continuity recovery receipts are accepted-reorientation prerequisites and -// publish atomically with the coordinator state transition. Direct insertion -// is fixture-only. -#[cfg(test)] -pub(crate) fn put_continuity_recovery_receipt( - store_path: impl AsRef, - receipt: &ContinuityRecoveryReceipt, -) -> Result<()> { - validate_non_empty(&receipt.receipt_id, "Continuity recovery receipt id")?; - validate_non_empty( - &receipt.source_result_id, - "Continuity recovery source result", - )?; - validate_non_empty(&receipt.source_job_id, "Continuity recovery source job")?; - validate_non_empty(&receipt.binding_id, "Continuity recovery binding")?; - validate_non_empty(&receipt.mode, "Continuity recovery mode")?; - validate_non_empty( - &receipt.checkpoint_still_valid, - "Continuity recovery checkpoint validity", - )?; - validate_non_empty(&receipt.emitted_at, "Continuity recovery timestamp")?; - let mut cache = runtime_spine_cache(store_path)?; - cache.pull_all_backing_stores()?; - require_identity(&cache)?; - cache.put(&receipt.receipt_id, receipt)?; - Ok(()) -} - pub fn runtime_continuity_recovery_receipt( store_path: impl AsRef, receipt_id: &str, diff --git a/epiphany-core/src/surfaces/planning.rs b/epiphany-core/src/surfaces/planning.rs index 0869fa8f..e1485997 100644 --- a/epiphany-core/src/surfaces/planning.rs +++ b/epiphany-core/src/surfaces/planning.rs @@ -130,6 +130,7 @@ mod tests { invariants: Vec::new(), observations: Vec::new(), evidence: Vec::new(), + persona_memories: Vec::new(), verification_audits: Vec::new(), reorientation_decisions: Vec::new(), reorientation_failures: Vec::new(), diff --git a/epiphany-core/src/surfaces/scene.rs b/epiphany-core/src/surfaces/scene.rs index a3f3154f..9f48221d 100644 --- a/epiphany-core/src/surfaces/scene.rs +++ b/epiphany-core/src/surfaces/scene.rs @@ -426,6 +426,7 @@ mod tests { ], observations: Vec::new(), evidence: Vec::new(), + persona_memories: Vec::new(), verification_audits: Vec::new(), reorientation_decisions: Vec::new(), reorientation_failures: Vec::new(), diff --git a/epiphany-openai-runtime/src/bin/epiphany-persona-service.rs b/epiphany-openai-runtime/src/bin/epiphany-persona-service.rs index e6e515ba..30381010 100644 --- a/epiphany-openai-runtime/src/bin/epiphany-persona-service.rs +++ b/epiphany-openai-runtime/src/bin/epiphany-persona-service.rs @@ -3,13 +3,13 @@ use std::{env, net::SocketAddr, path::PathBuf, thread, time::Duration}; use anyhow::{Context, Result, anyhow}; use cultcache_rs::{CacheBackingStore, DatabaseEntry, SingleFileMessagePackBackingStore}; use epiphany_core::{ - EpiphanyAgentMemoryEntry, EpiphanyHeartbeatStateEntry, EpiphanyMindDocumentVersion, + EpiphanyHeartbeatStateEntry, EpiphanyMindDocumentVersion, EpiphanyMindPersonaMemoryDocument, EpiphanyMindPersonaPassInputDocument, PersonaIdentity, PersonaProjectorInput, PersonaRepoActivity, PersonaSocialAffordance, PersonaTranscriptMessage, PersonaTurnRequest, PersonaTurnTerminalOptions, admit_persona_pass_input, assemble_mind_view, complete_persona_turn_request_store, default_organ_dependencies_for, exchange_persona_discord_delivery_rudp, load_admitted_persona_pass_input, - load_agent_memory_entry_for_role, load_epiphany_cultmesh_swarm_brake, + load_epiphany_cultmesh_swarm_brake, load_heartbeat_state_entry, load_persona_discord_receipt_anchor, load_persona_discord_service_anchor, open_persona_discord_request_identity, pending_persona_discord_delivery_request_for_turn, persona_delivery_receipt_exists_for_turn, @@ -186,9 +186,13 @@ fn ensure_persona_pass_input_admitted( return Ok(()); } - let memory = load_agent_memory_entry_for_role(&options.agent_store, &request.role_id) - .ok() - .flatten(); + let mind = assemble_mind_view(&options.runtime_store)?; + let memories = mind + .persona_memories + .iter() + .filter(|memory| memory.agent_id == request.agent_id) + .cloned() + .collect::>(); let identity = PersonaIdentity { identity_id: request.agent_id.clone(), display_name: options.persona_name.clone(), @@ -227,7 +231,6 @@ fn ensure_persona_pass_input_admitted( recent_message_ids: vec![mention.message_id.clone()], }) .collect(); - let mind = assemble_mind_view(&options.runtime_store)?; let repo_activity = mind .repository_body_observation .as_ref() @@ -241,10 +244,9 @@ fn ensure_persona_pass_input_admitted( }) .into_iter() .collect(); - let has_memory = memory.is_some(); let projector_input = PersonaProjectorInput { identity, - memory, + memories: memories.clone(), semantic_memory_recall: semantic_recall.clone(), pending_mentions: request.mentions.clone(), repo_activity, @@ -259,18 +261,16 @@ fn ensure_persona_pass_input_admitted( let heartbeat_source = EpiphanyMindDocumentVersion::from_envelope("epiphany-heartbeat", &heartbeat_envelope)?; let mut observed_sources = vec![heartbeat_source.clone()]; - if has_memory { - let agent_envelope = SingleFileMessagePackBackingStore::new(&options.agent_store) - .pull_all()? - .into_iter() - .find(|entry| { - entry.r#type == EpiphanyAgentMemoryEntry::TYPE && entry.key == request.role_id + for memory in &memories { + let source = mind + .source_documents + .iter() + .find(|source| { + source.document_type == EpiphanyMindPersonaMemoryDocument::TYPE + && source.document_key == memory.memory_id }) - .ok_or_else(|| anyhow!("Persona pass input lost its agent-memory source"))?; - observed_sources.push(EpiphanyMindDocumentVersion::from_envelope( - "epiphany-agent-memory", - &agent_envelope, - )?); + .ok_or_else(|| anyhow!("Persona memory is absent from the canonical Mind basis"))?; + observed_sources.push(source.clone()); } if let Some(source) = mind.source_documents.iter().find(|source| { source.document_type == epiphany_core::EpiphanyMindRepositoryBodyObservationDocument::TYPE @@ -303,7 +303,6 @@ fn ensure_persona_pass_input_admitted( struct Options { runtime_store: PathBuf, heartbeat_store: PathBuf, - agent_store: PathBuf, cultmesh_store: PathBuf, codex_home: PathBuf, provider_credential_path: Option, @@ -351,7 +350,6 @@ impl Options { Ok(Self { runtime_store: path("--runtime-store")?, heartbeat_store: path("--heartbeat-store")?, - agent_store: path("--agent-store")?, cultmesh_store: path("--cultmesh-store")?, codex_home: path("--codex-home")?, provider_credential_path: values.get("--provider-credential").map(PathBuf::from), @@ -452,7 +450,6 @@ mod tests { let options = Options { runtime_store: runtime_store.clone(), heartbeat_store: absent.join("heartbeat.cc"), - agent_store: absent.join("agent.cc"), cultmesh_store: absent.join("cultmesh.cc"), codex_home: absent.join("codex-home"), provider_credential_path: None, diff --git a/notes/fresh-workspace-handoff.md b/notes/fresh-workspace-handoff.md index d5eb71ce..ceef3803 100644 --- a/notes/fresh-workspace-handoff.md +++ b/notes/fresh-workspace-handoff.md @@ -455,7 +455,7 @@ the builder target root to 4 KiB. Exact `1d5a1f17` landed the first causal cut: - core library tests remain 494/494 and Hands remains 5/5 under the sole release package owner. -The second verified cut is not yet committed. It deletes the +Exact `387afe49` is the second verified cut. It deletes the `runtime_store_migration` module/test, all 29 remaining unshipped core executables, the shipped `epiphany-verse-query` parallel control plane, the local PowerShell launcher, and its orphan Rider bridge. `epiphany-core` now @@ -463,9 +463,18 @@ owns only `epiphany-prepare-compaction`; the release bundle owns 25 runtime executables. Focused core, supervisor, and release-construction tests pass 493/493, 32/32, and 21/21. -Next, split the few live Persona/heartbeat inputs from `agent_memory`, then -delete its aggregate identity, generation, SoA, migration/repair, and generic -selfPatch authorities with the tests that preserve them. Do not run a +The current third cut deletes the agent-memory migration, repair, lifecycle, +trait-seed, and SoA authorities with the tests that preserved them. It also +deletes the second CultMesh SoA mirror, its timestamp-selected latest head, and +its prompt injection. Persona now reads learned memory only from keyed +`EpiphanyMindPersonaMemoryDocument` rows in the assembled Mind view; those +documents participate in the exact projection digest and source set. Dead +test-only alternate Resident Self and direct Continuity writers are also gone. +The final core library passes 483/483 and Persona service re-entry passes 1/1. + +Next delete the global Mind semantic partition and heartbeat +personality/rumination selfPatch consumers, then delete the aggregate +agent-memory identity/generation store they alone preserve. Do not run a full-workspace compile, package, deployment, or Ox root until the subtraction audit closes. Never resume Ox10, Ox12, Ox13, Ox15, or Ox16. diff --git a/state/ledgers.msgpack b/state/ledgers.msgpack index 71adc387..8c1baa8f 100644 Binary files a/state/ledgers.msgpack and b/state/ledgers.msgpack differ diff --git a/state/map.yaml b/state/map.yaml index 068ecc34..433baac8 100644 --- a/state/map.yaml +++ b/state/map.yaml @@ -48,9 +48,9 @@ current_status: package_baseline: Exact d2ca66301fb6af4e7d2d27fff0b772b0f0fccdf4 remains the admitted production source and current symlink target. Its native serialized full-workspace gate passed with receipt SHA-256 de0fc6b360ce03493b13208d917dc8349801f03364617d966152c85846c47482. Idunn sealed 26 root-owned non-writable binaries plus release-witness.ccmp as release sha256-46407552b4a0937f63d2b7f2bd09a1dacb89d671a6e3807c97209159541aef06; witness-file SHA-256 is 348785ffb0fc3130d3b4538329870c6e6f8442a8da4a01b32fa9b7ffb1f01357. Source 9b9b5c85 also passed Idunn's native test gate under receipt SHA-256 6c6a71359f8c31297419665d2872f6982a89f84e197d71fc5b36a7fc86216093 and remains sealed as release sha256-30a910785a380224bbdbd56a7b742c8d058fa70cb72dab3562e3907903e0d191 with witness-file SHA-256 75bf52d31c56ea3b08e1a702273301c1db12a4b1ffae45e3d948ce8a29b50e1d. A foreign-target helper restarted Idunn during the Epiphany transaction; rollback restored d2ca, removed deployment.env, and left production Epiphany units inactive. OpenRouter model stealth/ox-alpha and its systemd-injected credential are readiness-proven. Idunn is active with zero restarts and its deployment brake is engaged. summary: The keyed RepoModel hard cut, decision-context foundation, Model Atlas code slice, complete Body, Proposal, frontier-verdict Modeling, frontier Research, frontier Verification, frontier Planning/PlanMind, Reorientation, and Persona decision-context lifecycles are landed on codex/epiphany-shakedown-live. Keyed Mind plus exact runtime receipts own coordinator presence, work discovery, deterministic launch, job-bound continuation, review, typed failure, semantic admission, and retained decision reachability. Exact 07d891ba and ec1431ff delete the persisted/global thread aggregate and its generic coordinator/prompt/revision surfaces. Exact f7948795 deletes MindGatewayReview, MindStateCommitReceipt v0, the generic interpreter prompt, runtime registration/read APIs, false thought/state-effect/adoption CultNet mouths, and stale launch proof profiles. CultMesh now projects only reasoning basis, decision context, and exact Mind commit receipt, read-only. Exact 1c9aafd8 seals the terminal decision context before model-backed frontier-Planning failure and binds the typed failure plus generic job result to it. Exact e0e75a30 deletes the residual aggregate role patch/parser/policy surface, gives Research one closed typed decision, and deletes the unowned generic Imagination planning patch. Exact 12b1b285 makes runtime model/tool binding validation the shared owner for execution and decision sealing; hostile provider, receipt, and stored-binding substitutions refuse, while retained basis/context audit survives transcript-delta deletion. Exact 01602fd3 advances Mind/runtime to v2/v1 and refuses historical or split writable identity pairs before registered writers see them. Exact 26b6a5bf proves simultaneous disjoint Modeling writes, simultaneous Verification+Modeling admission, same-identity conflict, exact replay, and reopened graph assembly; raw mutation primitives are crate-private. Exact 7c2ebd81 moves Persona state-note admission from the legacy agent-memory aggregate into keyed Mind documents and proves a concrete Hands consequence can commit simultaneously. Exact 79c0e373 seals the assembled Persona input and observed source versions in Mind before inference. Exact d3300bba authenticates that input through its one exact Mind commit, refuses naked typed documents, re-enters without external observation or model inference, and proves current-work reconstruction is identical and read-only across Launch, Wait, completed, and post-Research boundaries. No compatibility reader, bootstrap aggregate, schema migrator, or dual reader survives. The source-level Decision-Auditable Concurrent Mind migration is accepted; the fresh exact-package capstone remains open. The machine remains a supervised engineering alpha; Model Atlas operational Gate 1 stays paused. public_positioning: docs/positioning.md is the current public claim owner. Epiphany is the organizational layer above frontier AI workers; global coherence collapse is the primary failure; prompts are bounded projections of governed organizational state; humans retain purpose, judgment, authority, disagreement, exceptions, and acceptance. GameCult is the forge and longitudinal evidence program for a target federation of one locally owned Epiphany project organism per living project Body. Model Atlas carries typed offers, claims, evidence, and derived technical relationships; Bifrost is the intended external work, membership, governance, attribution, and identity-linked receipt rail. Public Persona relationships and visitor surfaces are intended social infrastructure, while private Mind and consequence authority remain local. The fleet, real three-repository Gate 1, complete Bifrost attribution, sustained autonomous production, visitor experience, and external design-partner pilots remain gated alpha targets. - latest_phase: The fresh-package Ox17 lane remains paused for source subtraction. Exact 1d5a1f17 removed 19,252 net non-vendor lines, 21 duplicate executable owners, 19 main-only smoke binaries, and the unshipped repo-personality birth path. The next verified cut deletes the forbidden runtime-store migration module/test, all 29 remaining unshipped core executables, the shipped 7,814-line verse-query parallel control plane, the 2,244-line local PowerShell launcher, and its orphan Rider bridge. epiphany-core now owns only the explicit prepare-compaction maintenance binary; the release bundle owns 25 runtime executables. Focused acceptance passes: core library 493/493, daemon supervisor 32/32, and release construction 21/21. Smoke-only feature exports and supervisor commands that pointed to deleted bodies are gone. The next cut is the surviving agent_memory aggregate and selfPatch/SoA/migration tests against keyed Mind Persona documents. No Idunn build, package, deployment, or Ox root is authorized yet. Ox16 remains sealed historical evidence and must never resume. + latest_phase: The fresh-package Ox17 lane remains paused for source subtraction. Exact 1d5a1f17 removed 19,252 net non-vendor lines and exact 387afe49 removed a further 25,317 by deleting parallel executable control planes. The current third cut removes another 2,600-plus net lines: obsolete agent-memory migration/repair/lifecycle/trait-seed/SoA authorities and their tests, the CultMesh SoA mirror and timestamp-selected latest head, its prompt injection, and dead test-only alternate Resident Self/Continuity writers. Persona now consumes keyed EpiphanyMindPersonaMemoryDocument rows from the exact assembled Mind projection; Persona documents participate in the projection digest and exact source set. The final core library suite passes 483/483, and the Persona service re-entry check passes 1/1. Next delete the global Mind semantic partition, heartbeat personality/rumination selfPatch inputs, and the aggregate agent-memory identity/generation store they alone preserve. No Idunn build, package, deployment, or Ox root is authorized yet. Ox16 remains sealed historical evidence and must never resume. operational_state: - local_worktree: source_head_1d5a1f17_pushed_second_subtraction_cut_verified_uncommitted_ox17_and_deployment_paused + local_worktree: source_head_387afe49_pushed_third_subtraction_cut_keyed_persona_memory_verified_uncommitted_ox17_and_deployment_paused docker_engine_access: starfire_compiler_retired_all_yggdrasil_epiphany_transient_containers_volumes_package_roots_and_per_run_test_target_removed_future_idunn_epiphany_builds_native_rust_1_95_0_via_gamecult_ops_89f0d78 operator_wireguard: restored starfire_persona_permit_firewall: persistent_exact_gamecult_ops_6ee08a4 diff --git a/state/scratch.md b/state/scratch.md index 8eaf587f..28387ebd 100644 --- a/state/scratch.md +++ b/state/scratch.md @@ -51,9 +51,12 @@ and janitors may reduce the symptom but cannot substitute for deleting code. ## Immediate audit order -1. Map `agent_memory` and every remaining aggregate consumer against keyed - Mind. Delete memories, social state, migrations, projections, and tests whose - only purpose is to preserve the parallel aggregate. +1. In progress: `agent_memory` migration/repair/lifecycle/trait-seed/SoA code + and its tests are gone. The CultMesh SoA mirror, timestamp-selected latest + head, and prompt injection are gone. Persona learned memory now comes from + exact keyed Mind Persona documents. Next cut the global Mind semantic + partition and heartbeat personality/rumination selfPatch consumers, then + delete the aggregate identity/generation store they preserve. 2. Done: delete `runtime_store_migration`, its receipt registration, and its test. The approved migration has no writable migrator. 3. Done: delete the local PowerShell operator wrapper, direct Persona/operator