Skip to main content

aicx_parser/
types.rs

1//! Shared canonical types used by the intent engine.
2//!
3//! Vibecrafted with AI Agents by Vetcoders (c)2026 Vetcoders
4
5use serde::{Deserialize, Serialize};
6use std::fmt;
7use std::hash::{Hash, Hasher};
8
9pub use crate::timeline::{FrameKind, Kind, RepoIdentity, SemanticSegment, SourceTier};
10
11// ── Intent Engine schema ─────────────────────────────────────────────
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
14#[serde(rename_all = "lowercase")]
15pub enum EntryType {
16    Intent,
17    Task,
18    Commitment,
19    Why,
20    Argue,
21    Decision,
22    Assumption,
23    Outcome,
24    Result,
25    Question,
26    Insight,
27}
28
29impl EntryType {
30    pub fn as_str(self) -> &'static str {
31        match self {
32            Self::Intent => "intent",
33            Self::Task => "task",
34            Self::Commitment => "commitment",
35            Self::Why => "why",
36            Self::Argue => "argue",
37            Self::Decision => "decision",
38            Self::Assumption => "assumption",
39            Self::Outcome => "outcome",
40            Self::Result => "result",
41            Self::Question => "question",
42            Self::Insight => "insight",
43        }
44    }
45
46    pub fn parse(s: &str) -> Option<Self> {
47        match s.to_ascii_lowercase().as_str() {
48            "intent" => Some(Self::Intent),
49            "task" | "todo" => Some(Self::Task),
50            "commitment" | "promise" => Some(Self::Commitment),
51            "why" => Some(Self::Why),
52            "argue" | "argument" | "debate" => Some(Self::Argue),
53            "decision" => Some(Self::Decision),
54            "assumption" | "hypothesis" => Some(Self::Assumption),
55            "outcome" => Some(Self::Outcome),
56            "result" => Some(Self::Result),
57            "question" => Some(Self::Question),
58            "insight" => Some(Self::Insight),
59            _ => None,
60        }
61    }
62}
63
64impl fmt::Display for EntryType {
65    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
66        f.write_str(self.as_str())
67    }
68}
69
70#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
71#[serde(rename_all = "lowercase")]
72pub enum EntryState {
73    Proposed,
74    Active,
75    Superseded,
76    Done,
77    Contradicted,
78}
79
80impl EntryState {
81    pub fn as_str(self) -> &'static str {
82        match self {
83            Self::Proposed => "proposed",
84            Self::Active => "active",
85            Self::Superseded => "superseded",
86            Self::Done => "done",
87            Self::Contradicted => "contradicted",
88        }
89    }
90}
91
92impl fmt::Display for EntryState {
93    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
94        f.write_str(self.as_str())
95    }
96}
97
98#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
99#[serde(rename_all = "snake_case")]
100pub enum LinkType {
101    DerivedFrom,
102    Supersedes,
103    Verifies,
104    Contradicts,
105    Supports,
106    ResultsIn,
107    Answers,
108    LinksTo,
109}
110
111#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
112pub struct Link {
113    pub relation: LinkType,
114    pub target: String,
115    #[serde(skip_serializing_if = "Option::is_none")]
116    pub confidence: Option<f32>,
117}
118
119impl Eq for Link {}
120
121#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
122pub struct IntentEntry {
123    pub id: String,
124    pub entry_type: EntryType,
125    #[serde(rename = "status", alias = "state")]
126    pub state: EntryState,
127    pub title: String,
128    #[serde(skip_serializing_if = "Option::is_none")]
129    pub body: Option<String>,
130    #[serde(default, skip_serializing_if = "Vec::is_empty")]
131    pub evidence: Vec<String>,
132    #[serde(default, skip_serializing_if = "Vec::is_empty")]
133    pub links: Vec<Link>,
134    #[serde(skip_serializing_if = "Option::is_none")]
135    pub superseded_by: Option<String>,
136    pub confidence: f32,
137    #[serde(default, skip_serializing_if = "Vec::is_empty")]
138    pub tags: Vec<String>,
139    #[serde(skip_serializing_if = "Option::is_none")]
140    pub project: Option<String>,
141    #[serde(skip_serializing_if = "Option::is_none")]
142    pub agent: Option<String>,
143    #[serde(skip_serializing_if = "Option::is_none")]
144    pub session_id: Option<String>,
145    #[serde(skip_serializing_if = "Option::is_none")]
146    pub timestamp: Option<String>,
147    pub date: String,
148    pub source_chunk: String,
149}
150
151impl Eq for IntentEntry {}
152
153impl IntentEntry {
154    pub fn stable_id(source_chunk: &str, byte_offset: usize, entry_type: EntryType) -> String {
155        let mut hasher = siphasher::sip::SipHasher13::new();
156        source_chunk.hash(&mut hasher);
157        byte_offset.hash(&mut hasher);
158        entry_type.as_str().hash(&mut hasher);
159        format!("{:016x}", hasher.finish())
160    }
161}