eidos-ekf 0.1.0

EKF package contract support for Eidos.
Documentation
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
use std::path::PathBuf;

#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct PackageRequest {
    pub manifest: Option<PathBuf>,
    pub start_dir: Option<PathBuf>,
    pub vault: Option<PathBuf>,
    pub docs: Option<PathBuf>,
    pub code: Option<PathBuf>,
}

#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct Manifest {
    pub ekf_version: Option<String>,
    pub name: Option<String>,
    #[serde(default)]
    pub partitions: Vec<PartitionManifest>,
    #[serde(default)]
    pub sources: SourceManifest,
    #[serde(default)]
    pub trust: BTreeMap<String, String>,
    #[serde(default)]
    pub gates: BTreeMap<String, BTreeMap<String, String>>,
    #[serde(default)]
    pub relations: BTreeMap<String, BTreeMap<String, String>>,
}

#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct PartitionManifest {
    pub id: String,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub title: Option<String>,
    pub source: String,
    #[serde(default)]
    pub roots: Vec<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub role: Option<String>,
    #[serde(default)]
    pub languages: Vec<String>,
    #[serde(default)]
    pub tags: Vec<String>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct TrustPolicy {
    pub gold_ground_threshold: u32,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub stale_after_days: Option<u64>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub agent_accepted_ttl_days: Option<u64>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct RelationPolicy {
    pub forward_phrase: String,
    pub reverse_phrase: String,
    pub context_score: i64,
    pub searchable: bool,
    pub traversable: bool,
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub domain_kinds: Vec<String>,
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub range_kinds: Vec<String>,
}

impl RelationPolicy {
    pub(crate) fn new(
        forward_phrase: impl Into<String>,
        reverse_phrase: impl Into<String>,
        context_score: i64,
    ) -> Self {
        Self {
            forward_phrase: forward_phrase.into(),
            reverse_phrase: reverse_phrase.into(),
            context_score,
            searchable: true,
            traversable: true,
            domain_kinds: Vec::new(),
            range_kinds: Vec::new(),
        }
    }
}

impl Default for TrustPolicy {
    fn default() -> Self {
        Self {
            gold_ground_threshold: 5,
            stale_after_days: None,
            agent_accepted_ttl_days: None,
        }
    }
}

#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct SourceManifest {
    #[serde(default)]
    pub docs: Vec<String>,
    #[serde(default)]
    pub skills: Vec<String>,
    #[serde(default)]
    pub agents: Vec<String>,
    #[serde(default)]
    pub capabilities: Vec<String>,
    #[serde(default)]
    pub brief_profiles: Vec<String>,
    #[serde(default)]
    pub workflows: Vec<String>,
    #[serde(default)]
    pub evals: Vec<String>,
    #[serde(default)]
    pub code: Vec<CodeSource>,
}

#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct CodeSource {
    pub path: String,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub source: Option<String>,
    #[serde(default)]
    pub languages: Vec<String>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum DiagnosticSeverity {
    Info,
    Warning,
    Error,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct EkfDiagnostic {
    pub severity: DiagnosticSeverity,
    pub kind: String,
    pub message: String,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub source: Option<String>,
}

#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct ResolvedSources {
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub vault: Option<PathBuf>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub docs: Option<PathBuf>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub code: Option<PathBuf>,
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub source_roots: Vec<ResolvedSourceRoot>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ResolvedSourceRoot {
    pub kind: String,
    pub path: PathBuf,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub partition: Option<String>,
    /// Declared extraction languages for code roots (from the owning partition or the legacy
    /// `sources.code` entry). Empty = no restriction. Extraction honors this as a filter.
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub languages: Vec<String>,
    /// How Eidos v0.1 treats this root: `compiled` feeds the graph, `planning` feeds the task
    /// planner, `eval` feeds package gates, and `declared` is validated/reported package contract
    /// only.
    pub status: String,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ResolvedPartition {
    pub id: String,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub title: Option<String>,
    pub source: String,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub role: Option<String>,
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub roots: Vec<PathBuf>,
    pub status: String,
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub languages: Vec<String>,
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub tags: Vec<String>,
}

#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct PackageReport {
    pub manifest_found: bool,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub manifest_path: Option<PathBuf>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub package_root: Option<PathBuf>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub name: Option<String>,
    pub resolved: ResolvedSources,
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub partitions: Vec<ResolvedPartition>,
    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
    pub gates: BTreeMap<String, BTreeMap<String, String>>,
    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
    pub gate_intents: BTreeMap<String, BTreeMap<String, GateIntent>>,
    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
    pub trust: BTreeMap<String, String>,
    #[serde(default)]
    pub trust_policy: TrustPolicy,
    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
    pub relation_profiles: BTreeMap<String, RelationPolicy>,
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub diagnostics: Vec<EkfDiagnostic>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct GateIntent {
    pub capability: String,
    pub signal: String,
    pub description: String,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PackageConfig {
    pub manifest: Option<Manifest>,
    pub report: PackageReport,
}