1use crate::VerificationSummary;
2use schemars::JsonSchema;
3use serde::{Deserialize, Serialize};
4use stack_ids::{AttemptId, ContentDigest, EnvelopeId, EpisodeId, ScopeKey, TraceCtx, TrialId};
5
6pub const EPISODE_BUNDLE_V1_SCHEMA: &str = "episode_bundle_v1";
7pub const EXECUTION_CONTEXT_V1_SCHEMA: &str = "execution_context_v1";
8
9#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
10#[serde(rename_all = "snake_case")]
11pub enum DispatchOutcomeV1 {
12 Succeeded,
13 Failed,
14 Degraded,
15 Cancelled,
16 Exhausted,
17}
18
19#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
20pub struct ExecutionContextV1 {
21 pub schema_version: String,
22 pub trace_ctx: TraceCtx,
23 #[serde(default, skip_serializing_if = "Option::is_none")]
24 pub attempt_id: Option<AttemptId>,
25 #[serde(default, skip_serializing_if = "Option::is_none")]
26 pub trial_id: Option<TrialId>,
27 #[serde(default, skip_serializing_if = "Option::is_none")]
28 pub replay_link: Option<String>,
29 #[serde(default, skip_serializing_if = "Option::is_none")]
30 pub workload_class: Option<String>,
31 #[serde(default, skip_serializing_if = "Vec::is_empty")]
32 pub queue_hops: Vec<String>,
33 #[serde(default, skip_serializing_if = "Option::is_none")]
34 pub deadline: Option<String>,
35 #[serde(default, skip_serializing_if = "Option::is_none")]
36 pub cost_budget_units: Option<u64>,
37 #[serde(default, skip_serializing_if = "Vec::is_empty")]
38 pub degradation_markers: Vec<String>,
39 pub dispatch_outcome: DispatchOutcomeV1,
40 #[serde(default, skip_serializing_if = "Option::is_none")]
41 pub environment_fingerprint: Option<String>,
42 #[serde(default, skip_serializing_if = "Option::is_none")]
43 pub provider_route: Option<String>,
44 #[serde(default, skip_serializing_if = "Option::is_none")]
45 pub cancellation_reason: Option<String>,
46 #[serde(default, skip_serializing_if = "Option::is_none")]
48 pub tool_receipt_ref: Option<String>,
49 #[serde(default, skip_serializing_if = "Option::is_none")]
51 pub provider_call_ref: Option<String>,
52 #[serde(default, skip_serializing_if = "Option::is_none")]
54 pub replay_parent_ref: Option<String>,
55}
56
57impl ExecutionContextV1 {
58 pub fn new(trace_ctx: TraceCtx) -> Self {
60 Self {
61 schema_version: EXECUTION_CONTEXT_V1_SCHEMA.into(),
62 trace_ctx,
63 attempt_id: None,
64 trial_id: None,
65 replay_link: None,
66 workload_class: None,
67 queue_hops: Vec::new(),
68 deadline: None,
69 cost_budget_units: None,
70 degradation_markers: Vec::new(),
71 dispatch_outcome: DispatchOutcomeV1::Succeeded,
72 environment_fingerprint: None,
73 provider_route: None,
74 cancellation_reason: None,
75 tool_receipt_ref: None,
76 provider_call_ref: None,
77 replay_parent_ref: None,
78 }
79 }
80}
81
82#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
83pub struct EpisodeBundleV1 {
84 pub schema_version: String,
85 pub bundle_id: String,
86 pub episode_id: EpisodeId,
87 pub primary_document_id: String,
88 pub namespace: String,
89 pub scope_key: ScopeKey,
90 #[serde(default, skip_serializing_if = "Option::is_none")]
91 pub valid_from: Option<String>,
92 #[serde(default, skip_serializing_if = "Option::is_none")]
93 pub valid_to: Option<String>,
94 pub exported_at: String,
95 #[serde(default, skip_serializing_if = "Option::is_none")]
96 pub recorded_at: Option<String>,
97 pub source_envelope_id: EnvelopeId,
98 pub content_digest: ContentDigest,
99 #[serde(default, skip_serializing_if = "Vec::is_empty")]
100 pub source_evidence_pointers: Vec<String>,
101 #[serde(default, skip_serializing_if = "Vec::is_empty")]
102 pub source_receipt_digests: Vec<String>,
103 #[serde(default, skip_serializing_if = "Vec::is_empty")]
104 pub claim_version_ids: Vec<String>,
105 #[serde(default, skip_serializing_if = "Vec::is_empty")]
106 pub relation_version_ids: Vec<String>,
107 #[serde(default, skip_serializing_if = "Option::is_none")]
108 pub verification_summary: Option<VerificationSummary>,
109 #[serde(default, skip_serializing_if = "Vec::is_empty")]
110 pub refutation_artifact_ids: Vec<String>,
111 #[serde(default, skip_serializing_if = "Vec::is_empty")]
112 pub control_plane_refs: Vec<String>,
113 pub execution_context: ExecutionContextV1,
114 pub thin_export: bool,
115 #[serde(default, skip_serializing_if = "Option::is_none")]
116 pub supersedes_bundle_id: Option<String>,
117 #[serde(default, skip_serializing_if = "Option::is_none")]
118 pub evidence_bundle_id: Option<String>,
119}