use super::ahi_contract::{AHIError, DoctrineConstraint, ElementKind};
use std::collections::HashMap;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum DoctrineSeverity {
Soft,
Hard,
Critical,
}
#[derive(Debug, Clone)]
pub enum RuleKind {
ImmutabilityRule,
ClosedWorldRule,
ChatmanConstantRule,
SectorIsolationRule,
DeterminismRule,
Custom(String),
}
#[derive(Debug, Clone)]
pub struct DoctrineRule {
pub id: String,
pub name: String,
pub doctrine_name: String,
pub severity: DoctrineSeverity,
pub rule_kind: RuleKind,
}
impl DoctrineRule {
pub fn check(&self, subject: &str, kind: ElementKind, _context: &str) -> bool {
match &self.rule_kind {
RuleKind::ImmutabilityRule => {
!(subject.contains("direct_edit") && subject.contains("sigma"))
}
RuleKind::ClosedWorldRule => {
!subject.contains("ad_hoc") && !subject.contains("undocumented")
}
RuleKind::ChatmanConstantRule => {
if kind == ElementKind::Pattern {
!subject.contains("exceeds_ticks")
} else {
true
}
}
RuleKind::SectorIsolationRule => {
!subject.contains("cross_sector") || subject.contains("shared_base")
}
RuleKind::DeterminismRule => {
if kind == ElementKind::Service {
!subject.contains("random_output") && !subject.contains("non_deterministic")
} else {
true
}
}
RuleKind::Custom(_) => {
true
}
}
}
}
#[derive(Debug, Clone)]
pub struct DoctrineEngine {
rules: HashMap<String, DoctrineRule>,
violations: Vec<(String, String)>, }
impl DoctrineEngine {
pub fn new() -> Self {
let mut engine = Self {
rules: HashMap::new(),
violations: Vec::new(),
};
engine.add_rule(
"immutability-no-direct-sigma-edit",
"No Direct Σ* Mutation",
"Immutability",
DoctrineSeverity::Critical,
RuleKind::ImmutabilityRule,
);
engine.add_rule(
"closed-world-changes-justified",
"All Changes Justified from Γ",
"Closed-World",
DoctrineSeverity::Critical,
RuleKind::ClosedWorldRule,
);
engine.add_rule(
"chatman-constant-tick-budget",
"Hot Path ≤8 Ticks",
"Performance",
DoctrineSeverity::Hard,
RuleKind::ChatmanConstantRule,
);
engine.add_rule(
"sector-isolation-no-cross-leak",
"Sector Isolation",
"Multi-Tenancy",
DoctrineSeverity::Hard,
RuleKind::SectorIsolationRule,
);
engine.add_rule(
"determinism-π-idempotent",
"Projections are Deterministic",
"Determinism",
DoctrineSeverity::Hard,
RuleKind::DeterminismRule,
);
engine
}
fn add_rule(
&mut self, id: &str, name: &str, doctrine: &str, severity: DoctrineSeverity,
rule_kind: RuleKind,
) {
self.rules.insert(
id.to_string(),
DoctrineRule {
id: id.to_string(),
name: name.to_string(),
doctrine_name: doctrine.to_string(),
severity,
rule_kind,
},
);
}
pub fn check_all(
&mut self, subject: &str, kind: ElementKind, context: &str,
) -> Result<(), Vec<AHIError>> {
let mut errors = Vec::new();
for (id, rule) in &self.rules.clone() {
let passes = rule.check(subject, kind, context);
if !passes {
let msg = format!(
"Doctrine violation: {} ({}). Subject: {}, Kind: {:?}, Context: {}",
rule.name, rule.doctrine_name, subject, kind, context
);
self.violations.push((id.clone(), msg.clone()));
match rule.severity {
DoctrineSeverity::Soft => {
}
DoctrineSeverity::Hard => {
errors.push(AHIError::DoctrineViolation(msg));
}
DoctrineSeverity::Critical => {
errors.push(AHIError::DoctrineViolation(msg));
}
}
}
}
if errors.is_empty() {
Ok(())
} else {
Err(errors)
}
}
pub fn violations(&self) -> &[(String, String)] {
&self.violations
}
pub fn clear_violations(&mut self) {
self.violations.clear();
}
pub fn doctrine_distance(&self, subject: &str, kind: ElementKind, context: &str) -> f64 {
let mut violation_count = 0;
let mut critical_violation = false;
for rule in self.rules.values() {
let passes = rule.check(subject, kind, context);
if !passes {
violation_count += 1;
if rule.severity == DoctrineSeverity::Critical {
critical_violation = true;
}
}
}
if critical_violation {
return 100.0; }
if self.rules.is_empty() {
return 0.0;
}
(violation_count as f64 / self.rules.len() as f64) * 100.0
}
pub fn get_rule(&self, id: &str) -> Option<&DoctrineRule> {
self.rules.get(id)
}
pub fn rules_by_doctrine(&self, doctrine: &str) -> Vec<&DoctrineRule> {
self.rules
.values()
.filter(|r| r.doctrine_name == doctrine)
.collect()
}
pub fn critical_rules(&self) -> Vec<&DoctrineRule> {
self.rules
.values()
.filter(|r| r.severity == DoctrineSeverity::Critical)
.collect()
}
}
impl Default for DoctrineEngine {
fn default() -> Self {
Self::new()
}
}
impl DoctrineConstraint for DoctrineEngine {
fn check(&self, subject: &str, kind: ElementKind, context: &str) -> Result<(), AHIError> {
let mut engine_clone = self.clone();
engine_clone
.check_all(subject, kind, context)
.map_err(|mut errs| {
errs.pop().unwrap_or_else(|| {
AHIError::DoctrineViolation("Unknown doctrine violation".to_string())
})
})
}
fn name(&self) -> &str {
"DOCTRINE_2027"
}
fn doctrine(&self) -> &str {
"Closed-World Governance with Deterministic Projections"
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_doctrine_engine_creation() {
let engine = DoctrineEngine::new();
assert!(!engine.rules.is_empty());
}
#[test]
fn test_doctrine_immutability_check() {
let mut engine = DoctrineEngine::new();
let result = engine.check_all("normal_update", ElementKind::Concept, "Σ update via ΔΣ");
assert!(result.is_ok());
}
#[test]
fn test_doctrine_violation_direct_edit() {
let mut engine = DoctrineEngine::new();
let result = engine.check_all(
"direct_edit_sigma_mutation",
ElementKind::Concept,
"Attempted direct Σ* mutation",
);
assert!(result.is_err());
}
#[test]
fn test_doctrine_distance_metric() {
let engine = DoctrineEngine::new();
let perfect = engine.doctrine_distance("normal_operation", ElementKind::Pattern, "");
assert_eq!(perfect, 0.0);
let violations = engine.doctrine_distance(
"direct_edit_sigma_bad_ad_hoc_undocumented",
ElementKind::Pattern,
"",
);
assert!(violations > 0.0);
}
#[test]
fn test_doctrine_rules_by_type() {
let engine = DoctrineEngine::new();
let immutability_rules = engine.rules_by_doctrine("Immutability");
assert!(!immutability_rules.is_empty());
let critical = engine.critical_rules();
assert!(!critical.is_empty());
}
#[test]
fn test_doctrine_violation_tracking() {
let mut engine = DoctrineEngine::new();
assert_eq!(engine.violations().len(), 0);
let _ = engine.check_all("direct_edit_sigma_mutation", ElementKind::Concept, "test");
assert!(!engine.violations().is_empty());
}
}