Skip to main content

aidens_contracts/
boundary.rs

1//! Boundary compiler and repair receipts for parser/treatment integrity.
2//!
3//! These are AiDENs-local receipt envelopes around boundary behavior. Canonical
4//! schema and boundary semantics remain with their owner crates.
5
6use super::*;
7
8#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
9pub struct BoundaryCompilerProfileV1 {
10    pub profile_id: ArtifactId,
11    pub language: String,
12    pub dialect: String,
13    pub schema_identity: String,
14    pub duplicate_key_policy: String,
15    pub unknown_field_policy: String,
16    pub canonicalization_profile: String,
17    pub repair_policy: String,
18    pub treatment_integrity_required: bool,
19    pub max_bytes: u64,
20    pub max_depth: u32,
21    #[serde(default, skip_serializing_if = "Vec::is_empty")]
22    pub reason_codes: Vec<String>,
23}
24
25impl BoundaryCompilerProfileV1 {
26    pub fn strict_json(schema_identity: impl Into<String>) -> Self {
27        let schema_identity = schema_identity.into();
28        Self {
29            profile_id: generated_artifact_id_from_material(
30                "boundary-profile",
31                &format!("strict-json|{schema_identity}"),
32            ),
33            language: "json".into(),
34            dialect: "json-schema-2020-12-subset".into(),
35            schema_identity,
36            duplicate_key_policy: "reject".into(),
37            unknown_field_policy: "schema-controlled".into(),
38            canonicalization_profile: "stack-ids-json-c14n-v1".into(),
39            repair_policy: "receipt-required-no-treatment-change".into(),
40            treatment_integrity_required: true,
41            max_bytes: 1_048_576,
42            max_depth: 64,
43            reason_codes: vec!["boundary-profile-strict-json".into()],
44        }
45    }
46
47    pub fn rejects_duplicate_keys(&self) -> bool {
48        self.duplicate_key_policy == "reject"
49    }
50}
51
52#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
53pub struct TreatmentIntegrityReceiptV1 {
54    pub receipt_id: ArtifactId,
55    pub profile_id: ArtifactId,
56    pub before_digest: DisplayDigestV1,
57    pub after_digest: DisplayDigestV1,
58    pub treatment_critical_fields: Vec<String>,
59    pub treatment_changed: bool,
60    pub hard_failed: bool,
61    #[serde(default, skip_serializing_if = "Vec::is_empty")]
62    pub reason_codes: Vec<String>,
63    pub recorded_at: DateTime<Utc>,
64}
65
66impl TreatmentIntegrityReceiptV1 {
67    pub fn compare(
68        profile: &BoundaryCompilerProfileV1,
69        before: &serde_json::Value,
70        after: &serde_json::Value,
71        treatment_critical_fields: Vec<String>,
72    ) -> Self {
73        let treatment_changed = treatment_critical_fields
74            .iter()
75            .any(|field| before.pointer(field) != after.pointer(field));
76        let hard_failed = treatment_changed && profile.treatment_integrity_required;
77        let material = serde_json::json!({
78            "profile": profile.profile_id,
79            "before": before,
80            "after": after,
81            "fields": treatment_critical_fields,
82        });
83        Self {
84            receipt_id: generated_artifact_id_from_material(
85                "treatment-integrity",
86                &non_authoritative_json_display_digest(&material),
87            ),
88            profile_id: profile.profile_id.clone(),
89            before_digest: DisplayDigestV1::for_json_value(before),
90            after_digest: DisplayDigestV1::for_json_value(after),
91            treatment_critical_fields,
92            treatment_changed,
93            hard_failed,
94            reason_codes: if hard_failed {
95                vec!["treatment-integrity-hard-fail".into()]
96            } else if treatment_changed {
97                vec!["treatment-integrity-degraded".into()]
98            } else {
99                vec!["treatment-integrity-preserved".into()]
100            },
101            recorded_at: Utc::now(),
102        }
103    }
104}
105
106#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
107pub struct BoundaryRepairReceiptV1 {
108    pub receipt_id: ArtifactId,
109    pub profile_id: ArtifactId,
110    pub repair_kind: String,
111    pub before_digest: DisplayDigestV1,
112    pub after_digest: DisplayDigestV1,
113    pub treatment_integrity_receipt_id: ArtifactId,
114    pub accepted: bool,
115    #[serde(default, skip_serializing_if = "Vec::is_empty")]
116    pub reason_codes: Vec<String>,
117    pub recorded_at: DateTime<Utc>,
118}
119
120impl BoundaryRepairReceiptV1 {
121    pub fn new(
122        profile: &BoundaryCompilerProfileV1,
123        repair_kind: impl Into<String>,
124        before: &serde_json::Value,
125        after: &serde_json::Value,
126        treatment: &TreatmentIntegrityReceiptV1,
127    ) -> Self {
128        let repair_kind = repair_kind.into();
129        let accepted = !treatment.hard_failed;
130        let material = format!(
131            "{}|{}|{}",
132            profile.profile_id.0, repair_kind, treatment.receipt_id.0
133        );
134        Self {
135            receipt_id: generated_artifact_id_from_material("boundary-repair", &material),
136            profile_id: profile.profile_id.clone(),
137            repair_kind,
138            before_digest: DisplayDigestV1::for_json_value(before),
139            after_digest: DisplayDigestV1::for_json_value(after),
140            treatment_integrity_receipt_id: treatment.receipt_id.clone(),
141            accepted,
142            reason_codes: if accepted {
143                vec!["boundary-repair-receipted".into()]
144            } else {
145                vec!["boundary-repair-rejected-treatment-change".into()]
146            },
147            recorded_at: Utc::now(),
148        }
149    }
150}
151
152#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
153pub struct BoundaryCompileReceiptV1 {
154    pub receipt_id: ArtifactId,
155    pub profile_id: ArtifactId,
156    pub input_digest: DisplayDigestV1,
157    #[serde(default, skip_serializing_if = "Option::is_none")]
158    pub output_digest: Option<DisplayDigestV1>,
159    pub accepted: bool,
160    pub duplicate_keys_rejected: bool,
161    pub schema_validated: bool,
162    pub canonicalized: bool,
163    #[serde(default, skip_serializing_if = "Option::is_none")]
164    pub repair_receipt_id: Option<ArtifactId>,
165    #[serde(default, skip_serializing_if = "Option::is_none")]
166    pub treatment_integrity_receipt_id: Option<ArtifactId>,
167    #[serde(default, skip_serializing_if = "Vec::is_empty")]
168    pub reason_codes: Vec<String>,
169    pub recorded_at: DateTime<Utc>,
170}
171
172impl BoundaryCompileReceiptV1 {
173    pub fn accepted(
174        profile: &BoundaryCompilerProfileV1,
175        input: &serde_json::Value,
176        output: &serde_json::Value,
177        repair_receipt_id: Option<ArtifactId>,
178        treatment_integrity_receipt_id: Option<ArtifactId>,
179    ) -> Self {
180        let input_digest = DisplayDigestV1::for_json_value(input);
181        let output_digest = DisplayDigestV1::for_json_value(output);
182        let material = format!(
183            "{}|{}|{}",
184            profile.profile_id.0, input_digest.digest, output_digest.digest
185        );
186        Self {
187            receipt_id: generated_artifact_id_from_material("boundary-compile", &material),
188            profile_id: profile.profile_id.clone(),
189            input_digest,
190            output_digest: Some(output_digest),
191            accepted: true,
192            duplicate_keys_rejected: profile.rejects_duplicate_keys(),
193            schema_validated: true,
194            canonicalized: true,
195            repair_receipt_id,
196            treatment_integrity_receipt_id,
197            reason_codes: vec!["boundary-compile-accepted".into()],
198            recorded_at: Utc::now(),
199        }
200    }
201
202    pub fn rejected_duplicate_key(
203        profile: &BoundaryCompilerProfileV1,
204        input: &serde_json::Value,
205    ) -> Self {
206        let input_digest = DisplayDigestV1::for_json_value(input);
207        let material = format!(
208            "{}|{}|duplicate-key",
209            profile.profile_id.0, input_digest.digest
210        );
211        Self {
212            receipt_id: generated_artifact_id_from_material("boundary-compile", &material),
213            profile_id: profile.profile_id.clone(),
214            input_digest,
215            output_digest: None,
216            accepted: false,
217            duplicate_keys_rejected: true,
218            schema_validated: false,
219            canonicalized: false,
220            repair_receipt_id: None,
221            treatment_integrity_receipt_id: None,
222            reason_codes: vec!["duplicate-json-object-key".into()],
223            recorded_at: Utc::now(),
224        }
225    }
226}
227
228/// Receipt for a conformance run — proves that a fixture corpus was evaluated
229/// and each fixture produced a pass/fail result.
230///
231/// This is the P32 schema-compatibility receipt type. It registers in the
232/// `boundary-compile` artifact family alongside BoundaryCompileReceiptV1.
233#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
234pub struct ConformanceRunReceiptV1 {
235    pub receipt_id: ArtifactId,
236    pub profile_id: ArtifactId,
237    pub fixture_count: u32,
238    pub passed_count: u32,
239    pub failed_count: u32,
240    pub fixture_results: Vec<ConformanceFixtureResultV1>,
241    #[serde(default, skip_serializing_if = "Option::is_none")]
242    pub environment: Option<ConformanceEnvironmentV1>,
243    #[serde(default, skip_serializing_if = "Vec::is_empty")]
244    pub reason_codes: Vec<String>,
245    pub recorded_at: DateTime<Utc>,
246}
247
248#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
249pub struct ConformanceFixtureResultV1 {
250    pub fixture_id: String,
251    pub passed: bool,
252    #[serde(default, skip_serializing_if = "Option::is_none")]
253    pub input_digest: Option<DisplayDigestV1>,
254    #[serde(default, skip_serializing_if = "Option::is_none")]
255    pub expected_digest: Option<DisplayDigestV1>,
256    #[serde(default, skip_serializing_if = "Option::is_none")]
257    pub actual_digest: Option<DisplayDigestV1>,
258    #[serde(default, skip_serializing_if = "Vec::is_empty")]
259    pub reason_codes: Vec<String>,
260}
261
262#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
263pub struct ConformanceEnvironmentV1 {
264    pub rustc_version: String,
265    pub target_triple: String,
266    #[serde(default, skip_serializing_if = "Option::is_none")]
267    pub ci_run_id: Option<String>,
268}
269
270impl ConformanceRunReceiptV1 {
271    pub fn new(
272        profile_id: ArtifactId,
273        fixture_results: Vec<ConformanceFixtureResultV1>,
274        environment: Option<ConformanceEnvironmentV1>,
275    ) -> Self {
276        let passed_count = fixture_results.iter().filter(|r| r.passed).count() as u32;
277        let failed_count = fixture_results.len() as u32 - passed_count;
278        let fixture_count = fixture_results.len() as u32;
279        let material = format!(
280            "{}|{}|{}|{}",
281            profile_id.0,
282            fixture_count,
283            passed_count,
284            fixture_results
285                .iter()
286                .map(|r| if r.passed { "p" } else { "f" })
287                .collect::<Vec<_>>()
288                .join("")
289        );
290        let reason_codes = if failed_count == 0 {
291            vec!["conformance-run-passed".into()]
292        } else {
293            vec!["conformance-run-failures".into()]
294        };
295        Self {
296            receipt_id: generated_artifact_id_from_material("conformance-run", &material),
297            profile_id,
298            fixture_count,
299            passed_count,
300            failed_count,
301            fixture_results,
302            environment,
303            reason_codes,
304            recorded_at: Utc::now(),
305        }
306    }
307}