@typeonce/effect-machine 0.5.1 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/README.md +1 -1
  2. package/package.json +8 -8
  3. package/src/Machine.ts +6873 -0
  4. package/src/index.ts +1 -0
  5. package/src/internal/machine/activities.ts +108 -0
  6. package/src/internal/machine/atom.ts +636 -0
  7. package/src/internal/machine/cluster.ts +394 -0
  8. package/src/internal/machine/command.ts +58 -0
  9. package/src/internal/machine/commandRuntime.ts +43 -0
  10. package/src/internal/machine/configuration.ts +1331 -0
  11. package/src/internal/machine/errors.ts +87 -0
  12. package/src/internal/machine/executionPlan.ts +996 -0
  13. package/src/internal/machine/invocation.ts +119 -0
  14. package/src/internal/machine/machine.ts +1747 -0
  15. package/src/internal/machine/planner.ts +1933 -0
  16. package/src/internal/machine/process.ts +906 -0
  17. package/src/internal/machine/protocol.ts +322 -0
  18. package/src/internal/machine/readiness.ts +10 -0
  19. package/src/internal/machine/runtime.ts +2512 -0
  20. package/src/internal/machine/serialization.ts +498 -0
  21. package/src/internal/machine/stateDefinition.ts +270 -0
  22. package/src/internal/machine/symbols.ts +2 -0
  23. package/src/internal/machine/topology.ts +479 -0
  24. package/src/internal/testing/machine/arbitrary.ts +102 -0
  25. package/src/internal/testing/machine/exploration.ts +331 -0
  26. package/src/internal/testing/machine/finiteModel.ts +1498 -0
  27. package/src/internal/testing/machine/invariant.ts +372 -0
  28. package/src/internal/testing/machine/probe.ts +79 -0
  29. package/src/internal/testing/machine/referenceModel.ts +1505 -0
  30. package/src/internal/testing/machine/runtime.ts +1710 -0
  31. package/src/internal/testing/machine/runtimeInvariant.ts +486 -0
  32. package/src/internal/testing/machine/trace.ts +150 -0
  33. package/src/internal/testing/machine/verification.ts +1890 -0
  34. package/src/testing/MachineTest.ts +2067 -0
  35. package/src/testing/index.ts +7 -0
  36. package/src/unstable/cluster/ClusterMachine.ts +390 -0
  37. package/src/unstable/cluster/index.ts +1 -0
  38. package/src/unstable/reactivity/AtomMachine.ts +649 -0
  39. package/src/unstable/reactivity/index.ts +1 -0
@@ -0,0 +1,2067 @@
1
+ /**
2
+ * Property-based scenario generation and planner trace utilities.
3
+ *
4
+ * @since 0.4.0
5
+ */
6
+
7
+ import type * as Effect from "effect/Effect"
8
+ import type * as Graph from "effect/Graph"
9
+ import type * as Schema from "effect/Schema"
10
+ import type { FastCheck } from "effect/testing"
11
+ import type { EnsureExecutable } from "../internal/machine/readiness.js"
12
+ import type { SchemaArbitraryReport } from "../internal/testing/machine/arbitrary.js"
13
+ import type { FiniteModel } from "../internal/testing/machine/finiteModel.js"
14
+ import type * as ReferenceModel from "../internal/testing/machine/referenceModel.js"
15
+ import * as internal from "../internal/testing/machine/verification.js"
16
+ import type { VerificationError } from "../internal/testing/machine/verification.js"
17
+ import type * as Machine from "../Machine.js"
18
+
19
+ export {
20
+ advanceCommand,
21
+ type CausalRuntimeAssertionContext,
22
+ type CausalRuntimeCommandActual,
23
+ CausalRuntimeCommandFailure,
24
+ type CausalRuntimeCommandRecord,
25
+ type CausalRuntimeCommandResult,
26
+ type CausalRuntimeInspectionContext,
27
+ type CausalRuntimeModelOptions,
28
+ type CausalRuntimeModelStep,
29
+ type CausalRuntimeTranscript,
30
+ type CausalVerificationAwaitContext,
31
+ type CausalVerificationOptions,
32
+ type CausalVerificationTranscript,
33
+ checkpointCommand,
34
+ type EnqueuedRuntimeAssertionContext,
35
+ type EnqueuedRuntimeCommandActual,
36
+ type EnqueuedRuntimeCommandRecord,
37
+ type EnqueuedRuntimeInspectionContext,
38
+ type EnqueuedRuntimeModelOptions,
39
+ type EnqueuedRuntimeModelStep,
40
+ type EnqueuedRuntimeTranscript,
41
+ formatCausalTranscript,
42
+ formatEnqueuedTranscript,
43
+ formatRuntimeTranscript,
44
+ runCausalCommands,
45
+ runEnqueuedCommands,
46
+ runRuntimeCommands,
47
+ type RuntimeAssertionContext,
48
+ type RuntimeAwait,
49
+ type RuntimeCommand,
50
+ type RuntimeCommandActual,
51
+ RuntimeCommandFailure,
52
+ type RuntimeCommandRecord,
53
+ type RuntimeCommandResult,
54
+ type RuntimeCommands,
55
+ runtimeCommands,
56
+ type RuntimeCommandsDiagnostics,
57
+ type RuntimeCommandsOptions,
58
+ type RuntimeInspectionContext,
59
+ type RuntimeModelOptions,
60
+ type RuntimeModelStep,
61
+ RuntimeObservationError,
62
+ RuntimeSynchronization,
63
+ type RuntimeTranscript,
64
+ sendCommand,
65
+ stopCommand,
66
+ verifyCausalCommands
67
+ } from "../internal/testing/machine/verification.js"
68
+
69
+ export type {
70
+ SchemaArbitraryOpaqueFilterWarning,
71
+ SchemaArbitraryReport,
72
+ SchemaArbitraryWarning
73
+ } from "../internal/testing/machine/verification.js"
74
+
75
+ export {
76
+ compileModel,
77
+ type FiniteAtomicState,
78
+ type FiniteAutomaticTransition,
79
+ type FiniteCompoundState,
80
+ type FiniteEventTransition,
81
+ type FiniteFinalState,
82
+ type FiniteHistoryMutation,
83
+ type FiniteHistoryScenario,
84
+ type FiniteHistoryState,
85
+ type FiniteHistoryTransfer,
86
+ type FiniteModel,
87
+ type FiniteModelDiagnostics,
88
+ type FiniteModelOptions,
89
+ type FiniteModels,
90
+ finiteModels,
91
+ type FiniteParallelState,
92
+ type FiniteState,
93
+ type FiniteTransition,
94
+ type FiniteTransitionTrigger
95
+ } from "../internal/testing/machine/verification.js"
96
+
97
+ export {
98
+ ModelVerificationError,
99
+ type ModelVerificationField,
100
+ type ModelVerificationLocation,
101
+ type ModelVerificationMismatch,
102
+ type ReferenceCompletion,
103
+ type ReferenceHistoryRecord,
104
+ type ReferenceInitialStep,
105
+ type ReferenceMicrostep,
106
+ type ReferenceState,
107
+ type ReferenceStateValue,
108
+ type ReferenceStep,
109
+ type ReferenceTrace,
110
+ type ReferenceTransition
111
+ } from "../internal/testing/machine/verification.js"
112
+
113
+ /**
114
+ * Purely interprets a finite hierarchical model without compiling a
115
+ * machine.
116
+ *
117
+ * @category verification
118
+ * @since 0.4.0
119
+ */
120
+ export const interpretModel: (model: FiniteModel, events: ReadonlyArray<string>) => ReferenceModel.ReferenceTrace =
121
+ internal.interpretModel
122
+
123
+ type AnyMachine = Machine.Machine.Any
124
+
125
+ type InputValue<M extends AnyMachine> = Machine.Machine.Input<M>["Type"]
126
+
127
+ type StatePath<M extends AnyMachine> = Machine.Machine.StateIdentifier<Machine.Machine.States<M>>
128
+
129
+ type StateNodePath<M extends AnyMachine> = Machine.Machine.StateNodeIdentifier<Machine.Machine.States<M>>
130
+
131
+ type IsAny<A> = 0 extends (1 & A) ? true : false
132
+
133
+ type ReadyMachine<M extends AnyMachine> =
134
+ & M
135
+ & EnsureExecutable<
136
+ Machine.Machine.States<M>,
137
+ Machine.Machine.UnhandledStates<M>,
138
+ Machine.Machine.OutputStates<M>
139
+ >
140
+
141
+ /**
142
+ * A generated public-input scenario for a machine.
143
+ *
144
+ * Machines without an input schema omit `input`; machines with one retain its
145
+ * exact decoded type. Events use only the public input protocol.
146
+ *
147
+ * @category models
148
+ * @since 0.4.0
149
+ */
150
+ export type Scenario<M extends AnyMachine> = Machine.Machine.Input<M> extends typeof Schema.Void ? {
151
+ readonly events: ReadonlyArray<Machine.Machine.InputEvent<M>>
152
+ }
153
+ : {
154
+ readonly input: InputValue<M>
155
+ readonly events: ReadonlyArray<Machine.Machine.InputEvent<M>>
156
+ }
157
+
158
+ /**
159
+ * Options for schema-derived scenario generation.
160
+ *
161
+ * `inputArbitrary` and `eventsArbitrary` replace their complete generated
162
+ * value. An events override therefore owns its own length distribution.
163
+ *
164
+ * @category models
165
+ * @since 0.4.0
166
+ */
167
+ export type ScenarioOptions<M extends AnyMachine> =
168
+ & {
169
+ readonly minEvents?: number
170
+ readonly maxEvents?: number
171
+ readonly eventsArbitrary?: FastCheck.Arbitrary<ReadonlyArray<Machine.Machine.InputEvent<M>>>
172
+ }
173
+ & (Machine.Machine.Input<M> extends typeof Schema.Void ? {
174
+ readonly inputArbitrary?: never
175
+ }
176
+ : {
177
+ readonly inputArbitrary?: FastCheck.Arbitrary<InputValue<M>>
178
+ })
179
+
180
+ /**
181
+ * Diagnostics for one schema-derived arbitrary.
182
+ *
183
+ * @category models
184
+ * @since 0.4.0
185
+ */
186
+ export interface SchemaArbitraryDiagnostic {
187
+ readonly boundary: "input" | "event"
188
+ readonly index: number | undefined
189
+ readonly report: SchemaArbitraryReport
190
+ }
191
+
192
+ /**
193
+ * Diagnostics describing how a scenario arbitrary was assembled.
194
+ *
195
+ * @category models
196
+ * @since 0.4.0
197
+ */
198
+ export interface ScenarioDiagnostics {
199
+ readonly input: "none" | "schema" | "override"
200
+ readonly events: "empty" | "schema" | "override"
201
+ readonly schemas: ReadonlyArray<SchemaArbitraryDiagnostic>
202
+ }
203
+
204
+ /**
205
+ * A scenario arbitrary together with schema-derivation diagnostics.
206
+ *
207
+ * @category models
208
+ * @since 0.4.0
209
+ */
210
+ export interface Scenarios<M extends AnyMachine> {
211
+ readonly arbitrary: FastCheck.Arbitrary<Scenario<M>>
212
+ readonly diagnostics: ScenarioDiagnostics
213
+ }
214
+
215
+ /**
216
+ * Derives valid machine inputs and public events from their schemas.
217
+ *
218
+ * Unsupported schema derivations fail immediately through `Schema.toArbitrary`.
219
+ * Non-fatal derivation warnings are returned instead of being hidden.
220
+ *
221
+ * **Example**
222
+ *
223
+ * ```ts
224
+ * import { Schema } from "effect"
225
+ * import { Machine } from "@typeonce/effect-machine"
226
+ * import { MachineTest } from "@typeonce/effect-machine/testing"
227
+ *
228
+ * class Idle extends Schema.TaggedClass<Idle>("Idle")("Idle", {}) {}
229
+ * class Reset extends Schema.TaggedClass<Reset>("Reset")("Reset", {}) {}
230
+ * const States = Machine.defineStates({ Idle })
231
+ * const machine = Machine.make({
232
+ * states: States.states,
233
+ * events: [Reset],
234
+ * initial: () => States.initial.Idle.from()
235
+ * }).handle({ Idle: { on: { Reset: () => States.initial.Idle.from() } } })
236
+ *
237
+ * const generated = MachineTest.scenarios(machine, { maxEvents: 5 })
238
+ * ```
239
+ *
240
+ * @category constructors
241
+ * @since 0.4.0
242
+ */
243
+ export const scenarios: <M extends AnyMachine>(machine: M, options?: ScenarioOptions<M>) => Scenarios<M> =
244
+ internal.scenarios
245
+
246
+ /**
247
+ * Completion information retained by an initial or event plan.
248
+ *
249
+ * @category models
250
+ * @since 0.4.0
251
+ */
252
+ export type PlanCompletion<M extends AnyMachine> =
253
+ | {
254
+ readonly done: true
255
+ readonly output: Machine.Machine.Output<M>
256
+ }
257
+ | {
258
+ readonly done: false
259
+ readonly output: undefined
260
+ }
261
+
262
+ /**
263
+ * One public planned microstep, including retained post-conflict transitions.
264
+ *
265
+ * @category models
266
+ * @since 0.4.0
267
+ */
268
+ export interface Microstep<
269
+ M extends AnyMachine,
270
+ Requirements = Machine.Machine.Services<M>
271
+ > {
272
+ readonly next: Machine.Machine.Snapshot<Machine.Machine.States<M>>
273
+ readonly event: Machine.Machine.Event<M> | Machine.InitialEvent
274
+ readonly transitions: ReadonlyArray<
275
+ Machine.Machine.RetainedTransition<
276
+ StatePath<M>,
277
+ Machine.Machine.TagOf<Machine.Machine.Events<M>[number]>,
278
+ StateNodePath<M>
279
+ >
280
+ >
281
+ readonly commands: ReadonlyArray<Machine.Command>
282
+ readonly raisedEvents: ReadonlyArray<Machine.Machine.Event<M>>
283
+ readonly emittedEvents: ReadonlyArray<Machine.Machine.Emit<M>>
284
+ readonly exitPaths: ReadonlyArray<string>
285
+ readonly entryPaths: ReadonlyArray<string>
286
+ readonly changed: boolean
287
+ }
288
+
289
+ /**
290
+ * The complete data returned while planning machine startup.
291
+ *
292
+ * @category models
293
+ * @since 0.4.0
294
+ */
295
+ export type InitialPlan<M extends AnyMachine> =
296
+ & {
297
+ readonly startingState: Machine.Machine.Snapshot<Machine.Machine.States<M>>
298
+ readonly initialEntryPaths: ReadonlyArray<StatePath<M>>
299
+ readonly state: Machine.Machine.Snapshot<Machine.Machine.States<M>>
300
+ readonly commands: ReadonlyArray<Machine.Command>
301
+ readonly emittedEvents: ReadonlyArray<Machine.Machine.Emit<M>>
302
+ readonly microsteps: ReadonlyArray<
303
+ Microstep<M, Machine.Machine.InitialServices<M> | Machine.Machine.Services<M>>
304
+ >
305
+ }
306
+ & PlanCompletion<M>
307
+
308
+ /**
309
+ * The complete data returned while planning one public event.
310
+ *
311
+ * @category models
312
+ * @since 0.4.0
313
+ */
314
+ export type EventPlan<M extends AnyMachine> =
315
+ & {
316
+ readonly next: Machine.Machine.Snapshot<Machine.Machine.States<M>>
317
+ readonly commands: ReadonlyArray<Machine.Command>
318
+ readonly emittedEvents: ReadonlyArray<Machine.Machine.Emit<M>>
319
+ readonly microsteps: ReadonlyArray<Microstep<M>>
320
+ }
321
+ & PlanCompletion<M>
322
+
323
+ /**
324
+ * Startup portion of an executable planner trace.
325
+ *
326
+ * @category models
327
+ * @since 0.4.0
328
+ */
329
+ export interface InitialTrace<M extends AnyMachine> {
330
+ readonly plan: InitialPlan<M>
331
+ readonly startingState: Machine.Machine.Snapshot<Machine.Machine.States<M>>
332
+ readonly startingConfiguration: ReadonlyArray<StatePath<M>>
333
+ readonly initialEntryPaths: ReadonlyArray<StatePath<M>>
334
+ readonly configuration: ReadonlyArray<StatePath<M>>
335
+ }
336
+
337
+ /**
338
+ * One event portion of an executable planner trace.
339
+ *
340
+ * @category models
341
+ * @since 0.4.0
342
+ */
343
+ export interface TraceStep<M extends AnyMachine> {
344
+ readonly index: number
345
+ readonly before: Machine.Machine.Snapshot<Machine.Machine.States<M>>
346
+ readonly beforeConfiguration: ReadonlyArray<StatePath<M>>
347
+ readonly event: Machine.Machine.InputEvent<M>
348
+ readonly plan: EventPlan<M>
349
+ readonly after: Machine.Machine.Snapshot<Machine.Machine.States<M>>
350
+ readonly afterConfiguration: ReadonlyArray<StatePath<M>>
351
+ }
352
+
353
+ /**
354
+ * A scenario and every plan produced by executing it without running actions.
355
+ *
356
+ * @category models
357
+ * @since 0.4.0
358
+ */
359
+ export interface Trace<M extends AnyMachine> {
360
+ readonly scenario: Scenario<M>
361
+ readonly initial: InitialTrace<M>
362
+ readonly steps: ReadonlyArray<TraceStep<M>>
363
+ readonly final: Machine.Machine.Snapshot<Machine.Machine.States<M>>
364
+ readonly finalConfiguration: ReadonlyArray<StatePath<M>>
365
+ }
366
+
367
+ /**
368
+ * One runtime microstep retained by the execution strategy used by a probe.
369
+ *
370
+ * Transition-definition metadata is intentionally not reconstructed here:
371
+ * optimized runtimes retain execution evidence, while `run` and `plan` remain
372
+ * the APIs for complete diagnostic transition metadata.
373
+ *
374
+ * @category runtime testing
375
+ * @since 0.4.0
376
+ */
377
+ export type ProbeMicrostep<M extends AnyMachine> = Omit<Microstep<M>, "transitions">
378
+
379
+ /**
380
+ * Runtime plan evidence associated with one acknowledged public event.
381
+ *
382
+ * @category runtime testing
383
+ * @since 0.4.0
384
+ */
385
+ export type ProbePlan<M extends AnyMachine> =
386
+ & {
387
+ readonly next: Machine.Machine.Snapshot<Machine.Machine.States<M>>
388
+ readonly commands: ReadonlyArray<Machine.Command>
389
+ readonly emittedEvents: ReadonlyArray<Machine.Machine.Emit<M>>
390
+ readonly microsteps: ReadonlyArray<ProbeMicrostep<M>>
391
+ }
392
+ & PlanCompletion<M>
393
+
394
+ /**
395
+ * Causal evidence produced after one event has completed its managed runtime
396
+ * macrostep.
397
+ *
398
+ * `handled` distinguishes an ignored event from a retained transition that
399
+ * deliberately leaves the logical state unchanged. `configurationChanged`
400
+ * reports whether any microstep changed or reentered the active statechart
401
+ * configuration; compare `before` and `after` for state-value assertions.
402
+ *
403
+ * @category runtime testing
404
+ * @since 0.4.0
405
+ */
406
+ export interface ProbeStep<M extends AnyMachine> {
407
+ readonly event: Machine.Machine.InputEvent<M>
408
+ readonly before: Machine.Machine.Snapshot<Machine.Machine.States<M>>
409
+ readonly plan: ProbePlan<M>
410
+ readonly after: Machine.Machine.Snapshot<Machine.Machine.States<M>>
411
+ readonly handled: boolean
412
+ readonly configurationChanged: boolean
413
+ }
414
+
415
+ /**
416
+ * Testing-only causal access to a managed statechart reference.
417
+ *
418
+ * A probe does not change ordinary machine scheduling. `sendAndAwait` uses an
419
+ * acknowledged mailbox delivery so an ignored event can be proven processed
420
+ * without waiting for a snapshot that will never be published.
421
+ *
422
+ * @category runtime testing
423
+ * @since 0.4.0
424
+ */
425
+ export interface Probe<M extends AnyMachine, Error = never, Output = never> {
426
+ readonly machine: M
427
+ readonly ref: Machine.MachineRef<
428
+ Machine.Machine.Snapshot<Machine.Machine.States<M>>,
429
+ Machine.Machine.InputEvent<M>,
430
+ Error,
431
+ Output
432
+ >
433
+ readonly sendAndAwait: (
434
+ event: Machine.Machine.InputEvent<M>
435
+ ) => Effect.Effect<ProbeStep<M>, Error | Machine.StoppedError>
436
+ /** Constructors for asynchronous observation after a causal command. */
437
+ readonly await: {
438
+ readonly none: internal.RuntimeAwait<
439
+ Machine.Machine.Snapshot<Machine.Machine.States<M>>,
440
+ Error,
441
+ Output
442
+ >
443
+ readonly until: (
444
+ predicate: (
445
+ snapshot: Machine.RuntimeSnapshot<
446
+ Machine.Machine.Snapshot<Machine.Machine.States<M>>,
447
+ Error,
448
+ Output
449
+ >
450
+ ) => boolean
451
+ ) => internal.RuntimeAwait<Machine.Machine.Snapshot<Machine.Machine.States<M>>, Error, Output>
452
+ }
453
+ }
454
+
455
+ /**
456
+ * Raised when `probe` receives a reference that is not backed by the managed
457
+ * statechart runtime.
458
+ *
459
+ * @category errors
460
+ * @since 0.4.0
461
+ */
462
+ export { ProbeUnavailableError } from "../internal/testing/machine/verification.js"
463
+
464
+ /**
465
+ * Attaches testing-only causal event delivery to a running statechart.
466
+ *
467
+ * The returned probe exposes `sendAndAwait`; ordinary production sends remain
468
+ * available exclusively through `MachineRef.send` and retain their
469
+ * asynchronous enqueue-only semantics.
470
+ *
471
+ * **Example**
472
+ *
473
+ * ```ts
474
+ * import { Effect, Schema } from "effect"
475
+ * import { Machine } from "@typeonce/effect-machine"
476
+ * import { MachineTest } from "@typeonce/effect-machine/testing"
477
+ *
478
+ * class Idle extends Schema.TaggedClass<Idle>("Idle")("Idle", {}) {}
479
+ * const States = Machine.defineStates({ Idle })
480
+ * const machine = Machine.make({
481
+ * states: States.states,
482
+ * events: [],
483
+ * initial: () => States.initial.Idle.from()
484
+ * }).handle({ Idle: {} })
485
+ *
486
+ * const program = Effect.gen(function*() {
487
+ * const ref = yield* Machine.start(machine)
488
+ * return yield* MachineTest.probe(machine, ref)
489
+ * })
490
+ * ```
491
+ *
492
+ * @category runtime testing
493
+ * @since 0.4.0
494
+ */
495
+ export const probe: <M extends AnyMachine, Error, Output>(
496
+ machine: ReadyMachine<M>,
497
+ ref: Machine.MachineRef<
498
+ Machine.Machine.Snapshot<Machine.Machine.States<M>>,
499
+ Machine.Machine.InputEvent<M>,
500
+ Error,
501
+ Output
502
+ >
503
+ ) => Effect.Effect<Probe<M, Error, Output>, internal.ProbeUnavailableError> = internal.probe
504
+
505
+ /**
506
+ * The runtime error channel exposed by a managed reference for a machine.
507
+ *
508
+ * @category utility types
509
+ * @since 0.4.0
510
+ */
511
+ export type RuntimeInvariantErrorChannel<M extends AnyMachine> =
512
+ | Machine.Machine.Error<M>
513
+ | Machine.ActionError<Machine.Machine.Services<M>>
514
+ | Machine.InfiniteTransitionError
515
+ | Machine.MachineSchemaDecodeError
516
+ | Machine.StoppedError
517
+
518
+ /**
519
+ * A causal command record projected independently of a reference model.
520
+ *
521
+ * @category models
522
+ * @since 0.4.0
523
+ */
524
+ export interface CausalRuntimeEvidenceRecord<M extends AnyMachine, Error, Output> {
525
+ readonly index: number
526
+ readonly command: internal.RuntimeCommand<Machine.Machine.InputEvent<M>>
527
+ readonly actual: internal.CausalRuntimeCommandActual<M, Error, Output, unknown>
528
+ }
529
+
530
+ /**
531
+ * The model-independent evidence shared by causal command transcripts.
532
+ *
533
+ * @category models
534
+ * @since 0.4.0
535
+ */
536
+ export interface CausalRuntimeEvidence<M extends AnyMachine, Error, Output> {
537
+ readonly commands: ReadonlyArray<internal.RuntimeCommand<Machine.Machine.InputEvent<M>>>
538
+ readonly initial: Machine.RuntimeSnapshot<Machine.Machine.Snapshot<Machine.Machine.States<M>>, Error, Output>
539
+ readonly records: ReadonlyArray<CausalRuntimeEvidenceRecord<M, Error, Output>>
540
+ readonly final: Machine.RuntimeSnapshot<Machine.Machine.Snapshot<Machine.Machine.States<M>>, Error, Output>
541
+ }
542
+
543
+ /**
544
+ * The runtime snapshots selected by one snapshot invariant.
545
+ *
546
+ * @category invariants
547
+ * @since 0.4.0
548
+ */
549
+ export type RuntimeSnapshotObservationMode = "settled" | "awaited" | "all" | "final"
550
+
551
+ /**
552
+ * The semantic location of one retained runtime snapshot.
553
+ *
554
+ * @category invariants
555
+ * @since 0.4.0
556
+ */
557
+ export type RuntimeSnapshotObservation = "initial" | "command" | "awaited" | "final"
558
+
559
+ /**
560
+ * A model-independent command record supplied to runtime laws.
561
+ *
562
+ * @category models
563
+ * @since 0.4.0
564
+ */
565
+ export interface RuntimeInvariantRecord<M extends AnyMachine> {
566
+ readonly index: number
567
+ readonly command: internal.RuntimeCommand<Machine.Machine.InputEvent<M>>
568
+ readonly result: internal.CausalRuntimeCommandResult<M>
569
+ readonly snapshot: Machine.RuntimeSnapshot<
570
+ Machine.Machine.Snapshot<Machine.Machine.States<M>>,
571
+ RuntimeInvariantErrorChannel<M>,
572
+ Machine.Machine.Output<M>
573
+ >
574
+ readonly awaited: ReadonlyArray<
575
+ Machine.RuntimeSnapshot<
576
+ Machine.Machine.Snapshot<Machine.Machine.States<M>>,
577
+ RuntimeInvariantErrorChannel<M>,
578
+ Machine.Machine.Output<M>
579
+ >
580
+ >
581
+ }
582
+
583
+ /**
584
+ * A model-independent causal transcript supplied to runtime laws.
585
+ *
586
+ * @category models
587
+ * @since 0.4.0
588
+ */
589
+ export interface RuntimeInvariantTranscript<M extends AnyMachine> {
590
+ readonly commands: ReadonlyArray<internal.RuntimeCommand<Machine.Machine.InputEvent<M>>>
591
+ readonly initial: Machine.RuntimeSnapshot<
592
+ Machine.Machine.Snapshot<Machine.Machine.States<M>>,
593
+ RuntimeInvariantErrorChannel<M>,
594
+ Machine.Machine.Output<M>
595
+ >
596
+ readonly records: ReadonlyArray<RuntimeInvariantRecord<M>>
597
+ readonly final: Machine.RuntimeSnapshot<
598
+ Machine.Machine.Snapshot<Machine.Machine.States<M>>,
599
+ RuntimeInvariantErrorChannel<M>,
600
+ Machine.Machine.Output<M>
601
+ >
602
+ }
603
+
604
+ /**
605
+ * Evidence passed to a runtime snapshot invariant.
606
+ *
607
+ * @category models
608
+ * @since 0.4.0
609
+ */
610
+ export interface RuntimeSnapshotInvariantContext<M extends AnyMachine> {
611
+ readonly machine: M
612
+ readonly transcript: RuntimeInvariantTranscript<M>
613
+ readonly snapshot: RuntimeInvariantTranscript<M>["initial"]
614
+ readonly observationIndex: number
615
+ readonly phase: RuntimeSnapshotObservation
616
+ readonly commandIndex: number | undefined
617
+ readonly awaitedIndex: number | undefined
618
+ readonly command: internal.RuntimeCommand<Machine.Machine.InputEvent<M>> | undefined
619
+ readonly result: internal.CausalRuntimeCommandResult<M> | undefined
620
+ }
621
+
622
+ /**
623
+ * Evidence passed to an invariant for one completed causal command.
624
+ *
625
+ * @category models
626
+ * @since 0.4.0
627
+ */
628
+ export interface RuntimeCommandInvariantContext<M extends AnyMachine> {
629
+ readonly machine: M
630
+ readonly transcript: RuntimeInvariantTranscript<M>
631
+ readonly record: RuntimeInvariantRecord<M>
632
+ readonly previous: RuntimeInvariantRecord<M> | undefined
633
+ readonly index: number
634
+ readonly command: internal.RuntimeCommand<Machine.Machine.InputEvent<M>>
635
+ readonly result: internal.CausalRuntimeCommandResult<M>
636
+ readonly snapshot: RuntimeInvariantRecord<M>["snapshot"]
637
+ readonly awaited: RuntimeInvariantRecord<M>["awaited"]
638
+ }
639
+
640
+ /**
641
+ * Evidence passed to a whole-runtime-transcript invariant.
642
+ *
643
+ * @category models
644
+ * @since 0.4.0
645
+ */
646
+ export interface RuntimeTranscriptInvariantContext<M extends AnyMachine> {
647
+ readonly machine: M
648
+ readonly transcript: RuntimeInvariantTranscript<M>
649
+ }
650
+
651
+ /**
652
+ * Options for a runtime snapshot invariant.
653
+ *
654
+ * @category models
655
+ * @since 0.4.0
656
+ */
657
+ export interface RuntimeSnapshotInvariantOptions<M extends AnyMachine>
658
+ extends InvariantOptions<RuntimeSnapshotInvariantContext<M>>
659
+ {
660
+ readonly observe?: RuntimeSnapshotObservationMode
661
+ }
662
+
663
+ /**
664
+ * A semantic property checked against selected live runtime snapshots.
665
+ *
666
+ * @category invariants
667
+ * @since 0.4.0
668
+ */
669
+ export interface RuntimeSnapshotInvariant<M extends AnyMachine>
670
+ extends InvariantOptions<RuntimeSnapshotInvariantContext<M>>
671
+ {
672
+ readonly _tag: "RuntimeSnapshotInvariant"
673
+ readonly name: string
674
+ readonly observe: RuntimeSnapshotObservationMode
675
+ readonly check: (context: RuntimeSnapshotInvariantContext<M>) => InvariantOutcome
676
+ }
677
+
678
+ /**
679
+ * A semantic property checked after every completed causal command.
680
+ *
681
+ * @category invariants
682
+ * @since 0.4.0
683
+ */
684
+ export interface RuntimeCommandInvariant<M extends AnyMachine>
685
+ extends InvariantOptions<RuntimeCommandInvariantContext<M>>
686
+ {
687
+ readonly _tag: "RuntimeCommandInvariant"
688
+ readonly name: string
689
+ readonly check: (context: RuntimeCommandInvariantContext<M>) => InvariantOutcome
690
+ }
691
+
692
+ /**
693
+ * A semantic property checked once against a complete causal transcript.
694
+ *
695
+ * @category invariants
696
+ * @since 0.4.0
697
+ */
698
+ export interface RuntimeTranscriptInvariant<M extends AnyMachine>
699
+ extends InvariantOptions<RuntimeTranscriptInvariantContext<M>>
700
+ {
701
+ readonly _tag: "RuntimeTranscriptInvariant"
702
+ readonly name: string
703
+ readonly check: (context: RuntimeTranscriptInvariantContext<M>) => InvariantOutcome
704
+ }
705
+
706
+ /**
707
+ * A user-defined semantic property over retained live runtime evidence.
708
+ *
709
+ * @category invariants
710
+ * @since 0.4.0
711
+ */
712
+ export type RuntimeInvariant<M extends AnyMachine> =
713
+ | RuntimeSnapshotInvariant<M>
714
+ | RuntimeCommandInvariant<M>
715
+ | RuntimeTranscriptInvariant<M>
716
+
717
+ /**
718
+ * Machine-bound runtime invariant constructors with exact event inference.
719
+ *
720
+ * @category models
721
+ * @since 0.4.0
722
+ */
723
+ export interface RuntimeInvariantBuilder<M extends AnyMachine> {
724
+ readonly snapshot: (
725
+ name: string,
726
+ check: (context: RuntimeSnapshotInvariantContext<M>) => InvariantOutcome,
727
+ options?: RuntimeSnapshotInvariantOptions<M>
728
+ ) => RuntimeSnapshotInvariant<M>
729
+ readonly command: (
730
+ name: string,
731
+ check: (context: RuntimeCommandInvariantContext<M>) => InvariantOutcome,
732
+ options?: InvariantOptions<RuntimeCommandInvariantContext<M>>
733
+ ) => RuntimeCommandInvariant<M>
734
+ readonly transcript: (
735
+ name: string,
736
+ check: (context: RuntimeTranscriptInvariantContext<M>) => InvariantOutcome,
737
+ options?: InvariantOptions<RuntimeTranscriptInvariantContext<M>>
738
+ ) => RuntimeTranscriptInvariant<M>
739
+ }
740
+
741
+ /**
742
+ * Creates reusable semantic laws for causal runtime evidence.
743
+ *
744
+ * @category constructors
745
+ * @since 0.4.0
746
+ */
747
+ export const runtimeInvariants: <M extends AnyMachine>(machine: M) => RuntimeInvariantBuilder<M> =
748
+ internal.runtimeInvariants
749
+
750
+ /**
751
+ * Scope of a runtime invariant.
752
+ *
753
+ * @category invariants
754
+ * @since 0.4.0
755
+ */
756
+ export type RuntimeInvariantScope = "snapshot" | "command" | "transcript"
757
+
758
+ /**
759
+ * Aggregate result for one runtime invariant.
760
+ *
761
+ * @category models
762
+ * @since 0.4.0
763
+ */
764
+ export interface RuntimeInvariantCheckResult {
765
+ readonly invariant: string
766
+ readonly scope: RuntimeInvariantScope
767
+ readonly status: InvariantStatus
768
+ readonly observations: number
769
+ readonly failures: number
770
+ }
771
+
772
+ /**
773
+ * Aggregate result for all checked runtime invariants.
774
+ *
775
+ * @category models
776
+ * @since 0.4.0
777
+ */
778
+ export interface RuntimeInvariantReport {
779
+ readonly checks: ReadonlyArray<RuntimeInvariantCheckResult>
780
+ }
781
+
782
+ /**
783
+ * One runtime invariant violation and its exact retained location.
784
+ *
785
+ * @category models
786
+ * @since 0.4.0
787
+ */
788
+ export interface RuntimeInvariantViolation<M extends AnyMachine = AnyMachine> {
789
+ readonly invariant: string
790
+ readonly scope: RuntimeInvariantScope
791
+ readonly kind: "predicate" | "observations"
792
+ readonly observationIndex?: number
793
+ readonly commandIndex: number | undefined
794
+ readonly awaitedIndex?: number
795
+ readonly phase?: RuntimeSnapshotObservation
796
+ readonly command?: internal.RuntimeCommand<Machine.Machine.InputEvent<M>>
797
+ readonly message: string
798
+ }
799
+
800
+ /**
801
+ * All violations found in one causal runtime transcript.
802
+ *
803
+ * @category errors
804
+ * @since 0.4.0
805
+ */
806
+ export { RuntimeInvariantError } from "../internal/testing/machine/verification.js"
807
+
808
+ /**
809
+ * Checks runtime invariants and returns their complete non-vacuity report.
810
+ *
811
+ * @category verification
812
+ * @since 0.4.0
813
+ */
814
+ export const checkRuntimeInvariants: <M extends AnyMachine, Error, Output>(
815
+ machine: M,
816
+ transcript: CausalRuntimeEvidence<M, Error, Output>,
817
+ invariants: ReadonlyArray<RuntimeInvariant<M>>
818
+ ) => Effect.Effect<RuntimeInvariantReport, internal.RuntimeInvariantError<M>> = internal.checkRuntimeInvariants
819
+
820
+ /**
821
+ * Asserts runtime invariants against an existing causal transcript.
822
+ *
823
+ * @category verification
824
+ * @since 0.4.0
825
+ */
826
+ export const assertRuntimeInvariants: <M extends AnyMachine, Error, Output>(
827
+ machine: M,
828
+ transcript: CausalRuntimeEvidence<M, Error, Output>,
829
+ invariants: ReadonlyArray<RuntimeInvariant<M>>
830
+ ) => Effect.Effect<void, internal.RuntimeInvariantError<M>> = internal.assertRuntimeInvariants
831
+
832
+ /**
833
+ * One disagreement between pure planning and a causally processed send.
834
+ *
835
+ * @category models
836
+ * @since 0.4.0
837
+ */
838
+ export interface PlannerRuntimeAgreementViolation<M extends AnyMachine = AnyMachine> {
839
+ readonly commandIndex: number
840
+ readonly command: internal.RuntimeCommand<Machine.Machine.InputEvent<M>>
841
+ readonly field:
842
+ | "planning"
843
+ | "handled"
844
+ | "configurationChanged"
845
+ | "planNext"
846
+ | "after"
847
+ | "completion"
848
+ | "commands"
849
+ | "emittedEvents"
850
+ | "microsteps"
851
+ readonly message: string
852
+ }
853
+
854
+ /**
855
+ * Raised when live causal evidence disagrees with a fresh pure plan.
856
+ *
857
+ * @category errors
858
+ * @since 0.4.0
859
+ */
860
+ export { PlannerRuntimeAgreementError } from "../internal/testing/machine/verification.js"
861
+
862
+ /**
863
+ * Checks that every processed public send agrees with a fresh pure plan.
864
+ *
865
+ * @category verification
866
+ * @since 0.4.0
867
+ */
868
+ export const assertPlannerRuntimeAgreement: <M extends AnyMachine, Error, Output>(
869
+ machine: ReadyMachine<M>,
870
+ transcript: CausalRuntimeEvidence<M, Error, Output>
871
+ ) => Effect.Effect<void, internal.PlannerRuntimeAgreementError<M>, RunServices<M>> =
872
+ internal.assertPlannerRuntimeAgreement
873
+
874
+ /**
875
+ * The result of evaluating one semantic invariant.
876
+ *
877
+ * `true` passes, `false` produces a default failure message, and a string
878
+ * fails with that string as its counterexample explanation.
879
+ *
880
+ * @category invariants
881
+ * @since 0.4.0
882
+ */
883
+ export type InvariantOutcome = boolean | string
884
+
885
+ /**
886
+ * The portions of a trace that a state invariant may observe.
887
+ *
888
+ * - `settled` observes startup and the state after every public event.
889
+ * - `microsteps` observes every internal microstep.
890
+ * - `all` observes both settled states and microsteps.
891
+ * - `final` observes only the final state.
892
+ *
893
+ * @category invariants
894
+ * @since 0.4.0
895
+ */
896
+ export type StateObservationMode = "settled" | "microsteps" | "all" | "final"
897
+
898
+ /**
899
+ * The semantic location of one state observation.
900
+ *
901
+ * @category invariants
902
+ * @since 0.4.0
903
+ */
904
+ export type StateObservation = "initial" | "event" | "microstep" | "final"
905
+
906
+ /**
907
+ * Evidence passed to a state invariant.
908
+ *
909
+ * @category invariants
910
+ * @since 0.4.0
911
+ */
912
+ export interface StateInvariantContext<M extends AnyMachine> {
913
+ readonly machine: M
914
+ readonly trace: Trace<M>
915
+ readonly snapshot: Machine.Machine.Snapshot<Machine.Machine.States<M>>
916
+ readonly configuration: ReadonlyArray<StatePath<M>>
917
+ readonly observationIndex: number
918
+ readonly phase: StateObservation
919
+ readonly eventIndex: number | undefined
920
+ readonly microstepIndex: number | undefined
921
+ readonly event: Machine.Machine.Event<M> | Machine.InitialEvent | undefined
922
+ }
923
+
924
+ /**
925
+ * Evidence passed to an invariant for one public event step.
926
+ *
927
+ * @category invariants
928
+ * @since 0.4.0
929
+ */
930
+ export interface StepInvariantContext<M extends AnyMachine> {
931
+ readonly machine: M
932
+ readonly trace: Trace<M>
933
+ readonly step: TraceStep<M>
934
+ readonly index: number
935
+ readonly before: Machine.Machine.Snapshot<Machine.Machine.States<M>>
936
+ readonly beforeConfiguration: ReadonlyArray<StatePath<M>>
937
+ readonly event: Machine.Machine.InputEvent<M>
938
+ readonly plan: EventPlan<M>
939
+ readonly after: Machine.Machine.Snapshot<Machine.Machine.States<M>>
940
+ readonly afterConfiguration: ReadonlyArray<StatePath<M>>
941
+ }
942
+
943
+ /**
944
+ * Evidence passed to a whole-trace invariant.
945
+ *
946
+ * @category invariants
947
+ * @since 0.4.0
948
+ */
949
+ export interface TraceInvariantContext<M extends AnyMachine> {
950
+ readonly machine: M
951
+ readonly trace: Trace<M>
952
+ }
953
+
954
+ /**
955
+ * Controls conditional invariant evaluation and optional non-vacuity checks.
956
+ *
957
+ * A condition that never matches is reported as `untested`. Set
958
+ * `require.minObservations` when that must fail the check instead.
959
+ *
960
+ * @category invariants
961
+ * @since 0.4.0
962
+ */
963
+ export interface InvariantOptions<Context> {
964
+ readonly when?: (context: Context) => boolean
965
+ readonly require?: {
966
+ readonly minObservations: number
967
+ }
968
+ }
969
+
970
+ /**
971
+ * Options for a state invariant.
972
+ *
973
+ * @category invariants
974
+ * @since 0.4.0
975
+ */
976
+ export interface StateInvariantOptions<M extends AnyMachine> extends InvariantOptions<StateInvariantContext<M>> {
977
+ readonly observe?: StateObservationMode
978
+ }
979
+
980
+ /**
981
+ * A semantic property checked against selected state observations.
982
+ *
983
+ * @category invariants
984
+ * @since 0.4.0
985
+ */
986
+ export interface StateInvariant<M extends AnyMachine> extends InvariantOptions<StateInvariantContext<M>> {
987
+ readonly _tag: "StateInvariant"
988
+ readonly name: string
989
+ readonly observe: StateObservationMode
990
+ readonly check: (context: StateInvariantContext<M>) => InvariantOutcome
991
+ }
992
+
993
+ /**
994
+ * A semantic property checked after every public event.
995
+ *
996
+ * @category invariants
997
+ * @since 0.4.0
998
+ */
999
+ export interface StepInvariant<M extends AnyMachine> extends InvariantOptions<StepInvariantContext<M>> {
1000
+ readonly _tag: "StepInvariant"
1001
+ readonly name: string
1002
+ readonly check: (context: StepInvariantContext<M>) => InvariantOutcome
1003
+ }
1004
+
1005
+ /**
1006
+ * A semantic property checked once against a complete trace.
1007
+ *
1008
+ * @category invariants
1009
+ * @since 0.4.0
1010
+ */
1011
+ export interface TraceInvariant<M extends AnyMachine> extends InvariantOptions<TraceInvariantContext<M>> {
1012
+ readonly _tag: "TraceInvariant"
1013
+ readonly name: string
1014
+ readonly check: (context: TraceInvariantContext<M>) => InvariantOutcome
1015
+ }
1016
+
1017
+ /**
1018
+ * A user-defined semantic property over a planner trace.
1019
+ *
1020
+ * @category invariants
1021
+ * @since 0.4.0
1022
+ */
1023
+ export type Invariant<M extends AnyMachine> = StateInvariant<M> | StepInvariant<M> | TraceInvariant<M>
1024
+
1025
+ /**
1026
+ * Machine-bound invariant constructors with complete contextual inference.
1027
+ *
1028
+ * @category invariants
1029
+ * @since 0.4.0
1030
+ */
1031
+ export interface InvariantBuilder<M extends AnyMachine> {
1032
+ readonly state: (
1033
+ name: string,
1034
+ check: (context: StateInvariantContext<M>) => InvariantOutcome,
1035
+ options?: StateInvariantOptions<M>
1036
+ ) => StateInvariant<M>
1037
+ readonly step: (
1038
+ name: string,
1039
+ check: (context: StepInvariantContext<M>) => InvariantOutcome,
1040
+ options?: InvariantOptions<StepInvariantContext<M>>
1041
+ ) => StepInvariant<M>
1042
+ readonly trace: (
1043
+ name: string,
1044
+ check: (context: TraceInvariantContext<M>) => InvariantOutcome,
1045
+ options?: InvariantOptions<TraceInvariantContext<M>>
1046
+ ) => TraceInvariant<M>
1047
+ }
1048
+
1049
+ /**
1050
+ * Direct invariant constructors. Prefer `invariants(machine)` when contextual
1051
+ * machine types should be inferred without an explicit type argument.
1052
+ *
1053
+ * @category constructors
1054
+ * @since 0.4.0
1055
+ */
1056
+ export const Invariant: {
1057
+ readonly state: <M extends AnyMachine>(
1058
+ name: string,
1059
+ check: (context: StateInvariantContext<M>) => InvariantOutcome,
1060
+ options?: StateInvariantOptions<M>
1061
+ ) => StateInvariant<M>
1062
+ readonly step: <M extends AnyMachine>(
1063
+ name: string,
1064
+ check: (context: StepInvariantContext<M>) => InvariantOutcome,
1065
+ options?: InvariantOptions<StepInvariantContext<M>>
1066
+ ) => StepInvariant<M>
1067
+ readonly trace: <M extends AnyMachine>(
1068
+ name: string,
1069
+ check: (context: TraceInvariantContext<M>) => InvariantOutcome,
1070
+ options?: InvariantOptions<TraceInvariantContext<M>>
1071
+ ) => TraceInvariant<M>
1072
+ } = internal.Invariant
1073
+
1074
+ /**
1075
+ * Creates invariant constructors bound to a machine's exact state and event
1076
+ * types. The machine is used only for inference; invariant evaluation remains
1077
+ * pure and reusable across traces from that machine.
1078
+ *
1079
+ * **Example**
1080
+ *
1081
+ * ```ts
1082
+ * import { Schema } from "effect"
1083
+ * import { Machine } from "@typeonce/effect-machine"
1084
+ * import { MachineTest } from "@typeonce/effect-machine/testing"
1085
+ *
1086
+ * class Count extends Schema.TaggedClass<Count>("Count")("Count", {
1087
+ * value: Schema.Number
1088
+ * }) {}
1089
+ * const States = Machine.defineStates({ Count })
1090
+ * const machine = Machine.make({
1091
+ * states: States.states,
1092
+ * events: [],
1093
+ * initial: () => States.initial.Count(new Count({ value: 0 }))
1094
+ * }).handle({ Count: {} })
1095
+ *
1096
+ * const nonNegative = MachineTest.invariants(machine).state(
1097
+ * "count is non-negative",
1098
+ * ({ snapshot }) => snapshot.value.value >= 0
1099
+ * )
1100
+ * ```
1101
+ *
1102
+ * @category constructors
1103
+ * @since 0.4.0
1104
+ */
1105
+ export const invariants: <M extends AnyMachine>(machine: M) => InvariantBuilder<M> = internal.invariants
1106
+
1107
+ /**
1108
+ * The scope of a semantic invariant.
1109
+ *
1110
+ * @category invariants
1111
+ * @since 0.4.0
1112
+ */
1113
+ export type InvariantScope = "state" | "step" | "trace"
1114
+
1115
+ /**
1116
+ * Result status for one invariant.
1117
+ *
1118
+ * `untested` is non-failing unless the invariant declares a minimum number of
1119
+ * observations, in which case it becomes `insufficient`.
1120
+ *
1121
+ * @category invariants
1122
+ * @since 0.4.0
1123
+ */
1124
+ export type InvariantStatus = "passed" | "failed" | "untested" | "insufficient"
1125
+
1126
+ /**
1127
+ * Aggregate result for one invariant.
1128
+ *
1129
+ * @category invariants
1130
+ * @since 0.4.0
1131
+ */
1132
+ export interface InvariantCheckResult {
1133
+ readonly invariant: string
1134
+ readonly scope: InvariantScope
1135
+ readonly status: InvariantStatus
1136
+ readonly observations: number
1137
+ readonly failures: number
1138
+ }
1139
+
1140
+ /**
1141
+ * Aggregate result for all checked invariants.
1142
+ *
1143
+ * @category invariants
1144
+ * @since 0.4.0
1145
+ */
1146
+ export interface InvariantReport {
1147
+ readonly checks: ReadonlyArray<InvariantCheckResult>
1148
+ }
1149
+
1150
+ /**
1151
+ * One semantic invariant violation with its precise trace location.
1152
+ *
1153
+ * @category invariants
1154
+ * @since 0.4.0
1155
+ */
1156
+ export interface InvariantViolation<M extends AnyMachine = AnyMachine> {
1157
+ readonly invariant: string
1158
+ readonly scope: InvariantScope
1159
+ readonly kind: "predicate" | "observations"
1160
+ readonly observationIndex?: number
1161
+ readonly eventIndex: number | undefined
1162
+ readonly microstepIndex?: number
1163
+ readonly phase?: StateObservation
1164
+ readonly configuration?: ReadonlyArray<StatePath<M>>
1165
+ readonly event?: Machine.Machine.Event<M> | Machine.InitialEvent
1166
+ readonly message: string
1167
+ }
1168
+
1169
+ /**
1170
+ * All semantic violations found in one trace, together with the complete
1171
+ * counterexample and aggregate report.
1172
+ *
1173
+ * @category errors
1174
+ * @since 0.4.0
1175
+ */
1176
+ export { InvariantError } from "../internal/testing/machine/verification.js"
1177
+
1178
+ /**
1179
+ * Checks user-defined semantic invariants against an existing planner trace.
1180
+ *
1181
+ * Every invariant and matching observation is evaluated so one failure
1182
+ * contains all relevant evidence. Combine this with `scenarios` and `run` in
1183
+ * an Effect property test to retain FastCheck shrinking.
1184
+ *
1185
+ * @category verification
1186
+ * @since 0.4.0
1187
+ */
1188
+ export const checkInvariants: <M extends AnyMachine>(
1189
+ machine: M,
1190
+ trace: Trace<M>,
1191
+ invariants: ReadonlyArray<Invariant<M>>
1192
+ ) => Effect.Effect<InvariantReport, internal.InvariantError<M>> = internal.checkInvariants
1193
+
1194
+ /**
1195
+ * Asserts user-defined semantic invariants and discards the success report.
1196
+ *
1197
+ * This is the property-test-oriented form of `checkInvariants`: its `void`
1198
+ * success works directly with `it.effect.prop`, while failures retain the
1199
+ * same complete report and trace evidence.
1200
+ *
1201
+ * @category verification
1202
+ * @since 0.4.0
1203
+ */
1204
+ export const assertInvariants: <M extends AnyMachine>(
1205
+ machine: M,
1206
+ trace: Trace<M>,
1207
+ invariants: ReadonlyArray<Invariant<M>>
1208
+ ) => Effect.Effect<void, internal.InvariantError<M>> = internal.assertInvariants
1209
+
1210
+ /**
1211
+ * User-defined identity for a logical exploration state.
1212
+ *
1213
+ * Equal keys deliberately collapse snapshots into one explored state. The key
1214
+ * therefore defines both finiteness and the semantic precision of an
1215
+ * exploration.
1216
+ *
1217
+ * @category exploration
1218
+ * @since 0.4.0
1219
+ */
1220
+ export type ExplorationKey = PropertyKey
1221
+
1222
+ /**
1223
+ * Hard bounds for one exploration.
1224
+ *
1225
+ * Defaults are 20 public events, 1,000 states, and 10,000 planned
1226
+ * transitions. A limit never makes an incomplete result appear exhaustive.
1227
+ *
1228
+ * @category exploration
1229
+ * @since 0.4.0
1230
+ */
1231
+ export interface ExplorationLimits {
1232
+ readonly maxDepth?: number
1233
+ readonly maxStates?: number
1234
+ readonly maxTransitions?: number
1235
+ }
1236
+
1237
+ /**
1238
+ * Resolved bounds retained by an exploration result.
1239
+ *
1240
+ * @category exploration
1241
+ * @since 0.4.0
1242
+ */
1243
+ export interface ResolvedExplorationLimits {
1244
+ readonly maxDepth: number
1245
+ readonly maxStates: number
1246
+ readonly maxTransitions: number
1247
+ }
1248
+
1249
+ /**
1250
+ * Evidence available while assigning a state key.
1251
+ *
1252
+ * @category exploration
1253
+ * @since 0.4.0
1254
+ */
1255
+ export interface ExplorationStateContext<M extends AnyMachine> {
1256
+ readonly machine: M
1257
+ readonly snapshot: Machine.Machine.Snapshot<Machine.Machine.States<M>>
1258
+ readonly configuration: ReadonlyArray<StatePath<M>>
1259
+ readonly depth: number
1260
+ readonly trace: Trace<M>
1261
+ }
1262
+
1263
+ /**
1264
+ * One logical state discovered by breadth-first exploration.
1265
+ *
1266
+ * `trace` is the first, and therefore shortest, trace that reached `key`.
1267
+ *
1268
+ * @category exploration
1269
+ * @since 0.4.0
1270
+ */
1271
+ export interface ExplorationNode<M extends AnyMachine, Key extends ExplorationKey = ExplorationKey>
1272
+ extends ExplorationStateContext<M>
1273
+ {
1274
+ readonly key: Key
1275
+ }
1276
+
1277
+ /**
1278
+ * One concretely planned public event in the exploration graph.
1279
+ *
1280
+ * @category exploration
1281
+ * @since 0.4.0
1282
+ */
1283
+ export interface ExplorationEdge<M extends AnyMachine> {
1284
+ readonly event: Machine.Machine.InputEvent<M>
1285
+ readonly step: TraceStep<M>
1286
+ readonly discovered: boolean
1287
+ }
1288
+
1289
+ /**
1290
+ * One boundary that could not be explored because a hard limit was reached.
1291
+ *
1292
+ * @category exploration
1293
+ * @since 0.4.0
1294
+ */
1295
+ export type ExplorationFrontier<M extends AnyMachine, Key extends ExplorationKey = ExplorationKey> =
1296
+ | {
1297
+ readonly _tag: "DepthLimit"
1298
+ readonly source: Key
1299
+ readonly trace: Trace<M>
1300
+ readonly event: Machine.Machine.InputEvent<M>
1301
+ }
1302
+ | {
1303
+ readonly _tag: "StateLimit"
1304
+ readonly source: Key
1305
+ readonly trace: Trace<M>
1306
+ readonly event: Machine.Machine.InputEvent<M>
1307
+ readonly target: Key
1308
+ readonly targetTrace: Trace<M>
1309
+ }
1310
+ | {
1311
+ readonly _tag: "TransitionLimit"
1312
+ readonly source: Key
1313
+ readonly trace: Trace<M>
1314
+ readonly event: Machine.Machine.InputEvent<M>
1315
+ }
1316
+
1317
+ /**
1318
+ * Honest completeness status for the supplied event representatives and state
1319
+ * key abstraction.
1320
+ *
1321
+ * @category exploration
1322
+ * @since 0.4.0
1323
+ */
1324
+ export type ExplorationCompleteness<M extends AnyMachine, Key extends ExplorationKey = ExplorationKey> =
1325
+ | {
1326
+ readonly _tag: "Complete"
1327
+ }
1328
+ | {
1329
+ readonly _tag: "Truncated"
1330
+ readonly reasons: ReadonlyArray<"depth" | "states" | "transitions">
1331
+ readonly frontier: ReadonlyArray<ExplorationFrontier<M, Key>>
1332
+ }
1333
+
1334
+ /**
1335
+ * Deterministic breadth-first exploration counts.
1336
+ *
1337
+ * @category exploration
1338
+ * @since 0.4.0
1339
+ */
1340
+ export interface ExplorationStats {
1341
+ readonly states: number
1342
+ readonly plannedTransitions: number
1343
+ readonly retainedEdges: number
1344
+ readonly maxDepth: number
1345
+ }
1346
+
1347
+ /**
1348
+ * A bounded logical state graph and its shortest-path evidence.
1349
+ *
1350
+ * @category exploration
1351
+ * @since 0.4.0
1352
+ */
1353
+ export interface Exploration<M extends AnyMachine, Key extends ExplorationKey = ExplorationKey> {
1354
+ readonly graph: Graph.DirectedGraph<ExplorationNode<M, Key>, ExplorationEdge<M>>
1355
+ readonly nodes: ReadonlyArray<ExplorationNode<M, Key>>
1356
+ readonly nodesByKey: ReadonlyMap<Key, Graph.NodeIndex>
1357
+ readonly start: Graph.NodeIndex
1358
+ readonly limits: ResolvedExplorationLimits
1359
+ readonly stats: ExplorationStats
1360
+ readonly completeness: ExplorationCompleteness<M, Key>
1361
+ }
1362
+
1363
+ interface ExploreOptionsBase<M extends AnyMachine, Key extends ExplorationKey> {
1364
+ readonly events: (context: ExplorationStateContext<M>) => ReadonlyArray<Machine.Machine.InputEvent<M>>
1365
+ readonly stateKey: (context: ExplorationStateContext<M>) => Key
1366
+ readonly limits?: ExplorationLimits
1367
+ readonly invariants?: ReadonlyArray<Invariant<M>>
1368
+ }
1369
+
1370
+ /**
1371
+ * Configuration for bounded planner exploration.
1372
+ *
1373
+ * Event representatives may depend on the current state. Exploration is
1374
+ * exhaustive only relative to those representatives and the equivalence
1375
+ * relation defined by `stateKey`.
1376
+ *
1377
+ * @category exploration
1378
+ * @since 0.4.0
1379
+ */
1380
+ export type ExploreOptions<M extends AnyMachine, Key extends ExplorationKey = ExplorationKey> =
1381
+ & ExploreOptionsBase<M, Key>
1382
+ & (Machine.Machine.Input<M> extends typeof Schema.Void ? {
1383
+ readonly input?: never
1384
+ }
1385
+ : {
1386
+ readonly input: InputValue<M>
1387
+ })
1388
+
1389
+ /**
1390
+ * Explores the bounded logical state graph in breadth-first order.
1391
+ *
1392
+ * Invariants are checked against startup and every concretely planned edge,
1393
+ * so a failure retains a shortest discovered counterexample. Staged actions
1394
+ * and runtime activities are not executed.
1395
+ *
1396
+ * **Example**
1397
+ *
1398
+ * ```ts
1399
+ * import { Schema } from "effect"
1400
+ * import { Machine } from "@typeonce/effect-machine"
1401
+ * import { MachineTest } from "@typeonce/effect-machine/testing"
1402
+ *
1403
+ * class Count extends Schema.TaggedClass<Count>("Count")("Count", {
1404
+ * value: Schema.Number
1405
+ * }) {}
1406
+ * class Increment extends Schema.TaggedClass<Increment>("Increment")("Increment", {}) {}
1407
+ * const States = Machine.defineStates({ Count })
1408
+ * const machine = Machine.make({
1409
+ * states: States.states,
1410
+ * events: [Increment],
1411
+ * initial: () => States.initial.Count(new Count({ value: 0 }))
1412
+ * }).handle({
1413
+ * Count: { on: { Increment: ({ state }) =>
1414
+ * States.initial.Count(new Count({ value: state.value + 1 })) } }
1415
+ * })
1416
+ *
1417
+ * const explored = MachineTest.explore(machine, {
1418
+ * events: ({ snapshot }) => snapshot.value.value < 2 ? [new Increment({})] : [],
1419
+ * stateKey: ({ snapshot }) => snapshot.value.value
1420
+ * })
1421
+ * ```
1422
+ *
1423
+ * @category exploration
1424
+ * @since 0.4.0
1425
+ */
1426
+ export const explore: <M extends AnyMachine, Key extends ExplorationKey>(
1427
+ machine: ReadyMachine<M>,
1428
+ options: ExploreOptions<M, Key>
1429
+ ) => Effect.Effect<
1430
+ Exploration<M, Key>,
1431
+ RunFailure<RunError<M>, M> | internal.InvariantError<M>,
1432
+ RunServices<M>
1433
+ > = internal.explore
1434
+
1435
+ /**
1436
+ * A predicate over one explored logical state.
1437
+ *
1438
+ * @category exploration
1439
+ * @since 0.4.0
1440
+ */
1441
+ export type ExplorationPredicate<M extends AnyMachine, Key extends ExplorationKey = ExplorationKey> = (
1442
+ node: ExplorationNode<M, Key>
1443
+ ) => boolean
1444
+
1445
+ /**
1446
+ * Why a reachability assertion failed.
1447
+ *
1448
+ * @category exploration
1449
+ * @since 0.4.0
1450
+ */
1451
+ export type ReachabilityFailure = "NotFound" | "UnexpectedMatch" | "Inconclusive"
1452
+
1453
+ /**
1454
+ * A failed or inconclusive reachability assertion.
1455
+ *
1456
+ * @category errors
1457
+ * @since 0.4.0
1458
+ */
1459
+ export { ReachabilityError } from "../internal/testing/machine/verification.js"
1460
+
1461
+ /**
1462
+ * Finds the first, and therefore shortest, explored state matching a
1463
+ * predicate.
1464
+ *
1465
+ * @category exploration
1466
+ * @since 0.4.0
1467
+ */
1468
+ export const findShortest: <M extends AnyMachine, Key extends ExplorationKey>(
1469
+ exploration: Exploration<M, Key>,
1470
+ predicate: ExplorationPredicate<M, Key>
1471
+ ) => ExplorationNode<M, Key> | undefined = internal.findShortest
1472
+
1473
+ /**
1474
+ * Requires a matching state and returns its shortest witness.
1475
+ *
1476
+ * A truncated exploration without a witness fails as inconclusive rather than
1477
+ * claiming the state is unreachable.
1478
+ *
1479
+ * @category exploration
1480
+ * @since 0.4.0
1481
+ */
1482
+ export const assertReachable: <M extends AnyMachine, Key extends ExplorationKey>(
1483
+ exploration: Exploration<M, Key>,
1484
+ name: string,
1485
+ predicate: ExplorationPredicate<M, Key>
1486
+ ) => Effect.Effect<ExplorationNode<M, Key>, internal.ReachabilityError<M, Key>> = internal.assertReachable
1487
+
1488
+ /**
1489
+ * Requires that no explored state matches a predicate.
1490
+ *
1491
+ * This assertion succeeds only for a complete exploration. A truncated
1492
+ * result without a witness fails as inconclusive.
1493
+ *
1494
+ * @category exploration
1495
+ * @since 0.4.0
1496
+ */
1497
+ export const assertUnreachable: <M extends AnyMachine, Key extends ExplorationKey>(
1498
+ exploration: Exploration<M, Key>,
1499
+ name: string,
1500
+ predicate: ExplorationPredicate<M, Key>
1501
+ ) => Effect.Effect<void, internal.ReachabilityError<M, Key>> = internal.assertUnreachable
1502
+
1503
+ /**
1504
+ * Checks a real planner trace against the independent finite statechart model
1505
+ * interpreter.
1506
+ *
1507
+ * The oracle does not import the machine compiler, planner, snapshot helpers,
1508
+ * or target builders. It compares semantic projections of the public trace
1509
+ * and accumulates every disagreement in one structured error.
1510
+ *
1511
+ * @category verification
1512
+ * @since 0.4.0
1513
+ */
1514
+ export const verifyModel: <M extends AnyMachine>(
1515
+ model: FiniteModel,
1516
+ actualTrace: Trace<M>
1517
+ ) => Effect.Effect<void, ReferenceModel.ModelVerificationError> = internal.verifyModel
1518
+
1519
+ /**
1520
+ * A typed planning failure together with every successfully completed trace
1521
+ * segment preceding it.
1522
+ *
1523
+ * @category models
1524
+ * @since 0.4.0
1525
+ */
1526
+ export type RunFailure<Cause, M extends AnyMachine = AnyMachine> =
1527
+ | {
1528
+ readonly _tag: "MachineTestRunFailure"
1529
+ readonly scenario: Scenario<M>
1530
+ readonly phase: "initial"
1531
+ readonly eventIndex: undefined
1532
+ readonly event: undefined
1533
+ readonly initial: undefined
1534
+ readonly steps: readonly []
1535
+ readonly cause: Cause
1536
+ }
1537
+ | {
1538
+ readonly _tag: "MachineTestRunFailure"
1539
+ readonly scenario: Scenario<M>
1540
+ readonly phase: "event"
1541
+ readonly eventIndex: number
1542
+ readonly event: Machine.Machine.InputEvent<M>
1543
+ readonly initial: InitialTrace<M>
1544
+ readonly steps: ReadonlyArray<TraceStep<M>>
1545
+ readonly cause: Cause
1546
+ }
1547
+
1548
+ /**
1549
+ * Errors that can be produced while planning a complete scenario.
1550
+ *
1551
+ * @category errors
1552
+ * @since 0.4.0
1553
+ */
1554
+ export type RunError<M extends AnyMachine> =
1555
+ | Machine.Machine.InitialError<M>
1556
+ | Machine.Machine.Error<M>
1557
+ | Machine.InfiniteTransitionError
1558
+ | Machine.MachineSchemaDecodeError
1559
+ | Machine.StartupError
1560
+
1561
+ /**
1562
+ * Services required while planning a complete scenario.
1563
+ *
1564
+ * Scenario execution delegates exclusively to the service-free `planInitial`
1565
+ * and `plan` APIs. Invoke services and managed runtime capabilities belong to
1566
+ * later execution, not synchronous planning.
1567
+ *
1568
+ * @category models
1569
+ * @since 0.4.0
1570
+ */
1571
+ export type RunServices<M extends AnyMachine> = IsAny<
1572
+ Machine.PlanningServices<Machine.Machine.InitialServices<M> | Machine.Machine.Services<M>>
1573
+ > extends true ? Machine.PlanningServices<Machine.Machine.InitialServices<M> | Machine.Machine.Services<M>> : never
1574
+
1575
+ /**
1576
+ * Executes a generated scenario exclusively through `planInitial` and `plan`.
1577
+ *
1578
+ * Staged actions are retained in each plan but are never executed. Every event
1579
+ * is planned, including events that occur after a terminal configuration.
1580
+ * Typed planning errors retain the scenario and successfully completed prefix
1581
+ * in a `RunFailure`.
1582
+ *
1583
+ * **Example**
1584
+ *
1585
+ * ```ts
1586
+ * import { Schema } from "effect"
1587
+ * import { Machine } from "@typeonce/effect-machine"
1588
+ * import { MachineTest } from "@typeonce/effect-machine/testing"
1589
+ *
1590
+ * class Idle extends Schema.TaggedClass<Idle>("Idle")("Idle", {}) {}
1591
+ * const States = Machine.defineStates({ Idle })
1592
+ * const machine = Machine.make({
1593
+ * states: States.states,
1594
+ * events: [],
1595
+ * initial: () => States.initial.Idle.from()
1596
+ * }).handle({ Idle: {} })
1597
+ *
1598
+ * const trace = MachineTest.run(machine, { events: [] })
1599
+ * ```
1600
+ *
1601
+ * @category constructors
1602
+ * @since 0.4.0
1603
+ */
1604
+ export const run: <M extends AnyMachine>(
1605
+ machine: ReadyMachine<M>,
1606
+ scenario: Scenario<M>
1607
+ ) => Effect.Effect<Trace<M>, RunFailure<RunError<M>, M>, RunServices<M>> = internal.run
1608
+
1609
+ /**
1610
+ * A deterministic hit/miss summary for a finite set declared by a machine.
1611
+ *
1612
+ * @category models
1613
+ * @since 0.4.0
1614
+ */
1615
+ export interface CoverageSummary<Item> {
1616
+ readonly total: number
1617
+ readonly hit: number
1618
+ readonly missing: number
1619
+ readonly hits: ReadonlyArray<Item>
1620
+ readonly misses: ReadonlyArray<Item>
1621
+ }
1622
+
1623
+ /**
1624
+ * One active (non-history) state node in a state coverage summary.
1625
+ *
1626
+ * @category models
1627
+ * @since 0.4.0
1628
+ */
1629
+ export interface StateCoverageItem<Path extends string = string> {
1630
+ readonly path: Path
1631
+ readonly type: Exclude<Machine.Machine.StateNode["type"], "history">
1632
+ }
1633
+
1634
+ /**
1635
+ * State activation and lifecycle coverage.
1636
+ *
1637
+ * @category models
1638
+ * @since 0.4.0
1639
+ */
1640
+ export interface StateCoverage<Path extends string = string> {
1641
+ readonly activation: CoverageSummary<StateCoverageItem<Path>>
1642
+ readonly entry: CoverageSummary<StateCoverageItem<Path>>
1643
+ readonly exit: CoverageSummary<StateCoverageItem<Path>>
1644
+ }
1645
+
1646
+ /**
1647
+ * One stable transition-definition identity in definition order.
1648
+ *
1649
+ * @category models
1650
+ * @since 0.4.0
1651
+ */
1652
+ export interface TransitionCoverageItem<
1653
+ SourcePath extends string = string,
1654
+ EventTag extends PropertyKey = PropertyKey,
1655
+ TargetPath extends string = SourcePath
1656
+ > {
1657
+ readonly id: string
1658
+ readonly index: number
1659
+ readonly source: SourcePath
1660
+ readonly trigger: Machine.Machine.TransitionTrigger<EventTag>
1661
+ readonly reenter: boolean
1662
+ readonly targets: Machine.Machine.TransitionTargets<TargetPath>
1663
+ }
1664
+
1665
+ /**
1666
+ * One public event tag declared by the machine.
1667
+ *
1668
+ * @category models
1669
+ * @since 0.4.0
1670
+ */
1671
+ export interface EventCoverageItem<Tag extends PropertyKey = PropertyKey> {
1672
+ readonly tag: Tag
1673
+ readonly count: number
1674
+ }
1675
+
1676
+ /**
1677
+ * Public event coverage, including events that no transition retained.
1678
+ *
1679
+ * @category models
1680
+ * @since 0.4.0
1681
+ */
1682
+ export type EventCoverage<Tag extends PropertyKey = PropertyKey> =
1683
+ | {
1684
+ readonly available: true
1685
+ readonly total: number
1686
+ readonly hit: number
1687
+ readonly missing: number
1688
+ readonly hits: ReadonlyArray<EventCoverageItem<Tag>>
1689
+ readonly misses: ReadonlyArray<EventCoverageItem<Tag>>
1690
+ readonly observed: ReadonlyArray<EventCoverageItem<Tag>>
1691
+ readonly diagnostics: readonly []
1692
+ }
1693
+ | {
1694
+ readonly available: false
1695
+ readonly total: undefined
1696
+ readonly hit: undefined
1697
+ readonly missing: undefined
1698
+ readonly hits: undefined
1699
+ readonly misses: undefined
1700
+ readonly observed: ReadonlyArray<EventCoverageItem<Tag>>
1701
+ readonly diagnostics: ReadonlyArray<{
1702
+ readonly schemaIndex: number
1703
+ readonly message: string
1704
+ }>
1705
+ }
1706
+
1707
+ /**
1708
+ * Trace-derived scenario counts. There is no finite declared scenario space.
1709
+ *
1710
+ * @category models
1711
+ * @since 0.4.0
1712
+ */
1713
+ export interface ScenarioCoverage {
1714
+ readonly traces: number
1715
+ readonly events: number
1716
+ readonly empty: number
1717
+ }
1718
+
1719
+ /**
1720
+ * Trace-derived logical configuration counts. There is no claimed exhaustive total.
1721
+ *
1722
+ * @category models
1723
+ * @since 0.4.0
1724
+ */
1725
+ export interface LogicalConfigurationCoverage {
1726
+ readonly observations: number
1727
+ readonly hit: number
1728
+ readonly identities: ReadonlyArray<string>
1729
+ }
1730
+
1731
+ /**
1732
+ * Directly observed startup and microstep evidence.
1733
+ *
1734
+ * @category models
1735
+ * @since 0.4.0
1736
+ */
1737
+ export interface MicrostepCoverageEvidence {
1738
+ readonly total: number
1739
+ readonly changed: number
1740
+ readonly targetless: number
1741
+ readonly raised: number
1742
+ readonly emitted: number
1743
+ readonly eventTriggered: number
1744
+ readonly alwaysTriggered: number
1745
+ readonly doneTriggered: number
1746
+ readonly choiceTriggered: number
1747
+ }
1748
+
1749
+ /**
1750
+ * Directly observed completion evidence.
1751
+ *
1752
+ * @category models
1753
+ * @since 0.4.0
1754
+ */
1755
+ export interface CompletionCoverageEvidence<Path extends string = string> {
1756
+ readonly donePlans: number
1757
+ readonly recordObservations: number
1758
+ readonly paths: ReadonlyArray<Path>
1759
+ }
1760
+
1761
+ /**
1762
+ * Directly observed history records and history-target transitions.
1763
+ *
1764
+ * @category models
1765
+ * @since 0.4.0
1766
+ */
1767
+ export interface HistoryCoverageEvidence<Path extends string = string> {
1768
+ readonly recordObservations: number
1769
+ readonly recorded: ReadonlyArray<{
1770
+ readonly path: Path
1771
+ readonly modes: ReadonlyArray<"shallow" | "deep">
1772
+ }>
1773
+ readonly targets: number
1774
+ readonly resolvedTargets: number
1775
+ }
1776
+
1777
+ /**
1778
+ * Coverage computed only from observable machine definitions and planner traces.
1779
+ *
1780
+ * @category models
1781
+ * @since 0.4.0
1782
+ */
1783
+ export interface Coverage<M extends AnyMachine> {
1784
+ readonly states: StateCoverage<StatePath<M>>
1785
+ readonly transitions: CoverageSummary<
1786
+ TransitionCoverageItem<
1787
+ StateNodePath<M>,
1788
+ Machine.Machine.TagOf<Machine.Machine.Events<M>[number]>,
1789
+ StateNodePath<M>
1790
+ >
1791
+ >
1792
+ readonly events: EventCoverage<Machine.Machine.TagOf<Machine.Machine.InputEvents<M>[number]>>
1793
+ readonly scenarios: ScenarioCoverage
1794
+ readonly logicalConfigurations: LogicalConfigurationCoverage
1795
+ readonly startup: {
1796
+ readonly traces: number
1797
+ readonly withMicrosteps: number
1798
+ }
1799
+ readonly microsteps: MicrostepCoverageEvidence
1800
+ readonly completion: CompletionCoverageEvidence<StatePath<M>>
1801
+ readonly history: HistoryCoverageEvidence<StateNodePath<M>>
1802
+ }
1803
+
1804
+ /**
1805
+ * Computes deterministic, definition-aware coverage from completed planner
1806
+ * traces. Finite declared sets report hits and misses; scenarios and logical
1807
+ * configurations report observations only because their complete spaces are
1808
+ * generally infinite.
1809
+ *
1810
+ * **Example**
1811
+ *
1812
+ * ```ts
1813
+ * import { Effect, Schema } from "effect"
1814
+ * import { Machine } from "@typeonce/effect-machine"
1815
+ * import { MachineTest } from "@typeonce/effect-machine/testing"
1816
+ *
1817
+ * class Idle extends Schema.TaggedClass<Idle>("Idle")("Idle", {}) {}
1818
+ * const States = Machine.defineStates({ Idle })
1819
+ * const machine = Machine.make({
1820
+ * states: States.states,
1821
+ * events: [],
1822
+ * initial: () => States.initial.Idle.from()
1823
+ * }).handle({ Idle: {} })
1824
+ *
1825
+ * const report = Effect.map(
1826
+ * MachineTest.run(machine, { events: [] }),
1827
+ * (trace) => MachineTest.coverage(machine, trace)
1828
+ * )
1829
+ * ```
1830
+ *
1831
+ * @category verification
1832
+ * @since 0.4.0
1833
+ */
1834
+ export const coverage: <M extends AnyMachine>(
1835
+ machine: M,
1836
+ traceOrTraces: Trace<M> | ReadonlyArray<Trace<M>>
1837
+ ) => Coverage<M> = internal.coverage
1838
+
1839
+ /**
1840
+ * The observation roles summarized for one logical graph node.
1841
+ *
1842
+ * @category models
1843
+ * @since 0.4.0
1844
+ */
1845
+ export interface ObservedGraphNodeObservations {
1846
+ readonly total: number
1847
+ readonly startup: number
1848
+ readonly event: number
1849
+ readonly microstep: number
1850
+ }
1851
+
1852
+ /**
1853
+ * One full encoded logical snapshot stored in the observed Effect graph.
1854
+ *
1855
+ * @category models
1856
+ * @since 0.4.0
1857
+ */
1858
+ export interface ObservedGraphNode<M extends AnyMachine> {
1859
+ readonly id: string
1860
+ readonly snapshot: Machine.Machine.Snapshot<Machine.Machine.States<M>>
1861
+ readonly encoded: Machine.Machine.EncodedSnapshot
1862
+ readonly configuration: ReadonlyArray<StatePath<M>>
1863
+ readonly observations: ObservedGraphNodeObservations
1864
+ }
1865
+
1866
+ /**
1867
+ * Retained evidence for one microstep inside an observed graph edge.
1868
+ *
1869
+ * @category models
1870
+ * @since 0.4.0
1871
+ */
1872
+ export interface ObservedGraphMicrostep<M extends AnyMachine> {
1873
+ readonly next: string
1874
+ readonly event: Machine.Machine.Event<M> | Machine.InitialEvent
1875
+ readonly transitions: Microstep<M, any>["transitions"]
1876
+ readonly raisedEvents: ReadonlyArray<Machine.Machine.Event<M>>
1877
+ readonly emittedEvents: ReadonlyArray<Machine.Machine.Emit<M>>
1878
+ readonly exitPaths: ReadonlyArray<StatePath<M>>
1879
+ readonly entryPaths: ReadonlyArray<StatePath<M>>
1880
+ readonly changed: boolean
1881
+ }
1882
+
1883
+ /**
1884
+ * A startup or public-event macrostep retained by the observed graph.
1885
+ *
1886
+ * @category models
1887
+ * @since 0.4.0
1888
+ */
1889
+ export type ObservedGraphEdge<M extends AnyMachine> =
1890
+ | {
1891
+ readonly _tag: "Startup"
1892
+ readonly traceIndex: number
1893
+ readonly microsteps: ReadonlyArray<ObservedGraphMicrostep<M>>
1894
+ readonly completion: PlanCompletion<M>
1895
+ }
1896
+ | {
1897
+ readonly _tag: "Event"
1898
+ readonly traceIndex: number
1899
+ readonly eventIndex: number
1900
+ readonly event: Machine.Machine.InputEvent<M>
1901
+ readonly microsteps: ReadonlyArray<ObservedGraphMicrostep<M>>
1902
+ readonly completion: PlanCompletion<M>
1903
+ }
1904
+
1905
+ /**
1906
+ * An Effect directed graph plus stable indexes useful to graph algorithms.
1907
+ *
1908
+ * @category models
1909
+ * @since 0.4.0
1910
+ */
1911
+ export interface ObservedGraph<M extends AnyMachine> {
1912
+ readonly graph: Graph.DirectedGraph<ObservedGraphNode<M>, ObservedGraphEdge<M>>
1913
+ readonly nodesById: ReadonlyMap<string, Graph.NodeIndex>
1914
+ /** Settled post-startup nodes from which public event paths begin. */
1915
+ readonly starts: ReadonlyArray<Graph.NodeIndex>
1916
+ /** Pre-settled startup sources connected to `starts` by `Startup` edges. */
1917
+ readonly startupSources: ReadonlyArray<Graph.NodeIndex>
1918
+ }
1919
+
1920
+ /**
1921
+ * Converts concrete planner traces into an observed logical-state graph.
1922
+ * Nodes are deduplicated by the public snapshot encoding and every edge is a
1923
+ * concrete startup or public-event macrostep. This intentionally does not
1924
+ * claim to be a static or exhaustive graph of the machine.
1925
+ *
1926
+ * @category verification
1927
+ * @since 0.4.0
1928
+ */
1929
+ export const observedGraph: <M extends AnyMachine>(
1930
+ machine: M,
1931
+ traceOrTraces: Trace<M> | ReadonlyArray<Trace<M>>
1932
+ ) => Effect.Effect<
1933
+ ObservedGraph<M>,
1934
+ Machine.MachineSchemaEncodeError,
1935
+ Machine.Machine.SnapshotEncodingServices<Machine.Machine.States<M>>
1936
+ > = internal.observedGraph
1937
+
1938
+ /**
1939
+ * Independently checked families of planner laws.
1940
+ *
1941
+ * @category models
1942
+ * @since 0.4.0
1943
+ */
1944
+ export type VerificationLawGroup =
1945
+ | "configuration"
1946
+ | "microsteps"
1947
+ | "completion"
1948
+ | "history"
1949
+ | "targetBounds"
1950
+
1951
+ /**
1952
+ * Stable identifiers for individual planner laws.
1953
+ *
1954
+ * @category models
1955
+ * @since 0.4.0
1956
+ */
1957
+ export type VerificationLaw =
1958
+ | "configuration.shape"
1959
+ | "configuration.path"
1960
+ | "configuration.schema"
1961
+ | "configuration.hierarchy"
1962
+ | "configuration.compound"
1963
+ | "configuration.parallel"
1964
+ | "configuration.duplicate"
1965
+ | "configuration.trace"
1966
+ | "microsteps.unique"
1967
+ | "microsteps.order"
1968
+ | "microsteps.activeBefore"
1969
+ | "microsteps.activeAfter"
1970
+ | "microsteps.changed"
1971
+ | "microsteps.continuity"
1972
+ | "microsteps.reentry"
1973
+ | "completion.record"
1974
+ | "completion.output"
1975
+ | "completion.done"
1976
+ | "history.record"
1977
+ | "history.mode"
1978
+ | "history.path"
1979
+ | "history.value"
1980
+ | "history.shallow"
1981
+ | "history.deep"
1982
+ | "targetBounds.definition"
1983
+ | "targetBounds.target"
1984
+
1985
+ /**
1986
+ * One independently observed violation in a planner trace.
1987
+ *
1988
+ * @category models
1989
+ * @since 0.4.0
1990
+ */
1991
+ export interface VerificationViolation {
1992
+ readonly law: VerificationLaw
1993
+ /** `undefined` identifies startup; otherwise this is the scenario event index. */
1994
+ readonly eventIndex: number | undefined
1995
+ readonly microstepIndex?: number
1996
+ readonly path?: string
1997
+ readonly message: string
1998
+ }
1999
+
2000
+ /**
2001
+ * All violations found while checking one trace.
2002
+ *
2003
+ * @category errors
2004
+ * @since 0.4.0
2005
+ */
2006
+ export { VerificationError } from "../internal/testing/machine/verification.js"
2007
+
2008
+ /**
2009
+ * Selects law families for the single canonical verifier. All run by default.
2010
+ *
2011
+ * @category models
2012
+ * @since 0.4.0
2013
+ */
2014
+ export interface VerifyOptions {
2015
+ readonly laws?: ReadonlyArray<VerificationLawGroup>
2016
+ }
2017
+
2018
+ /**
2019
+ * Verifies an executed trace using only public machine inspection and raw
2020
+ * snapshot data. The verifier deliberately does not reuse planner
2021
+ * normalization, encoding, finality, or other internal helpers.
2022
+ *
2023
+ * Every selected law is evaluated and returned in one structured error so a
2024
+ * shrunk property-test counterexample retains all relevant evidence.
2025
+ *
2026
+ * **Example**
2027
+ *
2028
+ * ```ts
2029
+ * import { Effect, Schema } from "effect"
2030
+ * import { Machine } from "@typeonce/effect-machine"
2031
+ * import { MachineTest } from "@typeonce/effect-machine/testing"
2032
+ *
2033
+ * class Idle extends Schema.TaggedClass<Idle>("Idle")("Idle", {}) {}
2034
+ * const States = Machine.defineStates({ Idle })
2035
+ * const machine = Machine.make({
2036
+ * states: States.states,
2037
+ * events: [],
2038
+ * initial: () => States.initial.Idle.from()
2039
+ * }).handle({ Idle: {} })
2040
+ *
2041
+ * const checked = Effect.gen(function*() {
2042
+ * const trace = yield* MachineTest.run(machine, { events: [] })
2043
+ * yield* MachineTest.verify(machine, trace)
2044
+ * })
2045
+ * ```
2046
+ *
2047
+ * @category verification
2048
+ * @since 0.4.0
2049
+ */
2050
+ export const verify: <M extends AnyMachine>(
2051
+ machine: M,
2052
+ trace: Trace<M>,
2053
+ options?: VerifyOptions
2054
+ ) => Effect.Effect<void, VerificationError> = internal.verify
2055
+
2056
+ /**
2057
+ * Formats a trace as deterministic, line-oriented counterexample evidence.
2058
+ *
2059
+ * Object keys and map/set entries are canonicalized. Deferred effects are
2060
+ * represented by counts so formatting never evaluates or inspects actions.
2061
+ * Failures include their original cause and every available successful prefix.
2062
+ *
2063
+ * @category formatting
2064
+ * @since 0.4.0
2065
+ */
2066
+ export const formatTrace: <M extends AnyMachine, Cause>(trace: Trace<M> | RunFailure<Cause, M>) => string =
2067
+ internal.formatTrace