Skip to main content

konjure_sdk/
recording.rs

1//! Ordered decisions with explicit scene time. Seeking backwards is permitted;
2//! replay preserves decision order and never executes proposed external effects.
3use crate::{Diagnostic, Event, MAX_SAMPLE_SECONDS};
4use alloc::vec::Vec;
5use serde::{Deserialize, Serialize};
6
7/// Maximum ordered steps accepted in a recording.
8pub const MAX_RECORDING_STEPS: usize = 4096;
9/// Maximum runtime events permitted in one recorded step.
10pub const MAX_EVENTS_PER_STEP: usize = 256;
11
12#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
13#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
14#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
15#[serde(deny_unknown_fields)]
16/// Owned, ordered runtime decisions for deterministic replay.
17///
18/// Recording preserves step order even when time moves backwards. It stores
19/// proposals as data; replaying it must not execute an external effect.
20pub struct Recording {
21    /// Recording wire version; only version `1` is accepted.
22    pub version: u16,
23    /// Ordered owned steps.
24    pub steps: Vec<RecordedStep>,
25}
26
27#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
28#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
29#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
30#[serde(deny_unknown_fields)]
31/// Runtime events observed or selected at one explicit scene time.
32pub struct RecordedStep {
33    /// Scene time in seconds; it need not be monotonic across steps.
34    pub time_seconds: f64,
35    /// Events in their original decision order.
36    pub events: Vec<Event>,
37}
38
39impl Recording {
40    /// Validates recording version, bounds, event counts, and finite time values.
41    ///
42    /// # Errors
43    ///
44    /// Returns a diagnostic for an unsupported version, too many steps or
45    /// events, or a non-finite or out-of-range step time.
46    pub fn validate(&self) -> Result<(), Diagnostic> {
47        if self.version != 1 || self.steps.len() > MAX_RECORDING_STEPS {
48            return Err(Diagnostic::plain("unsupported or oversized recording"));
49        }
50        for step in &self.steps {
51            if !step.time_seconds.is_finite()
52                || !(0.0..=MAX_SAMPLE_SECONDS).contains(&step.time_seconds)
53                || step.events.len() > MAX_EVENTS_PER_STEP
54            {
55                return Err(Diagnostic::plain(
56                    "recording time exceeds scene time bounds or step exceeds event limit",
57                ));
58            }
59        }
60        Ok(())
61    }
62}