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}