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}