evidential_protocol/types.rs
1//! Core types for the Evidential Protocol.
2//!
3//! Every AI-generated claim declares *how it knows* via an [`EvidenceClass`],
4//! confidence score, and provenance chain. These types implement the EP/1.0
5//! wire format.
6
7use serde::{Deserialize, Serialize};
8use std::collections::HashMap;
9use std::fmt;
10
11// ---------------------------------------------------------------------------
12// EvidenceClass
13// ---------------------------------------------------------------------------
14
15/// Classification of how a piece of evidence was obtained.
16///
17/// Ordered from strongest (Direct) to weakest (Conjecture). The ordering
18/// intentionally mirrors epistemic certainty so that `min()`/`max()` on
19/// collections of classes yield the weakest/strongest respectively.
20#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
21#[serde(rename_all = "lowercase")]
22pub enum EvidenceClass {
23 /// First-hand observation or authoritative API response.
24 Direct = 4,
25 /// Logically derived from direct evidence.
26 Inferred = 3,
27 /// Second-hand information from a named source.
28 Reported = 2,
29 /// Speculative or hypothetical — must include reasoning.
30 Conjecture = 1,
31}
32
33impl fmt::Display for EvidenceClass {
34 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
35 match self {
36 Self::Direct => write!(f, "direct"),
37 Self::Inferred => write!(f, "inferred"),
38 Self::Reported => write!(f, "reported"),
39 Self::Conjecture => write!(f, "conjecture"),
40 }
41 }
42}
43
44impl EvidenceClass {
45 /// Numeric strength ranking (4 = strongest).
46 pub fn strength(&self) -> u8 {
47 match self {
48 Self::Direct => 4,
49 Self::Inferred => 3,
50 Self::Reported => 2,
51 Self::Conjecture => 1,
52 }
53 }
54
55 /// Minimum confidence a claim of this class should carry.
56 pub fn confidence_floor(&self) -> f64 {
57 match self {
58 Self::Direct => 0.9,
59 Self::Inferred => 0.5,
60 Self::Reported => 0.3,
61 Self::Conjecture => 0.1,
62 }
63 }
64
65 /// Weight multiplier used in aggregate scoring.
66 pub fn weight(&self) -> f64 {
67 match self {
68 Self::Direct => 1.0,
69 Self::Inferred => 0.7,
70 Self::Reported => 0.4,
71 Self::Conjecture => 0.15,
72 }
73 }
74}
75
76// ---------------------------------------------------------------------------
77// EvidenceSource
78// ---------------------------------------------------------------------------
79
80/// A single provenance entry within an [`Evidence`] chain.
81#[derive(Debug, Clone, Serialize, Deserialize)]
82pub struct EvidenceSource {
83 /// Classification of this particular source.
84 pub class: EvidenceClass,
85 /// Human-readable source identifier (e.g. "coingecko-api").
86 pub source: String,
87 /// Freeform detail about what was retrieved.
88 pub detail: String,
89 /// Optional ISO-8601 timestamp of when the source was consulted.
90 pub timestamp: Option<String>,
91}
92
93// ---------------------------------------------------------------------------
94// Evidence
95// ---------------------------------------------------------------------------
96
97/// The epistemic metadata attached to a single claim.
98#[derive(Debug, Clone, Serialize, Deserialize)]
99pub struct Evidence {
100 /// Overall classification.
101 pub class: EvidenceClass,
102 /// Confidence score in `[0.0, 1.0]`.
103 pub confidence: f64,
104 /// Short identifier of the producing agent or tool.
105 pub source: String,
106 /// Required for [`EvidenceClass::Conjecture`]; explains the reasoning.
107 pub reasoning: Option<String>,
108 /// ISO-8601 timestamp of when the evidence was produced.
109 pub timestamp: String,
110 /// Optional time-to-live in seconds before the evidence expires.
111 pub ttl: Option<u64>,
112 /// Optional provenance chain of upstream sources.
113 pub sources: Option<Vec<EvidenceSource>>,
114}
115
116// ---------------------------------------------------------------------------
117// EvidentialClaim
118// ---------------------------------------------------------------------------
119
120/// A single claim with its supporting [`Evidence`].
121#[derive(Debug, Clone, Serialize, Deserialize)]
122pub struct EvidentialClaim {
123 /// The assertion being made.
124 pub claim: String,
125 /// Epistemic metadata backing the claim.
126 pub evidence: Evidence,
127 /// Optional list of reference URLs or identifiers.
128 pub refs: Option<Vec<String>>,
129}
130
131// ---------------------------------------------------------------------------
132// EvidentialResponse
133// ---------------------------------------------------------------------------
134
135/// A complete EP/1.0 response envelope wrapping arbitrary data with
136/// epistemic metadata.
137#[derive(Debug, Clone, Serialize, Deserialize)]
138pub struct EvidentialResponse<T: Serialize> {
139 /// Protocol version — always `"1.0"` for this implementation.
140 #[serde(default = "default_ep_version")]
141 pub ep_version: String,
142 /// The payload data.
143 pub data: T,
144 /// All claims made in this response.
145 pub claims: Vec<EvidentialClaim>,
146 /// Weakest class among all claims.
147 pub aggregate_class: EvidenceClass,
148 /// Weighted mean confidence across all claims.
149 pub aggregate_confidence: f64,
150 /// Identifier of the agent/system that produced this response.
151 pub producer: String,
152 /// ISO-8601 timestamp of when this response was produced.
153 pub produced_at: String,
154}
155
156fn default_ep_version() -> String {
157 "1.0".to_string()
158}
159
160// ---------------------------------------------------------------------------
161// TrustScore
162// ---------------------------------------------------------------------------
163
164/// Aggregate trust assessment computed from a set of claims.
165#[derive(Debug, Clone, Serialize, Deserialize)]
166pub struct TrustScore {
167 /// Weighted trust score in `[0.0, 1.0]`.
168 pub score: f64,
169 /// Total number of claims evaluated.
170 pub total_claims: usize,
171 /// Count of claims by evidence class.
172 pub breakdown: HashMap<EvidenceClass, usize>,
173 /// `true` if any claim is Conjecture or score < 0.5.
174 pub requires_review: bool,
175}
176
177// ---------------------------------------------------------------------------
178// DegradationReason / DegradationEvent
179// ---------------------------------------------------------------------------
180
181/// Reason an evidence class was downgraded.
182#[derive(Debug, Clone, Serialize, Deserialize)]
183pub enum DegradationReason {
184 /// The evidence's TTL has expired.
185 TtlExpired,
186 /// The evidence is older than 24 hours with no TTL.
187 Stale24h,
188 /// The upstream source is no longer reachable.
189 SourceUnavailable,
190}
191
192/// Record of a claim's evidence class being downgraded.
193#[derive(Debug, Clone, Serialize, Deserialize)]
194pub struct DegradationEvent {
195 /// Identifier for the claim that was degraded.
196 pub claim_id: String,
197 /// Original evidence class before degradation.
198 pub from_class: EvidenceClass,
199 /// New evidence class after degradation.
200 pub to_class: EvidenceClass,
201 /// Why the degradation happened.
202 pub reason: DegradationReason,
203 /// ISO-8601 timestamp of when degradation was detected.
204 pub timestamp: String,
205}
206
207// ---------------------------------------------------------------------------
208// ContentEvidenceMarker
209// ---------------------------------------------------------------------------
210
211/// Inline marker embedded in content to annotate evidence provenance.
212///
213/// Wire format: `[EP:<class>:<source>:<confidence>]`
214#[derive(Debug, Clone, Serialize, Deserialize)]
215pub struct ContentEvidenceMarker {
216 /// Evidence class for this content segment.
217 pub class: EvidenceClass,
218 /// Source identifier.
219 pub source: String,
220 /// Confidence score in `[0.0, 1.0]`.
221 pub confidence: f64,
222}