1use serde::{Deserialize, Serialize};
11
12use crate::context::ContextKey;
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
16pub enum FactActorKind {
17 Human,
19 Suggestor,
21 System,
23}
24
25#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
27pub struct FactActor {
28 id: String,
29 kind: FactActorKind,
30}
31
32impl FactActor {
33 #[must_use]
35 pub fn id(&self) -> &str {
36 &self.id
37 }
38
39 #[must_use]
41 pub fn kind(&self) -> FactActorKind {
42 self.kind
43 }
44
45 #[cfg(feature = "kernel-authority")]
46 #[doc(hidden)]
47 pub fn new(id: impl Into<String>, kind: FactActorKind) -> Self {
48 Self {
49 id: id.into(),
50 kind,
51 }
52 }
53}
54
55#[derive(Debug, Clone, PartialEq, Eq, Serialize, Default)]
57pub struct FactValidationSummary {
58 checks_passed: Vec<String>,
59 checks_skipped: Vec<String>,
60 warnings: Vec<String>,
61}
62
63impl FactValidationSummary {
64 #[must_use]
66 pub fn checks_passed(&self) -> &[String] {
67 &self.checks_passed
68 }
69
70 #[must_use]
72 pub fn checks_skipped(&self) -> &[String] {
73 &self.checks_skipped
74 }
75
76 #[must_use]
78 pub fn warnings(&self) -> &[String] {
79 &self.warnings
80 }
81
82 #[cfg(feature = "kernel-authority")]
83 #[doc(hidden)]
84 pub fn new(
85 checks_passed: Vec<String>,
86 checks_skipped: Vec<String>,
87 warnings: Vec<String>,
88 ) -> Self {
89 Self {
90 checks_passed,
91 checks_skipped,
92 warnings,
93 }
94 }
95}
96
97#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
99#[serde(tag = "type", content = "id")]
100pub enum FactEvidenceRef {
101 Observation(String),
103 HumanApproval(String),
105 Derived(String),
107}
108
109#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
111pub struct FactLocalTrace {
112 trace_id: String,
113 span_id: String,
114 parent_span_id: Option<String>,
115 sampled: bool,
116}
117
118impl FactLocalTrace {
119 #[must_use]
121 pub fn trace_id(&self) -> &str {
122 &self.trace_id
123 }
124
125 #[must_use]
127 pub fn span_id(&self) -> &str {
128 &self.span_id
129 }
130
131 #[must_use]
133 pub fn parent_span_id(&self) -> Option<&str> {
134 self.parent_span_id.as_deref()
135 }
136
137 #[must_use]
139 pub fn sampled(&self) -> bool {
140 self.sampled
141 }
142
143 #[cfg(feature = "kernel-authority")]
144 #[doc(hidden)]
145 pub fn new(
146 trace_id: impl Into<String>,
147 span_id: impl Into<String>,
148 parent_span_id: Option<String>,
149 sampled: bool,
150 ) -> Self {
151 Self {
152 trace_id: trace_id.into(),
153 span_id: span_id.into(),
154 parent_span_id,
155 sampled,
156 }
157 }
158}
159
160#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
162pub struct FactRemoteTrace {
163 system: String,
164 reference: String,
165 retrieval_auth: Option<String>,
166 retention_hint: Option<String>,
167}
168
169impl FactRemoteTrace {
170 #[must_use]
172 pub fn system(&self) -> &str {
173 &self.system
174 }
175
176 #[must_use]
178 pub fn reference(&self) -> &str {
179 &self.reference
180 }
181
182 #[must_use]
184 pub fn retrieval_auth(&self) -> Option<&str> {
185 self.retrieval_auth.as_deref()
186 }
187
188 #[must_use]
190 pub fn retention_hint(&self) -> Option<&str> {
191 self.retention_hint.as_deref()
192 }
193
194 #[cfg(feature = "kernel-authority")]
195 #[doc(hidden)]
196 pub fn new(
197 system: impl Into<String>,
198 reference: impl Into<String>,
199 retrieval_auth: Option<String>,
200 retention_hint: Option<String>,
201 ) -> Self {
202 Self {
203 system: system.into(),
204 reference: reference.into(),
205 retrieval_auth,
206 retention_hint,
207 }
208 }
209}
210
211#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
213#[serde(tag = "type")]
214pub enum FactTraceLink {
215 Local(FactLocalTrace),
217 Remote(FactRemoteTrace),
219}
220
221impl FactTraceLink {
222 #[must_use]
224 pub fn is_replay_eligible(&self) -> bool {
225 matches!(self, Self::Local(_))
226 }
227}
228
229#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
231pub struct FactPromotionRecord {
232 gate_id: String,
233 policy_version_hash: String,
234 approver: FactActor,
235 validation_summary: FactValidationSummary,
236 evidence_refs: Vec<FactEvidenceRef>,
237 trace_link: FactTraceLink,
238 promoted_at: String,
239}
240
241impl FactPromotionRecord {
242 #[must_use]
244 pub fn gate_id(&self) -> &str {
245 &self.gate_id
246 }
247
248 #[must_use]
250 pub fn policy_version_hash(&self) -> &str {
251 &self.policy_version_hash
252 }
253
254 #[must_use]
256 pub fn approver(&self) -> &FactActor {
257 &self.approver
258 }
259
260 #[must_use]
262 pub fn validation_summary(&self) -> &FactValidationSummary {
263 &self.validation_summary
264 }
265
266 #[must_use]
268 pub fn evidence_refs(&self) -> &[FactEvidenceRef] {
269 &self.evidence_refs
270 }
271
272 #[must_use]
274 pub fn trace_link(&self) -> &FactTraceLink {
275 &self.trace_link
276 }
277
278 #[must_use]
280 pub fn promoted_at(&self) -> &str {
281 &self.promoted_at
282 }
283
284 #[must_use]
286 pub fn is_replay_eligible(&self) -> bool {
287 self.trace_link.is_replay_eligible()
288 }
289
290 #[cfg(feature = "kernel-authority")]
291 #[doc(hidden)]
292 pub fn new(
293 gate_id: impl Into<String>,
294 policy_version_hash: impl Into<String>,
295 approver: FactActor,
296 validation_summary: FactValidationSummary,
297 evidence_refs: Vec<FactEvidenceRef>,
298 trace_link: FactTraceLink,
299 promoted_at: impl Into<String>,
300 ) -> Self {
301 Self {
302 gate_id: gate_id.into(),
303 policy_version_hash: policy_version_hash.into(),
304 approver,
305 validation_summary,
306 evidence_refs,
307 trace_link,
308 promoted_at: promoted_at.into(),
309 }
310 }
311}
312
313#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
318pub struct Fact {
319 key: ContextKey,
321 pub id: String,
323 pub content: String,
325 promotion_record: FactPromotionRecord,
327 created_at: String,
329}
330
331impl Fact {
332 #[must_use]
334 pub fn key(&self) -> ContextKey {
335 self.key
336 }
337
338 #[must_use]
340 pub fn promotion_record(&self) -> &FactPromotionRecord {
341 &self.promotion_record
342 }
343
344 #[must_use]
346 pub fn created_at(&self) -> &str {
347 &self.created_at
348 }
349
350 #[must_use]
352 pub fn is_replay_eligible(&self) -> bool {
353 self.promotion_record.is_replay_eligible()
354 }
355}
356
357#[cfg(feature = "kernel-authority")]
359#[doc(hidden)]
360pub mod kernel_authority {
361 use super::*;
362
363 #[must_use]
365 pub fn new_fact(key: ContextKey, id: impl Into<String>, content: impl Into<String>) -> Fact {
366 new_fact_with_promotion(
367 key,
368 id,
369 content,
370 FactPromotionRecord::new(
371 "kernel-authority",
372 "0000000000000000000000000000000000000000000000000000000000000000",
373 FactActor::new("converge-kernel", FactActorKind::System),
374 FactValidationSummary::default(),
375 Vec::new(),
376 FactTraceLink::Local(FactLocalTrace::new("kernel-authority", "seed", None, true)),
377 "1970-01-01T00:00:00Z",
378 ),
379 "1970-01-01T00:00:00Z",
380 )
381 }
382
383 #[must_use]
385 pub fn new_fact_with_promotion(
386 key: ContextKey,
387 id: impl Into<String>,
388 content: impl Into<String>,
389 promotion_record: FactPromotionRecord,
390 created_at: impl Into<String>,
391 ) -> Fact {
392 Fact {
393 key,
394 id: id.into(),
395 content: content.into(),
396 promotion_record,
397 created_at: created_at.into(),
398 }
399 }
400}
401
402#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
407pub struct ProposedFact {
408 pub key: ContextKey,
410 pub id: String,
412 pub content: String,
414 pub confidence: f64,
416 pub provenance: String,
418}
419
420impl ProposedFact {
421 #[must_use]
423 pub fn new(
424 key: ContextKey,
425 id: impl Into<String>,
426 content: impl Into<String>,
427 provenance: impl Into<String>,
428 ) -> Self {
429 Self {
430 key,
431 id: id.into(),
432 content: content.into(),
433 confidence: 1.0,
434 provenance: provenance.into(),
435 }
436 }
437
438 #[must_use]
440 pub fn with_confidence(mut self, confidence: f64) -> Self {
441 self.confidence = confidence;
442 self
443 }
444}
445
446#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
448pub struct ValidationError {
449 pub reason: String,
451}
452
453impl std::fmt::Display for ValidationError {
454 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
455 write!(f, "validation failed: {}", self.reason)
456 }
457}
458
459impl std::error::Error for ValidationError {}