//! Baseline and diff operations for tracking changes over time. use crate::config::AphoriaConfig; use crate::error::AphoriaError; use crate::scan::{generate_scan_id, run_scan}; use crate::types::{ScanArgs, ScanMode, Verdict}; use tracing::{info, instrument}; /// Set the current scan as the baseline. /// /// Future `aphoria diff` commands will compare against this baseline. #[instrument(skip(_config))] pub async fn set_baseline(_config: &AphoriaConfig) -> Result<(), AphoriaError> { info!("Setting baseline"); let project_root = std::env::current_dir()?; let aphoria_dir = project_root.join(".aphoria"); std::fs::create_dir_all(&aphoria_dir)?; // Record the current scan ID as baseline let scan_id = generate_scan_id(); std::fs::write(aphoria_dir.join("baseline"), &scan_id)?; info!(scan_id, "Baseline set"); Ok(()) } /// Show changes since the last baseline. #[instrument(skip(config))] pub async fn show_diff(config: &AphoriaConfig) -> Result { info!("Showing diff"); let project_root = std::env::current_dir()?; let baseline_path = project_root.join(".aphoria").join("baseline"); if !baseline_path.exists() { return Err(AphoriaError::NoBaseline); } // For now, just run a scan and compare against baseline // Full diff implementation would track assertion hashes // Diff needs persistent mode to access stored claims let args = ScanArgs { path: project_root, format: "table".to_string(), exit_code_enabled: false, mode: ScanMode::Persistent, debug: false, sync: false, // Diff does not write observations file_source: crate::types::FileSource::All, benchmark: false, show_claims: false, strict: false, show_observations: false, }; let result = run_scan(args, config).await?; let mut output = String::new(); output.push_str("Changes since baseline:\n\n"); output.push_str(&format!( " {} conflicts ({} BLOCK, {} FLAG)\n", result.conflicts.len(), result.count_by_verdict(Verdict::Block), result.count_by_verdict(Verdict::Flag), )); Ok(output) }