decision_cockpit 0.1.0

Layer — product decision memory with MCP tools and an embedded review dashboard
Documentation
//! Domain entities and shared enums.
//!
//! Enum-like columns are stored as `TEXT` in Postgres. We model the meaningful
//! ones as Rust enums (with snake_case serde) for request parsing and routing,
//! and keep free-form status fields as `String` on read structs.

pub mod actions;
pub mod assumptions;
pub mod candidates;
pub mod decisions;
pub mod documents;
pub mod drift;
pub mod evidence;
pub mod memos;
pub mod relations;

pub use actions::Action;
pub use assumptions::Assumption;
pub use candidates::ExtractionCandidate;
pub use decisions::Decision;
pub use documents::Document;
pub use drift::DriftSignal;
pub use evidence::Evidence;
pub use memos::Memo;
pub use relations::EntityRelation;

use serde::{Deserialize, Serialize};

/// The kind of item an extraction candidate represents.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum CandidateType {
    Decision,
    Assumption,
    Action,
    Evidence,
    Risk,
    Goal,
    OpenQuestion,
}

impl CandidateType {
    pub fn as_str(&self) -> &'static str {
        match self {
            CandidateType::Decision => "decision",
            CandidateType::Assumption => "assumption",
            CandidateType::Action => "action",
            CandidateType::Evidence => "evidence",
            CandidateType::Risk => "risk",
            CandidateType::Goal => "goal",
            CandidateType::OpenQuestion => "open_question",
        }
    }
}

/// Lifecycle state of an extraction candidate.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum CandidateStatus {
    Pending,
    Accepted,
    Rejected,
    Edited,
}

impl CandidateStatus {
    pub fn as_str(&self) -> &'static str {
        match self {
            CandidateStatus::Pending => "pending",
            CandidateStatus::Accepted => "accepted",
            CandidateStatus::Rejected => "rejected",
            CandidateStatus::Edited => "edited",
        }
    }
}

/// Canonical entity types that participate in the decision graph.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum EntityType {
    Decision,
    Assumption,
    Action,
    Evidence,
    DriftSignal,
    Memo,
}

impl EntityType {
    pub fn as_str(&self) -> &'static str {
        match self {
            EntityType::Decision => "decision",
            EntityType::Assumption => "assumption",
            EntityType::Action => "action",
            EntityType::Evidence => "evidence",
            EntityType::DriftSignal => "drift_signal",
            EntityType::Memo => "memo",
        }
    }
}

/// How two entities relate to each other in the graph.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum RelationType {
    Supports,
    DependsOn,
    Challenges,
    Contradicts,
    Produces,
    Mitigates,
    Replaces,
    RelatedTo,
}

impl RelationType {
    pub fn as_str(&self) -> &'static str {
        match self {
            RelationType::Supports => "supports",
            RelationType::DependsOn => "depends_on",
            RelationType::Challenges => "challenges",
            RelationType::Contradicts => "contradicts",
            RelationType::Produces => "produces",
            RelationType::Mitigates => "mitigates",
            RelationType::Replaces => "replaces",
            RelationType::RelatedTo => "related_to",
        }
    }
}

/// The flavor of drift a signal represents.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum DriftType {
    AssumptionChallenged,
    DecisionContradicted,
    ActionStale,
    GoalMismatch,
    EvidenceOutdated,
}

impl DriftType {
    pub fn as_str(&self) -> &'static str {
        match self {
            DriftType::AssumptionChallenged => "assumption_challenged",
            DriftType::DecisionContradicted => "decision_contradicted",
            DriftType::ActionStale => "action_stale",
            DriftType::GoalMismatch => "goal_mismatch",
            DriftType::EvidenceOutdated => "evidence_outdated",
        }
    }
}

/// Severity scale shared by risks and drift signals.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum Severity {
    Low,
    Medium,
    High,
}

impl Severity {
    pub fn as_str(&self) -> &'static str {
        match self {
            Severity::Low => "low",
            Severity::Medium => "medium",
            Severity::High => "high",
        }
    }
}