use serde::{Deserialize, Serialize}; use chrono::{DateTime, Utc}; use uuid::Uuid; /// Immutable, append-only audit log entry. /// Every command execution gets logged — no exceptions (BMad must-have). #[derive(Debug, Clone, Serialize, Deserialize)] pub struct AuditEntry { pub id: String, pub tenant_id: String, pub runbook_id: String, pub step_index: usize, pub command: String, pub safety_level: String, pub approved_by: Option, pub approval_method: Option, // "slack_button", "api", "auto" (read-only only) pub exit_code: Option, pub stdout_hash: Option, // SHA-256 of stdout (don't store raw output) pub stderr_hash: Option, pub started_at: DateTime, pub completed_at: Option>, pub duration_ms: Option, pub status: AuditStatus, } #[derive(Debug, Clone, Serialize, Deserialize)] pub enum AuditStatus { Pending, AwaitingApproval, Approved, Rejected, Executing, Completed, Failed, TimedOut, } impl AuditEntry { pub fn new(tenant_id: &str, runbook_id: &str, step_index: usize, command: &str, safety_level: &str) -> Self { Self { id: Uuid::new_v4().to_string(), tenant_id: tenant_id.to_string(), runbook_id: runbook_id.to_string(), step_index, command: command.to_string(), safety_level: safety_level.to_string(), approved_by: None, approval_method: None, exit_code: None, stdout_hash: None, stderr_hash: None, started_at: Utc::now(), completed_at: None, duration_ms: None, status: AuditStatus::Pending, } } }