1use super::*;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
9#[serde(rename_all = "kebab-case")]
10pub enum SemanticExactnessV1 {
11 Exact,
12 Degraded,
13 Partial,
14 Refuted,
15}
16
17#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
18pub struct SemanticStateV1 {
19 pub state_id: ArtifactId,
20 pub artifact_ref: ArtifactId,
21 pub provenance_carrier: String,
22 pub truth_carrier: String,
23 pub bitemporal_carrier: String,
24 pub identity_carrier: String,
25 pub execution_carrier: String,
26 pub view_carrier: String,
27 pub exactness: SemanticExactnessV1,
28 pub proof_carrier: String,
29 pub nuisance_or_degradation_carrier: String,
30 pub governance_carrier: String,
31 #[serde(default, skip_serializing_if = "Vec::is_empty")]
32 pub degradation_record_ids: Vec<ArtifactId>,
33 #[serde(default, skip_serializing_if = "Vec::is_empty")]
34 pub view_disclosure_ids: Vec<ArtifactId>,
35 #[serde(default, skip_serializing_if = "Vec::is_empty")]
36 pub contradiction_record_ids: Vec<ArtifactId>,
37 #[serde(default, skip_serializing_if = "Vec::is_empty")]
38 pub execution_contamination_ids: Vec<ArtifactId>,
39 #[serde(default, skip_serializing_if = "Vec::is_empty")]
40 pub reason_codes: Vec<String>,
41}
42
43impl SemanticStateV1 {
44 pub fn exact_supported(artifact_ref: ArtifactId, proof_carrier: impl Into<String>) -> Self {
45 Self {
46 state_id: generated_artifact_id_from_material("semantic-state", &artifact_ref.0),
47 artifact_ref,
48 provenance_carrier: "declared-input-manifest".into(),
49 truth_carrier: "canonical-owner-or-admitted-facade".into(),
50 bitemporal_carrier: "recorded-time-declared".into(),
51 identity_carrier: "stack-id-or-local-artifact-ref".into(),
52 execution_carrier: "execution-context-envelope".into(),
53 view_carrier: "view-disclosure-required".into(),
54 exactness: SemanticExactnessV1::Exact,
55 proof_carrier: proof_carrier.into(),
56 nuisance_or_degradation_carrier: "none".into(),
57 governance_carrier: "no-waiver".into(),
58 degradation_record_ids: Vec::new(),
59 view_disclosure_ids: Vec::new(),
60 contradiction_record_ids: Vec::new(),
61 execution_contamination_ids: Vec::new(),
62 reason_codes: vec!["semantic-state-exact".into()],
63 }
64 }
65
66 pub fn with_degradation(mut self, degradation: &LocalDegradationRecordV1) -> Self {
67 self.exactness = SemanticExactnessV1::Degraded;
68 self.nuisance_or_degradation_carrier = degradation.reason_code.clone();
69 self.degradation_record_ids
70 .push(degradation.degradation_id.clone());
71 self.reason_codes.push("semantic-state-degraded".into());
72 self.reason_codes.sort();
73 self.reason_codes.dedup();
74 self
75 }
76
77 pub fn with_view_disclosure(mut self, disclosure: &ViewDisclosureV1) -> Self {
78 self.view_disclosure_ids
79 .push(disclosure.disclosure_id.clone());
80 self.reason_codes
81 .push("semantic-state-view-disclosed".into());
82 self.reason_codes.sort();
83 self.reason_codes.dedup();
84 self
85 }
86
87 pub fn with_contradiction(mut self, contradiction: &SemanticContradictionRecordV1) -> Self {
88 self.exactness = SemanticExactnessV1::Refuted;
89 self.truth_carrier = "contradiction-disclosed".into();
90 self.contradiction_record_ids
91 .push(contradiction.contradiction_id.clone());
92 self.reason_codes
93 .push("semantic-state-contradiction-disclosed".into());
94 self.reason_codes.sort();
95 self.reason_codes.dedup();
96 self
97 }
98
99 pub fn with_execution_contamination(
100 mut self,
101 contamination: &ExecutionContaminationRecordV1,
102 ) -> Self {
103 self.exactness = SemanticExactnessV1::Degraded;
104 self.execution_carrier = contamination.reason_code.clone();
105 self.execution_contamination_ids
106 .push(contamination.contamination_id.clone());
107 self.reason_codes
108 .push("semantic-state-execution-contamination-disclosed".into());
109 self.reason_codes.sort();
110 self.reason_codes.dedup();
111 self
112 }
113
114 pub fn can_answer_as_exact(&self) -> bool {
115 self.exactness == SemanticExactnessV1::Exact
116 && self.degradation_record_ids.is_empty()
117 && self.contradiction_record_ids.is_empty()
118 && self.execution_contamination_ids.is_empty()
119 }
120
121 pub fn blocks_promotion(&self) -> bool {
122 !self.can_answer_as_exact()
123 || !self.contradiction_record_ids.is_empty()
124 || !self.execution_contamination_ids.is_empty()
125 }
126}
127
128#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
129pub struct SemanticContradictionRecordV1 {
130 pub contradiction_id: ArtifactId,
131 pub artifact_ref: ArtifactId,
132 #[serde(default, skip_serializing_if = "Vec::is_empty")]
133 pub witness_ids: Vec<ArtifactId>,
134 pub reason_code: String,
135 pub exactness_after: SemanticExactnessV1,
136 pub promotion_blocking: bool,
137 #[serde(default, skip_serializing_if = "Vec::is_empty")]
138 pub canonical_backpointers: Vec<CanonicalBackpointerV1>,
139 pub recorded_at: DateTime<Utc>,
140}
141
142impl SemanticContradictionRecordV1 {
143 pub fn new(
144 artifact_ref: ArtifactId,
145 witness_ids: Vec<ArtifactId>,
146 reason_code: impl Into<String>,
147 ) -> Self {
148 let reason_code = reason_code.into();
149 let material = format!(
150 "{}|{}|{}",
151 artifact_ref.0,
152 witness_ids
153 .iter()
154 .map(|id| id.0.clone())
155 .collect::<Vec<_>>()
156 .join("|"),
157 reason_code
158 );
159 Self {
160 contradiction_id: generated_artifact_id_from_material(
161 "semantic-contradiction",
162 &material,
163 ),
164 artifact_ref,
165 witness_ids,
166 reason_code,
167 exactness_after: SemanticExactnessV1::Refuted,
168 promotion_blocking: true,
169 canonical_backpointers: canonical_owner_backpointer(
170 "verification-adjudication",
171 "VerificationDisposition",
172 "canonical-contradiction-adjudication-owner",
173 ),
174 recorded_at: Utc::now(),
175 }
176 }
177}
178
179#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
180pub struct ExecutionContaminationRecordV1 {
181 pub contamination_id: ArtifactId,
182 pub artifact_ref: ArtifactId,
183 pub execution_context_ref: ArtifactId,
184 pub reason_code: String,
185 pub domain_truth_contaminated: bool,
186 pub exactness_after: SemanticExactnessV1,
187 #[serde(default, skip_serializing_if = "Vec::is_empty")]
188 pub canonical_backpointers: Vec<CanonicalBackpointerV1>,
189 pub recorded_at: DateTime<Utc>,
190}
191
192impl ExecutionContaminationRecordV1 {
193 pub fn new(
194 artifact_ref: ArtifactId,
195 execution_context_ref: ArtifactId,
196 reason_code: impl Into<String>,
197 ) -> Self {
198 let reason_code = reason_code.into();
199 Self {
200 contamination_id: generated_artifact_id_from_material(
201 "execution-contamination",
202 &format!(
203 "{}|{}|{}",
204 artifact_ref.0, execution_context_ref.0, reason_code
205 ),
206 ),
207 artifact_ref,
208 execution_context_ref,
209 reason_code,
210 domain_truth_contaminated: true,
211 exactness_after: SemanticExactnessV1::Degraded,
212 canonical_backpointers: canonical_owner_backpointer(
213 "llm-tool-runtime",
214 "ExecutionContext",
215 "canonical-execution-context-owner",
216 ),
217 recorded_at: Utc::now(),
218 }
219 }
220}
221
222#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
223pub struct ViewDisclosureV1 {
224 pub disclosure_id: ArtifactId,
225 pub view_family: String,
226 pub widening: bool,
227 pub support_label: String,
228 pub exactness: SemanticExactnessV1,
229 pub source_report_id: Option<ArtifactId>,
230 #[serde(default, skip_serializing_if = "Vec::is_empty")]
231 pub reason_codes: Vec<String>,
232 pub recorded_at: DateTime<Utc>,
233}
234
235impl ViewDisclosureV1 {
236 pub fn widening(
237 view_family: impl Into<String>,
238 support_label: impl Into<String>,
239 source_report_id: Option<ArtifactId>,
240 ) -> Self {
241 let view_family = view_family.into();
242 let support_label = support_label.into();
243 Self {
244 disclosure_id: generated_artifact_id_from_material(
245 "view-disclosure",
246 &format!("{view_family}|{support_label}|widening"),
247 ),
248 view_family,
249 widening: true,
250 support_label,
251 exactness: SemanticExactnessV1::Degraded,
252 source_report_id,
253 reason_codes: vec!["view-widening-disclosed".into()],
254 recorded_at: Utc::now(),
255 }
256 }
257}
258
259#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
260pub struct LocalDegradationRecordV1 {
261 pub degradation_id: ArtifactId,
262 pub artifact_ref: ArtifactId,
263 pub reason_code: String,
264 pub guarantee_weakened: String,
265 pub exactness_after: SemanticExactnessV1,
266 pub waived: bool,
267 #[serde(default, skip_serializing_if = "Vec::is_empty")]
268 pub reason_codes: Vec<String>,
269 pub recorded_at: DateTime<Utc>,
270}
271
272impl LocalDegradationRecordV1 {
273 pub fn new(
274 artifact_ref: ArtifactId,
275 reason_code: impl Into<String>,
276 guarantee_weakened: impl Into<String>,
277 ) -> Self {
278 let reason_code = reason_code.into();
279 let guarantee_weakened = guarantee_weakened.into();
280 Self {
281 degradation_id: generated_artifact_id_from_material(
282 "degradation-record",
283 &format!("{}|{}|{}", artifact_ref.0, reason_code, guarantee_weakened),
284 ),
285 artifact_ref,
286 reason_code,
287 guarantee_weakened,
288 exactness_after: SemanticExactnessV1::Degraded,
289 waived: false,
290 reason_codes: vec!["degradation-record-emitted".into()],
291 recorded_at: Utc::now(),
292 }
293 }
294
295 pub fn blocks_readiness_without_waiver(&self) -> bool {
296 !self.waived && self.exactness_after != SemanticExactnessV1::Exact
297 }
298}