use std::fmt::Debug;
use std::hash::Hash;
use std::marker::PhantomData;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ElementKind {
Concept,
Relation,
Pattern,
Guard,
Workflow,
Service,
}
#[derive(Debug, Clone)]
pub struct AHIInput<O, G, L, D>
where
O: Debug + Clone,
G: Debug + Clone,
L: Debug + Clone,
D: Debug + Clone,
{
pub ontology_snapshot: O,
pub observations: G,
pub lambda_config: L,
pub doctrine_history: D,
}
#[derive(Debug, Clone)]
pub struct AHIOutput<DS, DPI, DQ, MA>
where
DS: Debug + Clone,
DPI: Debug + Clone,
DQ: Debug + Clone,
MA: Debug + Clone,
{
pub delta_sigma: Option<DS>,
pub delta_pi: Option<DPI>,
pub delta_q: Option<DQ>,
pub marketplace_actions: Vec<MA>,
}
#[derive(Debug, Clone)]
pub struct AHIInvariants {
pub delta_sigma_justified: bool,
pub delta_pi_grounded: bool,
pub doctrine_aligned: bool,
pub immutability_preserved: bool,
pub no_hidden_changes: bool,
}
#[allow(clippy::type_complexity)]
pub trait AHIDecisionCycle: Send + Sync + Debug {
type Observation: Debug + Clone + Send + Sync;
type OntologySnapshot: Debug + Clone + Send + Sync;
type Config: Debug + Clone + Send + Sync;
type DoctrineHistory: Debug + Clone + Send + Sync;
type DeltaSigma: Debug + Clone + Send + Sync;
type DeltaPi: Debug + Clone + Send + Sync;
type DeltaQ: Debug + Clone + Send + Sync;
type MarketplaceAction: Debug + Clone + Send + Sync;
type Justification: Debug + Clone + Send + Sync;
fn cycle(
&self,
input: AHIInput<
Self::OntologySnapshot,
Vec<Self::Observation>,
Self::Config,
Self::DoctrineHistory,
>,
) -> Result<
AHIOutput<Self::DeltaSigma, Self::DeltaPi, Self::DeltaQ, Self::MarketplaceAction>,
AHIError,
>;
fn justify_delta_sigma(
&self, proposal: &Self::DeltaSigma, observations: &[Self::Observation],
) -> Result<Self::Justification, AHIError>;
fn validate_invariants(
&self,
input: &AHIInput<
Self::OntologySnapshot,
Vec<Self::Observation>,
Self::Config,
Self::DoctrineHistory,
>,
output: &AHIOutput<Self::DeltaSigma, Self::DeltaPi, Self::DeltaQ, Self::MarketplaceAction>,
) -> Result<AHIInvariants, AHIError>;
fn audit_path(
&self,
output: &AHIOutput<Self::DeltaSigma, Self::DeltaPi, Self::DeltaQ, Self::MarketplaceAction>,
) -> Vec<String>;
}
#[derive(Debug, Clone)]
pub enum AHIError {
DoctrineViolation(String),
InsufficientJustification(String),
InvariantBreach(String),
InvalidConfig(String),
InvalidSnapshot(String),
ProjectionError(String),
InvalidMarketplaceAction(String),
}
impl std::fmt::Display for AHIError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
AHIError::DoctrineViolation(msg) => write!(f, "Doctrine violation: {}", msg),
AHIError::InsufficientJustification(msg) => {
write!(f, "Insufficient justification: {}", msg)
}
AHIError::InvariantBreach(msg) => write!(f, "Invariant breach: {}", msg),
AHIError::InvalidConfig(msg) => write!(f, "Invalid config: {}", msg),
AHIError::InvalidSnapshot(msg) => write!(f, "Invalid snapshot: {}", msg),
AHIError::ProjectionError(msg) => write!(f, "Projection error: {}", msg),
AHIError::InvalidMarketplaceAction(msg) => {
write!(f, "Invalid marketplace action: {}", msg)
}
}
}
}
impl std::error::Error for AHIError {}
pub trait Measurable: Debug + Clone + Send + Sync {
fn metric(&self) -> f64;
fn category(&self) -> &str;
}
pub trait DoctrineConstraint: Send + Sync + Debug {
fn check(&self, subject: &str, kind: ElementKind, context: &str) -> Result<(), AHIError>;
fn name(&self) -> &str;
fn doctrine(&self) -> &str;
}
pub trait Proposal: Send + Sync + Debug + Clone {
fn id(&self) -> &str;
fn what(&self) -> String;
fn why(&self) -> Vec<String>;
fn expected_benefit(&self) -> f64;
fn risk_level(&self) -> f64;
fn doctrine_aligned(&self) -> bool;
fn is_actionable(&self) -> bool;
}
#[derive(Debug, Clone)]
pub struct SectorContext {
pub sector_id: String,
pub tenant_id: String,
pub regulatory_strictness: u32,
pub risk_appetite: u32,
pub performance_bias: u32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ProposalState {
Candidate,
Validated,
Approved,
Promoted,
Rejected,
Reverted,
}
pub struct ProposalTransition<P: Proposal> {
proposal: P,
current_state: ProposalState,
_phantom: PhantomData<P>,
}
impl<P: Proposal> ProposalTransition<P> {
pub fn new(proposal: P) -> Self {
Self {
proposal,
current_state: ProposalState::Candidate,
_phantom: PhantomData,
}
}
pub fn validate(mut self, doctrine_ok: bool) -> Result<Self, AHIError> {
if self.current_state != ProposalState::Candidate {
return Err(AHIError::InvalidConfig(
"Can only validate from Candidate state".to_string(),
));
}
if !doctrine_ok || !self.proposal.doctrine_aligned() {
self.current_state = ProposalState::Rejected;
return Err(AHIError::DoctrineViolation(
"Proposal fails doctrine alignment".to_string(),
));
}
self.current_state = ProposalState::Validated;
Ok(self)
}
pub fn approve(mut self, risk_acceptable: bool) -> Result<Self, AHIError> {
if self.current_state != ProposalState::Validated {
return Err(AHIError::InvalidConfig(
"Can only approve from Validated state".to_string(),
));
}
if !risk_acceptable || self.proposal.risk_level() > 75.0 {
self.current_state = ProposalState::Rejected;
return Err(AHIError::InvalidConfig(
"Risk level unacceptable".to_string(),
));
}
self.current_state = ProposalState::Approved;
Ok(self)
}
pub fn promote(mut self) -> Result<Self, AHIError> {
if self.current_state != ProposalState::Approved {
return Err(AHIError::InvalidConfig(
"Can only promote from Approved state".to_string(),
));
}
self.current_state = ProposalState::Promoted;
Ok(self)
}
pub fn revert(mut self) -> Self {
self.current_state = ProposalState::Reverted;
self
}
pub fn state(&self) -> ProposalState {
self.current_state
}
pub fn into_proposal(self) -> P {
self.proposal
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_proposal_state_machine() {
#[derive(Debug, Clone)]
struct MockProposal {
id: String,
}
impl Proposal for MockProposal {
fn id(&self) -> &str {
&self.id
}
fn what(&self) -> String {
"test".to_string()
}
fn why(&self) -> Vec<String> {
vec!["because".to_string()]
}
fn expected_benefit(&self) -> f64 {
50.0
}
fn risk_level(&self) -> f64 {
25.0
}
fn doctrine_aligned(&self) -> bool {
true
}
fn is_actionable(&self) -> bool {
true
}
}
let proposal = MockProposal {
id: "prop-1".to_string(),
};
let transition = ProposalTransition::new(proposal);
assert_eq!(transition.state(), ProposalState::Candidate);
let transition = transition.validate(true).unwrap();
assert_eq!(transition.state(), ProposalState::Validated);
let transition = transition.approve(true).unwrap();
assert_eq!(transition.state(), ProposalState::Approved);
let transition = transition.promote().unwrap();
assert_eq!(transition.state(), ProposalState::Promoted);
}
#[test]
fn test_proposal_state_revert() {
#[derive(Debug, Clone)]
struct MockProposal {
id: String,
}
impl Proposal for MockProposal {
fn id(&self) -> &str {
&self.id
}
fn what(&self) -> String {
"test".to_string()
}
fn why(&self) -> Vec<String> {
vec![]
}
fn expected_benefit(&self) -> f64 {
50.0
}
fn risk_level(&self) -> f64 {
25.0
}
fn doctrine_aligned(&self) -> bool {
true
}
fn is_actionable(&self) -> bool {
true
}
}
let proposal = MockProposal {
id: "prop-2".to_string(),
};
let transition = ProposalTransition::new(proposal)
.validate(true)
.unwrap()
.approve(true)
.unwrap()
.promote()
.unwrap();
let reverted = transition.revert();
assert_eq!(reverted.state(), ProposalState::Reverted);
}
}