use std::fmt;
use std::str::FromStr;
fn normalized_certificate_token(input: &str) -> String {
let trimmed = input.trim();
let mut normalized = String::with_capacity(trimmed.len());
let mut previous_was_lowercase = false;
let mut previous_was_separator = false;
for character in trimmed.chars() {
match character {
'-' | '_' => {
if !normalized.is_empty() && !previous_was_separator {
normalized.push('_');
}
previous_was_lowercase = false;
previous_was_separator = true;
}
character if character.is_ascii_uppercase() => {
if previous_was_lowercase && !previous_was_separator {
normalized.push('_');
}
normalized.push(character.to_ascii_lowercase());
previous_was_lowercase = false;
previous_was_separator = false;
}
character => {
normalized.push(character.to_ascii_lowercase());
previous_was_lowercase = character.is_ascii_lowercase();
previous_was_separator = false;
}
}
}
normalized
}
pub const CERTIFICATE_SCHEMA_V1: &str = "ee.certificate.v1";
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum CertificateKind {
Pack,
Curation,
TailRisk,
PrivacyBudget,
Lifecycle,
}
impl CertificateKind {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::Pack => "pack",
Self::Curation => "curation",
Self::TailRisk => "tail_risk",
Self::PrivacyBudget => "privacy_budget",
Self::Lifecycle => "lifecycle",
}
}
#[must_use]
pub const fn all() -> [Self; 5] {
[
Self::Pack,
Self::Curation,
Self::TailRisk,
Self::PrivacyBudget,
Self::Lifecycle,
]
}
}
impl fmt::Display for CertificateKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ParseCertificateKindError {
input: String,
}
impl ParseCertificateKindError {
pub fn input(&self) -> &str {
&self.input
}
}
impl fmt::Display for ParseCertificateKindError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"unknown certificate kind `{}`; expected one of pack, curation, tail_risk, privacy_budget, lifecycle",
self.input
)
}
}
impl std::error::Error for ParseCertificateKindError {}
impl FromStr for CertificateKind {
type Err = ParseCertificateKindError;
fn from_str(input: &str) -> Result<Self, Self::Err> {
match normalized_certificate_token(input).as_str() {
"pack" => Ok(Self::Pack),
"curation" => Ok(Self::Curation),
"tail_risk" => Ok(Self::TailRisk),
"privacy_budget" => Ok(Self::PrivacyBudget),
"lifecycle" => Ok(Self::Lifecycle),
_ => Err(ParseCertificateKindError {
input: input.to_owned(),
}),
}
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum CertificateStatus {
Valid,
Pending,
Invalid,
Expired,
Revoked,
}
impl CertificateStatus {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::Valid => "valid",
Self::Pending => "pending",
Self::Invalid => "invalid",
Self::Expired => "expired",
Self::Revoked => "revoked",
}
}
#[must_use]
pub const fn all() -> [Self; 5] {
[
Self::Valid,
Self::Pending,
Self::Invalid,
Self::Expired,
Self::Revoked,
]
}
#[must_use]
pub const fn is_terminal(self) -> bool {
matches!(self, Self::Invalid | Self::Expired | Self::Revoked)
}
#[must_use]
pub const fn is_usable(self) -> bool {
matches!(self, Self::Valid)
}
}
impl fmt::Display for CertificateStatus {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ParseCertificateStatusError {
input: String,
}
impl ParseCertificateStatusError {
pub fn input(&self) -> &str {
&self.input
}
}
impl fmt::Display for ParseCertificateStatusError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"unknown certificate status `{}`; expected one of valid, pending, invalid, expired, revoked",
self.input
)
}
}
impl std::error::Error for ParseCertificateStatusError {}
impl FromStr for CertificateStatus {
type Err = ParseCertificateStatusError;
fn from_str(input: &str) -> Result<Self, Self::Err> {
match normalized_certificate_token(input).as_str() {
"valid" => Ok(Self::Valid),
"pending" => Ok(Self::Pending),
"invalid" => Ok(Self::Invalid),
"expired" => Ok(Self::Expired),
"revoked" => Ok(Self::Revoked),
_ => Err(ParseCertificateStatusError {
input: input.to_owned(),
}),
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct PackCertificate {
pub pack_hash: String,
pub query: String,
pub budget_used: u32,
pub budget_limit: u32,
pub item_count: u32,
pub omitted_count: u32,
pub quotas_satisfied: bool,
pub redundancy_applied: bool,
pub provenance_complete: bool,
}
impl PackCertificate {
#[must_use]
pub const fn is_valid(&self) -> bool {
self.budget_used <= self.budget_limit && self.quotas_satisfied && self.provenance_complete
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct CurationCertificate {
pub candidate_id: String,
pub action_type: String,
pub confidence: f64,
pub threshold: f64,
pub evidence_count: u32,
pub feedback_count: u32,
pub requires_review: bool,
pub reversible: bool,
}
impl CurationCertificate {
#[must_use]
pub fn meets_threshold(&self) -> bool {
self.confidence >= self.threshold
}
#[must_use]
pub const fn has_evidence(&self) -> bool {
self.evidence_count > 0 || self.feedback_count > 0
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct TailRiskCertificate {
pub metric: String,
pub observed: f64,
pub threshold: f64,
pub confidence_level: f64,
pub upper_bound: f64,
pub exceeds_bounds: bool,
pub recommended_action: Option<String>,
}
impl TailRiskCertificate {
#[must_use]
pub const fn is_acceptable(&self) -> bool {
!self.exceeds_bounds
}
#[must_use]
pub fn margin(&self) -> f64 {
self.threshold - self.observed
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct PrivacyBudgetCertificate {
pub category: String,
pub consumed: f64,
pub total_consumed: f64,
pub budget_limit: f64,
pub remaining: f64,
pub operation_allowed: bool,
pub resets_at: Option<String>,
}
impl PrivacyBudgetCertificate {
#[must_use]
pub fn is_exhausted(&self) -> bool {
self.remaining <= 0.0
}
#[must_use]
pub fn utilization(&self) -> f64 {
if self.budget_limit > 0.0 {
self.total_consumed / self.budget_limit
} else {
0.0
}
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum ShareableAggregateKind {
Count,
Sum,
Mean,
Median,
StdDev,
Histogram,
Percentile,
TopK,
}
impl ShareableAggregateKind {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::Count => "count",
Self::Sum => "sum",
Self::Mean => "mean",
Self::Median => "median",
Self::StdDev => "std_dev",
Self::Histogram => "histogram",
Self::Percentile => "percentile",
Self::TopK => "top_k",
}
}
#[must_use]
pub const fn all() -> [Self; 8] {
[
Self::Count,
Self::Sum,
Self::Mean,
Self::Median,
Self::StdDev,
Self::Histogram,
Self::Percentile,
Self::TopK,
]
}
#[must_use]
pub const fn sensitivity_class(self) -> &'static str {
match self {
Self::Count => "bounded",
Self::Sum | Self::Mean => "unbounded",
Self::Median | Self::Percentile => "bounded",
Self::StdDev => "unbounded",
Self::Histogram => "bounded",
Self::TopK => "k_anonymous",
}
}
}
impl fmt::Display for ShareableAggregateKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ParseShareableAggregateKindError {
input: String,
}
impl ParseShareableAggregateKindError {
pub fn input(&self) -> &str {
&self.input
}
}
impl fmt::Display for ParseShareableAggregateKindError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"unknown shareable aggregate kind `{}`; expected one of count, sum, mean, median, std_dev, histogram, percentile, top_k",
self.input
)
}
}
impl std::error::Error for ParseShareableAggregateKindError {}
impl FromStr for ShareableAggregateKind {
type Err = ParseShareableAggregateKindError;
fn from_str(input: &str) -> Result<Self, Self::Err> {
match normalized_certificate_token(input).as_str() {
"count" => Ok(Self::Count),
"sum" => Ok(Self::Sum),
"mean" => Ok(Self::Mean),
"median" => Ok(Self::Median),
"std_dev" => Ok(Self::StdDev),
"histogram" => Ok(Self::Histogram),
"percentile" => Ok(Self::Percentile),
"top_k" => Ok(Self::TopK),
_ => Err(ParseShareableAggregateKindError {
input: input.to_owned(),
}),
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct PrivacyBudgetShareConstraint {
pub k_anonymity_threshold: u32,
pub max_epsilon: f64,
pub max_delta: f64,
pub noise_mechanism: String,
pub min_sample_size: u32,
}
impl PrivacyBudgetShareConstraint {
#[must_use]
pub fn default_safe() -> Self {
Self {
k_anonymity_threshold: 5,
max_epsilon: 1.0,
max_delta: 1e-5,
noise_mechanism: "laplace".to_string(),
min_sample_size: 10,
}
}
#[must_use]
pub fn strict() -> Self {
Self {
k_anonymity_threshold: 10,
max_epsilon: 0.1,
max_delta: 1e-7,
noise_mechanism: "gaussian".to_string(),
min_sample_size: 50,
}
}
#[must_use]
pub fn epsilon_valid(&self, epsilon: f64) -> bool {
epsilon > 0.0 && epsilon <= self.max_epsilon
}
#[must_use]
pub fn delta_valid(&self, delta: f64) -> bool {
delta > 0.0 && delta <= self.max_delta
}
}
impl Default for PrivacyBudgetShareConstraint {
fn default() -> Self {
Self::default_safe()
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct ShareableAggregateReport {
pub report_id: String,
pub aggregate_kind: ShareableAggregateKind,
pub value: f64,
pub sample_size: u32,
pub epsilon_consumed: f64,
pub delta_consumed: f64,
pub noise_scale: f64,
pub sensitivity: f64,
pub k_anonymity_satisfied: bool,
pub shareable: bool,
pub share_denial_reason: Option<String>,
pub generated_at: String,
}
impl ShareableAggregateReport {
#[must_use]
pub fn privacy_valid(&self) -> bool {
self.epsilon_consumed > 0.0 && self.delta_consumed >= 0.0 && self.noise_scale >= 0.0
}
#[must_use]
pub fn meets_constraints(&self, constraint: &PrivacyBudgetShareConstraint) -> bool {
self.k_anonymity_satisfied
&& constraint.epsilon_valid(self.epsilon_consumed)
&& constraint.delta_valid(self.delta_consumed)
&& self.sample_size >= constraint.min_sample_size
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct PrivacyBudgetShareCertificate {
pub budget: PrivacyBudgetCertificate,
pub report: ShareableAggregateReport,
pub constraints: PrivacyBudgetShareConstraint,
pub share_approved: bool,
pub validations: Vec<ShareValidationCheck>,
pub certified_at: String,
}
impl PrivacyBudgetShareCertificate {
#[must_use]
pub fn all_validations_passed(&self) -> bool {
self.validations.iter().all(|v| v.passed)
}
#[must_use]
pub fn failed_validations(&self) -> Vec<&ShareValidationCheck> {
self.validations.iter().filter(|v| !v.passed).collect()
}
#[must_use]
pub fn passed_count(&self) -> usize {
self.validations.iter().filter(|v| v.passed).count()
}
#[must_use]
pub fn total_count(&self) -> usize {
self.validations.len()
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct ShareValidationCheck {
pub check_id: String,
pub name: String,
pub passed: bool,
pub actual_value: String,
pub threshold: String,
pub explanation: String,
}
impl ShareValidationCheck {
#[must_use]
pub fn pass(
check_id: impl Into<String>,
name: impl Into<String>,
actual: impl Into<String>,
threshold: impl Into<String>,
) -> Self {
Self {
check_id: check_id.into(),
name: name.into(),
passed: true,
actual_value: actual.into(),
threshold: threshold.into(),
explanation: "Check passed".to_string(),
}
}
#[must_use]
pub fn fail(
check_id: impl Into<String>,
name: impl Into<String>,
actual: impl Into<String>,
threshold: impl Into<String>,
reason: impl Into<String>,
) -> Self {
Self {
check_id: check_id.into(),
name: name.into(),
passed: false,
actual_value: actual.into(),
threshold: threshold.into(),
explanation: reason.into(),
}
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum LifecycleEvent {
Import,
IndexPublish,
HookExecution,
Backup,
Shutdown,
Migration,
Maintenance,
}
impl LifecycleEvent {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::Import => "import",
Self::IndexPublish => "index_publish",
Self::HookExecution => "hook_execution",
Self::Backup => "backup",
Self::Shutdown => "shutdown",
Self::Migration => "migration",
Self::Maintenance => "maintenance",
}
}
#[must_use]
pub const fn all() -> [Self; 7] {
[
Self::Import,
Self::IndexPublish,
Self::HookExecution,
Self::Backup,
Self::Shutdown,
Self::Migration,
Self::Maintenance,
]
}
}
impl fmt::Display for LifecycleEvent {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ParseLifecycleEventError {
input: String,
}
impl ParseLifecycleEventError {
pub fn input(&self) -> &str {
&self.input
}
}
impl fmt::Display for ParseLifecycleEventError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"unknown lifecycle event `{}`; expected one of import, index_publish, hook_execution, backup, shutdown, migration, maintenance",
self.input
)
}
}
impl std::error::Error for ParseLifecycleEventError {}
impl FromStr for LifecycleEvent {
type Err = ParseLifecycleEventError;
fn from_str(input: &str) -> Result<Self, Self::Err> {
match normalized_certificate_token(input).as_str() {
"import" => Ok(Self::Import),
"index_publish" => Ok(Self::IndexPublish),
"hook_execution" => Ok(Self::HookExecution),
"backup" => Ok(Self::Backup),
"shutdown" => Ok(Self::Shutdown),
"migration" => Ok(Self::Migration),
"maintenance" => Ok(Self::Maintenance),
_ => Err(ParseLifecycleEventError {
input: input.to_owned(),
}),
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct LifecycleCertificate {
pub event: LifecycleEvent,
pub started_at: String,
pub completed_at: String,
pub duration_ms: u64,
pub success: bool,
pub items_processed: Option<u32>,
pub error: Option<String>,
pub idempotency_key: Option<String>,
}
impl LifecycleCertificate {
#[must_use]
pub const fn is_successful(&self) -> bool {
self.success
}
#[must_use]
pub fn has_idempotency_key(&self) -> bool {
self.idempotency_key.is_some()
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct Certificate {
pub id: String,
pub kind: CertificateKind,
pub status: CertificateStatus,
pub workspace_id: String,
pub issued_at: String,
pub expires_at: Option<String>,
pub payload_hash: String,
pub decision_metadata: super::decision::DecisionPlaneMetadata,
}
impl Certificate {
#[must_use]
pub const fn is_usable(&self) -> bool {
self.status.is_usable()
}
#[must_use]
pub const fn is_expired(&self) -> bool {
matches!(self.status, CertificateStatus::Expired)
}
}
pub const LIFECYCLE_AUTOMATON_SCHEMA_V1: &str = "ee.lifecycle.automaton.v1";
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum AutomatonState {
Idle,
Initializing,
Running,
Waiting,
Completed,
Failed,
Cancelled,
Rollback,
}
impl AutomatonState {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::Idle => "idle",
Self::Initializing => "initializing",
Self::Running => "running",
Self::Waiting => "waiting",
Self::Completed => "completed",
Self::Failed => "failed",
Self::Cancelled => "cancelled",
Self::Rollback => "rollback",
}
}
#[must_use]
pub const fn is_terminal(self) -> bool {
matches!(self, Self::Completed | Self::Failed | Self::Cancelled)
}
#[must_use]
pub const fn is_active(self) -> bool {
matches!(
self,
Self::Initializing | Self::Running | Self::Waiting | Self::Rollback
)
}
#[must_use]
pub const fn all() -> [Self; 8] {
[
Self::Idle,
Self::Initializing,
Self::Running,
Self::Waiting,
Self::Completed,
Self::Failed,
Self::Cancelled,
Self::Rollback,
]
}
}
impl fmt::Display for AutomatonState {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct AutomatonTransition {
pub from: AutomatonState,
pub to: AutomatonState,
pub trigger: String,
pub timestamp: String,
pub metadata: Option<String>,
}
impl AutomatonTransition {
#[must_use]
pub fn new(from: AutomatonState, to: AutomatonState, trigger: impl Into<String>) -> Self {
Self {
from,
to,
trigger: trigger.into(),
timestamp: chrono::Utc::now().to_rfc3339(),
metadata: None,
}
}
#[must_use]
pub fn with_metadata(mut self, metadata: impl Into<String>) -> Self {
self.metadata = Some(metadata.into());
self
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct ImportAutomatonCertificate {
pub source_type: String,
pub source_id: String,
pub state: AutomatonState,
pub transitions: Vec<AutomatonTransition>,
pub sessions_imported: u32,
pub memories_extracted: u32,
pub items_skipped: u32,
pub validation_passed: bool,
pub idempotency_fingerprint: Option<String>,
}
impl ImportAutomatonCertificate {
#[must_use]
pub fn new(source_type: impl Into<String>, source_id: impl Into<String>) -> Self {
Self {
source_type: source_type.into(),
source_id: source_id.into(),
state: AutomatonState::Idle,
transitions: Vec::new(),
sessions_imported: 0,
memories_extracted: 0,
items_skipped: 0,
validation_passed: false,
idempotency_fingerprint: None,
}
}
#[must_use]
pub const fn is_complete(&self) -> bool {
matches!(self.state, AutomatonState::Completed)
}
#[must_use]
pub const fn is_successful(&self) -> bool {
self.is_complete() && self.validation_passed
}
pub fn record_transition(&mut self, to: AutomatonState, trigger: impl Into<String>) {
let transition = AutomatonTransition::new(self.state, to, trigger);
self.transitions.push(transition);
self.state = to;
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct IndexPublishAutomatonCertificate {
pub index_type: String,
pub db_generation_before: u64,
pub db_generation_after: u64,
pub state: AutomatonState,
pub transitions: Vec<AutomatonTransition>,
pub documents_indexed: u32,
pub documents_removed: u32,
pub index_size_bytes: u64,
pub consistency_check: bool,
pub published_at: Option<String>,
}
impl IndexPublishAutomatonCertificate {
#[must_use]
pub fn new(index_type: impl Into<String>) -> Self {
Self {
index_type: index_type.into(),
db_generation_before: 0,
db_generation_after: 0,
state: AutomatonState::Idle,
transitions: Vec::new(),
documents_indexed: 0,
documents_removed: 0,
index_size_bytes: 0,
consistency_check: false,
published_at: None,
}
}
#[must_use]
pub const fn is_complete(&self) -> bool {
matches!(self.state, AutomatonState::Completed)
}
#[must_use]
pub const fn generations_match(&self) -> bool {
self.db_generation_after >= self.db_generation_before
}
pub fn record_transition(&mut self, to: AutomatonState, trigger: impl Into<String>) {
let transition = AutomatonTransition::new(self.state, to, trigger);
self.transitions.push(transition);
self.state = to;
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct HookAutomatonCertificate {
pub hook_name: String,
pub hook_type: String,
pub trigger_event: String,
pub state: AutomatonState,
pub transitions: Vec<AutomatonTransition>,
pub exit_code: Option<i32>,
pub duration_ms: u64,
pub output_summary: Option<String>,
pub skipped: bool,
pub skip_reason: Option<String>,
}
impl HookAutomatonCertificate {
#[must_use]
pub fn new(hook_name: impl Into<String>, hook_type: impl Into<String>) -> Self {
Self {
hook_name: hook_name.into(),
hook_type: hook_type.into(),
trigger_event: String::new(),
state: AutomatonState::Idle,
transitions: Vec::new(),
exit_code: None,
duration_ms: 0,
output_summary: None,
skipped: false,
skip_reason: None,
}
}
#[must_use]
pub fn is_successful(&self) -> bool {
matches!(self.state, AutomatonState::Completed)
&& self.exit_code.is_some_and(|code| code == 0)
}
#[must_use]
pub const fn was_skipped(&self) -> bool {
self.skipped
}
pub fn record_transition(&mut self, to: AutomatonState, trigger: impl Into<String>) {
let transition = AutomatonTransition::new(self.state, to, trigger);
self.transitions.push(transition);
self.state = to;
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct BackupAutomatonCertificate {
pub backup_type: String,
pub destination: String,
pub state: AutomatonState,
pub transitions: Vec<AutomatonTransition>,
pub files_count: u32,
pub total_bytes: u64,
pub checksum: Option<String>,
pub verified: bool,
pub retention_applied: bool,
pub pruned_count: u32,
}
impl BackupAutomatonCertificate {
#[must_use]
pub fn new(backup_type: impl Into<String>, destination: impl Into<String>) -> Self {
Self {
backup_type: backup_type.into(),
destination: destination.into(),
state: AutomatonState::Idle,
transitions: Vec::new(),
files_count: 0,
total_bytes: 0,
checksum: None,
verified: false,
retention_applied: false,
pruned_count: 0,
}
}
#[must_use]
pub const fn is_complete(&self) -> bool {
matches!(self.state, AutomatonState::Completed)
}
#[must_use]
pub const fn is_verified(&self) -> bool {
self.is_complete() && self.verified
}
pub fn record_transition(&mut self, to: AutomatonState, trigger: impl Into<String>) {
let transition = AutomatonTransition::new(self.state, to, trigger);
self.transitions.push(transition);
self.state = to;
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct ShutdownAutomatonCertificate {
pub shutdown_type: String,
pub reason: String,
pub state: AutomatonState,
pub transitions: Vec<AutomatonTransition>,
pub pending_operations: u32,
pub operations_completed: u32,
pub operations_cancelled: u32,
pub cleanup_tasks_run: u32,
pub state_persisted: bool,
pub connections_closed: bool,
}
impl ShutdownAutomatonCertificate {
#[must_use]
pub fn new(shutdown_type: impl Into<String>, reason: impl Into<String>) -> Self {
Self {
shutdown_type: shutdown_type.into(),
reason: reason.into(),
state: AutomatonState::Idle,
transitions: Vec::new(),
pending_operations: 0,
operations_completed: 0,
operations_cancelled: 0,
cleanup_tasks_run: 0,
state_persisted: false,
connections_closed: false,
}
}
#[must_use]
pub const fn is_complete(&self) -> bool {
matches!(self.state, AutomatonState::Completed)
}
#[must_use]
pub const fn is_clean(&self) -> bool {
self.is_complete() && self.state_persisted && self.connections_closed
}
#[must_use]
pub const fn had_data_loss(&self) -> bool {
self.operations_cancelled > 0 && !self.state_persisted
}
pub fn record_transition(&mut self, to: AutomatonState, trigger: impl Into<String>) {
let transition = AutomatonTransition::new(self.state, to, trigger);
self.transitions.push(transition);
self.state = to;
}
}
#[cfg(test)]
mod tests {
use std::str::FromStr;
use super::{
CERTIFICATE_SCHEMA_V1, Certificate, CertificateKind, CertificateStatus,
CurationCertificate, LifecycleCertificate, LifecycleEvent, PackCertificate,
ParseCertificateKindError, ParseCertificateStatusError, ParseLifecycleEventError,
ParseShareableAggregateKindError, PrivacyBudgetCertificate, PrivacyBudgetShareCertificate,
PrivacyBudgetShareConstraint, ShareValidationCheck, ShareableAggregateKind,
ShareableAggregateReport, TailRiskCertificate,
};
use crate::models::DecisionPlaneMetadata;
type TestResult = Result<(), String>;
fn ensure(condition: bool, message: impl Into<String>) -> TestResult {
if condition {
Ok(())
} else {
Err(message.into())
}
}
fn ensure_equal<T: std::fmt::Debug + PartialEq>(
actual: &T,
expected: &T,
ctx: &str,
) -> TestResult {
if actual == expected {
Ok(())
} else {
Err(format!("{ctx}: expected {expected:?}, got {actual:?}"))
}
}
#[test]
fn certificate_schema_is_stable() -> TestResult {
ensure_equal(
&CERTIFICATE_SCHEMA_V1,
&"ee.certificate.v1",
"schema version",
)
}
#[test]
fn certificate_kind_round_trip_for_every_variant() -> TestResult {
for kind in CertificateKind::all() {
let rendered = kind.to_string();
let parsed = CertificateKind::from_str(&rendered)
.map_err(|e| format!("kind {kind:?} failed to round-trip: {e}"))?;
ensure_equal(&parsed, &kind, &format!("round-trip for {kind:?}"))?;
}
Ok(())
}
#[test]
fn certificate_kind_rejects_unknown_input() -> TestResult {
let err = CertificateKind::from_str("unknown_kind");
ensure(
matches!(err, Err(ParseCertificateKindError { .. })),
"should reject unknown kind",
)
}
#[test]
fn certificate_status_round_trip_for_every_variant() -> TestResult {
for status in CertificateStatus::all() {
let rendered = status.to_string();
let parsed = CertificateStatus::from_str(&rendered)
.map_err(|e| format!("status {status:?} failed to round-trip: {e}"))?;
ensure_equal(&parsed, &status, &format!("round-trip for {status:?}"))?;
}
Ok(())
}
#[test]
fn certificate_status_rejects_unknown_input() -> TestResult {
let err = CertificateStatus::from_str("unknown_status");
ensure(
matches!(err, Err(ParseCertificateStatusError { .. })),
"should reject unknown status",
)
}
#[test]
fn certificate_enums_accept_operator_spelling_variants() -> TestResult {
ensure_equal(
&CertificateKind::from_str(" Tail-Risk ").map_err(|e| e.to_string())?,
&CertificateKind::TailRisk,
"certificate kind alias",
)?;
ensure_equal(
&CertificateKind::from_str("privacyBudget").map_err(|e| e.to_string())?,
&CertificateKind::PrivacyBudget,
"camel certificate kind alias",
)?;
ensure_equal(
&CertificateStatus::from_str("VALID").map_err(|e| e.to_string())?,
&CertificateStatus::Valid,
"certificate status alias",
)?;
ensure_equal(
&ShareableAggregateKind::from_str("std-dev").map_err(|e| e.to_string())?,
&ShareableAggregateKind::StdDev,
"aggregate kind alias",
)?;
ensure_equal(
&ShareableAggregateKind::from_str("topK").map_err(|e| e.to_string())?,
&ShareableAggregateKind::TopK,
"camel aggregate kind alias",
)?;
ensure_equal(
&LifecycleEvent::from_str(" Hook-Execution ").map_err(|e| e.to_string())?,
&LifecycleEvent::HookExecution,
"lifecycle event alias",
)?;
ensure_equal(
&LifecycleEvent::from_str("indexPublish").map_err(|e| e.to_string())?,
&LifecycleEvent::IndexPublish,
"camel lifecycle event alias",
)
}
#[test]
fn certificate_status_terminal_and_usable() -> TestResult {
ensure(
!CertificateStatus::Valid.is_terminal(),
"valid is not terminal",
)?;
ensure(
!CertificateStatus::Pending.is_terminal(),
"pending is not terminal",
)?;
ensure(
CertificateStatus::Invalid.is_terminal(),
"invalid is terminal",
)?;
ensure(
CertificateStatus::Expired.is_terminal(),
"expired is terminal",
)?;
ensure(
CertificateStatus::Revoked.is_terminal(),
"revoked is terminal",
)?;
ensure(CertificateStatus::Valid.is_usable(), "valid is usable")?;
ensure(
!CertificateStatus::Pending.is_usable(),
"pending is not usable",
)?;
ensure(
!CertificateStatus::Invalid.is_usable(),
"invalid is not usable",
)
}
#[test]
fn lifecycle_event_round_trip_for_every_variant() -> TestResult {
for event in LifecycleEvent::all() {
let rendered = event.to_string();
let parsed = LifecycleEvent::from_str(&rendered)
.map_err(|e| format!("event {event:?} failed to round-trip: {e}"))?;
ensure_equal(&parsed, &event, &format!("round-trip for {event:?}"))?;
}
Ok(())
}
#[test]
fn lifecycle_event_rejects_unknown_input() -> TestResult {
let err = LifecycleEvent::from_str("unknown_event");
ensure(
matches!(err, Err(ParseLifecycleEventError { .. })),
"should reject unknown event",
)
}
#[test]
fn pack_certificate_validity_checks() -> TestResult {
let valid = PackCertificate {
pack_hash: "hash123".to_string(),
query: "test query".to_string(),
budget_used: 100,
budget_limit: 200,
item_count: 5,
omitted_count: 2,
quotas_satisfied: true,
redundancy_applied: true,
provenance_complete: true,
};
ensure(valid.is_valid(), "should be valid")?;
let over_budget = PackCertificate {
budget_used: 300,
budget_limit: 200,
..valid.clone()
};
ensure(!over_budget.is_valid(), "over budget should be invalid")?;
let no_provenance = PackCertificate {
provenance_complete: false,
..valid.clone()
};
ensure(
!no_provenance.is_valid(),
"missing provenance should be invalid",
)?;
let quotas_failed = PackCertificate {
quotas_satisfied: false,
..valid
};
ensure(!quotas_failed.is_valid(), "failed quotas should be invalid")
}
#[test]
fn curation_certificate_threshold_checks() -> TestResult {
let cert = CurationCertificate {
candidate_id: "curate_123".to_string(),
action_type: "promote".to_string(),
confidence: 0.8,
threshold: 0.7,
evidence_count: 3,
feedback_count: 5,
requires_review: false,
reversible: true,
};
ensure(cert.meets_threshold(), "0.8 >= 0.7 should meet threshold")?;
ensure(cert.has_evidence(), "should have evidence")?;
let below_threshold = CurationCertificate {
confidence: 0.5,
..cert.clone()
};
ensure(
!below_threshold.meets_threshold(),
"0.5 < 0.7 should not meet threshold",
)?;
let no_evidence = CurationCertificate {
evidence_count: 0,
feedback_count: 0,
..cert
};
ensure(!no_evidence.has_evidence(), "should not have evidence")
}
#[test]
fn tail_risk_certificate_bounds_checks() -> TestResult {
let acceptable = TailRiskCertificate {
metric: "latency_p99".to_string(),
observed: 50.0,
threshold: 100.0,
confidence_level: 0.95,
upper_bound: 75.0,
exceeds_bounds: false,
recommended_action: None,
};
ensure(acceptable.is_acceptable(), "should be acceptable")?;
ensure(acceptable.margin() > 0.0, "margin should be positive")?;
let exceeded = TailRiskCertificate {
observed: 120.0,
exceeds_bounds: true,
recommended_action: Some("scale up".to_string()),
..acceptable
};
ensure(!exceeded.is_acceptable(), "should not be acceptable")?;
ensure(exceeded.margin() < 0.0, "margin should be negative")
}
#[test]
fn privacy_budget_certificate_utilization() -> TestResult {
let cert = PrivacyBudgetCertificate {
category: "aggregation".to_string(),
consumed: 0.1,
total_consumed: 0.5,
budget_limit: 1.0,
remaining: 0.5,
operation_allowed: true,
resets_at: None,
};
ensure(!cert.is_exhausted(), "should not be exhausted")?;
ensure_equal(&cert.utilization(), &0.5, "utilization should be 50%")?;
let exhausted = PrivacyBudgetCertificate {
remaining: 0.0,
total_consumed: 1.0,
operation_allowed: false,
..cert
};
ensure(exhausted.is_exhausted(), "should be exhausted")?;
ensure_equal(&exhausted.utilization(), &1.0, "utilization should be 100%")
}
#[test]
fn shareable_aggregate_kind_round_trip_for_every_variant() -> TestResult {
for kind in ShareableAggregateKind::all() {
let rendered = kind.to_string();
let parsed = ShareableAggregateKind::from_str(&rendered)
.map_err(|e| format!("kind {kind:?} failed to round-trip: {e}"))?;
ensure_equal(&parsed, &kind, &format!("round-trip for {kind:?}"))?;
}
Ok(())
}
#[test]
fn shareable_aggregate_kind_rejects_unknown_input() -> TestResult {
let err = ShareableAggregateKind::from_str("unknown_aggregate");
ensure(
matches!(err, Err(ParseShareableAggregateKindError { .. })),
"should reject unknown aggregate kind",
)
}
#[test]
fn shareable_aggregate_kind_sensitivity_classes() -> TestResult {
ensure_equal(
&ShareableAggregateKind::Count.sensitivity_class(),
&"bounded",
"count sensitivity",
)?;
ensure_equal(
&ShareableAggregateKind::Sum.sensitivity_class(),
&"unbounded",
"sum sensitivity",
)?;
ensure_equal(
&ShareableAggregateKind::TopK.sensitivity_class(),
&"k_anonymous",
"top_k sensitivity",
)
}
#[test]
fn privacy_budget_share_constraint_defaults() -> TestResult {
let safe = PrivacyBudgetShareConstraint::default_safe();
ensure_equal(&safe.k_anonymity_threshold, &5, "default k-anonymity")?;
ensure_equal(&safe.max_epsilon, &1.0, "default max epsilon")?;
ensure(safe.epsilon_valid(0.5), "0.5 epsilon should be valid")?;
ensure(!safe.epsilon_valid(1.5), "1.5 epsilon should be invalid")?;
let strict = PrivacyBudgetShareConstraint::strict();
ensure_equal(&strict.k_anonymity_threshold, &10, "strict k-anonymity")?;
ensure_equal(&strict.max_epsilon, &0.1, "strict max epsilon")?;
ensure(
!strict.epsilon_valid(0.5),
"0.5 epsilon should be invalid for strict",
)
}
#[test]
fn shareable_aggregate_report_privacy_validation() -> TestResult {
let report = ShareableAggregateReport {
report_id: "rpt_001".to_string(),
aggregate_kind: ShareableAggregateKind::Mean,
value: 42.5,
sample_size: 100,
epsilon_consumed: 0.1,
delta_consumed: 1e-6,
noise_scale: 0.5,
sensitivity: 1.0,
k_anonymity_satisfied: true,
shareable: true,
share_denial_reason: None,
generated_at: "2026-04-30T12:00:00Z".to_string(),
};
ensure(
report.privacy_valid(),
"report should have valid privacy params",
)?;
ensure(
report.meets_constraints(&PrivacyBudgetShareConstraint::default_safe()),
"report should meet default constraints",
)?;
let invalid = ShareableAggregateReport {
epsilon_consumed: 0.0,
..report.clone()
};
ensure(!invalid.privacy_valid(), "zero epsilon should be invalid")?;
let below_min_sample = ShareableAggregateReport {
sample_size: 5,
..report
};
ensure(
!below_min_sample.meets_constraints(&PrivacyBudgetShareConstraint::default_safe()),
"below min sample should fail constraints",
)
}
#[test]
fn privacy_budget_share_certificate_validation_checks() -> TestResult {
let budget = PrivacyBudgetCertificate {
category: "aggregation".to_string(),
consumed: 0.1,
total_consumed: 0.5,
budget_limit: 1.0,
remaining: 0.5,
operation_allowed: true,
resets_at: None,
};
let report = ShareableAggregateReport {
report_id: "rpt_001".to_string(),
aggregate_kind: ShareableAggregateKind::Count,
value: 150.0,
sample_size: 200,
epsilon_consumed: 0.1,
delta_consumed: 1e-6,
noise_scale: 1.0,
sensitivity: 1.0,
k_anonymity_satisfied: true,
shareable: true,
share_denial_reason: None,
generated_at: "2026-04-30T12:00:00Z".to_string(),
};
let cert = PrivacyBudgetShareCertificate {
budget,
report,
constraints: PrivacyBudgetShareConstraint::default_safe(),
share_approved: true,
validations: vec![
ShareValidationCheck::pass("k_anon", "K-Anonymity", "true", "5"),
ShareValidationCheck::pass("epsilon", "Epsilon Budget", "0.1", "1.0"),
ShareValidationCheck::pass("sample", "Sample Size", "200", "10"),
],
certified_at: "2026-04-30T12:00:00Z".to_string(),
};
ensure(cert.all_validations_passed(), "all validations should pass")?;
ensure_equal(&cert.passed_count(), &3, "passed count")?;
ensure_equal(&cert.total_count(), &3, "total count")?;
ensure(
cert.failed_validations().is_empty(),
"no failed validations",
)?;
let with_failure = PrivacyBudgetShareCertificate {
share_approved: false,
validations: vec![
ShareValidationCheck::pass("k_anon", "K-Anonymity", "true", "5"),
ShareValidationCheck::fail(
"epsilon",
"Epsilon Budget",
"1.5",
"1.0",
"Epsilon exceeds maximum allowed",
),
],
..cert
};
ensure(
!with_failure.all_validations_passed(),
"should have failures",
)?;
ensure_equal(
&with_failure.passed_count(),
&1,
"passed count with failure",
)?;
ensure_equal(
&with_failure.failed_validations().len(),
&1,
"failed validation count",
)
}
#[test]
fn lifecycle_certificate_success_checks() -> TestResult {
let successful = LifecycleCertificate {
event: LifecycleEvent::Import,
started_at: "2026-04-29T12:00:00Z".to_string(),
completed_at: "2026-04-29T12:01:00Z".to_string(),
duration_ms: 60000,
success: true,
items_processed: Some(100),
error: None,
idempotency_key: Some("import-abc123".to_string()),
};
ensure(successful.is_successful(), "should be successful")?;
ensure(
successful.has_idempotency_key(),
"should have idempotency key",
)?;
let failed = LifecycleCertificate {
success: false,
error: Some("connection timeout".to_string()),
..successful.clone()
};
ensure(!failed.is_successful(), "should not be successful")?;
let no_key = LifecycleCertificate {
idempotency_key: None,
..successful
};
ensure(
!no_key.has_idempotency_key(),
"should not have idempotency key",
)
}
#[test]
fn certificate_envelope_usability() -> TestResult {
let valid = Certificate {
id: "cert_123".to_string(),
kind: CertificateKind::Pack,
status: CertificateStatus::Valid,
workspace_id: "wsp_456".to_string(),
issued_at: "2026-04-29T12:00:00Z".to_string(),
expires_at: None,
payload_hash: "hash789".to_string(),
decision_metadata: DecisionPlaneMetadata::empty(),
};
ensure(valid.is_usable(), "valid cert should be usable")?;
ensure(!valid.is_expired(), "valid cert should not be expired")?;
let expired = Certificate {
status: CertificateStatus::Expired,
..valid
};
ensure(!expired.is_usable(), "expired cert should not be usable")?;
ensure(
expired.is_expired(),
"expired cert should report as expired",
)
}
#[test]
fn automaton_state_terminal_and_active() -> TestResult {
use super::AutomatonState;
ensure(!AutomatonState::Idle.is_terminal(), "idle is not terminal")?;
ensure(!AutomatonState::Idle.is_active(), "idle is not active")?;
ensure(AutomatonState::Running.is_active(), "running is active")?;
ensure(
!AutomatonState::Running.is_terminal(),
"running is not terminal",
)?;
ensure(
AutomatonState::Completed.is_terminal(),
"completed is terminal",
)?;
ensure(AutomatonState::Failed.is_terminal(), "failed is terminal")?;
ensure(
AutomatonState::Cancelled.is_terminal(),
"cancelled is terminal",
)?;
ensure(
!AutomatonState::Completed.is_active(),
"completed is not active",
)?;
Ok(())
}
#[test]
fn automaton_state_round_trip() -> TestResult {
use super::AutomatonState;
for state in AutomatonState::all() {
let rendered = state.to_string();
ensure(
!rendered.is_empty(),
format!("state {:?} should have string", state),
)?;
}
Ok(())
}
#[test]
fn import_automaton_certificate_transitions() -> TestResult {
use super::{AutomatonState, ImportAutomatonCertificate};
let mut cert = ImportAutomatonCertificate::new("cass", "/path/to/sessions");
ensure_equal(&cert.state, &AutomatonState::Idle, "initial state")?;
ensure(!cert.is_complete(), "should not be complete initially")?;
cert.record_transition(AutomatonState::Initializing, "start_import");
ensure_equal(&cert.state, &AutomatonState::Initializing, "after init")?;
ensure_equal(&cert.transitions.len(), &1, "one transition")?;
cert.record_transition(AutomatonState::Running, "begin_scan");
cert.sessions_imported = 10;
cert.memories_extracted = 50;
cert.validation_passed = true;
cert.record_transition(AutomatonState::Completed, "finish");
ensure(cert.is_complete(), "should be complete")?;
ensure(cert.is_successful(), "should be successful")
}
#[test]
fn index_publish_automaton_certificate_generations() -> TestResult {
use super::IndexPublishAutomatonCertificate;
let mut cert = IndexPublishAutomatonCertificate::new("fts5");
cert.db_generation_before = 10;
cert.db_generation_after = 12;
ensure(
cert.generations_match(),
"generations should match when after >= before",
)?;
cert.db_generation_after = 8;
ensure(
!cert.generations_match(),
"should not match when after < before",
)
}
#[test]
fn hook_automaton_certificate_success_check() -> TestResult {
use super::{AutomatonState, HookAutomatonCertificate};
let mut cert = HookAutomatonCertificate::new("pre_commit", "pre");
cert.trigger_event = "commit".to_owned();
cert.record_transition(AutomatonState::Running, "execute");
cert.exit_code = Some(0);
cert.record_transition(AutomatonState::Completed, "finish");
ensure(cert.is_successful(), "exit 0 + completed = successful")?;
let mut failed_cert = HookAutomatonCertificate::new("pre_commit", "pre");
failed_cert.record_transition(AutomatonState::Completed, "finish");
failed_cert.exit_code = Some(1);
ensure(!failed_cert.is_successful(), "exit 1 = not successful")
}
#[test]
fn backup_automaton_certificate_verification() -> TestResult {
use super::{AutomatonState, BackupAutomatonCertificate};
let mut cert = BackupAutomatonCertificate::new("full", "/backups/daily");
cert.files_count = 100;
cert.total_bytes = 1024 * 1024;
cert.checksum = Some("abc123".to_owned());
cert.verified = true;
cert.record_transition(AutomatonState::Completed, "finish");
ensure(cert.is_complete(), "should be complete")?;
ensure(cert.is_verified(), "should be verified")
}
#[test]
fn shutdown_automaton_certificate_clean_check() -> TestResult {
use super::{AutomatonState, ShutdownAutomatonCertificate};
let mut cert = ShutdownAutomatonCertificate::new("graceful", "user_request");
cert.pending_operations = 5;
cert.operations_completed = 5;
cert.cleanup_tasks_run = 3;
cert.state_persisted = true;
cert.connections_closed = true;
cert.record_transition(AutomatonState::Completed, "shutdown_complete");
ensure(cert.is_complete(), "should be complete")?;
ensure(cert.is_clean(), "should be clean")?;
ensure(!cert.had_data_loss(), "should not have data loss")?;
let mut dirty = ShutdownAutomatonCertificate::new("immediate", "crash");
dirty.operations_cancelled = 3;
dirty.state_persisted = false;
ensure(dirty.had_data_loss(), "should have data loss")
}
}