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}