1use super::*;
2
3#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
4pub struct BoundaryRepairReportV1 {
5 pub receipt_id: ArtifactId,
6 pub changed: bool,
7 pub repair_kind: String,
8 pub before_digest: Option<String>,
9 pub after_digest: Option<String>,
10 #[serde(default, skip_serializing_if = "Vec::is_empty")]
11 pub canonical_repair_record_ids: Vec<StackBoundaryRepairRecordId>,
12 #[serde(default, skip_serializing_if = "Vec::is_empty")]
13 pub canonical_backpointers: Vec<CanonicalBackpointerV1>,
14 pub warnings: Vec<String>,
15}
16
17#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
18pub struct DisplayDigestV1 {
19 pub algorithm: String,
20 pub canonicalization: String,
21 pub digest: String,
22 #[serde(default = "default_true")]
23 pub non_authoritative: bool,
24 #[serde(default, skip_serializing_if = "Vec::is_empty")]
25 pub reason_codes: Vec<String>,
26}
27
28impl DisplayDigestV1 {
29 pub fn for_json_value(value: &serde_json::Value) -> Self {
30 Self {
31 algorithm: "blake3".into(),
32 canonicalization: "stack-ids-json-c14n-v1-display-only".into(),
33 digest: non_authoritative_json_display_digest(value),
34 non_authoritative: true,
35 reason_codes: vec!["display-only-not-artifact-identity".into()],
36 }
37 }
38
39 pub fn for_text(text: &str) -> Self {
40 Self {
41 algorithm: "blake3".into(),
42 canonicalization: "stack-ids-utf8-text-v1-display-only".into(),
43 digest: non_authoritative_text_display_digest(text),
44 non_authoritative: true,
45 reason_codes: vec!["display-only-not-artifact-identity".into()],
46 }
47 }
48
49 pub fn from_hex(hex_digest: impl Into<String>) -> Self {
54 Self {
55 algorithm: "sha256".into(),
56 canonicalization: "boundary-compiler-sha256-hex-display-only".into(),
57 digest: hex_digest.into(),
58 non_authoritative: true,
59 reason_codes: vec!["display-only-not-artifact-identity".into()],
60 }
61 }
62
63 pub fn from_stack_content_digest_for_display(
64 digest: StackContentDigest,
65 canonicalization: impl Into<String>,
66 ) -> Self {
67 Self {
68 algorithm: "blake3".into(),
69 canonicalization: format!("{}-display-only", canonicalization.into()),
70 digest: non_authoritative_display_digest_string(&digest),
71 non_authoritative: true,
72 reason_codes: vec!["display-only-not-artifact-identity".into()],
73 }
74 }
75}
76
77fn default_true() -> bool {
78 true
79}
80
81#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
82pub struct DuplicateKeyFindingV1 {
83 pub finding_id: ArtifactId,
84 pub path: String,
85 pub key: String,
86 pub first_offset: Option<usize>,
87 pub duplicate_offset: Option<usize>,
88 #[serde(default, skip_serializing_if = "Vec::is_empty")]
89 pub reason_codes: Vec<String>,
90}
91
92impl DuplicateKeyFindingV1 {
93 pub fn new(
94 path: impl Into<String>,
95 key: impl Into<String>,
96 first_offset: Option<usize>,
97 duplicate_offset: Option<usize>,
98 ) -> Self {
99 Self {
100 finding_id: display_only_unstable_id("duplicate-key-finding"),
101 path: path.into(),
102 key: key.into(),
103 first_offset,
104 duplicate_offset,
105 reason_codes: vec!["duplicate-json-object-key".into()],
106 }
107 }
108}
109
110#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
111pub struct JsonBoundaryRepairDisplayReportV1 {
112 pub receipt_id: ArtifactId,
113 pub kind: ArtifactKindV1,
114 pub changed: bool,
115 pub repair_kind: String,
116 pub degraded: bool,
117 pub before_raw_digest: Option<String>,
118 pub after_raw_digest: Option<String>,
119 #[serde(default, skip_serializing_if = "Option::is_none")]
120 pub before_display_digest: Option<DisplayDigestV1>,
121 #[serde(default, skip_serializing_if = "Option::is_none")]
122 pub after_display_digest: Option<DisplayDigestV1>,
123 #[serde(default, skip_serializing_if = "Vec::is_empty")]
124 pub treatment_critical_fields: Vec<String>,
125 #[serde(default, skip_serializing_if = "Vec::is_empty")]
126 pub treatment_integrity_warnings: Vec<String>,
127 #[serde(default)]
128 pub hard_failed: bool,
129 #[serde(default, skip_serializing_if = "Vec::is_empty")]
130 pub warnings: Vec<String>,
131 #[serde(default, skip_serializing_if = "Vec::is_empty")]
132 pub reason_codes: Vec<String>,
133 #[serde(default, skip_serializing_if = "Vec::is_empty")]
134 pub canonical_repair_record_ids: Vec<StackBoundaryRepairRecordId>,
135 #[serde(default, skip_serializing_if = "Vec::is_empty")]
136 pub canonical_backpointers: Vec<CanonicalBackpointerV1>,
137}
138
139impl JsonBoundaryRepairDisplayReportV1 {
140 pub fn none() -> Self {
141 Self {
142 receipt_id: display_only_unstable_id("json-repair"),
143 kind: ArtifactKindV1::BoundaryRepair,
144 changed: false,
145 repair_kind: "none".into(),
146 degraded: false,
147 before_raw_digest: None,
148 after_raw_digest: None,
149 before_display_digest: None,
150 after_display_digest: None,
151 treatment_critical_fields: Vec::new(),
152 treatment_integrity_warnings: Vec::new(),
153 hard_failed: false,
154 warnings: Vec::new(),
155 reason_codes: Vec::new(),
156 canonical_repair_record_ids: Vec::new(),
157 canonical_backpointers: canonical_owner_backpointer(
158 "verification-control",
159 "BoundaryRepairRecord",
160 "canonical-boundary-repair-owner",
161 ),
162 }
163 }
164}
165
166impl From<JsonBoundaryRepairDisplayReportV1> for BoundaryRepairReportV1 {
167 fn from(receipt: JsonBoundaryRepairDisplayReportV1) -> Self {
168 Self {
169 receipt_id: receipt.receipt_id,
170 changed: receipt.changed,
171 repair_kind: receipt.repair_kind,
172 before_digest: receipt.before_raw_digest,
173 after_digest: receipt.after_raw_digest,
174 canonical_repair_record_ids: receipt.canonical_repair_record_ids,
175 canonical_backpointers: receipt.canonical_backpointers,
176 warnings: receipt
177 .warnings
178 .into_iter()
179 .chain(receipt.treatment_integrity_warnings)
180 .collect(),
181 }
182 }
183}
184
185impl From<&JsonBoundaryRepairDisplayReportV1> for BoundaryRepairReportV1 {
186 fn from(receipt: &JsonBoundaryRepairDisplayReportV1) -> Self {
187 receipt.clone().into()
188 }
189}
190
191#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
192pub struct SchemaValidationReportV1 {
193 pub receipt_id: ArtifactId,
194 pub kind: ArtifactKindV1,
195 #[serde(default, skip_serializing_if = "Option::is_none")]
196 pub run_id: Option<ArtifactId>,
197 #[serde(default, skip_serializing_if = "Option::is_none")]
198 pub attempt_id: Option<ArtifactId>,
199 #[serde(default, skip_serializing_if = "Option::is_none")]
200 pub tool_id: Option<String>,
201 #[serde(default, skip_serializing_if = "Option::is_none")]
202 pub schema_digest: Option<DisplayDigestV1>,
203 pub input_digest: DisplayDigestV1,
204 pub valid: bool,
205 #[serde(default, skip_serializing_if = "Vec::is_empty")]
206 pub errors: Vec<String>,
207 #[serde(default, skip_serializing_if = "Vec::is_empty")]
208 pub reason_codes: Vec<String>,
209 #[serde(default, skip_serializing_if = "Vec::is_empty")]
210 pub canonical_control_receipt_ids: Vec<StackControlReceiptId>,
211 #[serde(default, skip_serializing_if = "Vec::is_empty")]
212 pub canonical_backpointers: Vec<CanonicalBackpointerV1>,
213 pub checked_at: DateTime<Utc>,
214}
215
216impl SchemaValidationReportV1 {
217 pub fn new(
218 schema: Option<&serde_json::Value>,
219 input: &serde_json::Value,
220 errors: Vec<String>,
221 ) -> Self {
222 let valid = errors.is_empty();
223 Self {
224 receipt_id: display_only_unstable_id("schema-validation"),
225 kind: ArtifactKindV1::SchemaValidation,
226 run_id: None,
227 attempt_id: None,
228 tool_id: None,
229 schema_digest: schema.map(DisplayDigestV1::for_json_value),
230 input_digest: DisplayDigestV1::for_json_value(input),
231 valid,
232 errors,
233 reason_codes: if valid {
234 vec!["schema-validation-passed".into()]
235 } else {
236 vec!["schema-validation-failed".into()]
237 },
238 canonical_control_receipt_ids: Vec::new(),
239 canonical_backpointers: canonical_owner_backpointer(
240 "verification-control",
241 "ControlReceipt",
242 "canonical-schema-validation-owner",
243 ),
244 checked_at: Utc::now(),
245 }
246 }
247
248 pub fn with_tool_id(mut self, tool_id: impl Into<String>) -> Self {
249 self.tool_id = Some(tool_id.into());
250 self
251 }
252
253 pub fn with_execution_context(mut self, context: &AidensRunContextV1) -> Self {
254 self.run_id = Some(context.run_id.clone());
255 self.attempt_id = Some(context.attempt_id.clone());
256 self
257 }
258}
259
260#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
261pub struct BoundaryCompileRequestV1 {
262 pub request_id: ArtifactId,
263 pub input: String,
264 #[serde(default, skip_serializing_if = "Option::is_none")]
265 pub schema: Option<serde_json::Value>,
266 pub schema_dialect: String,
267 pub allow_markdown_fence_repair: bool,
268 pub allow_json_substring_extract: bool,
269 #[serde(default, skip_serializing_if = "Vec::is_empty")]
270 pub treatment_critical_fields: Vec<String>,
271 #[serde(default)]
272 pub hard_fail_on_treatment_change: bool,
273}
274
275impl BoundaryCompileRequestV1 {
276 pub fn new(input: impl Into<String>) -> Self {
277 Self {
278 request_id: display_only_unstable_id("boundary-compile-request"),
279 input: input.into(),
280 schema: None,
281 schema_dialect: "json-schema-2020-12-subset".into(),
282 allow_markdown_fence_repair: false,
283 allow_json_substring_extract: false,
284 treatment_critical_fields: Vec::new(),
285 hard_fail_on_treatment_change: false,
286 }
287 }
288
289 pub fn with_schema(mut self, schema: serde_json::Value) -> Self {
290 self.schema = Some(schema);
291 self
292 }
293
294 pub fn with_treatment_critical_fields(mut self, fields: Vec<String>) -> Self {
295 self.treatment_critical_fields = fields;
296 self
297 }
298
299 pub fn with_hard_fail_on_treatment_change(mut self, hard_fail: bool) -> Self {
300 self.hard_fail_on_treatment_change = hard_fail;
301 self
302 }
303}
304
305#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
306pub struct BoundaryCompileOutcomeV1 {
307 pub outcome_id: ArtifactId,
308 pub request_id: ArtifactId,
309 pub accepted: bool,
310 pub degraded: bool,
311 #[serde(default, skip_serializing_if = "Option::is_none")]
312 pub value: Option<serde_json::Value>,
313 #[serde(default, skip_serializing_if = "Option::is_none")]
314 pub display_digest: Option<DisplayDigestV1>,
315 #[serde(default, skip_serializing_if = "Vec::is_empty")]
316 pub duplicate_key_findings: Vec<DuplicateKeyFindingV1>,
317 #[serde(default, skip_serializing_if = "Option::is_none")]
318 pub schema_validation: Option<SchemaValidationReportV1>,
319 #[serde(default, skip_serializing_if = "Option::is_none")]
320 pub repair_receipt: Option<JsonBoundaryRepairDisplayReportV1>,
321 #[serde(default, skip_serializing_if = "Vec::is_empty")]
322 pub reason_codes: Vec<String>,
323 pub compiled_at: DateTime<Utc>,
324}
325
326impl BoundaryCompileOutcomeV1 {
327 pub fn rejected(request_id: ArtifactId, reason_codes: Vec<String>) -> Self {
328 Self {
329 outcome_id: display_only_unstable_id("boundary-compile-outcome"),
330 request_id,
331 accepted: false,
332 degraded: true,
333 value: None,
334 display_digest: None,
335 duplicate_key_findings: Vec::new(),
336 schema_validation: None,
337 repair_receipt: None,
338 reason_codes,
339 compiled_at: Utc::now(),
340 }
341 }
342}
343
344#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
349pub struct ArtifactFamilyRegistryV1 {
350 pub registry_id: ArtifactId,
351 pub registry_version: u32,
352 pub families: Vec<ArtifactFamilyRegistrationV1>,
353 #[serde(default = "default_schema_canonical_truth_owner")]
354 pub canonical_truth_owner: String,
355 #[serde(default = "schema_registry_governance_local_display")]
356 pub governance_status: SchemaRegistryGovernanceStatusV1,
357 #[serde(default, skip_serializing_if = "Vec::is_empty")]
358 pub reason_codes: Vec<String>,
359}
360
361#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
362#[serde(rename_all = "kebab-case")]
363pub enum SchemaRegistryGovernanceStatusV1 {
364 LocalDisplayIndexOnly,
365 QuarantinedExternalFamilies,
366 AdmittedByCanonicalOwner,
367}
368
369fn default_schema_canonical_truth_owner() -> String {
370 "contract-schema-gen".into()
371}
372
373fn schema_registry_governance_local_display() -> SchemaRegistryGovernanceStatusV1 {
374 SchemaRegistryGovernanceStatusV1::LocalDisplayIndexOnly
375}
376
377impl ArtifactFamilyRegistryV1 {
378 pub fn new(mut families: Vec<ArtifactFamilyRegistrationV1>) -> Self {
379 families.sort_by(|left, right| {
380 left.family
381 .cmp(&right.family)
382 .then(left.version.cmp(&right.version))
383 });
384 Self {
385 registry_id: ArtifactId("artifact-family-registry:v1".into()),
386 registry_version: 1,
387 families,
388 canonical_truth_owner: "contract-schema-gen".into(),
389 governance_status: SchemaRegistryGovernanceStatusV1::LocalDisplayIndexOnly,
390 reason_codes: vec!["aidens-local-non-authoritative-schema-index".into()],
391 }
392 }
393
394 pub fn contains_family_version(&self, family: &str, version: u32) -> bool {
395 self.families
396 .iter()
397 .any(|entry| entry.family == family && entry.version == version)
398 }
399}
400
401#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
405pub struct ArtifactFamilyRegistrationV1 {
406 pub family: String,
407 pub version: u32,
408 #[serde(default, skip_serializing_if = "String::is_empty")]
409 pub schema_identity: String,
410 pub rust_type: String,
411 pub owner_crate: String,
412 #[serde(default = "schema_family_admission_local_display")]
413 pub admission: SchemaFamilyAdmissionV1,
414 pub first_pass: String,
415 pub schema_path: String,
416 #[serde(default, skip_serializing_if = "Option::is_none")]
417 pub fixture_path: Option<String>,
418 pub compatibility_policy: String,
419}
420
421#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
422#[serde(rename_all = "kebab-case")]
423pub enum SchemaFamilyAdmissionV1 {
424 LocalDisplayOnly,
425 QuarantinedExternal,
426 AdmittedByCanonicalOwner,
427}
428
429fn schema_family_admission_local_display() -> SchemaFamilyAdmissionV1 {
430 SchemaFamilyAdmissionV1::LocalDisplayOnly
431}
432
433impl SchemaFamilyAdmissionV1 {
434 pub fn allows_local_generation(self) -> bool {
435 matches!(
436 self,
437 Self::LocalDisplayOnly | Self::AdmittedByCanonicalOwner
438 )
439 }
440
441 pub fn allows_truth_ownership_claim(self) -> bool {
442 matches!(self, Self::AdmittedByCanonicalOwner)
443 }
444}
445
446impl ArtifactFamilyRegistrationV1 {
447 pub fn new(
448 family: impl Into<String>,
449 version: u32,
450 rust_type: impl Into<String>,
451 first_pass: impl Into<String>,
452 fixture_path: Option<String>,
453 compatibility_policy: impl Into<String>,
454 ) -> Self {
455 let family = family.into();
456 Self {
457 schema_path: format!("{family}/v{version}.schema.json"),
458 schema_identity: format!("schema:{family}:v{version}"),
459 family,
460 version,
461 rust_type: rust_type.into(),
462 owner_crate: "aidens-orchestration".into(),
463 admission: SchemaFamilyAdmissionV1::LocalDisplayOnly,
464 first_pass: first_pass.into(),
465 fixture_path,
466 compatibility_policy: compatibility_policy.into(),
467 }
468 }
469
470 pub fn quarantined_external(
471 family: impl Into<String>,
472 version: u32,
473 schema_path: impl Into<String>,
474 reason: impl Into<String>,
475 ) -> Self {
476 let family = family.into();
477 let reason = reason.into();
478 Self {
479 schema_identity: format!("schema:{family}:v{version}:external-quarantined"),
480 schema_path: schema_path.into(),
481 family,
482 version,
483 rust_type: "external-unadmitted".into(),
484 owner_crate: "external-unadmitted".into(),
485 admission: SchemaFamilyAdmissionV1::QuarantinedExternal,
486 first_pass: "external".into(),
487 fixture_path: None,
488 compatibility_policy: format!("quarantined:{reason}"),
489 }
490 }
491}
492
493#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
497pub struct GeneratedSchemaManifestV1 {
498 pub manifest_id: ArtifactId,
499 pub manifest_version: u32,
500 pub registry_digest: String,
501 pub schemas: Vec<GeneratedSchemaEntryV1>,
502 #[serde(default, skip_serializing_if = "Vec::is_empty")]
503 pub reason_codes: Vec<String>,
504}
505
506impl GeneratedSchemaManifestV1 {
507 pub fn new(
508 registry: &ArtifactFamilyRegistryV1,
509 mut schemas: Vec<GeneratedSchemaEntryV1>,
510 ) -> Self {
511 schemas.sort_by(|left, right| {
512 left.family
513 .cmp(&right.family)
514 .then(left.version.cmp(&right.version))
515 });
516 Self {
517 manifest_id: ArtifactId("generated-schema-manifest:v1".into()),
518 manifest_version: 1,
519 registry_digest: non_authoritative_json_display_digest(
520 &serde_json::to_value(registry).unwrap_or(serde_json::Value::Null),
521 ),
522 schemas,
523 reason_codes: vec!["aidens-local-display-report-schemas-only".into()],
524 }
525 }
526}
527
528#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
529pub struct GeneratedSchemaEntryV1 {
530 pub family: String,
531 pub version: u32,
532 #[serde(default, skip_serializing_if = "String::is_empty")]
533 pub schema_identity: String,
534 pub rust_type: String,
535 pub schema_path: String,
536 pub schema_digest: String,
537}
538
539#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
541pub struct GeneratedSchemaDocumentV1 {
542 pub registration: ArtifactFamilyRegistrationV1,
543 pub schema: serde_json::Value,
544}
545
546impl GeneratedSchemaDocumentV1 {
547 pub fn entry(&self) -> GeneratedSchemaEntryV1 {
548 GeneratedSchemaEntryV1 {
549 family: self.registration.family.clone(),
550 version: self.registration.version,
551 schema_identity: self.content_addressed_identity(),
552 rust_type: self.registration.rust_type.clone(),
553 schema_path: self.registration.schema_path.clone(),
554 schema_digest: non_authoritative_json_display_digest(&self.schema),
555 }
556 }
557
558 pub fn content_addressed_identity(&self) -> String {
559 format!(
560 "{}:{}",
561 self.registration.schema_identity,
562 non_authoritative_json_display_digest(&self.schema)
563 )
564 }
565
566 pub fn pretty_json(&self) -> String {
567 let mut encoded = serde_json::to_string_pretty(&self.schema).unwrap_or_default();
568 encoded.push('\n');
569 encoded
570 }
571}
572
573#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
574#[serde(rename_all = "kebab-case")]
575pub enum SchemaCompatibilityModeV1 {
576 Backward,
577 Forward,
578 Full,
579 Transitive,
580}
581
582impl fmt::Display for SchemaCompatibilityModeV1 {
583 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
584 f.write_str(match self {
585 Self::Backward => "backward",
586 Self::Forward => "forward",
587 Self::Full => "full",
588 Self::Transitive => "transitive",
589 })
590 }
591}
592
593#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
594#[serde(rename_all = "kebab-case")]
595pub enum SchemaChangeClassV1 {
596 Exact,
597 AdditiveMinor,
598 MajorBreaking,
599 UnknownIncompatible,
600}
601
602#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
603pub struct SchemaCompatibilityCheckV1 {
604 pub family: String,
605 pub version: u32,
606 pub mode: SchemaCompatibilityModeV1,
607 #[serde(default = "schema_change_class_unknown_incompatible")]
608 pub change_class: SchemaChangeClassV1,
609 #[serde(default)]
610 pub requires_major_bump: bool,
611 pub compatible: bool,
612 #[serde(default, skip_serializing_if = "Vec::is_empty")]
613 pub reason_codes: Vec<String>,
614}
615
616fn schema_change_class_unknown_incompatible() -> SchemaChangeClassV1 {
617 SchemaChangeClassV1::UnknownIncompatible
618}
619
620impl SchemaCompatibilityCheckV1 {
621 pub fn exact(family: impl Into<String>, version: u32, mode: SchemaCompatibilityModeV1) -> Self {
622 Self {
623 family: family.into(),
624 version,
625 mode,
626 change_class: SchemaChangeClassV1::Exact,
627 requires_major_bump: false,
628 compatible: true,
629 reason_codes: vec![format!("schema-compatible-{mode}")],
630 }
631 }
632
633 pub fn incompatible(
634 family: impl Into<String>,
635 version: u32,
636 mode: SchemaCompatibilityModeV1,
637 reason_codes: Vec<String>,
638 ) -> Self {
639 Self {
640 family: family.into(),
641 version,
642 mode,
643 change_class: SchemaChangeClassV1::UnknownIncompatible,
644 requires_major_bump: true,
645 compatible: false,
646 reason_codes,
647 }
648 }
649}
650
651#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
652pub struct SchemaCompatibilityReportV1 {
653 pub report_id: ArtifactId,
654 pub registry_digest: String,
655 pub compatible: bool,
656 pub checked_schema_count: usize,
657 #[serde(default, skip_serializing_if = "Vec::is_empty")]
658 pub checks: Vec<SchemaCompatibilityCheckV1>,
659 #[serde(default, skip_serializing_if = "Vec::is_empty")]
660 pub missing_schema_paths: Vec<String>,
661 #[serde(default, skip_serializing_if = "Vec::is_empty")]
662 pub unregistered_schema_paths: Vec<String>,
663 #[serde(default, skip_serializing_if = "Vec::is_empty")]
664 pub incompatible_schema_paths: Vec<String>,
665 #[serde(default, skip_serializing_if = "Vec::is_empty")]
666 pub path_collision_findings: Vec<SchemaPathCollisionFindingV1>,
667 #[serde(default, skip_serializing_if = "Vec::is_empty")]
668 pub reason_codes: Vec<String>,
669}
670
671#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
672pub struct SchemaPathCollisionFindingV1 {
673 pub finding_id: ArtifactId,
674 pub normalized_path: String,
675 pub colliding_paths: Vec<String>,
676 #[serde(default, skip_serializing_if = "Vec::is_empty")]
677 pub reason_codes: Vec<String>,
678}
679
680impl SchemaPathCollisionFindingV1 {
681 pub fn new(normalized_path: impl Into<String>, colliding_paths: Vec<String>) -> Self {
682 Self {
683 finding_id: display_only_unstable_id("schema-path-collision"),
684 normalized_path: normalized_path.into(),
685 colliding_paths: sorted_unique_strings(colliding_paths),
686 reason_codes: vec!["schema-path-case-fold-collision".into()],
687 }
688 }
689}
690
691impl SchemaCompatibilityReportV1 {
692 pub fn new(
693 registry: &ArtifactFamilyRegistryV1,
694 checked_schema_count: usize,
695 checks: Vec<SchemaCompatibilityCheckV1>,
696 missing_schema_paths: Vec<String>,
697 unregistered_schema_paths: Vec<String>,
698 incompatible_schema_paths: Vec<String>,
699 path_collision_findings: Vec<SchemaPathCollisionFindingV1>,
700 ) -> Self {
701 let compatible = missing_schema_paths.is_empty()
702 && unregistered_schema_paths.is_empty()
703 && incompatible_schema_paths.is_empty()
704 && path_collision_findings.is_empty()
705 && checks.iter().all(|check| check.compatible);
706 let mut reason_codes = Vec::new();
707 if compatible {
708 reason_codes.push("schema-compatibility-passed".into());
709 }
710 if !missing_schema_paths.is_empty() {
711 reason_codes.push("registered-schema-missing".into());
712 }
713 if !unregistered_schema_paths.is_empty() {
714 reason_codes.push("unregistered-artifact-family-schema".into());
715 }
716 if !incompatible_schema_paths.is_empty() {
717 reason_codes.push("schema-content-drift-without-major-bump".into());
718 }
719 if !path_collision_findings.is_empty() {
720 reason_codes.push("schema-path-case-fold-collision".into());
721 }
722 Self {
723 report_id: ArtifactId("schema-compatibility-report:v1".into()),
724 registry_digest: non_authoritative_json_display_digest(
725 &serde_json::to_value(registry).unwrap_or(serde_json::Value::Null),
726 ),
727 compatible,
728 checked_schema_count,
729 checks,
730 missing_schema_paths,
731 unregistered_schema_paths,
732 incompatible_schema_paths,
733 path_collision_findings,
734 reason_codes,
735 }
736 }
737}
738
739#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
740#[serde(rename_all = "kebab-case")]
741pub enum MigrationPhaseV1 {
742 Expand,
743 Backfill,
744 FlipRead,
745 Contract,
746}
747
748#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
749pub struct MigrationPlanV1 {
750 pub plan_id: ArtifactId,
751 pub artifact_family: String,
752 pub from_version: u32,
753 pub to_version: u32,
754 pub phases: Vec<MigrationPhaseV1>,
755 pub requires_major_bump: bool,
756 pub compatible_without_backfill: bool,
757 #[serde(default, skip_serializing_if = "Vec::is_empty")]
758 pub reason_codes: Vec<String>,
759}
760
761impl MigrationPlanV1 {
762 pub fn expand_backfill_flip_contract(
763 artifact_family: impl Into<String>,
764 from_version: u32,
765 to_version: u32,
766 requires_major_bump: bool,
767 ) -> Self {
768 Self {
769 plan_id: ArtifactId("migration-plan:v1".into()),
770 artifact_family: artifact_family.into(),
771 from_version,
772 to_version,
773 phases: vec![
774 MigrationPhaseV1::Expand,
775 MigrationPhaseV1::Backfill,
776 MigrationPhaseV1::FlipRead,
777 MigrationPhaseV1::Contract,
778 ],
779 requires_major_bump,
780 compatible_without_backfill: !requires_major_bump,
781 reason_codes: vec!["expand-backfill-flip-read-contract".into()],
782 }
783 }
784}
785
786#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
787pub struct BackfillReportV1 {
788 pub receipt_id: ArtifactId,
789 pub kind: ArtifactKindV1,
790 pub migration_plan_id: ArtifactId,
791 pub artifact_family: String,
792 pub from_version: u32,
793 pub to_version: u32,
794 pub migrated_fixture_count: usize,
795 pub succeeded: bool,
796 #[serde(default, skip_serializing_if = "Vec::is_empty")]
797 pub source_fixture_paths: Vec<String>,
798 #[serde(default, skip_serializing_if = "Vec::is_empty")]
799 pub reason_codes: Vec<String>,
800 pub backfilled_at: DateTime<Utc>,
801}
802
803impl BackfillReportV1 {
804 pub fn succeeded(
805 plan: &MigrationPlanV1,
806 migrated_fixture_count: usize,
807 source_fixture_paths: Vec<String>,
808 ) -> Self {
809 Self {
810 receipt_id: display_only_unstable_id("backfill"),
811 kind: ArtifactKindV1::Backfill,
812 migration_plan_id: plan.plan_id.clone(),
813 artifact_family: plan.artifact_family.clone(),
814 from_version: plan.from_version,
815 to_version: plan.to_version,
816 migrated_fixture_count,
817 succeeded: true,
818 source_fixture_paths,
819 reason_codes: vec!["historical-fixtures-readable".into()],
820 backfilled_at: Utc::now(),
821 }
822 }
823}
824
825#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
826#[serde(rename_all = "kebab-case")]
827pub enum ReferenceDomainV1 {
828 PlanConfig,
829 ProviderRoute,
830 ToolExposure,
831 Permit,
832 BoundaryRepair,
833 ReceiptLineage,
834 TemporalQuery,
835 ProofDebt,
836 SemanticState,
837 ViewDisclosure,
838}
839
840impl fmt::Display for ReferenceDomainV1 {
841 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
842 f.write_str(match self {
843 Self::PlanConfig => "plan-config",
844 Self::ProviderRoute => "provider-route",
845 Self::ToolExposure => "tool-exposure",
846 Self::Permit => "permit",
847 Self::BoundaryRepair => "boundary-repair",
848 Self::ReceiptLineage => "receipt-lineage",
849 Self::TemporalQuery => "temporal-query",
850 Self::ProofDebt => "proof-debt",
851 Self::SemanticState => "semantic-state",
852 Self::ViewDisclosure => "view-disclosure",
853 })
854 }
855}
856
857#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
858pub struct ReferenceCaseV1 {
859 pub case_id: ArtifactId,
860 pub domain: ReferenceDomainV1,
861 pub title: String,
862 pub input: serde_json::Value,
863 pub expected: serde_json::Value,
864 #[serde(default, skip_serializing_if = "Vec::is_empty")]
865 pub covered_provider_kinds: Vec<String>,
866 #[serde(default, skip_serializing_if = "Vec::is_empty")]
867 pub covered_risk_classes: Vec<CanonicalToolSideEffectClass>,
868 #[serde(default, skip_serializing_if = "Vec::is_empty")]
869 pub covered_memory_modes: Vec<MemoryModeV1>,
870 #[serde(default, skip_serializing_if = "Vec::is_empty")]
871 pub covered_receipt_levels: Vec<ReportLevelV1>,
872 #[serde(default, skip_serializing_if = "Vec::is_empty")]
873 pub covered_tool_lifecycle_states: Vec<ToolLifecycleStateV1>,
874 #[serde(default, skip_serializing_if = "Vec::is_empty")]
875 pub source_fixture_paths: Vec<String>,
876 #[serde(default, skip_serializing_if = "Vec::is_empty")]
877 pub reason_codes: Vec<String>,
878}
879
880impl ReferenceCaseV1 {
881 pub fn new(
882 domain: ReferenceDomainV1,
883 title: impl Into<String>,
884 input: serde_json::Value,
885 expected: serde_json::Value,
886 ) -> Self {
887 Self {
888 case_id: display_only_unstable_id("reference-case"),
889 domain,
890 title: title.into(),
891 input,
892 expected,
893 covered_provider_kinds: Vec::new(),
894 covered_risk_classes: Vec::new(),
895 covered_memory_modes: Vec::new(),
896 covered_receipt_levels: Vec::new(),
897 covered_tool_lifecycle_states: Vec::new(),
898 source_fixture_paths: Vec::new(),
899 reason_codes: vec!["reference-semantics-case".into()],
900 }
901 }
902
903 pub fn with_provider_kind(mut self, provider_kind: impl Into<String>) -> Self {
904 self.covered_provider_kinds.push(provider_kind.into());
905 self
906 }
907
908 pub fn with_risk_class(mut self, risk_class: CanonicalToolSideEffectClass) -> Self {
909 self.covered_risk_classes.push(risk_class);
910 self
911 }
912
913 pub fn with_memory_mode(mut self, memory_mode: MemoryModeV1) -> Self {
914 self.covered_memory_modes.push(memory_mode);
915 self
916 }
917
918 pub fn with_receipt_level(mut self, receipt_level: ReportLevelV1) -> Self {
919 self.covered_receipt_levels.push(receipt_level);
920 self
921 }
922
923 pub fn with_tool_lifecycle_state(mut self, state: ToolLifecycleStateV1) -> Self {
924 self.covered_tool_lifecycle_states.push(state);
925 self
926 }
927
928 pub fn with_source_fixture(mut self, path: impl Into<String>) -> Self {
929 self.source_fixture_paths.push(path.into());
930 self
931 }
932}
933
934#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
935pub struct DifferentialConformanceFindingV1 {
936 pub finding_id: ArtifactId,
937 pub case_id: ArtifactId,
938 pub domain: ReferenceDomainV1,
939 pub production_subject: String,
940 pub path: String,
941 pub expected: serde_json::Value,
942 pub actual: serde_json::Value,
943 pub human_diff: String,
944 #[serde(default, skip_serializing_if = "Vec::is_empty")]
945 pub reason_codes: Vec<String>,
946}
947
948impl DifferentialConformanceFindingV1 {
949 pub fn mismatch(
950 case: &ReferenceCaseV1,
951 production_subject: impl Into<String>,
952 path: impl Into<String>,
953 expected: serde_json::Value,
954 actual: serde_json::Value,
955 ) -> Self {
956 let path = path.into();
957 let human_diff = format!(
958 "{} mismatch at {path}: expected {}, actual {}",
959 case.title,
960 display_json_string(&expected),
961 display_json_string(&actual)
962 );
963 Self {
964 finding_id: display_only_unstable_id("differential-conformance-finding"),
965 case_id: case.case_id.clone(),
966 domain: case.domain,
967 production_subject: production_subject.into(),
968 path,
969 expected,
970 actual,
971 human_diff,
972 reason_codes: vec!["reference-production-mismatch".into()],
973 }
974 }
975}
976
977#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
978pub struct ReferenceInterpreterReportV1 {
979 pub report_id: ArtifactId,
980 pub interpreter_id: String,
981 pub case_count: usize,
982 pub passed: bool,
983 #[serde(default, skip_serializing_if = "Vec::is_empty")]
984 pub findings: Vec<DifferentialConformanceFindingV1>,
985 #[serde(default, skip_serializing_if = "Vec::is_empty")]
986 pub covered_provider_kinds: Vec<String>,
987 #[serde(default, skip_serializing_if = "Vec::is_empty")]
988 pub covered_risk_classes: Vec<CanonicalToolSideEffectClass>,
989 #[serde(default, skip_serializing_if = "Vec::is_empty")]
990 pub covered_memory_modes: Vec<MemoryModeV1>,
991 #[serde(default, skip_serializing_if = "Vec::is_empty")]
992 pub covered_receipt_levels: Vec<ReportLevelV1>,
993 #[serde(default, skip_serializing_if = "Vec::is_empty")]
994 pub covered_tool_lifecycle_states: Vec<ToolLifecycleStateV1>,
995 #[serde(default, skip_serializing_if = "Vec::is_empty")]
996 pub reason_codes: Vec<String>,
997 pub generated_at: DateTime<Utc>,
998}
999
1000impl ReferenceInterpreterReportV1 {
1001 pub fn new(
1002 interpreter_id: impl Into<String>,
1003 case_count: usize,
1004 findings: Vec<DifferentialConformanceFindingV1>,
1005 cases: &[ReferenceCaseV1],
1006 ) -> Self {
1007 let passed = findings.is_empty();
1008 Self {
1009 report_id: display_only_unstable_id("reference-interpreter-report"),
1010 interpreter_id: interpreter_id.into(),
1011 case_count,
1012 passed,
1013 findings,
1014 covered_provider_kinds: sorted_unique_strings(
1015 cases
1016 .iter()
1017 .flat_map(|case| case.covered_provider_kinds.iter().cloned())
1018 .collect(),
1019 ),
1020 covered_risk_classes: sorted_unique_risk_classes(
1021 cases
1022 .iter()
1023 .flat_map(|case| case.covered_risk_classes.iter().cloned())
1024 .collect(),
1025 ),
1026 covered_memory_modes: unique_memory_modes(
1027 cases
1028 .iter()
1029 .flat_map(|case| case.covered_memory_modes.iter().cloned())
1030 .collect(),
1031 ),
1032 covered_receipt_levels: unique_receipt_levels(
1033 cases
1034 .iter()
1035 .flat_map(|case| case.covered_receipt_levels.iter().cloned())
1036 .collect(),
1037 ),
1038 covered_tool_lifecycle_states: unique_tool_lifecycle_states(
1039 cases
1040 .iter()
1041 .flat_map(|case| case.covered_tool_lifecycle_states.iter().cloned())
1042 .collect(),
1043 ),
1044 reason_codes: if passed {
1045 vec!["reference-conformance-passed".into()]
1046 } else {
1047 vec!["reference-conformance-failed".into()]
1048 },
1049 generated_at: Utc::now(),
1050 }
1051 }
1052}
1053
1054#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
1055pub struct GoldenFixtureManifestV1 {
1056 pub manifest_id: ArtifactId,
1057 pub fixture_paths: Vec<String>,
1058 pub reference_case_ids: Vec<ArtifactId>,
1059 pub provider_kinds: Vec<String>,
1060 pub risk_classes: Vec<CanonicalToolSideEffectClass>,
1061 pub memory_modes: Vec<MemoryModeV1>,
1062 pub receipt_levels: Vec<ReportLevelV1>,
1063 pub tool_lifecycle_states: Vec<ToolLifecycleStateV1>,
1064 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1065 pub reason_codes: Vec<String>,
1066 pub generated_at: DateTime<Utc>,
1067}
1068
1069impl GoldenFixtureManifestV1 {
1070 pub fn new(fixture_paths: Vec<String>, cases: &[ReferenceCaseV1]) -> Self {
1071 Self {
1072 manifest_id: display_only_unstable_id("golden-fixture-manifest"),
1073 fixture_paths: sorted_unique_strings(fixture_paths),
1074 reference_case_ids: cases.iter().map(|case| case.case_id.clone()).collect(),
1075 provider_kinds: sorted_unique_strings(
1076 cases
1077 .iter()
1078 .flat_map(|case| case.covered_provider_kinds.iter().cloned())
1079 .collect(),
1080 ),
1081 risk_classes: sorted_unique_risk_classes(
1082 cases
1083 .iter()
1084 .flat_map(|case| case.covered_risk_classes.iter().cloned())
1085 .collect(),
1086 ),
1087 memory_modes: unique_memory_modes(
1088 cases
1089 .iter()
1090 .flat_map(|case| case.covered_memory_modes.iter().cloned())
1091 .collect(),
1092 ),
1093 receipt_levels: unique_receipt_levels(
1094 cases
1095 .iter()
1096 .flat_map(|case| case.covered_receipt_levels.iter().cloned())
1097 .collect(),
1098 ),
1099 tool_lifecycle_states: unique_tool_lifecycle_states(
1100 cases
1101 .iter()
1102 .flat_map(|case| case.covered_tool_lifecycle_states.iter().cloned())
1103 .collect(),
1104 ),
1105 reason_codes: vec!["golden-fixture-coverage-manifest".into()],
1106 generated_at: Utc::now(),
1107 }
1108 }
1109}
1110
1111pub(crate) fn sorted_unique_artifact_ids(mut values: Vec<ArtifactId>) -> Vec<ArtifactId> {
1112 values.sort();
1113 values.dedup();
1114 values
1115}
1116
1117pub(crate) fn sorted_unique_invariants(
1118 mut values: Vec<PreservedInvariantV1>,
1119) -> Vec<PreservedInvariantV1> {
1120 values.sort();
1121 values.dedup();
1122 values
1123}
1124
1125pub(crate) fn sorted_unique_strings(mut values: Vec<String>) -> Vec<String> {
1126 values.sort();
1127 values.dedup();
1128 values
1129}
1130
1131fn sorted_unique_risk_classes(
1132 mut values: Vec<CanonicalToolSideEffectClass>,
1133) -> Vec<CanonicalToolSideEffectClass> {
1134 values.sort();
1135 values.dedup();
1136 values
1137}
1138
1139fn unique_memory_modes(values: Vec<MemoryModeV1>) -> Vec<MemoryModeV1> {
1140 let mut out = Vec::new();
1141 for candidate in [
1142 MemoryModeV1::Disabled,
1143 MemoryModeV1::Optional,
1144 MemoryModeV1::Required,
1145 ] {
1146 if values.contains(&candidate) {
1147 out.push(candidate);
1148 }
1149 }
1150 out
1151}
1152
1153fn unique_receipt_levels(values: Vec<ReportLevelV1>) -> Vec<ReportLevelV1> {
1154 let mut out = Vec::new();
1155 for candidate in [
1156 ReportLevelV1::Minimal,
1157 ReportLevelV1::Standard,
1158 ReportLevelV1::Full,
1159 ] {
1160 if values.contains(&candidate) {
1161 out.push(candidate);
1162 }
1163 }
1164 out
1165}
1166
1167fn unique_tool_lifecycle_states(values: Vec<ToolLifecycleStateV1>) -> Vec<ToolLifecycleStateV1> {
1168 let mut out = Vec::new();
1169 for candidate in [
1170 ToolLifecycleStateV1::Declared,
1171 ToolLifecycleStateV1::Registered,
1172 ToolLifecycleStateV1::Executable,
1173 ToolLifecycleStateV1::Exposed,
1174 ToolLifecycleStateV1::ExposedThisTurn,
1175 ToolLifecycleStateV1::Invoked,
1176 ToolLifecycleStateV1::Succeeded,
1177 ToolLifecycleStateV1::Failed,
1178 ToolLifecycleStateV1::Hidden,
1179 ToolLifecycleStateV1::Blocked,
1180 ] {
1181 if values.contains(&candidate) {
1182 out.push(candidate);
1183 }
1184 }
1185 out
1186}
1187
1188pub fn current_artifact_family_registry() -> ArtifactFamilyRegistryV1 {
1189 ArtifactFamilyRegistryV1::new(artifact_family_registrations())
1190}
1191
1192pub fn generated_schema_documents() -> Vec<GeneratedSchemaDocumentV1> {
1193 generated_schema_documents_inner()
1194}
1195
1196pub fn generated_schema_manifest() -> GeneratedSchemaManifestV1 {
1197 let registry = current_artifact_family_registry();
1198 GeneratedSchemaManifestV1::new(
1199 ®istry,
1200 generated_schema_documents()
1201 .into_iter()
1202 .map(|document| document.entry())
1203 .collect(),
1204 )
1205}
1206
1207pub fn generated_schema_manifest_pretty_json() -> String {
1208 let mut encoded =
1209 serde_json::to_string_pretty(&generated_schema_manifest()).unwrap_or_default();
1210 encoded.push('\n');
1211 encoded
1212}
1213
1214macro_rules! artifact_registration {
1215 ($family:literal, $version:literal, $type_name:literal, $pass:literal, $fixture:expr, $policy:literal) => {{
1216 let fixture_path: Option<&str> = $fixture;
1217 ArtifactFamilyRegistrationV1::new(
1218 $family,
1219 $version,
1220 $type_name,
1221 $pass,
1222 fixture_path.map(str::to_string),
1223 $policy,
1224 )
1225 }};
1226}
1227
1228macro_rules! schema_document {
1229 ($family:literal, $version:literal, $ty:ty, $type_name:literal, $pass:literal, $fixture:expr, $policy:literal) => {
1230 GeneratedSchemaDocumentV1 {
1231 registration: artifact_registration!(
1232 $family, $version, $type_name, $pass, $fixture, $policy
1233 ),
1234 schema: serde_json::to_value(schemars::schema_for!($ty))
1235 .unwrap_or(serde_json::Value::Null),
1236 }
1237 };
1238}
1239
1240fn artifact_family_registrations() -> Vec<ArtifactFamilyRegistrationV1> {
1241 generated_schema_documents_inner()
1242 .into_iter()
1243 .map(|document| document.registration)
1244 .collect()
1245}
1246
1247fn generated_schema_documents_inner() -> Vec<GeneratedSchemaDocumentV1> {
1248 vec![
1249 schema_document!(
1250 "agent-spec",
1251 1,
1252 AgentSpecV1,
1253 "AgentSpecV1",
1254 "P26",
1255 Some("tests/fixtures/p26/agent_spec_v1.json"),
1256 "local-agent-declarative-contract-major-immutable"
1257 ),
1258 schema_document!(
1259 "api-honesty-receipt",
1260 1,
1261 ApiHonestyReportV1,
1262 "ApiHonestyReportV1",
1263 "P01",
1264 Some("tests/fixtures/p01/api_honesty_receipt_v1.json"),
1265 "additive-compatible-with-defaults"
1266 ),
1267 schema_document!(
1268 "approval-decision",
1269 1,
1270 ApprovalDecisionV1,
1271 "ApprovalDecisionV1",
1272 "P04",
1273 Some("tests/fixtures/p04/approval_decision_v1.json"),
1274 "append-only-approval-outcome"
1275 ),
1276 schema_document!(
1277 "approval-request",
1278 1,
1279 ApprovalRequestV1,
1280 "ApprovalRequestV1",
1281 "P04",
1282 Some("tests/fixtures/p04/approval_request_v1.json"),
1283 "scope-additions-require-defaults"
1284 ),
1285 schema_document!(
1286 "artifact-family-registry",
1287 1,
1288 ArtifactFamilyRegistryV1,
1289 "ArtifactFamilyRegistryV1",
1290 "P07",
1291 Some("tests/fixtures/p07/artifact_family_registry_v1.json"),
1292 "registered-family-additions-only"
1293 ),
1294 schema_document!(
1295 "artifact-envelope",
1296 1,
1297 ArtifactEnvelopeV1,
1298 "ArtifactEnvelopeV1",
1299 "P30",
1300 None,
1301 "aidens-local-envelope-additive-compatible"
1302 ),
1303 schema_document!(
1304 "backfill-receipt",
1305 1,
1306 BackfillReportV1,
1307 "BackfillReportV1",
1308 "P07",
1309 Some("tests/fixtures/p07/backfill_receipt_v1.json"),
1310 "append-only-backfill-evidence"
1311 ),
1312 schema_document!(
1313 "boundary-compile-outcome",
1314 1,
1315 BoundaryCompileOutcomeV1,
1316 "BoundaryCompileOutcomeV1",
1317 "P06",
1318 Some("tests/fixtures/p06/boundary_compile_outcome_v1.json"),
1319 "additive-compatible-with-defaults"
1320 ),
1321 schema_document!(
1322 "boundary-compile-request",
1323 1,
1324 BoundaryCompileRequestV1,
1325 "BoundaryCompileRequestV1",
1326 "P06",
1327 Some("tests/fixtures/p06/boundary_compile_request_v1.json"),
1328 "policy-field-additions-require-defaults"
1329 ),
1330 schema_document!(
1331 "budget-exhaustion-receipt",
1332 1,
1333 BudgetExhaustionReportV1,
1334 "BudgetExhaustionReportV1",
1335 "P03",
1336 Some("tests/fixtures/p03/budget_exhaustion_receipt_v1.json"),
1337 "receipt-major-immutable"
1338 ),
1339 schema_document!(
1340 "display-digest",
1341 1,
1342 DisplayDigestV1,
1343 "DisplayDigestV1",
1344 "P06",
1345 Some("tests/fixtures/p06/display_digest_v1.json"),
1346 "algorithm-change-requires-new-version"
1347 ),
1348 schema_document!(
1349 "capability-gate-decision",
1350 1,
1351 CapabilityGateDecisionV1,
1352 "CapabilityGateDecisionV1",
1353 "P04",
1354 Some("tests/fixtures/p04/capability_gate_decision_v1.json"),
1355 "additive-compatible-with-defaults"
1356 ),
1357 schema_document!(
1358 "config-apply-receipt",
1359 1,
1360 ConfigApplyReportV1,
1361 "ConfigApplyReportV1",
1362 "P01",
1363 Some("tests/fixtures/p01/config_apply_receipt_v1.json"),
1364 "receipt-major-immutable"
1365 ),
1366 schema_document!(
1367 "codex-packet",
1368 1,
1369 CodexPacketV1,
1370 "CodexPacketV1",
1371 "P10",
1372 Some("tests/fixtures/p10/codex_packet_v1.json"),
1373 "handoff-context-major-immutable"
1374 ),
1375 schema_document!(
1376 "command-run-receipt",
1377 1,
1378 CommandRunReportV1,
1379 "CommandRunReportV1",
1380 "P10",
1381 Some("tests/fixtures/p10/command_run_receipt_v1.json"),
1382 "command-evidence-major-immutable"
1383 ),
1384 schema_document!(
1385 "completion-audit-report",
1386 1,
1387 CompletionAuditReportV1,
1388 "CompletionAuditReportV1",
1389 "P19",
1390 Some("tests/fixtures/p19/completion_audit_report_v1.json"),
1391 "completion-state-and-release-bar-major-immutable"
1392 ),
1393 schema_document!(
1394 "example-app-manifest",
1395 1,
1396 ExampleAppManifestV1,
1397 "ExampleAppManifestV1",
1398 "P14",
1399 Some("tests/fixtures/p14/example_app_manifest_v1.json"),
1400 "example-coverage-major-immutable"
1401 ),
1402 schema_document!(
1403 "duplicate-key-finding",
1404 1,
1405 DuplicateKeyFindingV1,
1406 "DuplicateKeyFindingV1",
1407 "P06",
1408 Some("tests/fixtures/p06/duplicate_key_finding_v1.json"),
1409 "finding-major-immutable"
1410 ),
1411 schema_document!(
1412 "cross-pass-traceability-matrix",
1413 1,
1414 CrossPassTraceabilityMatrixV1,
1415 "CrossPassTraceabilityMatrixV1",
1416 "P19",
1417 Some("tests/fixtures/p19/cross_pass_traceability_matrix_v1.json"),
1418 "requirement-evidence-mapping-major-immutable"
1419 ),
1420 schema_document!(
1421 "fake-ready-finding",
1422 1,
1423 FakeReadyFindingV1,
1424 "FakeReadyFindingV1",
1425 "P00",
1426 Some("tests/fixtures/p00/fake_ready_finding_v1.json"),
1427 "finding-major-immutable"
1428 ),
1429 schema_document!(
1430 "generated-schema-manifest",
1431 1,
1432 GeneratedSchemaManifestV1,
1433 "GeneratedSchemaManifestV1",
1434 "P07",
1435 Some("tests/fixtures/p07/generated_schema_manifest_v1.json"),
1436 "generated-output-major-immutable"
1437 ),
1438 schema_document!(
1439 "golden-fixture-manifest",
1440 1,
1441 GoldenFixtureManifestV1,
1442 "GoldenFixtureManifestV1",
1443 "P08",
1444 Some("tests/fixtures/reference/golden_fixture_manifest_v1.json"),
1445 "fixture-coverage-major-immutable"
1446 ),
1447 schema_document!(
1448 "install-smoke-report",
1449 1,
1450 InstallSmokeReportV1,
1451 "InstallSmokeReportV1",
1452 "P14",
1453 Some("tests/fixtures/p14/install_smoke_receipt_v1.json"),
1454 "operator-smoke-evidence-major-immutable"
1455 ),
1456 schema_document!(
1457 "known-limitations-register",
1458 1,
1459 KnownLimitationsRegisterV1,
1460 "KnownLimitationsRegisterV1",
1461 "P19",
1462 Some("tests/fixtures/p19/known_limitations_register_v1.json"),
1463 "limitations-disclosure-major-immutable"
1464 ),
1465 schema_document!(
1466 "job",
1467 1,
1468 JobV1,
1469 "JobV1",
1470 "P11",
1471 Some("tests/fixtures/p11/job_v1.json"),
1472 "job-identity-idempotency-major-immutable"
1473 ),
1474 schema_document!(
1475 "queue-lease",
1476 1,
1477 QueueLeaseV1,
1478 "QueueLeaseV1",
1479 "P11",
1480 Some("tests/fixtures/p11/queue_lease_v1.json"),
1481 "lease-owner-expiry-major-immutable"
1482 ),
1483 schema_document!(
1484 "daemon-namespace",
1485 1,
1486 DaemonNamespaceV1,
1487 "DaemonNamespaceV1",
1488 "P11",
1489 Some("tests/fixtures/p11/daemon_namespace_v1.json"),
1490 "daemon-namespace-owner-major-immutable"
1491 ),
1492 schema_document!(
1493 "duplicate-suppression-receipt",
1494 1,
1495 DuplicateSuppressionReportV1,
1496 "DuplicateSuppressionReportV1",
1497 "P11",
1498 Some("tests/fixtures/p11/duplicate_suppression_receipt_v1.json"),
1499 "idempotency-suppression-major-immutable"
1500 ),
1501 schema_document!(
1502 "migration-plan",
1503 1,
1504 MigrationPlanV1,
1505 "MigrationPlanV1",
1506 "P07",
1507 Some("tests/fixtures/p07/migration_plan_v1.json"),
1508 "phase-law-major-immutable"
1509 ),
1510 schema_document!(
1511 "patch-apply-receipt",
1512 1,
1513 PatchApplyReportV1,
1514 "PatchApplyReportV1",
1515 "P10",
1516 Some("tests/fixtures/p10/patch_apply_receipt_v1.json"),
1517 "write-evidence-major-immutable"
1518 ),
1519 schema_document!(
1520 "operator-status-report",
1521 1,
1522 OperatorStatusReportV1,
1523 "OperatorStatusReportV1",
1524 "P14",
1525 Some("tests/fixtures/p14/operator_status_report_v1.json"),
1526 "operator-diagnostics-additive-compatible"
1527 ),
1528 schema_document!(
1529 "patch-proposal",
1530 1,
1531 PatchProposalV1,
1532 "PatchProposalV1",
1533 "P10",
1534 Some("tests/fixtures/p10/patch_proposal_v1.json"),
1535 "proposal-is-non-mutating"
1536 ),
1537 schema_document!(
1538 "permit-grant",
1539 1,
1540 PermitGrantV1,
1541 "PermitGrantV1",
1542 "P04",
1543 Some("tests/fixtures/p04/permit_grant_v1.json"),
1544 "scope-narrowing-requires-new-version"
1545 ),
1546 schema_document!(
1547 "permit-use-receipt",
1548 1,
1549 PermitUseReportV1,
1550 "PermitUseReportV1",
1551 "P04",
1552 Some("tests/fixtures/p04/permit_use_receipt_v1.json"),
1553 "receipt-major-immutable"
1554 ),
1555 schema_document!(
1556 "plan-runtime-parity-report",
1557 1,
1558 PlanRuntimeParityReportV1,
1559 "PlanRuntimeParityReportV1",
1560 "P01",
1561 Some("tests/fixtures/p01/plan_runtime_parity_report_v1.json"),
1562 "additive-compatible-with-defaults"
1563 ),
1564 schema_document!(
1565 "queue-hop-receipt",
1566 1,
1567 QueueHopReportV1,
1568 "QueueHopReportV1",
1569 "P11",
1570 Some("tests/fixtures/p11/queue_hop_receipt_v1.json"),
1571 "queue-state-transition-major-immutable"
1572 ),
1573 schema_document!(
1574 "provider-backend-matrix",
1575 1,
1576 ProviderBackendMatrixV1,
1577 "ProviderBackendMatrixV1",
1578 "P02",
1579 Some("tests/fixtures/p02/provider_backend_matrix_v1.json"),
1580 "provider-additions-compatible"
1581 ),
1582 schema_document!(
1583 "provider-certification-fixture",
1584 1,
1585 ProviderCertificationFixtureV1,
1586 "ProviderCertificationFixtureV1",
1587 "P02",
1588 Some("tests/fixtures/p02/provider_certification_fixture_v1.json"),
1589 "expectation-meaning-major-immutable"
1590 ),
1591 schema_document!(
1592 "provider-readiness-receipt",
1593 1,
1594 ProviderReadinessReportV1,
1595 "ProviderReadinessReportV1",
1596 "P02",
1597 Some("tests/fixtures/p02/provider_readiness_receipt_v1.json"),
1598 "receipt-major-immutable"
1599 ),
1600 schema_document!(
1601 "provider-route-receipt",
1602 2,
1603 ProviderRouteReportV2,
1604 "ProviderRouteReportV2",
1605 "P02",
1606 Some("tests/fixtures/p02/provider_route_receipt_v2.json"),
1607 "native-capability-major-immutable"
1608 ),
1609 schema_document!(
1610 "release-readiness-report",
1611 1,
1612 ReleaseReadinessReportV1,
1613 "ReleaseReadinessReportV1",
1614 "P14",
1615 Some("tests/fixtures/p14/release_readiness_report_v1.json"),
1616 "release-blocking-semantics-major-immutable"
1617 ),
1618 schema_document!(
1619 "release-artifact-manifest",
1620 1,
1621 ReleaseArtifactManifestV1,
1622 "ReleaseArtifactManifestV1",
1623 "P19",
1624 Some("tests/fixtures/p19/release_artifact_manifest_v1.json"),
1625 "release-package-content-major-immutable"
1626 ),
1627 schema_document!(
1628 "regression-debt-ledger",
1629 1,
1630 RegressionDebtLedgerV1,
1631 "RegressionDebtLedgerV1",
1632 "P19",
1633 Some("tests/fixtures/p19/regression_debt_ledger_v1.json"),
1634 "regression-debt-status-major-immutable"
1635 ),
1636 schema_document!(
1637 "repo-list-receipt",
1638 1,
1639 RepoListReportV1,
1640 "RepoListReportV1",
1641 "P10",
1642 Some("tests/fixtures/p10/repo_list_receipt_v1.json"),
1643 "sandbox-list-evidence-major-immutable"
1644 ),
1645 schema_document!(
1646 "repo-read-receipt",
1647 1,
1648 RepoReadReportV1,
1649 "RepoReadReportV1",
1650 "P10",
1651 Some("tests/fixtures/p10/repo_read_receipt_v1.json"),
1652 "sandbox-read-evidence-major-immutable"
1653 ),
1654 schema_document!(
1655 "run-receipt",
1656 1,
1657 RunReportV1,
1658 "RunReportV1",
1659 "P05",
1660 Some("tests/fixtures/p05/run_receipt_v1.json"),
1661 "receipt-major-immutable"
1662 ),
1663 schema_document!(
1664 "aidens-run-bundle",
1665 2,
1666 AiDENsRunBundleV2,
1667 "AiDENsRunBundleV2",
1668 "P24",
1669 Some("tests/fixtures/p24/aidens_run_bundle_v2.json"),
1670 "operator-bundle-major-immutable"
1671 ),
1672 schema_document!(
1673 "aidens-run-bundle",
1674 3,
1675 AiDENsRunBundleV3,
1676 "AiDENsRunBundleV3",
1677 "P26",
1678 Some("tests/fixtures/p26/aidens_run_bundle_v3.json"),
1679 "operator-bundle-major-immutable"
1680 ),
1681 schema_document!(
1682 "scaffold-surface-report",
1683 1,
1684 ScaffoldSurfaceReportV1,
1685 "ScaffoldSurfaceReportV1",
1686 "P00",
1687 Some("tests/fixtures/p00/scaffold_surface_report_v1.json"),
1688 "status-meaning-major-immutable"
1689 ),
1690 schema_document!(
1691 "schema-compatibility-report",
1692 1,
1693 SchemaCompatibilityReportV1,
1694 "SchemaCompatibilityReportV1",
1695 "P07",
1696 Some("tests/fixtures/p07/schema_compatibility_report_v1.json"),
1697 "report-additions-compatible"
1698 ),
1699 schema_document!(
1700 "sandbox-capability-truth",
1701 1,
1702 SandboxCapabilityTruthV1,
1703 "SandboxCapabilityTruthV1",
1704 "P10",
1705 Some("tests/fixtures/p10/sandbox_capability_truth_v1.json"),
1706 "sandbox-policy-major-immutable"
1707 ),
1708 schema_document!(
1709 "safe-mode-receipt",
1710 1,
1711 SafeModeReportV1,
1712 "SafeModeReportV1",
1713 "P11",
1714 Some("tests/fixtures/p11/safe_mode_receipt_v1.json"),
1715 "safe-mode-law-major-immutable"
1716 ),
1717 schema_document!(
1718 "schedule-occurrence",
1719 1,
1720 ScheduleOccurrenceV1,
1721 "ScheduleOccurrenceV1",
1722 "P11",
1723 Some("tests/fixtures/p11/schedule_occurrence_v1.json"),
1724 "occurrence-idempotency-major-immutable"
1725 ),
1726 schema_document!(
1727 "source-basis-lock",
1728 1,
1729 SourceBasisLockV1,
1730 "SourceBasisLockV1",
1731 "P00",
1732 Some("tests/fixtures/p00/source_basis_lock_v1.json"),
1733 "source-basis-major-immutable"
1734 ),
1735 schema_document!(
1736 "super-pass-status",
1737 1,
1738 SuperPassStatusV1,
1739 "SuperPassStatusV1",
1740 "P00",
1741 Some("tests/fixtures/p00/super_pass_status_v1.json"),
1742 "status-meaning-major-immutable"
1743 ),
1744 schema_document!(
1745 "tool-call-request",
1746 1,
1747 ToolCallRequestV1,
1748 "ToolCallRequestV1",
1749 "P03",
1750 Some("tests/fixtures/p03/tool_call_request_v1.json"),
1751 "parser-fallback-meaning-major-immutable"
1752 ),
1753 schema_document!(
1754 "tool-call-result",
1755 1,
1756 ToolCallResultV1,
1757 "ToolCallResultV1",
1758 "P03",
1759 Some("tests/fixtures/p03/tool_call_result_v1.json"),
1760 "digest-link-major-immutable"
1761 ),
1762 schema_document!(
1763 "tool-exposure-plan",
1764 2,
1765 ToolExposurePlanV1,
1766 "ToolExposurePlanV2",
1767 "P04",
1768 Some("tests/fixtures/p04/tool_exposure_plan_v2.json"),
1769 "lifecycle-meaning-major-immutable"
1770 ),
1771 schema_document!(
1772 "tool-invocation-receipt",
1773 1,
1774 ToolInvocationReportV1,
1775 "ToolInvocationReportV1",
1776 "P03",
1777 Some("tests/fixtures/p03/tool_invocation_receipt_v1.json"),
1778 "receipt-major-immutable"
1779 ),
1780 schema_document!(
1781 "turn-execution-plan",
1782 1,
1783 TurnExecutionPlanV1,
1784 "TurnExecutionPlanV1",
1785 "P03",
1786 Some("tests/fixtures/p03/turn_execution_plan_v1.json"),
1787 "mode-meaning-major-immutable"
1788 ),
1789 schema_document!(
1790 "turn-receipt",
1791 1,
1792 TurnReportV1,
1793 "TurnReportV1",
1794 "P03",
1795 Some("tests/fixtures/p03/turn_receipt_v1.json"),
1796 "final-state-major-immutable"
1797 ),
1798 schema_document!(
1799 "wake-signal",
1800 1,
1801 WakeSignalV1,
1802 "WakeSignalV1",
1803 "P11",
1804 Some("tests/fixtures/p11/wake_signal_v1.json"),
1805 "wake-idempotency-major-immutable"
1806 ),
1807 ]
1808}