Skip to main content

telltale_vm/vm/
vm_config.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    /// Retention policy for observable and diagnostic artifacts.
55    #[serde(default)]
56    pub observability_retention: ObservabilityRetentionConfig,
57    /// Runtime payload hardening mode for inbound/outbound messages.
58    #[serde(default)]
59    pub payload_validation_mode: PayloadValidationMode,
60    /// Communication replay-consumption mode.
61    #[serde(default)]
62    pub communication_replay_mode: CommunicationReplayMode,
63    /// Upper bound for VM payload values in estimated wire bytes.
64    #[serde(default = "default_max_payload_bytes")]
65    pub max_payload_bytes: usize,
66    /// Enable runtime host-contract assertions with deterministic diagnostics.
67    #[serde(default)]
68    pub host_contract_assertions: bool,
69}
70
71impl Default for VMConfig {
72    fn default() -> Self {
73        Self {
74            config_schema_version: default_config_schema_version(),
75            sched_policy: SchedPolicy::Cooperative,
76            buffer_config: BufferConfig::default(),
77            max_sessions: 256,
78            max_coroutines: 1024,
79            num_registers: 16,
80            tick_duration: Duration::from_millis(1),
81            guard_layers: Vec::new(),
82            speculation_enabled: false,
83            determinism_mode: DeterminismMode::Full,
84            effect_determinism_tier: EffectDeterminismTier::StrictDeterministic,
85            output_condition_policy: OutputConditionPolicy::AllowAll,
86            monitor_mode: MonitorMode::SessionTypePrecheck,
87            flow_policy: FlowPolicy::AllowAll,
88            instruction_cost: 1,
89            initial_cost_budget: usize::MAX,
90            footprint_guided_wave_widening: false,
91            runtime_tuning_profile: RuntimeTuningProfile::Standard,
92            threaded_round_semantics: ThreadedRoundSemantics::CanonicalOneStep,
93            effect_trace_capture_mode: EffectTraceCaptureMode::Full,
94            observability_retention: ObservabilityRetentionConfig::default(),
95            payload_validation_mode: PayloadValidationMode::Structural,
96            communication_replay_mode: CommunicationReplayMode::Off,
97            max_payload_bytes: default_max_payload_bytes(),
98            host_contract_assertions: false,
99        }
100    }
101}
102
103impl VMConfig {
104    /// Validate VM configuration invariants required for safe state initialization.
105    ///
106    /// # Errors
107    ///
108    /// Returns a reason string if a required invariant is violated.
109    pub fn validate_invariants(&self) -> Result<(), String> {
110        if self.config_schema_version < 1 {
111            return Err("config_schema_version must be >= 1".to_string());
112        }
113        if self.max_sessions == 0 {
114            return Err("max_sessions must be > 0".to_string());
115        }
116        if self.max_coroutines == 0 {
117            return Err("max_coroutines must be > 0".to_string());
118        }
119        if self.num_registers == 0 {
120            return Err("num_registers must be > 0".to_string());
121        }
122        if self.instruction_cost == 0 {
123            return Err("instruction_cost must be > 0".to_string());
124        }
125        if self.max_payload_bytes == 0 {
126            return Err("max_payload_bytes must be > 0".to_string());
127        }
128        if self.observability_retention.mode == ObservabilityRetentionMode::Capped
129            && self.observability_retention.capacity == 0
130        {
131            return Err("observability_retention.capacity must be > 0 in capped mode".to_string());
132        }
133        Ok(())
134    }
135
136    /// Assert VM configuration invariants required for safe state initialization.
137    ///
138    /// # Panics
139    ///
140    /// Panics when a required invariant is violated.
141    pub fn assert_invariants(&self) {
142        if let Err(reason) = self.validate_invariants() {
143            panic!("{reason}");
144        }
145    }
146
147    /// Deterministic baseline profile with minimal retained instrumentation.
148    #[must_use]
149    pub fn strict_minimal() -> Self {
150        Self {
151            determinism_mode: DeterminismMode::Full,
152            threaded_round_semantics: ThreadedRoundSemantics::CanonicalOneStep,
153            effect_trace_capture_mode: EffectTraceCaptureMode::Disabled,
154            payload_validation_mode: PayloadValidationMode::Structural,
155            communication_replay_mode: CommunicationReplayMode::Off,
156            observability_retention: ObservabilityRetentionConfig {
157                mode: ObservabilityRetentionMode::Capped,
158                capacity: 1_024,
159            },
160            ..Self::default()
161        }
162    }
163
164    /// Deterministic profile with full observable/effect tracing enabled.
165    #[must_use]
166    pub fn strict_observable() -> Self {
167        Self {
168            effect_trace_capture_mode: EffectTraceCaptureMode::Full,
169            observability_retention: ObservabilityRetentionConfig::default(),
170            ..Self::strict_minimal()
171        }
172    }
173
174    /// Deterministic profile with strict validation and replay tracking enabled.
175    #[must_use]
176    pub fn strict_verified() -> Self {
177        Self {
178            effect_trace_capture_mode: EffectTraceCaptureMode::Full,
179            payload_validation_mode: PayloadValidationMode::StrictSchema,
180            communication_replay_mode: CommunicationReplayMode::Nullifier,
181            observability_retention: ObservabilityRetentionConfig::default(),
182            ..Self::strict_minimal()
183        }
184    }
185
186    /// Deterministic churn profile for repeated short-lived sessions.
187    #[must_use]
188    pub fn strict_churn() -> Self {
189        Self {
190            observability_retention: ObservabilityRetentionConfig {
191                mode: ObservabilityRetentionMode::Capped,
192                capacity: 256,
193            },
194            ..Self::strict_minimal()
195        }
196    }
197
198    /// Deterministic buffer-pressure profile for allocator and queue stress.
199    #[must_use]
200    pub fn strict_buffer_pressure() -> Self {
201        Self {
202            buffer_config: BufferConfig {
203                mode: crate::buffer::BufferMode::Fifo,
204                initial_capacity: 1,
205                policy: crate::buffer::BackpressurePolicy::Resize { max_capacity: 8 },
206            },
207            ..Self::strict_minimal()
208        }
209    }
210
211    /// Deterministic large-fanout profile for scheduler and metadata scaling tests.
212    #[must_use]
213    pub fn strict_large_fanout() -> Self {
214        Self {
215            observability_retention: ObservabilityRetentionConfig {
216                mode: ObservabilityRetentionMode::Capped,
217                capacity: 4_096,
218            },
219            ..Self::strict_minimal()
220        }
221    }
222}
223
224/// Observable event emitted by the VM.
225#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
226pub struct TickedObsEvent {
227    /// Scheduler tick when the wrapped event occurred.
228    pub tick: u64,
229    /// Underlying observable event payload.
230    pub event: ObsEvent,
231}
232
233/// Observable event emitted by the VM.
234#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
235pub enum ObsEvent {
236    /// Value sent on an edge.
237    Sent {
238        /// Scheduler tick when the event occurred.
239        tick: u64,
240        /// Session-scoped edge for this send.
241        edge: Edge,
242        /// Session ID.
243        session: SessionId,
244        /// Sender role.
245        from: String,
246        /// Receiver role.
247        to: String,
248        /// Message label.
249        label: String,
250    },
251    /// Value received on an edge.
252    Received {
253        /// Scheduler tick when the event occurred.
254        tick: u64,
255        /// Session-scoped edge for this receive.
256        edge: Edge,
257        /// Session ID.
258        session: SessionId,
259        /// Sender role.
260        from: String,
261        /// Receiver role.
262        to: String,
263        /// Message label.
264        label: String,
265    },
266    /// Label offered on an edge.
267    Offered {
268        /// Scheduler tick when the event occurred.
269        tick: u64,
270        /// Session-scoped edge for this offer.
271        edge: Edge,
272        /// Label offered.
273        label: String,
274    },
275    /// Label chosen on an edge.
276    Chose {
277        /// Scheduler tick when the event occurred.
278        tick: u64,
279        /// Session-scoped edge for this choice.
280        edge: Edge,
281        /// Label chosen.
282        label: String,
283    },
284    /// Session opened.
285    Opened {
286        /// Scheduler tick when the event occurred.
287        tick: u64,
288        /// Session ID.
289        session: SessionId,
290        /// Participating roles.
291        roles: Vec<String>,
292    },
293    /// Session closed.
294    Closed {
295        /// Scheduler tick when the event occurred.
296        tick: u64,
297        /// Session ID.
298        session: SessionId,
299    },
300    /// Session epoch advanced.
301    EpochAdvanced {
302        /// Scheduler tick when the event occurred.
303        tick: u64,
304        /// Session ID.
305        sid: SessionId,
306        /// New epoch number.
307        epoch: usize,
308    },
309    /// Coroutine halted.
310    Halted {
311        /// Scheduler tick when the event occurred.
312        tick: u64,
313        /// Coroutine ID.
314        coro_id: usize,
315    },
316    /// Effect handler invoked.
317    Invoked {
318        /// Scheduler tick when the event occurred.
319        tick: u64,
320        /// Coroutine ID.
321        coro_id: usize,
322        /// Role name.
323        role: String,
324    },
325    /// Guard layer acquired.
326    Acquired {
327        /// Scheduler tick when the event occurred.
328        tick: u64,
329        /// Session ID.
330        session: SessionId,
331        /// Role name.
332        role: String,
333        /// Guard layer identifier.
334        layer: String,
335    },
336    /// Guard layer released.
337    Released {
338        /// Scheduler tick when the event occurred.
339        tick: u64,
340        /// Session ID.
341        session: SessionId,
342        /// Role name.
343        role: String,
344        /// Guard layer identifier.
345        layer: String,
346    },
347    /// Endpoint transferred between coroutines.
348    Transferred {
349        /// Scheduler tick when the event occurred.
350        tick: u64,
351        /// Session ID.
352        session: SessionId,
353        /// Role name.
354        role: String,
355        /// Source coroutine.
356        from: usize,
357        /// Target coroutine.
358        to: usize,
359    },
360    /// Speculation forked for a ghost session.
361    Forked {
362        /// Scheduler tick when the event occurred.
363        tick: u64,
364        /// Session ID.
365        session: SessionId,
366        /// Ghost session id.
367        ghost: usize,
368    },
369    /// Speculation joined.
370    Joined {
371        /// Scheduler tick when the event occurred.
372        tick: u64,
373        /// Session ID.
374        session: SessionId,
375    },
376    /// Speculation aborted.
377    Aborted {
378        /// Scheduler tick when the event occurred.
379        tick: u64,
380        /// Session ID.
381        session: SessionId,
382    },
383    /// Knowledge fact tagged.
384    Tagged {
385        /// Scheduler tick when the event occurred.
386        tick: u64,
387        /// Session ID.
388        session: SessionId,
389        /// Role name.
390        role: String,
391        /// Fact payload.
392        fact: String,
393    },
394    /// Knowledge fact checked.
395    Checked {
396        /// Scheduler tick when the event occurred.
397        tick: u64,
398        /// Session ID.
399        session: SessionId,
400        /// Role name.
401        role: String,
402        /// Target role.
403        target: String,
404        /// Whether the flow policy permitted the fact.
405        permitted: bool,
406    },
407    /// Coroutine faulted.
408    Faulted {
409        /// Scheduler tick when the event occurred.
410        tick: u64,
411        /// Coroutine ID.
412        coro_id: usize,
413        /// The fault.
414        fault: Fault,
415    },
416    /// Output-condition verification was evaluated at commit time.
417    OutputConditionChecked {
418        /// Scheduler tick when the event occurred.
419        tick: u64,
420        /// Predicate reference that was checked.
421        predicate_ref: String,
422        /// Optional witness reference used by the check.
423        witness_ref: Option<String>,
424        /// Opaque output digest checked by the verifier.
425        output_digest: String,
426        /// Verification outcome.
427        passed: bool,
428    },
429}
430
431/// The VM execution result for a single step.
432#[derive(Debug)]
433pub enum StepResult {
434    /// A coroutine executed an instruction and may continue.
435    Continue,
436    /// No coroutines are ready (all blocked or done).
437    Stuck,
438    /// All coroutines have completed.
439    AllDone,
440}
441
442/// Terminal status returned by bounded VM run APIs.
443#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
444pub enum RunStatus {
445    /// All coroutines reached terminal states.
446    AllDone,
447    /// No runnable coroutines remain (blocked/stuck).
448    Stuck,
449    /// `max_rounds`/`max_steps` budget was exhausted before termination.
450    MaxRoundsExceeded,
451}
452
453/// Debug metadata for the most recent scheduler-dispatched step.
454#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
455pub enum SchedExecStatus {
456    /// Instruction continued execution.
457    Continue,
458    /// Instruction yielded cooperative control.
459    Yielded,
460    /// Instruction blocked.
461    Blocked,
462    /// Coroutine halted normally.
463    Halted,
464    /// Coroutine faulted.
465    Faulted,
466}
467
468/// Debug metadata for the most recent scheduler-dispatched step.
469#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
470pub struct SchedStepDebug {
471    /// Selected coroutine id.
472    pub selected_coro: usize,
473    /// Instruction-step execution status.
474    pub exec_status: SchedExecStatus,
475}