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}