//! Alias management for local Episteme operations. //! //! Handles auto-alias creation and manual alias management. use stemedb_core::types::{AliasOrigin, ConceptAlias, ConceptPath}; use stemedb_storage::AliasStore; use tracing::{debug, instrument}; use crate::AphoriaError; use super::corpus::current_timestamp; use super::local::LocalEpisteme; impl LocalEpisteme { /// Create an alias from a code path to an authoritative path, if it doesn't already exist. /// /// This is used during conflict detection to persist the relationship between /// code concepts and their authoritative counterparts. #[instrument(skip(self), fields(code_path = %code_path, auth_path = %auth_path))] pub async fn create_alias_if_new( &self, code_path: &str, auth_path: &str, agent_id: [u8; 32], timestamp: u64, ) -> Result<(), AphoriaError> { // Check if alias already exists let existing = self.alias_store().get_canonical(code_path).await.map_err(|e| { AphoriaError::Storage(format!("Failed to get canonical alias for {code_path}: {e}")) })?; if existing.is_some() { debug!("Alias already exists, skipping"); return Ok(()); } // Parse paths let alias_path = ConceptPath::parse(code_path) .map_err(|e| AphoriaError::Storage(format!("Invalid code path: {}", e)))?; let canonical_path = ConceptPath::parse(auth_path) .map_err(|e| AphoriaError::Storage(format!("Invalid auth path: {}", e)))?; // Create and persist alias let alias = ConceptAlias::new( alias_path, canonical_path, agent_id, timestamp, AliasOrigin::AutoDetected, ); self.alias_store().set_alias(&alias).await.map_err(|e| { AphoriaError::Storage(format!( "Failed to set alias from {code_path} to {auth_path}: {e}" )) })?; debug!("Created auto-detected alias"); Ok(()) } /// Fetch manual aliases for policy export. /// /// Returns all aliases stored in the local Episteme instance. /// These can be auto-detected aliases from conflict detection or /// manually created aliases. pub async fn fetch_manual_aliases(&self) -> Result, AphoriaError> { let alias_tuples = self .alias_store() .list_all_aliases() .await .map_err(|e| AphoriaError::Storage(format!("Failed to list all aliases: {e}")))?; let timestamp = current_timestamp(); let agent_id = self.agent_id(); // Convert (alias_str, canonical_str) tuples to ConceptAlias structs let aliases = alias_tuples .into_iter() .filter_map(|(alias_str, canonical_str)| { let alias_path = ConceptPath::parse(&alias_str).ok()?; let canonical_path = ConceptPath::parse(&canonical_str).ok()?; Some(ConceptAlias::new( alias_path, canonical_path, agent_id, timestamp, AliasOrigin::Manual, // Treat all exported aliases as manual )) }) .collect(); Ok(aliases) } }