use serde::{Deserialize, Serialize};
use crate::context::ContextKey;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
pub enum FactActorKind {
Human,
Suggestor,
System,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct FactActor {
id: String,
kind: FactActorKind,
}
impl FactActor {
#[must_use]
pub fn id(&self) -> &str {
&self.id
}
#[must_use]
pub fn kind(&self) -> FactActorKind {
self.kind
}
#[cfg(feature = "kernel-authority")]
#[doc(hidden)]
pub fn new(id: impl Into<String>, kind: FactActorKind) -> Self {
Self {
id: id.into(),
kind,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Default)]
pub struct FactValidationSummary {
checks_passed: Vec<String>,
checks_skipped: Vec<String>,
warnings: Vec<String>,
}
impl FactValidationSummary {
#[must_use]
pub fn checks_passed(&self) -> &[String] {
&self.checks_passed
}
#[must_use]
pub fn checks_skipped(&self) -> &[String] {
&self.checks_skipped
}
#[must_use]
pub fn warnings(&self) -> &[String] {
&self.warnings
}
#[cfg(feature = "kernel-authority")]
#[doc(hidden)]
pub fn new(
checks_passed: Vec<String>,
checks_skipped: Vec<String>,
warnings: Vec<String>,
) -> Self {
Self {
checks_passed,
checks_skipped,
warnings,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
#[serde(tag = "type", content = "id")]
pub enum FactEvidenceRef {
Observation(String),
HumanApproval(String),
Derived(String),
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct FactLocalTrace {
trace_id: String,
span_id: String,
parent_span_id: Option<String>,
sampled: bool,
}
impl FactLocalTrace {
#[must_use]
pub fn trace_id(&self) -> &str {
&self.trace_id
}
#[must_use]
pub fn span_id(&self) -> &str {
&self.span_id
}
#[must_use]
pub fn parent_span_id(&self) -> Option<&str> {
self.parent_span_id.as_deref()
}
#[must_use]
pub fn sampled(&self) -> bool {
self.sampled
}
#[cfg(feature = "kernel-authority")]
#[doc(hidden)]
pub fn new(
trace_id: impl Into<String>,
span_id: impl Into<String>,
parent_span_id: Option<String>,
sampled: bool,
) -> Self {
Self {
trace_id: trace_id.into(),
span_id: span_id.into(),
parent_span_id,
sampled,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct FactRemoteTrace {
system: String,
reference: String,
retrieval_auth: Option<String>,
retention_hint: Option<String>,
}
impl FactRemoteTrace {
#[must_use]
pub fn system(&self) -> &str {
&self.system
}
#[must_use]
pub fn reference(&self) -> &str {
&self.reference
}
#[must_use]
pub fn retrieval_auth(&self) -> Option<&str> {
self.retrieval_auth.as_deref()
}
#[must_use]
pub fn retention_hint(&self) -> Option<&str> {
self.retention_hint.as_deref()
}
#[cfg(feature = "kernel-authority")]
#[doc(hidden)]
pub fn new(
system: impl Into<String>,
reference: impl Into<String>,
retrieval_auth: Option<String>,
retention_hint: Option<String>,
) -> Self {
Self {
system: system.into(),
reference: reference.into(),
retrieval_auth,
retention_hint,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
#[serde(tag = "type")]
pub enum FactTraceLink {
Local(FactLocalTrace),
Remote(FactRemoteTrace),
}
impl FactTraceLink {
#[must_use]
pub fn is_replay_eligible(&self) -> bool {
matches!(self, Self::Local(_))
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct FactPromotionRecord {
gate_id: String,
policy_version_hash: String,
approver: FactActor,
validation_summary: FactValidationSummary,
evidence_refs: Vec<FactEvidenceRef>,
trace_link: FactTraceLink,
promoted_at: String,
}
impl FactPromotionRecord {
#[must_use]
pub fn gate_id(&self) -> &str {
&self.gate_id
}
#[must_use]
pub fn policy_version_hash(&self) -> &str {
&self.policy_version_hash
}
#[must_use]
pub fn approver(&self) -> &FactActor {
&self.approver
}
#[must_use]
pub fn validation_summary(&self) -> &FactValidationSummary {
&self.validation_summary
}
#[must_use]
pub fn evidence_refs(&self) -> &[FactEvidenceRef] {
&self.evidence_refs
}
#[must_use]
pub fn trace_link(&self) -> &FactTraceLink {
&self.trace_link
}
#[must_use]
pub fn promoted_at(&self) -> &str {
&self.promoted_at
}
#[must_use]
pub fn is_replay_eligible(&self) -> bool {
self.trace_link.is_replay_eligible()
}
#[cfg(feature = "kernel-authority")]
#[doc(hidden)]
pub fn new(
gate_id: impl Into<String>,
policy_version_hash: impl Into<String>,
approver: FactActor,
validation_summary: FactValidationSummary,
evidence_refs: Vec<FactEvidenceRef>,
trace_link: FactTraceLink,
promoted_at: impl Into<String>,
) -> Self {
Self {
gate_id: gate_id.into(),
policy_version_hash: policy_version_hash.into(),
approver,
validation_summary,
evidence_refs,
trace_link,
promoted_at: promoted_at.into(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct Fact {
key: ContextKey,
pub id: String,
pub content: String,
promotion_record: FactPromotionRecord,
created_at: String,
}
impl Fact {
#[must_use]
pub fn key(&self) -> ContextKey {
self.key
}
#[must_use]
pub fn promotion_record(&self) -> &FactPromotionRecord {
&self.promotion_record
}
#[must_use]
pub fn created_at(&self) -> &str {
&self.created_at
}
#[must_use]
pub fn is_replay_eligible(&self) -> bool {
self.promotion_record.is_replay_eligible()
}
}
#[cfg(feature = "kernel-authority")]
#[doc(hidden)]
pub mod kernel_authority {
use super::*;
#[must_use]
pub fn new_fact(key: ContextKey, id: impl Into<String>, content: impl Into<String>) -> Fact {
new_fact_with_promotion(
key,
id,
content,
FactPromotionRecord::new(
"kernel-authority",
"0000000000000000000000000000000000000000000000000000000000000000",
FactActor::new("converge-kernel", FactActorKind::System),
FactValidationSummary::default(),
Vec::new(),
FactTraceLink::Local(FactLocalTrace::new("kernel-authority", "seed", None, true)),
"1970-01-01T00:00:00Z",
),
"1970-01-01T00:00:00Z",
)
}
#[must_use]
pub fn new_fact_with_promotion(
key: ContextKey,
id: impl Into<String>,
content: impl Into<String>,
promotion_record: FactPromotionRecord,
created_at: impl Into<String>,
) -> Fact {
Fact {
key,
id: id.into(),
content: content.into(),
promotion_record,
created_at: created_at.into(),
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ProposedFact {
pub key: ContextKey,
pub id: String,
pub content: String,
pub confidence: f64,
pub provenance: String,
}
impl ProposedFact {
#[must_use]
pub fn new(
key: ContextKey,
id: impl Into<String>,
content: impl Into<String>,
provenance: impl Into<String>,
) -> Self {
Self {
key,
id: id.into(),
content: content.into(),
confidence: 1.0,
provenance: provenance.into(),
}
}
#[must_use]
pub fn with_confidence(mut self, confidence: f64) -> Self {
self.confidence = confidence;
self
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ValidationError {
pub reason: String,
}
impl std::fmt::Display for ValidationError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "validation failed: {}", self.reason)
}
}
impl std::error::Error for ValidationError {}