Skip to main content

agent_sdk_core/records/
journal.rs

1//! Durable run-journal records. Use these records as replayable truth for messages,
2//! effects, checkpoints, output, and recovery. Record constructors are data-only;
3//! append side effects happen through journal ports.
4//!
5pub use crate::domain::JournalCursor;
6
7use serde::{Deserialize, Serialize};
8
9use crate::{
10    approval_records::ApprovalRecord,
11    domain::{
12        AgentError, AgentId, AgentPoolId, AttemptId, ContentRef, ContextProjectionId,
13        CorrelationEntry, DedupeKey, DestinationRef, EntityRef, IdempotencyKey, MessageId,
14        PolicyRef, PrivacyClass, RunId, SessionId, SourceRef, TopicId, TurnId, WakeConditionId,
15    },
16    effect::{EffectIntent, EffectResult},
17    event::{EventCorrelation, EventFilterFingerprint},
18    extension_records::ExtensionActionRecord,
19    hook_records::HookRecord,
20    output_delivery::OutputDeliveryRecord,
21    provider::{ProviderStopReason, ProviderUsage},
22    realtime_records::RealtimeSessionRecord,
23    records_isolation::IsolationRecord,
24    stream_records::StreamRuleRecord,
25    structured_output::{
26        RepairExhaustionRecord, RepairRecord, StructuredOutputLifecycleRecord, ValidationRecord,
27    },
28    subagent_records::{ChildLifecycleRecord, SubagentRecord},
29    tool_records::ToolCallRecord,
30    validated_output::{TypedResultPublicationRecord, ValidatedOutput, ValidationReportRecord},
31};
32
33/// Constant value for the records::journal contract. Use it to keep SDK
34/// records and tests aligned on the same stable value.
35pub const JOURNAL_SCHEMA_VERSION: u16 = 1;
36
37#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
38#[serde(rename_all = "snake_case")]
39/// Enumerates the finite journal record kind cases.
40/// Serialized names are part of the SDK contract; update fixtures when variants change.
41pub enum JournalRecordKind {
42    /// Use this variant when the contract needs to represent run; selecting it has no side effect by itself.
43    Run,
44    /// Use this variant when the contract needs to represent turn; selecting it has no side effect by itself.
45    Turn,
46    /// Use this variant when the contract needs to represent context; selecting it has no side effect by itself.
47    Context,
48    /// Use this variant when the contract needs to represent message; selecting it has no side effect by itself.
49    Message,
50    /// Use this variant when the contract needs to represent model attempt; selecting it has no side effect by itself.
51    ModelAttempt,
52    /// Use this variant when the contract needs to represent structured output; selecting it has no side effect by itself.
53    StructuredOutput,
54    /// Use this variant when the contract needs to represent stream rule; selecting it has no side effect by itself.
55    StreamRule,
56    /// Use this variant when the contract needs to represent realtime session; selecting it has no side effect by itself.
57    RealtimeSession,
58    /// Use this variant when the contract needs to represent hook; selecting it has no side effect by itself.
59    Hook,
60    /// Use this variant when the contract needs to represent approval; selecting it has no side effect by itself.
61    Approval,
62    /// Use this variant when the contract needs to represent tool; selecting it has no side effect by itself.
63    Tool,
64    /// Use this variant when the contract needs to represent isolation; selecting it has no side effect by itself.
65    Isolation,
66    /// Use this variant when the contract needs to represent child lifecycle; selecting it has no side effect by itself.
67    ChildLifecycle,
68    /// Use this variant when the contract needs to represent agent pool; selecting it has no side effect by itself.
69    AgentPool,
70    /// Use this variant when the contract needs to represent run message; selecting it has no side effect by itself.
71    RunMessage,
72    /// Use this variant when the contract needs to represent wake; selecting it has no side effect by itself.
73    Wake,
74    /// Use this variant when the contract needs to represent output dispatch; selecting it has no side effect by itself.
75    OutputDispatch,
76    /// Use this variant when the contract needs to represent subagent; selecting it has no side effect by itself.
77    Subagent,
78    /// Use this variant when the contract needs to represent extension action; selecting it has no side effect by itself.
79    ExtensionAction,
80    /// Use this variant when the contract needs to represent telemetry; selecting it has no side effect by itself.
81    Telemetry,
82    /// Use this variant when the contract needs to represent recovery; selecting it has no side effect by itself.
83    Recovery,
84    /// Use this variant when the contract needs to represent checkpoint; selecting it has no side effect by itself.
85    Checkpoint,
86    /// Use this variant when the contract needs to represent effect intent; selecting it has no side effect by itself.
87    EffectIntent,
88    /// Use this variant when the contract needs to represent effect result; selecting it has no side effect by itself.
89    EffectResult,
90}
91
92#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
93/// Carries the event index projection record payload for journal, event, or fixture surfaces.
94/// Creating or cloning it only preserves serialized SDK state; append, publish, replay, or export effects are documented on the runtime and port methods that store it.
95pub struct EventIndexProjection {
96    /// Run identifier used for lineage, filtering, replay, and dedupe.
97    pub run_id: RunId,
98    #[serde(skip_serializing_if = "Option::is_none")]
99    /// Optional host-provided session identifier for grouping related turns.
100    pub session_id: Option<SessionId>,
101    /// Agent identifier used for lineage, filtering, and ownership checks.
102    pub agent_id: AgentId,
103    #[serde(skip_serializing_if = "Option::is_none")]
104    /// Turn identifier for one loop turn within a run.
105    pub turn_id: Option<TurnId>,
106    /// Event family used by this record or request.
107    pub event_family: String,
108    /// Kind discriminator for event kind.
109    /// Use it to route finite match arms without parsing display text.
110    pub event_kind: String,
111    /// Source label or ref for this item; it is metadata and does not fetch
112    /// content by itself.
113    pub source: SourceRef,
114    #[serde(skip_serializing_if = "Option::is_none")]
115    /// Destination label or ref for this item; it is metadata and does not
116    /// deliver content by itself.
117    pub destination: Option<DestinationRef>,
118    /// Typed subject ref reference. Resolving or executing it is a separate
119    /// policy-gated step.
120    pub subject_ref: EntityRef,
121    #[serde(default, skip_serializing_if = "Vec::is_empty")]
122    /// Typed related refs references. Resolving them is separate from
123    /// constructing this record.
124    pub related_refs: Vec<EntityRef>,
125    #[serde(default, skip_serializing_if = "Vec::is_empty")]
126    /// Correlation-key selector for event filtering.
127    /// `Any` leaves correlation keys unconstrained; `Include` restricts matches to listed keys.
128    pub correlation_keys: Vec<CorrelationEntry>,
129    #[serde(default, skip_serializing_if = "Vec::is_empty")]
130    /// Tag selector for event filtering.
131    /// `Any` leaves tags unconstrained; `Include` restricts matches to listed event tags.
132    pub tags: Vec<String>,
133    /// Privacy class used for projection, telemetry, and raw-content access
134    /// decisions.
135    pub privacy_class: PrivacyClass,
136    /// Delivery-semantic selector for event filtering.
137    /// `Any` leaves delivery semantics unconstrained; `Include` restricts matches to listed
138    /// semantics.
139    pub delivery_semantics: String,
140}
141
142#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
143/// Carries the journal record record payload for journal, event, or fixture surfaces.
144/// Creating or cloning it only preserves serialized SDK state; append, publish, replay, or export effects are documented on the runtime and port methods that store it.
145pub struct JournalRecord {
146    /// Wire schema version for this record shape.
147    /// Use it for compatibility checks before deserializing or replaying stored data.
148    pub journal_schema_version: u16,
149    /// Journal seq used by this record or request.
150    pub journal_seq: u64,
151    /// Stable record id used for typed lineage, lookup, or dedupe.
152    pub record_id: String,
153    /// Kind discriminator for record kind.
154    /// Use it to route finite match arms without parsing display text.
155    pub record_kind: JournalRecordKind,
156    /// Run identifier used for lineage, filtering, replay, and dedupe.
157    pub run_id: RunId,
158    #[serde(skip_serializing_if = "Option::is_none")]
159    /// Optional host-provided session identifier for grouping related turns.
160    pub session_id: Option<SessionId>,
161    /// Agent identifier used for lineage, filtering, and ownership checks.
162    pub agent_id: AgentId,
163    #[serde(skip_serializing_if = "Option::is_none")]
164    /// Turn identifier for one loop turn within a run.
165    pub turn_id: Option<TurnId>,
166    #[serde(skip_serializing_if = "Option::is_none")]
167    /// Attempt identifier for retry, repair, provider, or tool execution
168    /// evidence.
169    pub attempt_id: Option<AttemptId>,
170    /// Typed subject ref reference. Resolving or executing it is a separate
171    /// policy-gated step.
172    pub subject_ref: EntityRef,
173    #[serde(default, skip_serializing_if = "Vec::is_empty")]
174    /// Typed related refs references. Resolving them is separate from
175    /// constructing this record.
176    pub related_refs: Vec<EntityRef>,
177    #[serde(default, skip_serializing_if = "Vec::is_empty")]
178    /// Typed causal refs references. Resolving them is separate from
179    /// constructing this record.
180    pub causal_refs: Vec<String>,
181    /// Source label or ref for this item; it is metadata and does not fetch
182    /// content by itself.
183    pub source: SourceRef,
184    #[serde(skip_serializing_if = "Option::is_none")]
185    /// Destination label or ref for this item; it is metadata and does not
186    /// deliver content by itself.
187    pub destination: Option<DestinationRef>,
188    #[serde(default, skip_serializing_if = "Vec::is_empty")]
189    /// Correlation-key selector for event filtering.
190    /// `Any` leaves correlation keys unconstrained; `Include` restricts matches to listed keys.
191    pub correlation_keys: Vec<CorrelationEntry>,
192    #[serde(default, skip_serializing_if = "Vec::is_empty")]
193    /// Tag selector for event filtering.
194    /// `Any` leaves tags unconstrained; `Include` restricts matches to listed event tags.
195    pub tags: Vec<String>,
196    /// Delivery-semantic selector for event filtering.
197    /// `Any` leaves delivery semantics unconstrained; `Include` restricts matches to listed
198    /// semantics.
199    pub delivery_semantics: String,
200    /// Event index used by this record or request.
201    pub event_index: EventIndexProjection,
202    /// Timestamp in milliseconds associated with this record.
203    /// Use it for ordering and diagnostics; durable causality still comes from ids and cursors.
204    pub timestamp_millis: u64,
205    /// Fingerprint of the runtime package snapshot in force when this value was produced.
206    /// Use it for replay, dedupe, and package-lineage checks; the field is evidence and does
207    /// not execute package behavior.
208    pub runtime_package_fingerprint: String,
209    /// Privacy class used for projection, telemetry, and raw-content access
210    /// decisions.
211    pub privacy: PrivacyClass,
212    #[serde(default, skip_serializing_if = "Vec::is_empty")]
213    /// Content references associated with this record; resolving them is a
214    /// separate policy-gated step.
215    pub content_refs: Vec<ContentRef>,
216    /// Stable redaction policy id used for typed lineage, lookup, or dedupe.
217    pub redaction_policy_id: String,
218    #[serde(skip_serializing_if = "Option::is_none")]
219    /// Idempotency setting or key for deduping retries.
220    /// Use it to prevent duplicate side effects during replay or repair.
221    pub idempotency_key: Option<IdempotencyKey>,
222    #[serde(skip_serializing_if = "Option::is_none")]
223    /// Dedupe policy or key for a side-effecting operation.
224    /// Replay and repair use it to avoid sending or executing the same effect twice.
225    pub dedupe_key: Option<DedupeKey>,
226    #[serde(skip_serializing_if = "Option::is_none")]
227    /// Typed checkpoint ref reference. Resolving or executing it is a
228    /// separate policy-gated step.
229    pub checkpoint_ref: Option<String>,
230    /// Payload carried by this record.
231    /// Use the surrounding policy and redaction fields to decide whether it can be exposed.
232    pub payload: JournalRecordPayload,
233}
234
235impl JournalRecord {
236    /// Returns an updated records::journal value with effect intent applied.
237    /// This is data construction only and does not execute the configured
238    /// behavior.
239    pub fn effect_intent(base: JournalRecordBase, intent: EffectIntent) -> Self {
240        let mut event_index = base.event_index("effect", "intent");
241        let effect_ref =
242            EntityRef::new(crate::domain::EntityKind::Effect, intent.effect_id.clone());
243        event_index.subject_ref = intent.subject_ref.clone();
244        event_index.related_refs = vec![effect_ref.clone()];
245        Self {
246            journal_schema_version: JOURNAL_SCHEMA_VERSION,
247            journal_seq: base.journal_seq,
248            record_id: base.record_id,
249            record_kind: JournalRecordKind::EffectIntent,
250            run_id: base.run_id,
251            session_id: base.session_id,
252            agent_id: base.agent_id,
253            turn_id: base.turn_id,
254            attempt_id: base.attempt_id,
255            subject_ref: intent.subject_ref.clone(),
256            related_refs: vec![effect_ref],
257            causal_refs: base.causal_refs,
258            source: intent.source.clone(),
259            destination: intent.destination.clone(),
260            correlation_keys: Vec::new(),
261            tags: base.tags,
262            delivery_semantics: "journal_backed".to_string(),
263            event_index,
264            timestamp_millis: base.timestamp_millis,
265            runtime_package_fingerprint: base.runtime_package_fingerprint,
266            privacy: base.privacy,
267            content_refs: intent.content_refs.clone(),
268            redaction_policy_id: base.redaction_policy_id,
269            idempotency_key: intent.idempotency_key.clone(),
270            dedupe_key: intent.dedupe_key.clone(),
271            checkpoint_ref: base.checkpoint_ref,
272            payload: JournalRecordPayload::EffectIntent(intent),
273        }
274    }
275
276    /// Builds the effect result record or result value.
277    /// This is data-only and does not perform I/O, call host ports, append journals, publish
278    /// events, or start processes.
279    pub fn effect_result(base: JournalRecordBase, result: EffectResult) -> Self {
280        let effect_ref =
281            EntityRef::new(crate::domain::EntityKind::Effect, result.effect_id.clone());
282        let mut event_index = base.event_index("effect", "result");
283        event_index.subject_ref = effect_ref.clone();
284        event_index.related_refs = vec![effect_ref.clone()];
285        Self {
286            journal_schema_version: JOURNAL_SCHEMA_VERSION,
287            journal_seq: base.journal_seq,
288            record_id: base.record_id,
289            record_kind: JournalRecordKind::EffectResult,
290            run_id: base.run_id,
291            session_id: base.session_id,
292            agent_id: base.agent_id,
293            turn_id: base.turn_id,
294            attempt_id: base.attempt_id,
295            subject_ref: effect_ref.clone(),
296            related_refs: vec![effect_ref],
297            causal_refs: base.causal_refs,
298            source: base.source,
299            destination: base.destination,
300            correlation_keys: Vec::new(),
301            tags: base.tags,
302            delivery_semantics: "journal_backed".to_string(),
303            event_index,
304            timestamp_millis: base.timestamp_millis,
305            runtime_package_fingerprint: base.runtime_package_fingerprint,
306            privacy: base.privacy,
307            content_refs: result.content_refs.clone(),
308            redaction_policy_id: base.redaction_policy_id,
309            idempotency_key: None,
310            dedupe_key: None,
311            checkpoint_ref: base.checkpoint_ref,
312            payload: JournalRecordPayload::EffectResult(result),
313        }
314    }
315
316    /// Builds the checkpoint record or result value.
317    /// This is data-only and does not perform I/O, call host ports, append journals, publish
318    /// events, or start processes.
319    pub fn checkpoint(base: JournalRecordBase, checkpoint: RunCheckpoint) -> Self {
320        let event_index = base.event_index("checkpoint", "saved");
321        Self {
322            journal_schema_version: JOURNAL_SCHEMA_VERSION,
323            journal_seq: base.journal_seq,
324            record_id: base.record_id,
325            record_kind: JournalRecordKind::Checkpoint,
326            run_id: base.run_id.clone(),
327            session_id: base.session_id,
328            agent_id: base.agent_id,
329            turn_id: base.turn_id,
330            attempt_id: base.attempt_id,
331            subject_ref: EntityRef::run(base.run_id),
332            related_refs: Vec::new(),
333            causal_refs: base.causal_refs,
334            source: base.source,
335            destination: base.destination,
336            correlation_keys: Vec::new(),
337            tags: base.tags,
338            delivery_semantics: "journal_backed".to_string(),
339            event_index,
340            timestamp_millis: base.timestamp_millis,
341            runtime_package_fingerprint: base.runtime_package_fingerprint,
342            privacy: base.privacy,
343            content_refs: checkpoint.content_ref_manifest.clone(),
344            redaction_policy_id: base.redaction_policy_id,
345            idempotency_key: None,
346            dedupe_key: None,
347            checkpoint_ref: Some(checkpoint.checkpoint_id.clone()),
348            payload: JournalRecordPayload::Checkpoint(checkpoint),
349        }
350    }
351
352    /// Recovery.
353    /// This is data-only and does not perform I/O, call host ports, append journals, publish
354    /// events, or start processes.
355    pub fn recovery(base: JournalRecordBase, recovery: RecoveryMarker) -> Self {
356        let event_index = base.event_index("recovery", "marker");
357        Self {
358            journal_schema_version: JOURNAL_SCHEMA_VERSION,
359            journal_seq: base.journal_seq,
360            record_id: base.record_id,
361            record_kind: JournalRecordKind::Recovery,
362            run_id: base.run_id.clone(),
363            session_id: base.session_id,
364            agent_id: base.agent_id,
365            turn_id: base.turn_id,
366            attempt_id: base.attempt_id,
367            subject_ref: EntityRef::run(base.run_id),
368            related_refs: Vec::new(),
369            causal_refs: base.causal_refs,
370            source: base.source,
371            destination: base.destination,
372            correlation_keys: Vec::new(),
373            tags: base.tags,
374            delivery_semantics: "journal_backed".to_string(),
375            event_index,
376            timestamp_millis: base.timestamp_millis,
377            runtime_package_fingerprint: base.runtime_package_fingerprint,
378            privacy: base.privacy,
379            content_refs: Vec::new(),
380            redaction_policy_id: base.redaction_policy_id,
381            idempotency_key: None,
382            dedupe_key: None,
383            checkpoint_ref: base.checkpoint_ref,
384            payload: JournalRecordPayload::Recovery(recovery),
385        }
386    }
387
388    /// Builds the feature record record or result value.
389    /// This is data-only and does not perform I/O, call host ports, append journals, publish
390    /// events, or start processes.
391    #[expect(
392        clippy::too_many_arguments,
393        reason = "feature records intentionally expose journal/event lineage fields; replacing this with a builder needs a separate public API review"
394    )]
395    pub fn feature_record(
396        base: JournalRecordBase,
397        record_kind: JournalRecordKind,
398        event_family: impl Into<String>,
399        event_kind: impl Into<String>,
400        subject_ref: EntityRef,
401        related_refs: Vec<EntityRef>,
402        content_refs: Vec<ContentRef>,
403        payload: JournalRecordPayload,
404    ) -> Self {
405        let mut event_index = base.event_index(event_family, event_kind);
406        event_index.subject_ref = subject_ref.clone();
407        event_index.related_refs = related_refs.clone();
408        Self {
409            journal_schema_version: JOURNAL_SCHEMA_VERSION,
410            journal_seq: base.journal_seq,
411            record_id: base.record_id,
412            record_kind,
413            run_id: base.run_id,
414            session_id: base.session_id,
415            agent_id: base.agent_id,
416            turn_id: base.turn_id,
417            attempt_id: base.attempt_id,
418            subject_ref,
419            related_refs,
420            causal_refs: base.causal_refs,
421            source: base.source,
422            destination: base.destination,
423            correlation_keys: Vec::new(),
424            tags: base.tags,
425            delivery_semantics: "journal_backed".to_string(),
426            event_index,
427            timestamp_millis: base.timestamp_millis,
428            runtime_package_fingerprint: base.runtime_package_fingerprint,
429            privacy: base.privacy,
430            content_refs,
431            redaction_policy_id: base.redaction_policy_id,
432            idempotency_key: None,
433            dedupe_key: None,
434            checkpoint_ref: base.checkpoint_ref,
435            payload,
436        }
437    }
438}
439
440#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
441/// Carries the journal record base record payload for journal, event, or fixture surfaces.
442/// Creating or cloning it only preserves serialized SDK state; append, publish, replay, or export effects are documented on the runtime and port methods that store it.
443pub struct JournalRecordBase {
444    /// Journal seq used by this record or request.
445    pub journal_seq: u64,
446    /// Stable record id used for typed lineage, lookup, or dedupe.
447    pub record_id: String,
448    /// Run identifier used for lineage, filtering, replay, and dedupe.
449    pub run_id: RunId,
450    #[serde(skip_serializing_if = "Option::is_none")]
451    /// Optional host-provided session identifier for grouping related turns.
452    pub session_id: Option<SessionId>,
453    /// Agent identifier used for lineage, filtering, and ownership checks.
454    pub agent_id: AgentId,
455    #[serde(skip_serializing_if = "Option::is_none")]
456    /// Turn identifier for one loop turn within a run.
457    pub turn_id: Option<TurnId>,
458    #[serde(skip_serializing_if = "Option::is_none")]
459    /// Attempt identifier for retry, repair, provider, or tool execution
460    /// evidence.
461    pub attempt_id: Option<AttemptId>,
462    /// Source label or ref for this item; it is metadata and does not fetch
463    /// content by itself.
464    pub source: SourceRef,
465    #[serde(skip_serializing_if = "Option::is_none")]
466    /// Destination label or ref for this item; it is metadata and does not
467    /// deliver content by itself.
468    pub destination: Option<DestinationRef>,
469    #[serde(default, skip_serializing_if = "Vec::is_empty")]
470    /// Typed causal refs references. Resolving them is separate from
471    /// constructing this record.
472    pub causal_refs: Vec<String>,
473    #[serde(default, skip_serializing_if = "Vec::is_empty")]
474    /// Tag selector for event filtering.
475    /// `Any` leaves tags unconstrained; `Include` restricts matches to listed event tags.
476    pub tags: Vec<String>,
477    /// Timestamp in milliseconds associated with this record.
478    /// Use it for ordering and diagnostics; durable causality still comes from ids and cursors.
479    pub timestamp_millis: u64,
480    /// Fingerprint of the runtime package snapshot in force when this value was produced.
481    /// Use it for replay, dedupe, and package-lineage checks; the field is evidence and does
482    /// not execute package behavior.
483    pub runtime_package_fingerprint: String,
484    /// Privacy class used for projection, telemetry, and raw-content access
485    /// decisions.
486    pub privacy: PrivacyClass,
487    /// Stable redaction policy id used for typed lineage, lookup, or dedupe.
488    pub redaction_policy_id: String,
489    #[serde(skip_serializing_if = "Option::is_none")]
490    /// Typed checkpoint ref reference. Resolving or executing it is a
491    /// separate policy-gated step.
492    pub checkpoint_ref: Option<String>,
493}
494
495impl JournalRecordBase {
496    /// Creates a new records::journal value with explicit
497    /// caller-provided inputs. This constructor is data-only and
498    /// performs no I/O or external side effects.
499    pub fn new(
500        journal_seq: u64,
501        record_id: impl Into<String>,
502        run_id: RunId,
503        agent_id: AgentId,
504        source: SourceRef,
505    ) -> Self {
506        Self {
507            journal_seq,
508            record_id: record_id.into(),
509            run_id,
510            session_id: None,
511            agent_id,
512            turn_id: None,
513            attempt_id: None,
514            source,
515            destination: None,
516            causal_refs: Vec::new(),
517            tags: Vec::new(),
518            timestamp_millis: 0,
519            runtime_package_fingerprint: "runtime.package.fingerprint.test".to_string(),
520            privacy: PrivacyClass::ContentRefsOnly,
521            redaction_policy_id: "redaction.default".to_string(),
522            checkpoint_ref: None,
523        }
524    }
525
526    fn event_index(
527        &self,
528        event_family: impl Into<String>,
529        event_kind: impl Into<String>,
530    ) -> EventIndexProjection {
531        EventIndexProjection {
532            run_id: self.run_id.clone(),
533            session_id: self.session_id.clone(),
534            agent_id: self.agent_id.clone(),
535            turn_id: self.turn_id.clone(),
536            event_family: event_family.into(),
537            event_kind: event_kind.into(),
538            source: self.source.clone(),
539            destination: self.destination.clone(),
540            subject_ref: EntityRef::run(self.run_id.clone()),
541            related_refs: Vec::new(),
542            correlation_keys: Vec::new(),
543            tags: self.tags.clone(),
544            privacy_class: self.privacy,
545            delivery_semantics: "journal_backed".to_string(),
546        }
547    }
548}
549
550#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
551#[serde(tag = "type", rename_all = "snake_case")]
552/// Enumerates the finite journal record payload cases.
553/// Serialized names are part of the SDK contract; update fixtures when variants change.
554#[expect(
555    clippy::large_enum_variant,
556    reason = "journal payloads are durable serde records; direct variant payloads are intentionally preserved until a fixture-reviewed envelope redesign"
557)]
558pub enum JournalRecordPayload {
559    /// Use this variant when the contract needs to represent run lifecycle; selecting it has no side effect by itself.
560    RunLifecycle(RunLifecycleRecord),
561    /// Use this variant when the contract needs to represent turn lifecycle; selecting it has no side effect by itself.
562    TurnLifecycle(TurnLifecycleRecord),
563    /// Use this variant when the contract needs to represent context projection; selecting it has no side effect by itself.
564    ContextProjection(ContextProjectionRecord),
565    /// Use this variant when the contract needs to represent model attempt; selecting it has no side effect by itself.
566    ModelAttempt(ModelAttemptRecord),
567    /// Use this variant when the contract needs to represent message; selecting it has no side effect by itself.
568    Message(MessageRecord),
569    /// Use this variant when the contract needs to represent structured output; selecting it has no side effect by itself.
570    StructuredOutput(StructuredOutputRecord),
571    /// Use this variant when the contract needs to represent approval; selecting it has no side effect by itself.
572    Approval(ApprovalRecord),
573    /// Use this variant when the contract needs to represent tool; selecting it has no side effect by itself.
574    Tool(ToolCallRecord),
575    /// Use this variant when the contract needs to represent output delivery; selecting it has no side effect by itself.
576    OutputDelivery(OutputDeliveryRecord),
577    /// Use this variant when the contract needs to represent hook; selecting it has no side effect by itself.
578    Hook(HookRecord),
579    /// Use this variant when the contract needs to represent stream rule; selecting it has no side effect by itself.
580    StreamRule(StreamRuleRecord),
581    /// Use this variant when the contract needs to represent realtime session; selecting it has no side effect by itself.
582    RealtimeSession(RealtimeSessionRecord),
583    /// Use this variant when the contract needs to represent isolation; selecting it has no side effect by itself.
584    Isolation(IsolationRecord),
585    /// Use this variant when the contract needs to represent child lifecycle; selecting it has no side effect by itself.
586    ChildLifecycle(ChildLifecycleRecord),
587    /// Use this variant when the contract needs to represent agent pool; selecting it has no side effect by itself.
588    AgentPool(AgentPoolRecord),
589    /// Use this variant when the contract needs to represent run message; selecting it has no side effect by itself.
590    RunMessage(RunMessageRecord),
591    /// Use this variant when the contract needs to represent wake; selecting it has no side effect by itself.
592    Wake(WakeRecord),
593    /// Use this variant when the contract needs to represent subagent; selecting it has no side effect by itself.
594    Subagent(SubagentRecord),
595    /// Use this variant when the contract needs to represent extension action; selecting it has no side effect by itself.
596    ExtensionAction(ExtensionActionRecord),
597    /// Use this variant when the contract needs to represent effect intent; selecting it has no side effect by itself.
598    EffectIntent(EffectIntent),
599    /// Use this variant when the contract needs to represent effect result; selecting it has no side effect by itself.
600    EffectResult(EffectResult),
601    /// Use this variant when the contract needs to represent checkpoint; selecting it has no side effect by itself.
602    Checkpoint(RunCheckpoint),
603    /// Use this variant when the contract needs to represent recovery; selecting it has no side effect by itself.
604    Recovery(RecoveryMarker),
605    /// Use this variant when the contract needs to represent terminal result; selecting it has no side effect by itself.
606    TerminalResult(TerminalResultMarker),
607}
608
609#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
610#[serde(tag = "record_type", content = "record", rename_all = "snake_case")]
611/// Enumerates the finite structured output record cases.
612/// Serialized names are part of the SDK contract; update fixtures when variants change.
613#[expect(
614    clippy::large_enum_variant,
615    reason = "structured-output payloads preserve serde and pattern-match ergonomics; boxing variants should be a dedicated contract migration"
616)]
617pub enum StructuredOutputRecord {
618    /// Use this variant when the contract needs to represent lifecycle; selecting it has no side effect by itself.
619    Lifecycle(StructuredOutputLifecycleRecord),
620    /// Use this variant when the contract needs to represent validation; selecting it has no side effect by itself.
621    Validation(ValidationRecord),
622    /// Use this variant when the contract needs to represent repair; selecting it has no side effect by itself.
623    Repair(RepairRecord),
624    /// Use this variant when the contract needs to represent repair exhaustion; selecting it has no side effect by itself.
625    RepairExhaustion(RepairExhaustionRecord),
626    /// Use this variant when the contract needs to represent validation report; selecting it has no side effect by itself.
627    ValidationReport(ValidationReportRecord),
628    /// Use this variant when the contract needs to represent validated output; selecting it has no side effect by itself.
629    ValidatedOutput(ValidatedOutput),
630    /// Use this variant when the contract needs to represent typed result publication; selecting it has no side effect by itself.
631    TypedResultPublication(TypedResultPublicationRecord),
632}
633
634#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
635/// Carries the run lifecycle record record payload for journal, event, or fixture surfaces.
636/// Creating or cloning it only preserves serialized SDK state; append, publish, replay, or export effects are documented on the runtime and port methods that store it.
637pub struct RunLifecycleRecord {
638    /// Finite status for this record or lifecycle stage.
639    pub status: String,
640    /// Redacted explanation for a denial, failure, status, or package delta.
641    pub reason: String,
642}
643
644#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
645/// Carries the turn lifecycle record payload for journal, event, or fixture surfaces.
646/// Creating or cloning it only preserves serialized SDK state; append, publish, replay, or export effects are documented on the runtime and port methods that store it.
647pub struct TurnLifecycleRecord {
648    /// Turn identifier for one loop turn within a run.
649    pub turn_id: TurnId,
650    #[serde(default, skip_serializing_if = "Vec::is_empty")]
651    /// Run identifiers causally associated with this turn.
652    pub run_ids: Vec<RunId>,
653    /// Lifecycle status used by this record or request.
654    pub status: TurnLifecycleStatus,
655    #[serde(skip_serializing_if = "Option::is_none")]
656    /// Message identifier for transcript, projection, or provider-response
657    /// lineage.
658    pub input_message_id: Option<MessageId>,
659    #[serde(skip_serializing_if = "Option::is_none")]
660    /// Message identifier for transcript, projection, or provider-response
661    /// lineage.
662    pub output_message_id: Option<MessageId>,
663    #[serde(skip_serializing_if = "Option::is_none")]
664    /// Stable projection id used for typed lineage, lookup, or dedupe.
665    pub context_projection_id: Option<ContextProjectionId>,
666    /// Redacted human-readable summary safe for events, telemetry, and logs.
667    pub redacted_summary: String,
668}
669
670#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
671#[serde(rename_all = "snake_case")]
672/// Enumerates the finite turn lifecycle status cases.
673/// Serialized names are part of the SDK contract; update fixtures when variants change.
674pub enum TurnLifecycleStatus {
675    /// Use this variant when the contract needs to represent started; selecting it has no side effect by itself.
676    Started,
677    /// Use this variant when the contract needs to represent completed; selecting it has no side effect by itself.
678    Completed,
679    /// Use this variant when the contract needs to represent failed; selecting it has no side effect by itself.
680    Failed,
681}
682
683impl TurnLifecycleStatus {
684    /// Returns the stable wire label for this lifecycle status.
685    /// This is a pure helper for journal and event lowering.
686    pub fn as_str(&self) -> &'static str {
687        match self {
688            Self::Started => "started",
689            Self::Completed => "completed",
690            Self::Failed => "failed",
691        }
692    }
693}
694
695#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
696/// Carries the context projection record record payload for journal, event, or fixture surfaces.
697/// Creating or cloning it only preserves serialized SDK state; append, publish, replay, or export effects are documented on the runtime and port methods that store it.
698pub struct ContextProjectionRecord {
699    /// Stable projection id used for typed lineage, lookup, or dedupe.
700    pub projection_id: ContextProjectionId,
701    /// Count of selected item items observed or included in this record.
702    pub selected_item_count: u32,
703    /// Provider destination used by this record or request.
704    pub provider_destination: DestinationRef,
705}
706
707#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
708/// Carries the model attempt record record payload for journal, event, or fixture surfaces.
709/// Creating or cloning it only preserves serialized SDK state; append, publish, replay, or export effects are documented on the runtime and port methods that store it.
710pub struct ModelAttemptRecord {
711    /// Stable provider route id used for typed lineage, lookup, or dedupe.
712    pub provider_route_id: String,
713    /// Stable provider model id used for typed lineage, lookup, or dedupe.
714    pub provider_model_id: String,
715    /// Count of request message items observed or included in this record.
716    pub request_message_count: u32,
717    #[serde(skip_serializing_if = "Option::is_none")]
718    /// Optional stop reason value.
719    /// When absent, callers should use the documented default or skip that optional behavior.
720    pub stop_reason: Option<ProviderStopReason>,
721    #[serde(skip_serializing_if = "Option::is_none")]
722    /// Optional usage value.
723    /// When absent, callers should use the documented default or skip that optional behavior.
724    pub usage: Option<ProviderUsage>,
725}
726
727#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
728/// Carries the message record record payload for journal, event, or fixture surfaces.
729/// Creating or cloning it only preserves serialized SDK state; append, publish, replay, or export effects are documented on the runtime and port methods that store it.
730pub struct MessageRecord {
731    /// Message identifier for transcript, projection, or provider-response
732    /// lineage.
733    pub message_id: MessageId,
734    /// Role used by this record or request.
735    pub role: String,
736    /// Redacted human-readable summary safe for events, telemetry, and logs.
737    pub redacted_summary: String,
738}
739
740#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
741/// Carries the agent pool record record payload for journal, event, or fixture surfaces.
742/// Creating or cloning it only preserves serialized SDK state; append, publish, replay, or export effects are documented on the runtime and port methods that store it.
743pub struct AgentPoolRecord {
744    /// Stable pool id used for typed lineage, lookup, or dedupe.
745    pub pool_id: AgentPoolId,
746    #[serde(default, skip_serializing_if = "Vec::is_empty")]
747    /// Identifiers used to select or correlate member run values.
748    /// Use them for typed lookup, filtering, or lineage instead of stringly typed matching.
749    pub member_run_ids: Vec<RunId>,
750    #[serde(default, skip_serializing_if = "Vec::is_empty")]
751    /// Collection of topics values.
752    /// Ordering and membership should be treated as part of the serialized contract when
753    /// relevant.
754    pub topics: Vec<TopicId>,
755    #[serde(default, skip_serializing_if = "Vec::is_empty")]
756    /// Policy references that govern admission, projection, execution, or
757    /// delivery.
758    pub policy_refs: Vec<PolicyRef>,
759    /// Lifecycle status used by this record or request.
760    pub lifecycle_status: AgentPoolLifecycleStatus,
761}
762
763#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
764#[serde(rename_all = "snake_case")]
765/// Enumerates the finite agent pool lifecycle status cases.
766/// Serialized names are part of the SDK contract; update fixtures when variants change.
767pub enum AgentPoolLifecycleStatus {
768    /// Use this variant when the contract needs to represent created; selecting it has no side effect by itself.
769    Created,
770    /// Use this variant when the contract needs to represent run joined; selecting it has no side effect by itself.
771    RunJoined,
772    /// Use this variant when the contract needs to represent run left; selecting it has no side effect by itself.
773    RunLeft,
774}
775
776#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
777/// Carries the run message record record payload for journal, event, or fixture surfaces.
778/// Creating or cloning it only preserves serialized SDK state; append, publish, replay, or export effects are documented on the runtime and port methods that store it.
779pub struct RunMessageRecord {
780    /// Message identifier for transcript, projection, or provider-response
781    /// lineage.
782    pub message_id: MessageId,
783    /// Stable source run id used for typed lineage, lookup, or dedupe.
784    pub source_run_id: RunId,
785    /// Address target used by this record or request.
786    pub address_target: RunMessageAddressTargetRecord,
787    /// Content reference where payload bytes or structured tool output are
788    /// stored.
789    pub content_ref: ContentRef,
790    /// Correlation used by this record or request.
791    pub correlation: EventCorrelation,
792    #[serde(skip_serializing_if = "Option::is_none")]
793    /// Optional reply to value.
794    /// When absent, callers should use the documented default or skip that optional behavior.
795    pub reply_to: Option<MessageId>,
796    /// Output delivery setting or policy.
797    /// Delivery coordinators use it to decide sink mode, dedupe, and required evidence.
798    pub delivery_status: RunMessageDeliveryStatus,
799    #[serde(default, skip_serializing_if = "Vec::is_empty")]
800    /// Collection of delivered to values.
801    /// Ordering and membership should be treated as part of the serialized contract when
802    /// relevant.
803    pub delivered_to: Vec<RunId>,
804    #[serde(default, skip_serializing_if = "Vec::is_empty")]
805    /// Policy references that govern admission, projection, execution, or
806    /// delivery.
807    pub policy_refs: Vec<PolicyRef>,
808    /// Idempotency setting or key for deduping retries.
809    /// Use it to prevent duplicate side effects during replay or repair.
810    pub idempotency_key: IdempotencyKey,
811    #[serde(skip_serializing_if = "Option::is_none")]
812    /// Optional effect intent value.
813    /// When absent, callers should use the documented default or skip that optional behavior.
814    pub effect_intent: Option<EffectIntent>,
815    #[serde(skip_serializing_if = "Option::is_none")]
816    /// Optional effect result value.
817    /// When absent, callers should use the documented default or skip that optional behavior.
818    pub effect_result: Option<EffectResult>,
819}
820
821#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
822#[serde(tag = "type", rename_all = "snake_case")]
823/// Enumerates the finite run message address target record cases.
824/// Serialized names are part of the SDK contract; update fixtures when variants change.
825pub enum RunMessageAddressTargetRecord {
826    /// Use this variant when the contract needs to represent run; selecting it has no side effect by itself.
827    Run {
828        /// Run identifier used for lineage, filtering, replay, and dedupe.
829        run_id: RunId,
830    },
831    /// Use this variant when the contract needs to represent agent; selecting it has no side effect by itself.
832    Agent {
833        /// Agent identifier used for lineage, filtering, and ownership
834        /// checks.
835        agent_id: AgentId,
836    },
837    /// Use this variant when the contract needs to represent topic; selecting it has no side effect by itself.
838    Topic {
839        /// Stable topic id used for typed lineage, lookup, or dedupe.
840        topic_id: TopicId,
841    },
842    /// Use this variant when the contract needs to represent pool; selecting it has no side effect by itself.
843    Pool {
844        /// Stable pool id used for typed lineage, lookup, or dedupe.
845        pool_id: AgentPoolId,
846    },
847}
848
849#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
850#[serde(rename_all = "snake_case")]
851/// Enumerates the finite run message delivery status cases.
852/// Serialized names are part of the SDK contract; update fixtures when variants change.
853pub enum RunMessageDeliveryStatus {
854    /// Use this variant when the contract needs to represent accepted; selecting it has no side effect by itself.
855    Accepted,
856    /// Use this variant when the contract needs to represent delivered; selecting it has no side effect by itself.
857    Delivered,
858    /// Use this variant when the contract needs to represent responded; selecting it has no side effect by itself.
859    Responded,
860    /// Use this variant when the contract needs to represent failed; selecting it has no side effect by itself.
861    Failed,
862    /// Use this variant when the contract needs to represent timed out; selecting it has no side effect by itself.
863    TimedOut,
864    /// Use this variant when the contract needs to represent expired; selecting it has no side effect by itself.
865    Expired,
866    /// Use this variant when the contract needs to represent cancelled; selecting it has no side effect by itself.
867    Cancelled,
868}
869
870#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
871/// Carries the wake record record payload for journal, event, or fixture surfaces.
872/// Creating or cloning it only preserves serialized SDK state; append, publish, replay, or export effects are documented on the runtime and port methods that store it.
873pub struct WakeRecord {
874    /// Stable condition id used for typed lineage, lookup, or dedupe.
875    pub condition_id: WakeConditionId,
876    /// Run identifier used for lineage, filtering, replay, and dedupe.
877    pub run_id: RunId,
878    /// Deterministic event filter fingerprint used for stale checks, package
879    /// evidence, or replay comparisons.
880    pub event_filter_fingerprint: EventFilterFingerprint,
881    #[serde(skip_serializing_if = "Option::is_none")]
882    /// Time value in milliseconds for timeout millis.
883    /// Use it for timeout, ordering, or diagnostic calculations.
884    pub timeout_millis: Option<u64>,
885    /// Resume policy used by this record or request.
886    pub resume_policy: WakeResumeInputPolicyRecord,
887    /// Trigger status used by this record or request.
888    pub trigger_status: WakeTriggerStatus,
889    #[serde(default, skip_serializing_if = "Vec::is_empty")]
890    /// Policy references that govern admission, projection, execution, or
891    /// delivery.
892    pub policy_refs: Vec<PolicyRef>,
893    /// Idempotency setting or key for deduping retries.
894    /// Use it to prevent duplicate side effects during replay or repair.
895    pub idempotency_key: IdempotencyKey,
896    #[serde(skip_serializing_if = "Option::is_none")]
897    /// Stable matched event id used for typed lineage, lookup, or dedupe.
898    pub matched_event_id: Option<crate::domain::EventId>,
899}
900
901#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
902#[serde(rename_all = "snake_case")]
903/// Enumerates the finite wake resume input policy record cases.
904/// Serialized names are part of the SDK contract; update fixtures when variants change.
905pub enum WakeResumeInputPolicyRecord {
906    /// Use this variant when the contract needs to represent matching event refs; selecting it has no side effect by itself.
907    MatchingEventRefs,
908    /// Use this variant when the contract needs to represent redacted summary; selecting it has no side effect by itself.
909    RedactedSummary,
910    /// Use this variant when the contract needs to represent none; selecting it has no side effect by itself.
911    None,
912}
913
914#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
915#[serde(rename_all = "snake_case")]
916/// Enumerates the finite wake trigger status cases.
917/// Serialized names are part of the SDK contract; update fixtures when variants change.
918pub enum WakeTriggerStatus {
919    /// Use this variant when the contract needs to represent registered; selecting it has no side effect by itself.
920    Registered,
921    /// Use this variant when the contract needs to represent triggered; selecting it has no side effect by itself.
922    Triggered,
923    /// Use this variant when the contract needs to represent timed out; selecting it has no side effect by itself.
924    TimedOut,
925    /// Use this variant when the contract needs to represent cancelled; selecting it has no side effect by itself.
926    Cancelled,
927    /// Use this variant when the contract needs to represent failed; selecting it has no side effect by itself.
928    Failed,
929}
930
931#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
932/// Carries the run checkpoint record payload for journal, event, or fixture surfaces.
933/// Creating or cloning it only preserves serialized SDK state; append, publish, replay, or export effects are documented on the runtime and port methods that store it.
934pub struct RunCheckpoint {
935    /// Stable checkpoint id used for typed lineage, lookup, or dedupe.
936    pub checkpoint_id: String,
937    /// Run identifier used for lineage, filtering, replay, and dedupe.
938    pub run_id: RunId,
939    /// Checkpoint seq used by this record or request.
940    pub checkpoint_seq: u64,
941    /// Covers journal seq used by this record or request.
942    pub covers_journal_seq: u64,
943    /// Loop state used by this record or request.
944    pub loop_state: String,
945    #[serde(skip_serializing_if = "Option::is_none")]
946    /// Turn identifier for one loop turn within a run.
947    pub turn_id: Option<TurnId>,
948    #[serde(skip_serializing_if = "Option::is_none")]
949    /// Attempt identifier for retry, repair, provider, or tool execution
950    /// evidence.
951    pub attempt_id: Option<AttemptId>,
952    /// Fingerprint of the runtime package snapshot in force when this value was produced.
953    /// Use it for replay, dedupe, and package-lineage checks; the field is evidence and does
954    /// not execute package behavior.
955    pub runtime_package_fingerprint: String,
956    #[serde(default, skip_serializing_if = "Vec::is_empty")]
957    /// Collection of pending side effects values.
958    /// Ordering and membership should be treated as part of the serialized contract when
959    /// relevant.
960    pub pending_side_effects: Vec<PendingSideEffect>,
961    #[serde(default, skip_serializing_if = "Vec::is_empty")]
962    /// Collection of pending approvals values.
963    /// Ordering and membership should be treated as part of the serialized contract when
964    /// relevant.
965    pub pending_approvals: Vec<String>,
966    #[serde(default, skip_serializing_if = "Vec::is_empty")]
967    /// Content reference associated with this value.
968    /// Resolve it through policy-gated content stores instead of embedding raw content.
969    pub content_ref_manifest: Vec<ContentRef>,
970    /// Deterministic state hash used for stale checks, package evidence, or
971    /// replay comparisons.
972    pub state_hash: String,
973    /// Time value in milliseconds for created at millis.
974    /// Use it for timeout, ordering, or diagnostic calculations.
975    pub created_at_millis: u64,
976    /// Stable writer id used for typed lineage, lookup, or dedupe.
977    pub writer_id: String,
978}
979
980impl RunCheckpoint {
981    /// Validates the records::journal invariants and returns a typed
982    /// error on failure. Validation is pure and does not perform I/O,
983    /// dispatch, journal appends, or adapter calls.
984    pub fn validate_against_latest_seq(&self, latest_journal_seq: u64) -> Result<(), AgentError> {
985        if self.covers_journal_seq > latest_journal_seq {
986            return Err(AgentError::contract_violation(
987                "checkpoint covers_journal_seq cannot point past latest committed journal record",
988            ));
989        }
990        Ok(())
991    }
992}
993
994#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
995/// Carries the pending side effect record payload for journal, event, or fixture surfaces.
996/// Creating or cloning it only preserves serialized SDK state; append, publish, replay, or export effects are documented on the runtime and port methods that store it.
997pub struct PendingSideEffect {
998    /// Stable effect id used for typed lineage, lookup, or dedupe.
999    pub effect_id: crate::domain::EffectId,
1000    /// Stable intent record id used for typed lineage, lookup, or dedupe.
1001    pub intent_record_id: String,
1002    #[serde(skip_serializing_if = "Option::is_none")]
1003    /// Idempotency setting or key for deduping retries.
1004    /// Use it to prevent duplicate side effects during replay or repair.
1005    pub idempotency_key: Option<IdempotencyKey>,
1006    #[serde(skip_serializing_if = "Option::is_none")]
1007    /// Dedupe policy or key for a side-effecting operation.
1008    /// Replay and repair use it to avoid sending or executing the same effect twice.
1009    pub dedupe_key: Option<DedupeKey>,
1010    /// Reason a pending side effect is unsafe to retry automatically.
1011    /// Recovery uses it to require repair or reconciliation before continuing.
1012    pub unsafe_pending_reason: String,
1013}
1014
1015#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1016/// Carries the terminal result marker record payload for journal, event, or fixture surfaces.
1017/// Creating or cloning it only preserves serialized SDK state; append, publish, replay, or export effects are documented on the runtime and port methods that store it.
1018pub struct TerminalResultMarker {
1019    /// Stable effect id used for typed lineage, lookup, or dedupe.
1020    pub effect_id: crate::domain::EffectId,
1021    /// Stable result record id used for typed lineage, lookup, or dedupe.
1022    pub result_record_id: String,
1023    /// Terminal status used by this record or request.
1024    pub terminal_status: String,
1025}
1026
1027#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1028/// Carries the recovery marker record payload for journal, event, or fixture surfaces.
1029/// Creating or cloning it only preserves serialized SDK state; append, publish, replay, or export effects are documented on the runtime and port methods that store it.
1030pub struct RecoveryMarker {
1031    /// Collection of unsafe pending values.
1032    /// Ordering and membership should be treated as part of the serialized contract when
1033    /// relevant.
1034    pub unsafe_pending: Vec<PendingSideEffect>,
1035    /// Recovery reason used by this record or request.
1036    pub recovery_reason: String,
1037    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1038    /// Policy references that govern admission, projection, execution, or
1039    /// delivery.
1040    pub policy_refs: Vec<PolicyRef>,
1041}