Skip to main content

mythos_skill/
schema.rs

1use serde::{Deserialize, Serialize};
2
3/// Current Mythos packet schema version. Bump when the `NextPassPacket` or
4/// `Snapshot` shape changes in a way downstream consumers must react to.
5pub const MYTHOS_SCHEMA_VERSION: &str = "1.1.0";
6
7/// Canonical hash algorithm label emitted for every `SourceRef.hash` value.
8/// The strict gate and ingester must reject source refs whose `hash_alg` does
9/// not match, so bumping this is a breaking change for evidence in flight.
10pub const MYTHOS_HASH_ALG: &str = "fnv1a-64";
11
12fn default_hash_alg() -> String {
13    MYTHOS_HASH_ALG.to_string()
14}
15
16fn default_schema_version() -> String {
17    MYTHOS_SCHEMA_VERSION.to_string()
18}
19
20#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
21pub struct SourceRef {
22    pub source_id: String,
23    pub path: String,
24    pub kind: String,
25    pub hash: String,
26    #[serde(default = "default_hash_alg")]
27    pub hash_alg: String,
28    pub span: Option<String>,
29    pub observed_at: String,
30}
31
32#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
33pub struct CompiledFact {
34    pub id: String,
35    pub statement: String,
36    pub confidence: f32,
37    pub objective_relevance: f32,
38    pub novelty_gain: f32,
39    pub needs_raw_drilldown: bool,
40    pub source_ids: Vec<String>,
41}
42
43#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
44pub struct EvidenceRecord {
45    pub id: String,
46    pub kind: String,
47    pub summary: String,
48    pub source_ids: Vec<String>,
49    #[serde(default, skip_serializing_if = "Vec::is_empty")]
50    pub source_refs: Vec<SourceRef>,
51    pub observed_at: String,
52    #[serde(default, skip_serializing_if = "Option::is_none")]
53    pub agent_id: Option<String>,
54    #[serde(default, skip_serializing_if = "Option::is_none")]
55    pub lane: Option<String>,
56    #[serde(default, skip_serializing_if = "Option::is_none")]
57    pub confidence: Option<f64>,
58    #[serde(default, skip_serializing_if = "Option::is_none")]
59    pub rationale: Option<String>,
60    #[serde(default, skip_serializing_if = "Option::is_none")]
61    pub diff_ref: Option<String>,
62    #[serde(default, skip_serializing_if = "Option::is_none")]
63    pub span_before: Option<String>,
64    #[serde(default, skip_serializing_if = "Option::is_none")]
65    pub span_after: Option<String>,
66}
67
68#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
69pub struct Hypothesis {
70    pub id: String,
71    pub statement: String,
72    pub confidence: f32,
73    pub verifier_score: Option<f32>,
74    pub source_ids: Vec<String>,
75}
76
77#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
78pub struct Contradiction {
79    pub id: String,
80    pub summary: String,
81    pub conflicting_item_ids: Vec<String>,
82    pub severity: String,
83    pub source_ids: Vec<String>,
84    /// G5: Optional source_refs so tampered files cited by a contradiction are
85    /// detectable. Back-compat: serializes as absent when None.
86    #[serde(default, skip_serializing_if = "Option::is_none")]
87    pub source_refs: Option<Vec<SourceRef>>,
88}
89
90#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
91pub struct RecurringFailurePattern {
92    pub id: String,
93    pub summary: String,
94    pub count: u32,
95    pub last_seen_at: String,
96    pub impact: String,
97    pub source_ids: Vec<String>,
98}
99
100#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
101pub struct CandidateAction {
102    pub id: String,
103    pub title: String,
104    pub rationale: String,
105    pub actionability_score: f32,
106    pub decision_dependency_ids: Vec<String>,
107    pub source_ids: Vec<String>,
108}
109
110#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
111pub struct VerifierFinding {
112    pub id: String,
113    pub summary: String,
114    pub status: String,
115    pub verifier_score: f32,
116    pub source_ids: Vec<String>,
117    #[serde(default, skip_serializing_if = "Vec::is_empty")]
118    pub source_refs: Vec<SourceRef>,
119    #[serde(default, skip_serializing_if = "Option::is_none")]
120    pub agent_id: Option<String>,
121    #[serde(default, skip_serializing_if = "Option::is_none")]
122    pub lane: Option<String>,
123    /// Optional justification stamp explaining why a `status:"passed"` finding
124    /// is a bounded-audit / bounded-investigation closure rather than a
125    /// genuine green. When present and non-empty, the strict gate treats the
126    /// finding as explicitly closed with scope, so the string-hack prefix
127    /// ("AUDIT-SCOPE PASSED:" etc.) Prime used in Pass 1/2 becomes typed.
128    #[serde(default, skip_serializing_if = "Option::is_none")]
129    pub closure_reason: Option<String>,
130}
131
132#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
133pub struct HaltSignal {
134    pub id: String,
135    pub kind: String,
136    pub contribution: f32,
137    pub rationale: String,
138    pub source_ids: Vec<String>,
139}
140
141#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
142pub struct NextPassPacket {
143    #[serde(default = "default_schema_version")]
144    pub schema_version: String,
145    pub objective_id: String,
146    pub run_id: String,
147    pub branch_id: String,
148    pub pass_id: String,
149    pub objective: String,
150    pub evidence: Vec<EvidenceRecord>,
151    pub trusted_facts: Vec<CompiledFact>,
152    pub active_hypotheses: Vec<Hypothesis>,
153    pub contradictions: Vec<Contradiction>,
154    pub recurring_failure_patterns: Vec<RecurringFailurePattern>,
155    pub candidate_actions: Vec<CandidateAction>,
156    pub verifier_findings: Vec<VerifierFinding>,
157    pub open_questions: Vec<String>,
158    pub raw_drilldown_refs: Vec<SourceRef>,
159    pub halt_signals: Vec<HaltSignal>,
160    pub sources: Vec<SourceRef>,
161}
162
163#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
164pub struct SnapshotInput {
165    pub id: String,
166    pub kind: String,
167    pub summary: String,
168    pub ref_ids: Vec<String>,
169}
170
171#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
172pub struct WorkerResult {
173    pub id: String,
174    pub worker: String,
175    pub status: String,
176    pub output_ids: Vec<String>,
177    pub notes: String,
178}
179
180#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
181pub struct StateDelta {
182    pub id: String,
183    pub kind: String,
184    pub target_id: String,
185    pub summary: String,
186}
187
188#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
189pub struct Snapshot {
190    #[serde(default = "default_schema_version")]
191    pub schema_version: String,
192    pub run_id: String,
193    pub pass_id: String,
194    pub branch_id: String,
195    pub created_at: String,
196    pub inputs: Vec<SnapshotInput>,
197    pub worker_results: Vec<WorkerResult>,
198    pub state_delta: Vec<StateDelta>,
199    pub artifact_refs: Vec<SourceRef>,
200}
201
202#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
203pub struct PromotionRecord {
204    pub id: String,
205    pub kind: String,
206    pub source_ids: Vec<String>,
207    pub decision: String,
208    pub reason: String,
209    pub expires_after_pass: Option<String>,
210}
211
212#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
213pub struct DecisionLogRecord {
214    pub id: String,
215    pub run_id: String,
216    pub pass_id: String,
217    pub decision_kind: String,
218    pub summary: String,
219    pub source_ids: Vec<String>,
220    pub selected_action_ids: Vec<String>,
221    pub created_at: String,
222    pub promotion: Option<PromotionRecord>,
223}