Skip to main content

telltale_vm/vm/
vm_config_and_observability.rs

1/// VM configuration.
2#[derive(Debug, Clone, Serialize, Deserialize)]
3pub struct VMConfig {
4    /// Migration-safe config schema version.
5    #[serde(default = "default_config_schema_version")]
6    pub config_schema_version: u32,
7    /// Scheduling policy.
8    pub sched_policy: SchedPolicy,
9    /// Default buffer configuration for new sessions.
10    pub buffer_config: BufferConfig,
11    /// Maximum number of concurrent sessions.
12    pub max_sessions: usize,
13    /// Maximum number of concurrent coroutines.
14    pub max_coroutines: usize,
15    /// Number of registers per coroutine.
16    pub num_registers: u16,
17    /// Simulated time per scheduler round.
18    pub tick_duration: Duration,
19    /// Guard layers configured for the VM.
20    pub guard_layers: Vec<GuardLayerConfig>,
21    /// Whether speculative execution is enabled.
22    pub speculation_enabled: bool,
23    /// Determinism profile for replay/equivalence behavior.
24    pub determinism_mode: DeterminismMode,
25    /// Effect determinism tier used by admission and envelope artifacts.
26    #[serde(default)]
27    pub effect_determinism_tier: EffectDeterminismTier,
28    /// Output-condition policy for commit eligibility of observable outputs.
29    pub output_condition_policy: OutputConditionPolicy,
30    /// Monitor mode for pre-dispatch type checks.
31    #[serde(default)]
32    pub monitor_mode: MonitorMode,
33    /// Flow policy for epistemic knowledge checks.
34    #[serde(default)]
35    pub flow_policy: FlowPolicy,
36    /// Deterministic cost charged for each instruction dispatch.
37    #[serde(default = "default_instruction_cost")]
38    pub instruction_cost: usize,
39    /// Initial cost budget assigned to each coroutine.
40    #[serde(default = "default_initial_cost_budget")]
41    pub initial_cost_budget: usize,
42    /// Whether threaded scheduler may admit same-session picks when footprint-disjoint.
43    #[serde(default)]
44    pub footprint_guided_wave_widening: bool,
45    /// Runtime tuning profile used by instrumentation/benchmark harnesses.
46    #[serde(default)]
47    pub runtime_tuning_profile: RuntimeTuningProfile,
48    /// Round semantics mode used by threaded scheduler.
49    #[serde(default)]
50    pub threaded_round_semantics: ThreadedRoundSemantics,
51    /// Effect-trace capture mode for integration/perf tuning.
52    #[serde(default)]
53    pub effect_trace_capture_mode: EffectTraceCaptureMode,
54    /// Runtime payload hardening mode for inbound/outbound messages.
55    #[serde(default)]
56    pub payload_validation_mode: PayloadValidationMode,
57    /// Communication replay-consumption mode.
58    #[serde(default)]
59    pub communication_replay_mode: CommunicationReplayMode,
60    /// Upper bound for VM payload values in estimated wire bytes.
61    #[serde(default = "default_max_payload_bytes")]
62    pub max_payload_bytes: usize,
63    /// Enable runtime host-contract assertions with deterministic diagnostics.
64    #[serde(default)]
65    pub host_contract_assertions: bool,
66}
67
68impl Default for VMConfig {
69    fn default() -> Self {
70        Self {
71            config_schema_version: default_config_schema_version(),
72            sched_policy: SchedPolicy::Cooperative,
73            buffer_config: BufferConfig::default(),
74            max_sessions: 256,
75            max_coroutines: 1024,
76            num_registers: 16,
77            tick_duration: Duration::from_millis(1),
78            guard_layers: Vec::new(),
79            speculation_enabled: false,
80            determinism_mode: DeterminismMode::Full,
81            effect_determinism_tier: EffectDeterminismTier::StrictDeterministic,
82            output_condition_policy: OutputConditionPolicy::AllowAll,
83            monitor_mode: MonitorMode::SessionTypePrecheck,
84            flow_policy: FlowPolicy::AllowAll,
85            instruction_cost: 1,
86            initial_cost_budget: usize::MAX,
87            footprint_guided_wave_widening: false,
88            runtime_tuning_profile: RuntimeTuningProfile::Standard,
89            threaded_round_semantics: ThreadedRoundSemantics::CanonicalOneStep,
90            effect_trace_capture_mode: EffectTraceCaptureMode::Full,
91            payload_validation_mode: PayloadValidationMode::Structural,
92            communication_replay_mode: CommunicationReplayMode::Off,
93            max_payload_bytes: default_max_payload_bytes(),
94            host_contract_assertions: false,
95        }
96    }
97}
98
99impl VMConfig {
100    /// Validate VM configuration invariants required for safe state initialization.
101    ///
102    /// # Errors
103    ///
104    /// Returns a reason string if a required invariant is violated.
105    pub fn validate_invariants(&self) -> Result<(), String> {
106        if self.config_schema_version < 1 {
107            return Err("config_schema_version must be >= 1".to_string());
108        }
109        if self.max_sessions == 0 {
110            return Err("max_sessions must be > 0".to_string());
111        }
112        if self.max_coroutines == 0 {
113            return Err("max_coroutines must be > 0".to_string());
114        }
115        if self.num_registers == 0 {
116            return Err("num_registers must be > 0".to_string());
117        }
118        if self.instruction_cost == 0 {
119            return Err("instruction_cost must be > 0".to_string());
120        }
121        if self.max_payload_bytes == 0 {
122            return Err("max_payload_bytes must be > 0".to_string());
123        }
124        Ok(())
125    }
126
127    /// Assert VM configuration invariants required for safe state initialization.
128    ///
129    /// # Panics
130    ///
131    /// Panics when a required invariant is violated.
132    pub fn assert_invariants(&self) {
133        if let Err(reason) = self.validate_invariants() {
134            panic!("{reason}");
135        }
136    }
137}
138
139/// Observable event emitted by the VM.
140#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
141pub struct TickedObsEvent {
142    /// Scheduler tick when the wrapped event occurred.
143    pub tick: u64,
144    /// Underlying observable event payload.
145    pub event: ObsEvent,
146}
147
148/// Observable event emitted by the VM.
149#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
150pub enum ObsEvent {
151    /// Value sent on an edge.
152    Sent {
153        /// Scheduler tick when the event occurred.
154        tick: u64,
155        /// Session-scoped edge for this send.
156        edge: Edge,
157        /// Session ID.
158        session: SessionId,
159        /// Sender role.
160        from: String,
161        /// Receiver role.
162        to: String,
163        /// Message label.
164        label: String,
165    },
166    /// Value received on an edge.
167    Received {
168        /// Scheduler tick when the event occurred.
169        tick: u64,
170        /// Session-scoped edge for this receive.
171        edge: Edge,
172        /// Session ID.
173        session: SessionId,
174        /// Sender role.
175        from: String,
176        /// Receiver role.
177        to: String,
178        /// Message label.
179        label: String,
180    },
181    /// Label offered on an edge.
182    Offered {
183        /// Scheduler tick when the event occurred.
184        tick: u64,
185        /// Session-scoped edge for this offer.
186        edge: Edge,
187        /// Label offered.
188        label: String,
189    },
190    /// Label chosen on an edge.
191    Chose {
192        /// Scheduler tick when the event occurred.
193        tick: u64,
194        /// Session-scoped edge for this choice.
195        edge: Edge,
196        /// Label chosen.
197        label: String,
198    },
199    /// Session opened.
200    Opened {
201        /// Scheduler tick when the event occurred.
202        tick: u64,
203        /// Session ID.
204        session: SessionId,
205        /// Participating roles.
206        roles: Vec<String>,
207    },
208    /// Session closed.
209    Closed {
210        /// Scheduler tick when the event occurred.
211        tick: u64,
212        /// Session ID.
213        session: SessionId,
214    },
215    /// Session epoch advanced.
216    EpochAdvanced {
217        /// Scheduler tick when the event occurred.
218        tick: u64,
219        /// Session ID.
220        sid: SessionId,
221        /// New epoch number.
222        epoch: usize,
223    },
224    /// Coroutine halted.
225    Halted {
226        /// Scheduler tick when the event occurred.
227        tick: u64,
228        /// Coroutine ID.
229        coro_id: usize,
230    },
231    /// Effect handler invoked.
232    Invoked {
233        /// Scheduler tick when the event occurred.
234        tick: u64,
235        /// Coroutine ID.
236        coro_id: usize,
237        /// Role name.
238        role: String,
239    },
240    /// Guard layer acquired.
241    Acquired {
242        /// Scheduler tick when the event occurred.
243        tick: u64,
244        /// Session ID.
245        session: SessionId,
246        /// Role name.
247        role: String,
248        /// Guard layer identifier.
249        layer: String,
250    },
251    /// Guard layer released.
252    Released {
253        /// Scheduler tick when the event occurred.
254        tick: u64,
255        /// Session ID.
256        session: SessionId,
257        /// Role name.
258        role: String,
259        /// Guard layer identifier.
260        layer: String,
261    },
262    /// Endpoint transferred between coroutines.
263    Transferred {
264        /// Scheduler tick when the event occurred.
265        tick: u64,
266        /// Session ID.
267        session: SessionId,
268        /// Role name.
269        role: String,
270        /// Source coroutine.
271        from: usize,
272        /// Target coroutine.
273        to: usize,
274    },
275    /// Speculation forked for a ghost session.
276    Forked {
277        /// Scheduler tick when the event occurred.
278        tick: u64,
279        /// Session ID.
280        session: SessionId,
281        /// Ghost session id.
282        ghost: usize,
283    },
284    /// Speculation joined.
285    Joined {
286        /// Scheduler tick when the event occurred.
287        tick: u64,
288        /// Session ID.
289        session: SessionId,
290    },
291    /// Speculation aborted.
292    Aborted {
293        /// Scheduler tick when the event occurred.
294        tick: u64,
295        /// Session ID.
296        session: SessionId,
297    },
298    /// Knowledge fact tagged.
299    Tagged {
300        /// Scheduler tick when the event occurred.
301        tick: u64,
302        /// Session ID.
303        session: SessionId,
304        /// Role name.
305        role: String,
306        /// Fact payload.
307        fact: String,
308    },
309    /// Knowledge fact checked.
310    Checked {
311        /// Scheduler tick when the event occurred.
312        tick: u64,
313        /// Session ID.
314        session: SessionId,
315        /// Role name.
316        role: String,
317        /// Target role.
318        target: String,
319        /// Whether the flow policy permitted the fact.
320        permitted: bool,
321    },
322    /// Coroutine faulted.
323    Faulted {
324        /// Scheduler tick when the event occurred.
325        tick: u64,
326        /// Coroutine ID.
327        coro_id: usize,
328        /// The fault.
329        fault: Fault,
330    },
331    /// Output-condition verification was evaluated at commit time.
332    OutputConditionChecked {
333        /// Scheduler tick when the event occurred.
334        tick: u64,
335        /// Predicate reference that was checked.
336        predicate_ref: String,
337        /// Optional witness reference used by the check.
338        witness_ref: Option<String>,
339        /// Opaque output digest checked by the verifier.
340        output_digest: String,
341        /// Verification outcome.
342        passed: bool,
343    },
344}
345
346/// The VM execution result for a single step.
347#[derive(Debug)]
348pub enum StepResult {
349    /// A coroutine executed an instruction and may continue.
350    Continue,
351    /// No coroutines are ready (all blocked or done).
352    Stuck,
353    /// All coroutines have completed.
354    AllDone,
355}
356
357/// Terminal status returned by bounded VM run APIs.
358#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
359pub enum RunStatus {
360    /// All coroutines reached terminal states.
361    AllDone,
362    /// No runnable coroutines remain (blocked/stuck).
363    Stuck,
364    /// `max_rounds`/`max_steps` budget was exhausted before termination.
365    MaxRoundsExceeded,
366}
367
368/// Debug metadata for the most recent scheduler-dispatched step.
369#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
370pub enum SchedExecStatus {
371    /// Instruction continued execution.
372    Continue,
373    /// Instruction yielded cooperative control.
374    Yielded,
375    /// Instruction blocked.
376    Blocked,
377    /// Coroutine halted normally.
378    Halted,
379    /// Coroutine faulted.
380    Faulted,
381}
382
383/// Debug metadata for the most recent scheduler-dispatched step.
384#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
385pub struct SchedStepDebug {
386    /// Selected coroutine id.
387    pub selected_coro: usize,
388    /// Instruction-step execution status.
389    pub exec_status: SchedExecStatus,
390}