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 SessionTerminalReason {
236    /// Session closed normally.
237    Closed {
238        /// Deterministic terminal explanation recorded in the trace.
239        reason: String,
240    },
241    /// Session cancelled through an explicit cancellation path.
242    Cancelled {
243        /// Deterministic terminal explanation recorded in the trace.
244        reason: String,
245    },
246    /// Session aborted through an explicit abort path.
247    Aborted {
248        /// Deterministic terminal explanation recorded in the trace.
249        reason: String,
250    },
251    /// Session faulted with an explicit terminal reason.
252    Faulted {
253        /// Deterministic terminal explanation recorded in the trace.
254        reason: String,
255    },
256}
257
258/// Observable event emitted by the VM.
259#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
260pub enum ObsEvent {
261    /// Value sent on an edge.
262    Sent {
263        /// Scheduler tick when the event occurred.
264        tick: u64,
265        /// Session-scoped edge for this send.
266        edge: Edge,
267        /// Session ID.
268        session: SessionId,
269        /// Sender role.
270        from: String,
271        /// Receiver role.
272        to: String,
273        /// Message label.
274        label: String,
275    },
276    /// Value received on an edge.
277    Received {
278        /// Scheduler tick when the event occurred.
279        tick: u64,
280        /// Session-scoped edge for this receive.
281        edge: Edge,
282        /// Session ID.
283        session: SessionId,
284        /// Sender role.
285        from: String,
286        /// Receiver role.
287        to: String,
288        /// Message label.
289        label: String,
290    },
291    /// Label offered on an edge.
292    Offered {
293        /// Scheduler tick when the event occurred.
294        tick: u64,
295        /// Session-scoped edge for this offer.
296        edge: Edge,
297        /// Label offered.
298        label: String,
299    },
300    /// Label chosen on an edge.
301    Chose {
302        /// Scheduler tick when the event occurred.
303        tick: u64,
304        /// Session-scoped edge for this choice.
305        edge: Edge,
306        /// Label chosen.
307        label: String,
308    },
309    /// Session opened.
310    Opened {
311        /// Scheduler tick when the event occurred.
312        tick: u64,
313        /// Session ID.
314        session: SessionId,
315        /// Participating roles.
316        roles: Vec<String>,
317    },
318    /// Session closed.
319    Closed {
320        /// Scheduler tick when the event occurred.
321        tick: u64,
322        /// Session ID.
323        session: SessionId,
324    },
325    /// Explicit terminal transition for one session.
326    SessionTerminal {
327        /// Scheduler tick when the event occurred.
328        tick: u64,
329        /// Session ID.
330        session: SessionId,
331        /// Explicit terminal reason.
332        reason: SessionTerminalReason,
333    },
334    /// Session epoch advanced.
335    EpochAdvanced {
336        /// Scheduler tick when the event occurred.
337        tick: u64,
338        /// Session ID.
339        sid: SessionId,
340        /// New epoch number.
341        epoch: usize,
342    },
343    /// Coroutine halted.
344    Halted {
345        /// Scheduler tick when the event occurred.
346        tick: u64,
347        /// Coroutine ID.
348        coro_id: usize,
349    },
350    /// Effect handler invoked.
351    Invoked {
352        /// Scheduler tick when the event occurred.
353        tick: u64,
354        /// Coroutine ID.
355        coro_id: usize,
356        /// Role name.
357        role: String,
358    },
359    /// Guard layer acquired.
360    Acquired {
361        /// Scheduler tick when the event occurred.
362        tick: u64,
363        /// Session ID.
364        session: SessionId,
365        /// Role name.
366        role: String,
367        /// Guard layer identifier.
368        layer: String,
369    },
370    /// Guard layer released.
371    Released {
372        /// Scheduler tick when the event occurred.
373        tick: u64,
374        /// Session ID.
375        session: SessionId,
376        /// Role name.
377        role: String,
378        /// Guard layer identifier.
379        layer: String,
380    },
381    /// Endpoint transferred between coroutines.
382    Transferred {
383        /// Scheduler tick when the event occurred.
384        tick: u64,
385        /// Session ID.
386        session: SessionId,
387        /// Role name.
388        role: String,
389        /// Source coroutine.
390        from: usize,
391        /// Target coroutine.
392        to: usize,
393    },
394    /// Speculation forked for a ghost session.
395    Forked {
396        /// Scheduler tick when the event occurred.
397        tick: u64,
398        /// Session ID.
399        session: SessionId,
400        /// Ghost session id.
401        ghost: usize,
402    },
403    /// Speculation joined.
404    Joined {
405        /// Scheduler tick when the event occurred.
406        tick: u64,
407        /// Session ID.
408        session: SessionId,
409    },
410    /// Speculation aborted.
411    Aborted {
412        /// Scheduler tick when the event occurred.
413        tick: u64,
414        /// Session ID.
415        session: SessionId,
416    },
417    /// Knowledge fact tagged.
418    Tagged {
419        /// Scheduler tick when the event occurred.
420        tick: u64,
421        /// Session ID.
422        session: SessionId,
423        /// Role name.
424        role: String,
425        /// Fact payload.
426        fact: String,
427    },
428    /// Knowledge fact checked.
429    Checked {
430        /// Scheduler tick when the event occurred.
431        tick: u64,
432        /// Session ID.
433        session: SessionId,
434        /// Role name.
435        role: String,
436        /// Target role.
437        target: String,
438        /// Whether the flow policy permitted the fact.
439        permitted: bool,
440    },
441    /// Coroutine faulted.
442    Faulted {
443        /// Scheduler tick when the event occurred.
444        tick: u64,
445        /// Coroutine ID.
446        coro_id: usize,
447        /// The fault.
448        fault: Fault,
449    },
450    /// Typed failure branch entry became visible before terminal fault handling.
451    FailureBranchEntered {
452        /// Scheduler tick when the event occurred.
453        tick: u64,
454        /// Session ID.
455        session: SessionId,
456        /// Coroutine ID.
457        coro_id: usize,
458        /// Failure that entered the branch.
459        fault: Fault,
460    },
461    /// Explicit timeout occurrence became active for one site.
462    TimeoutIssued {
463        /// Scheduler tick when the event occurred.
464        tick: u64,
465        /// Site that timed out.
466        site: String,
467        /// Tick until which the timeout remains active.
468        until_tick: u64,
469        /// Timeout witness issued for the occurrence.
470        witness_id: AuthorityWitnessId,
471    },
472    /// Explicit cancellation path was requested.
473    CancellationRequested {
474        /// Scheduler tick when the event occurred.
475        tick: u64,
476        /// Session ID.
477        session: SessionId,
478        /// Cancellation witness issued for the request.
479        witness_id: AuthorityWitnessId,
480        /// Owner whose lifecycle triggered the cancellation.
481        owner_id: FragmentOwnerId,
482        /// Ownership reason for the cancellation request.
483        reason: OwnershipTerminalReason,
484    },
485    /// Explicit cancellation path completed.
486    Cancelled {
487        /// Scheduler tick when the event occurred.
488        tick: u64,
489        /// Session ID.
490        session: SessionId,
491        /// Cancellation witness used for the completion.
492        witness_id: AuthorityWitnessId,
493        /// Ownership reason for the completed cancellation.
494        reason: OwnershipTerminalReason,
495    },
496    /// Output-condition verification was evaluated at commit time.
497    OutputConditionChecked {
498        /// Scheduler tick when the event occurred.
499        tick: u64,
500        /// Predicate reference that was checked.
501        predicate_ref: String,
502        /// Optional witness reference used by the check.
503        witness_ref: Option<String>,
504        /// Opaque output digest checked by the verifier.
505        output_digest: String,
506        /// Verification outcome.
507        passed: bool,
508    },
509}
510
511/// The VM execution result for a single step.
512#[derive(Debug)]
513pub enum StepResult {
514    /// A coroutine executed an instruction and may continue.
515    Continue,
516    /// No coroutines are ready (all blocked or done).
517    Stuck,
518    /// All coroutines have completed.
519    AllDone,
520}
521
522/// Terminal status returned by bounded VM run APIs.
523#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
524pub enum RunStatus {
525    /// All coroutines reached terminal states.
526    AllDone,
527    /// No runnable coroutines remain (blocked/stuck).
528    Stuck,
529    /// `max_rounds`/`max_steps` budget was exhausted before termination.
530    MaxRoundsExceeded,
531}
532
533/// Debug metadata for the most recent scheduler-dispatched step.
534#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
535pub enum SchedExecStatus {
536    /// Instruction continued execution.
537    Continue,
538    /// Instruction yielded cooperative control.
539    Yielded,
540    /// Instruction blocked.
541    Blocked,
542    /// Coroutine halted normally.
543    Halted,
544    /// Coroutine faulted.
545    Faulted,
546}
547
548/// Debug metadata for the most recent scheduler-dispatched step.
549#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
550pub struct SchedStepDebug {
551    /// Selected coroutine id.
552    pub selected_coro: usize,
553    /// Instruction-step execution status.
554    pub exec_status: SchedExecStatus,
555}