xstate 6.0.0-beta.0 → 6.0.0-beta.2

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 (81) hide show
  1. package/bin/xstate.js +10 -4
  2. package/dist/{createActor-551369be.development.esm.js → createActor-0250284a.development.esm.js} +244 -41
  3. package/dist/{createActor-cbe3abac.esm.js → createActor-20a97166.esm.js} +243 -40
  4. package/dist/{createActor-a49ae0ce.development.cjs.js → createActor-c029d29f.development.cjs.js} +245 -40
  5. package/dist/{createActor-25900f76.cjs.js → createActor-c91c9ffe.cjs.js} +244 -39
  6. package/dist/declarations/src/createActor.d.ts +3 -1
  7. package/dist/declarations/src/createMachine.d.ts +6 -3
  8. package/dist/declarations/src/durable/index.d.ts +43 -8
  9. package/dist/declarations/src/effectDescriptor.d.ts +2 -2
  10. package/dist/declarations/src/machineVersion.types.d.ts +2 -2
  11. package/dist/declarations/src/setup.d.ts +5 -5
  12. package/dist/declarations/src/systemTransition.d.ts +1 -1
  13. package/dist/declarations/src/types.d.ts +11 -3
  14. package/dist/declarations/src/types.v6.d.ts +37 -2
  15. package/dist/{effectDescriptor-f73da4ec.development.cjs.js → effectDescriptor-3fff5c51.development.cjs.js} +5 -5
  16. package/dist/{effectDescriptor-2fa27c54.cjs.js → effectDescriptor-47f73e7e.cjs.js} +5 -5
  17. package/dist/{effectDescriptor-321a8ce9.development.esm.js → effectDescriptor-5ac7dccb.development.esm.js} +5 -5
  18. package/dist/{effectDescriptor-d8b0f749.esm.js → effectDescriptor-e93df910.esm.js} +5 -5
  19. package/dist/{machineSchemas-88d01821.cjs.js → legacySnapshot-8d595687.cjs.js} +117 -1
  20. package/dist/{machineSchemas-45c21bc2.esm.js → legacySnapshot-9e06feb4.esm.js} +116 -2
  21. package/dist/{machineSchemas-b5736804.development.esm.js → legacySnapshot-ae6594a7.development.esm.js} +117 -3
  22. package/dist/{machineSchemas-6d96bef4.development.cjs.js → legacySnapshot-ebf0b2b8.development.cjs.js} +118 -2
  23. package/dist/xstate-actors.cjs.js +1 -1
  24. package/dist/xstate-actors.development.cjs.js +1 -1
  25. package/dist/xstate-actors.development.esm.js +2 -2
  26. package/dist/xstate-actors.esm.js +2 -2
  27. package/dist/xstate-actors.umd.min.js +1 -1
  28. package/dist/xstate-actors.umd.min.js.map +1 -1
  29. package/dist/xstate-durable.cjs.js +120 -10
  30. package/dist/xstate-durable.development.cjs.js +120 -10
  31. package/dist/xstate-durable.development.esm.js +120 -10
  32. package/dist/xstate-durable.esm.js +120 -10
  33. package/dist/xstate-durable.umd.min.js +1 -1
  34. package/dist/xstate-durable.umd.min.js.map +1 -1
  35. package/dist/xstate-fsm.cjs.js +4 -4
  36. package/dist/xstate-fsm.development.cjs.js +4 -4
  37. package/dist/xstate-fsm.development.esm.js +4 -4
  38. package/dist/xstate-fsm.esm.js +4 -4
  39. package/dist/xstate-fsm.umd.min.js +1 -1
  40. package/dist/xstate-fsm.umd.min.js.map +1 -1
  41. package/dist/xstate-graph.cjs.js +1 -1
  42. package/dist/xstate-graph.development.cjs.js +1 -1
  43. package/dist/xstate-graph.development.esm.js +2 -2
  44. package/dist/xstate-graph.esm.js +2 -2
  45. package/dist/xstate-graph.umd.min.js +1 -1
  46. package/dist/xstate-graph.umd.min.js.map +1 -1
  47. package/dist/xstate-persist.cjs.js +23 -12
  48. package/dist/xstate-persist.development.cjs.js +23 -12
  49. package/dist/xstate-persist.development.esm.js +17 -6
  50. package/dist/xstate-persist.esm.js +17 -6
  51. package/dist/xstate-persist.umd.min.js +1 -1
  52. package/dist/xstate-persist.umd.min.js.map +1 -1
  53. package/dist/xstate-validation.cjs.js +1 -1
  54. package/dist/xstate-validation.development.cjs.js +1 -1
  55. package/dist/xstate-validation.development.esm.js +1 -1
  56. package/dist/xstate-validation.esm.js +1 -1
  57. package/dist/xstate.cjs.js +63 -51
  58. package/dist/xstate.development.cjs.js +63 -51
  59. package/dist/xstate.development.esm.js +53 -41
  60. package/dist/xstate.esm.js +53 -41
  61. package/dist/xstate.umd.min.js +1 -1
  62. package/dist/xstate.umd.min.js.map +1 -1
  63. package/docs/agents.md +9 -0
  64. package/docs/api-stability.json +273 -249
  65. package/docs/backend-workflows.md +3 -3
  66. package/docs/durable-execution.md +72 -48
  67. package/docs/errors.md +2 -2
  68. package/docs/examples.md +34 -34
  69. package/docs/fsm.md +2 -1
  70. package/docs/guards.md +1 -1
  71. package/docs/index.md +1 -1
  72. package/docs/invoke.md +34 -2
  73. package/docs/persistence.md +16 -10
  74. package/docs/serialization.md +1 -1
  75. package/docs/setup-and-provide.md +3 -3
  76. package/docs/spawn.md +1 -1
  77. package/docs/system-transitions.md +6 -6
  78. package/docs/tutorial.md +5 -5
  79. package/docs/typescript.md +9 -0
  80. package/docs/xstate-v5-to-v6.md +176 -19
  81. package/package.json +1 -1
@@ -56,7 +56,7 @@ The same rule applies to writes the workflow makes. Pass an idempotency key to t
56
56
 
57
57
  ## Durable steps
58
58
 
59
- Async logic that runs several external calls can record each one on the snapshot with `enq.step(...)` — this page's pattern is hostless durability, where the snapshot is the journal, and `enq.step` is built for it (on a [durable host](durable-execution.md), write plain promise actors instead):
59
+ Async logic that runs several external calls can record each one on the snapshot with `enq.step(...)`. This page's pattern is hostless durability, where the snapshot is the journal, and `enq.step` is built for it (on a [durable host](durable-execution.md), write plain promise actors instead):
60
60
 
61
61
  ```ts
62
62
  const fulfillOrder = createAsyncLogic({
@@ -91,7 +91,7 @@ Store the version alongside the snapshot so a restored workflow is either migrat
91
91
 
92
92
  ## Request-response endpoints
93
93
 
94
- Some endpoints have to answer with the result, not just acknowledge the event. Start the actor, send the event and wait for the snapshot you need:
94
+ Some endpoints have to answer with the result, not only acknowledge the event. Start the actor, send the event and wait for the snapshot you need:
95
95
 
96
96
  ```ts
97
97
  import { waitFor } from 'xstate';
@@ -111,7 +111,7 @@ const settled = await waitFor(
111
111
 
112
112
  ## Long delays across restarts
113
113
 
114
- Pending delayed transitions are part of the snapshot: `snapshot.timers` lists each one with its declared delay and the event it will deliver. A snapshot persisted from a running actor also records each timer's wall-clock start (`scheduledAt`), and restoring it schedules the remaining time toward the original deadline — a timer already past due fires immediately, so a three-day `after` survives deploys. The deadline only holds while some process restores the actor and stays up; for timers that must fire with no process running, use a durable host that owns timer scheduling.
114
+ Pending delayed transitions are part of the snapshot: `snapshot.timers` lists each one with its declared delay and the event it will deliver. A snapshot persisted from a running actor also records each timer's wall-clock start (`scheduledAt`), and restoring it schedules the remaining time toward the original deadline. A timer already past due fires immediately, so a three-day `after` survives deploys. The deadline only holds while some process restores the actor and stays up; for timers that must fire with no process running, use a durable host that owns timer scheduling.
115
115
 
116
116
  For deadlines that must respect real time, persist the scheduled and due times next to the snapshot and have the host (a scheduler, a queue with a visibility timeout, or a cron job) deliver the event when it is due. Keep short delays such as debounces and retry backoffs inside the machine, where restarting the delay has little effect.
117
117
 
@@ -39,8 +39,8 @@ const durable = createDurable(machine, {
39
39
  const output = await durable.run(input);
40
40
  ```
41
41
 
42
- Runtime operations you omit keep their local behavior — spawned machine
43
- children run in this process, for example.
42
+ Runtime operations you omit keep their local behavior. For example, spawned machine
43
+ children run in this process.
44
44
 
45
45
  `enqueueRootEvent` is the narrow hook most co-located hosts need: it receives
46
46
  events sent to the execution root while the loop is parked, or at any time
@@ -53,8 +53,8 @@ runtime.
53
53
  The primary durable unit for async work is the actor itself: a developer
54
54
  writes a normal promise (`fromPromise`, `createAsyncLogic`) and invokes it;
55
55
  the `runLogic` operation hands the host the whole body as one journal
56
- entry. The actor's identity — `address`, string `src` key and serializable
57
- `input` — crosses any boundary, so a host either wraps the provided `exec`
56
+ entry. The actor's identity (`address`, string `src` key and serializable
57
+ `input`) crosses any boundary, so a host either wraps the provided `exec`
58
58
  in its own step primitive, or ignores it entirely and re-runs the
59
59
  registered logic on a remote executor from `(src, input)` alone; the output
60
60
  flows back as the actor's ordinary completion event. No step vocabulary
@@ -65,7 +65,7 @@ runLogic: (actor, exec) => ctx.run(actor.address, exec) // in-process j
65
65
  runLogic: (actor) => activities.runLogic(actor.src, actor.getSnapshot().input) // remote executor
66
66
  ```
67
67
 
68
- Custom actions follow the same rule — durability crosses a boundary as
68
+ Custom actions follow the same rule: durability crosses a boundary as
69
69
  declared identity plus serializable data, never as a closure. An action
70
70
  enqueued as a named function with serializable arguments
71
71
  (`enq(notifyApprover, context.orderId)`) carries `(type, args)` in its
@@ -75,19 +75,19 @@ descriptor, so a remote-executor host can dispatch it worker-side and ignore
75
75
  by its effect ID. Work heavy enough to deserve an activity is usually an
76
76
  actor, not an action.
77
77
 
78
- For finer granularity — several independently-journaled awaits — split into
78
+ For finer granularity (several independently-journaled awaits), split into
79
79
  separate invoked actors: each actor is its own journal entry, and the states
80
80
  between them make the progress visible. Do not reach for `enq.step` on a
81
- durable host; it is the [hostless durability](actor-logic.md) tool — the
82
- snapshot is its journal — and on a durable host it only duplicates what
81
+ durable host; it is the [hostless durability](actor-logic.md) tool (the
82
+ snapshot is its journal), and on a durable host it only duplicates what
83
83
  `runLogic` already provides. When restored snapshots that carry step results
84
84
  do run under a durable host, `enq.step` routes through the `runStep`
85
85
  operation:
86
- implement it to journal steps in the host's journal — a memoized result
87
- replays without re-running the step — and the built-in
86
+ implement it to journal steps in the host's journal (a memoized result
87
+ replays without re-running the step), and the built-in
88
88
  snapshot memoization steps aside. Unlike other operations, a step is an
89
89
  orchestration frame that may itself await runtime operations of the same
90
- execution, so it is never serialized behind them — and for the same reason
90
+ execution, so it is never serialized behind them. For the same reason,
91
91
  `executeEffects` resolves without awaiting it. The `runStep` helper exported
92
92
  from `xstate` exposes the built-in behavior. `exec` is a closure over live
93
93
  execution state: run it in this process and journal its result; it cannot be
@@ -99,7 +99,7 @@ either race its durable wait against the pending `runLogic`/`runStep`
99
99
  promises it created (the natural shape when they map to the engine's own
100
100
  step primitives), or drain them to empty first. An in-flight step ends by
101
101
  completing its actor, and that completion event cannot satisfy a durable
102
- wait that was already registered with the engine — the run deadlocks until
102
+ wait that was already registered with the engine, so the run deadlocks until
103
103
  its idle timeout. The in-process loop above needs none of this, because any
104
104
  later event resolves its wait. Operations initiated by live child
105
105
  actors (parent sends, timers, terminations) route to your implementations
@@ -120,19 +120,38 @@ while (state.status === 'active') {
120
120
  reporting up), and only when none are queued defers to the adapter's durable
121
121
  wait.
122
122
 
123
- Checkpoint with `getPersistedSnapshot(state)` after `executeEffects`
124
- resolves, and persist `durable.nextTransitionIndex` alongside; pass it as
125
- `transitionIndex` when recreating the execution from a checkpoint instead of
126
- replaying from the beginning. Restore the checkpoint before entering the loop:
123
+ Checkpoint with `durable.checkpoint(state)` after `executeEffects` resolves,
124
+ and persist the whole result atomically. A checkpoint holds three things:
125
+
126
+ - `snapshot`: the persisted snapshot. `checkpoint(state, options)` passes
127
+ `options` to the logic's `getPersistedSnapshot`.
128
+ - `nextTransitionIndex`: the index the next transition gets.
129
+ - `inbox`: root-addressed events that settled batches captured and
130
+ `waitForEvent()` has not handed out yet, such as a child's
131
+ `xstate.done.actor` or an event a child sent to its parent. Each entry's
132
+ `source` is the sender's address, `{ '@actor': address }`.
133
+
134
+ Without the inbox, an event captured in the last batch exists only in memory.
135
+ A child that completed during that batch is not restarted on restore, so its
136
+ completion would never arrive.
137
+
138
+ Inbox event payloads are stored as they are and must be serializable data,
139
+ like timer events in a persisted snapshot. Child snapshot updates
140
+ (`xstate.snapshot.actor`) are not stored: a restored active child reports its
141
+ current snapshot when it starts. Completion, error and timeout events for a
142
+ child that is no longer in the snapshot, or that a later transition replaced,
143
+ are dropped.
144
+
145
+ Restore the checkpoint on a fresh execution before entering the loop:
127
146
 
128
147
  <!-- checkpoint restoration contract from packages/core/src/durable/index.ts -->
129
148
 
130
149
  ```ts
131
- const resumed = createDurable(machine, {
132
- ...adapter,
133
- transitionIndex: checkpoint.nextTransitionIndex
134
- });
135
- let [state, effects] = resumed.restore(checkpoint.snapshot);
150
+ await db.save(durable.checkpoint(state));
151
+
152
+ // later, on any worker
153
+ const resumed = createDurable(machine, adapter);
154
+ let [state, effects] = resumed.restore(await db.load());
136
155
  await resumed.executeEffects(effects);
137
156
 
138
157
  while (state.status === 'active') {
@@ -141,6 +160,12 @@ while (state.status === 'active') {
141
160
  }
142
161
  ```
143
162
 
163
+ The resumed execution continues from the checkpoint's `nextTransitionIndex`,
164
+ and `waitForEvent()` hands out the inbox before the adapter's wait. If the
165
+ adapter also sets `transitionIndex`, it must equal the checkpoint's
166
+ `nextTransitionIndex`. `restore()` also accepts a bare persisted snapshot; pass
167
+ `transitionIndex` to `createDurable` in that case.
168
+
144
169
  `restore()` rehydrates the snapshot and attaches the adapter across the actor
145
170
  tree. It returns effects that start active embedded children and re-arm pending
146
171
  root timers. Nothing starts until `executeEffects()` runs. Completed children
@@ -188,14 +213,14 @@ of a local child. Session ids embed a random per-process system id, so a
188
213
  host that journals the execution's own events (rather than only external
189
214
  ones) must pin `executionId` on the adapter: session ids become
190
215
  `<executionId>:<n>`, a deterministic function of actor-creation order, and a
191
- replayed execution re-creates the same ids — a journaled completion still
216
+ replayed execution re-creates the same ids, so a journaled completion still
192
217
  matches the child the replay re-creates. Uniqueness across executions is the
193
218
  host's responsibility. For a remote child the owning runtime is the authority on
194
219
  staleness by default; a host that stores an opaque `incarnation` token on
195
220
  the persisted child entry gets the same guard on the referencing side. The
196
221
  token is the owner's `sessionId` for the child and the restored handle
197
- carries it as its own `sessionId` — one incarnation identity, one rule: a
198
- ref that knows its incarnation compares it, one that does not defers to the
222
+ carries it as its own `sessionId`. A
223
+ ref that knows its incarnation compares it; one that does not defers to the
199
224
  owner. `sendTo` effect descriptors journal the target's token so a send
200
225
  replayed after the address was reincarnated is detectable.
201
226
 
@@ -277,11 +302,11 @@ timer is no longer pending is ignored.
277
302
  ordered effects with stable IDs such as `0:0` and `1:0`, and event waits with
278
303
  IDs such as `event:0`. Memoize or deduplicate each operation using that ID:
279
304
  replaying the same events from the beginning reconstructs the same snapshots,
280
- effects, waits and IDs — not just the same resulting snapshot, but the same
281
- effects in the same order, which is what hosts that match journal entries
305
+ effects, waits and IDs. This means the same effects in the same order, not
306
+ only the same resulting snapshot, which is what hosts that match journal entries
282
307
  positionally (Temporal, Restate) depend on. Each `DurableEffect` also carries a serializable
283
- `descriptor` — actor references replaced by `{ $actor: address, incarnation? }` and actor sources by
284
- source keys — for journaling; payload fields such as `event` and `input` pass
308
+ `descriptor` for journaling, with actor references replaced by `{ '@actor': address, incarnation? }` and actor sources by
309
+ source keys; payload fields such as `event` and `input` pass
285
310
  through by reference and are only as serializable as their values.
286
311
 
287
312
  Every inter-actor edge is an asynchronous handoff to the runtime.
@@ -305,9 +330,9 @@ pairwise sender-to-receiver ordering: for a given pair of actors, events sent
305
330
  from the first to the second are enqueued in send order, and an undeliverable
306
331
  event is dead-lettered rather than retried. This matches the Erlang and Akka
307
332
  defaults. A dead letter is reported through the `deadLetter` runtime
308
- operation (and the `onRejectedEvent` hook) — observability, not
333
+ operation (and the `onRejectedEvent` hook). This is for observability, not
309
334
  retry. Ordering is not transitive
310
- across intermediaries. The durable path is stronger — the handoff queue serializes
335
+ across intermediaries. The durable path is stronger: the handoff queue serializes
311
336
  every operation of an execution globally. A host `sendEvent` that routes
312
337
  remotely is responsible for preserving pairwise ordering on its transport.
313
338
 
@@ -320,11 +345,11 @@ children. Incarnations of local children do not survive persist and restore
320
345
  unless `executionId` is pinned.
321
346
 
322
347
  Because every handoff queues, a runtime operation must never await another
323
- runtime operation of the same execution through the actor system — it would
348
+ runtime operation of the same execution through the actor system, because it would
324
349
  wait behind itself. The `deliverEvent`, `stopActor` and `terminateActor`
325
350
  helpers exported from `xstate` expose the local behaviors and are always safe
326
351
  to call directly. An implemented runtime operation replaces the local
327
- behavior entirely, including its bookkeeping — a `stopActor` that only journals
352
+ behavior entirely, including its bookkeeping. A `stopActor` that only journals
328
353
  must call the `stopActor` helper for the local stop cascade, and a `sendEvent`
329
354
  that routes locally must call `deliverEvent`. When a remote runtime can
330
355
  deliver the same completion more than once (retries, replays), the host is
@@ -332,7 +357,7 @@ responsible for deduplicating before handing it to the execution.
332
357
 
333
358
  By default, events addressed to the root actor do not reach `sendEvent` during
334
359
  `executeEffects`: the execution captures and retains them, and
335
- `waitForEvent()` hands them out — in capture order — before deferring to the
360
+ `waitForEvent()` hands them out in capture order before deferring to the
336
361
  adapter. While the loop is parked in the adapter's wait, a root-addressed
337
362
  event reaches `enqueueRootEvent`, or `sendEvent` when the adapter implements
338
363
  the broader routing override, and belongs in the host's mailbox. Producing one
@@ -360,7 +385,7 @@ Change the state of other actors only at the top of the drive loop.
360
385
 
361
386
  ### Journaling rules
362
387
 
363
- A journaled step's closure must contain only the external side effect —
388
+ A journaled step's closure must contain only the external side effect:
364
389
  nothing that re-enters the engine's journal, and nothing that mutates local
365
390
  actors.
366
391
 
@@ -373,8 +398,8 @@ actors.
373
398
  the runtime operations make.
374
399
  - **Local actor mutations stay outside the journal.** A journaled closure is
375
400
  skipped on replay: calling `deliverEvent`, `stopActor`, or starting an
376
- actor inside one rebuilds a different actor tree than the recorded run —
377
- silently, with no error. Re-run local mutations unjournaled on every
401
+ actor inside one rebuilds a different actor tree than the recorded run,
402
+ silently and with no error. Re-run local mutations unjournaled on every
378
403
  replay; journal only work that must not run twice.
379
404
 
380
405
  A per-effect `runtime(metadata, effect)` factory is available for hosts that
@@ -396,7 +421,7 @@ I/O:
396
421
  different effect sequence on replay, and nothing detects the divergence.
397
422
  Perform such work inside journaled operations (the host's `executeAction`,
398
423
  where the recorded result replays), or derive values deterministically from
399
- what the snapshot already carries — addresses and effect IDs are stable
424
+ what the snapshot already carries. Addresses and effect IDs are stable
400
425
  across replays and make good seeds and idempotency keys.
401
426
 
402
427
  ```ts
@@ -416,27 +441,26 @@ entry: ({ context }, enq) => {
416
441
  `getPersistedSnapshot(snapshot)` embeds each child's persisted state: the
417
442
  whole-tree checkpoint of a runtime that co-locates the tree.
418
443
  `getPersistedSnapshot(snapshot, { embedChildren: false })` instead references
419
- children by logical address, leaving each child's state — including its own
420
- subtree — with the runtime that owns it; the option applies to the whole
444
+ children by logical address, leaving each child's state (including its own
445
+ subtree) with the runtime that owns it; the option applies to the whole
421
446
  tree, not per placement boundary. Restoring an address-only child produces a
422
447
  location-transparent handle: sends route through the system runtime, its
423
448
  snapshot exposes lifecycle only (a full snapshot is the last value an actor
424
449
  published, which only co-located actors observe), `subscribe` and `on` are
425
450
  inert (observation is co-location), and completion staleness for it is the
426
451
  owning runtime's responsibility. Persisting a handle from the referencing
427
- side throws — its state lives with the runtime that owns it. A restored handle keeps its
428
- persisted address verbatim — the owning runtime's identity for the child.
452
+ side throws, because its state lives with the runtime that owns it. A restored handle keeps its
453
+ persisted address verbatim as the owning runtime's identity for the child.
429
454
 
430
455
  `durable.getActorRef(snapshot)` returns the root actor reference behind a
431
456
  snapshot this execution produced, for addressing and inspection.
432
457
  `durable.getActorRef(snapshot, address)` resolves a logical address against
433
- the snapshot's live actor tree instead — the tool for hosts whose mailbox
434
- stores addresses as strings and must deliver to the actor behind one —
435
- returning `undefined` when no live actor has that address (a timer firing
458
+ the snapshot's live actor tree instead. Use it in hosts whose mailbox
459
+ stores addresses as strings and must deliver to the actor behind one. It returns `undefined` when no live actor has that address (a timer firing
436
460
  for an actor that already completed).
437
461
 
438
462
  `createDurable(logic, adapter, { inspect })` observes the execution's
439
- inspection events — the `@xstate.actor` / `@xstate.transition` protocol,
463
+ inspection events (the `@xstate.actor` / `@xstate.transition` protocol)
440
464
  across the whole live actor tree, including transitions computed by the pure
441
465
  path. This is host observability, not part of the durable contract: use it
442
466
  for operation logs, tracing and test instrumentation, and keep the adapter
@@ -447,7 +471,7 @@ itself pure physics.
447
471
  When the machine declares a runtime validator, an external event whose payload
448
472
  fails its schema is rejected at the boundary: `transition()` returns the
449
473
  snapshot unchanged together with a `@xstate.deadLetter` effect, and never
450
- throws. Replay stays total — replaying a poisoned queued event produces the
474
+ throws. Replay stays total: replaying a poisoned queued event produces the
451
475
  same unchanged snapshot and the same rejection effect every time.
452
476
 
453
477
  The effect executes through the `deadLetter` runtime operation, so a host
@@ -491,8 +515,8 @@ the effects and the runtime installation. `createActor` is the built-in
491
515
  in-process runtime over the same pure relations; `createDurable` is the
492
516
  journaled one.
493
517
 
494
- Effects are plain objects — each has a serializable `descriptor` and an
495
- `exec(runtime)` method — so a host may take `[snapshot, effects]` from the
518
+ Effects are plain objects. Each has a serializable `descriptor` and an
519
+ `exec(runtime)` method, so a host may take `[snapshot, effects]` from the
496
520
  pure APIs and execute them entirely its own way. Dropping `executeEffects`
497
521
  means providing its four services yourself: transitive settlement (an
498
522
  effect's operations trigger further operations; "executed" must mean the
package/docs/errors.md CHANGED
@@ -551,7 +551,7 @@ Unable to send event to actor '{address}' (incarnation '{incarnation}'): no live
551
551
 
552
552
  Values: the address, and the incarnation when the reference has one.
553
553
 
554
- A serialized actor reference (`{ $actor }`) points to an actor that is not running in this system, or to an earlier incarnation of it. Check that the actor is running, or refresh the stored reference.
554
+ A serialized actor reference (`{ '@actor' }`) points to an actor that is not running in this system, or to an earlier incarnation of it. Check that the actor is running, or refresh the stored reference.
555
555
 
556
556
  ## E51
557
557
 
@@ -616,7 +616,7 @@ Values: the address.
616
616
  ## E57
617
617
 
618
618
  ```text
619
- State key "{key}" contains "." — nest the states or escape it as "{escapedKey}".
619
+ State key "{key}" contains ".". Nest the states or escape it as "{escapedKey}".
620
620
  ```
621
621
 
622
622
  Values: the state key.
package/docs/examples.md CHANGED
@@ -9,61 +9,61 @@ These examples use XState v6 syntax: transition functions that return `{ target,
9
9
 
10
10
  ## Start here
11
11
 
12
- - [counter](https://github.com/statelyai/xstate/tree/next/examples/counter) — the smallest useful machine: `increment` and `decrement` transitions updating one context value, wired to plain DOM.
13
- - [toggle](https://github.com/statelyai/xstate/tree/next/examples/toggle) — two states and one event, with no context at all.
14
- - [fetch](https://github.com/statelyai/xstate/tree/next/examples/fetch) — `idle` / `loading` / `success` / `failure` around `createAsyncLogic(...)` invoked by the `loading` state.
12
+ - [counter](https://github.com/statelyai/xstate/tree/next/examples/counter): the smallest useful machine: `increment` and `decrement` transitions updating one context value, wired to plain DOM.
13
+ - [toggle](https://github.com/statelyai/xstate/tree/next/examples/toggle): two states and one event, with no context at all.
14
+ - [fetch](https://github.com/statelyai/xstate/tree/next/examples/fetch): `idle` / `loading` / `success` / `failure` around `createAsyncLogic(...)` invoked by the `loading` state.
15
15
 
16
16
  ## UI patterns
17
17
 
18
- - [stopwatch](https://github.com/statelyai/xstate/tree/next/examples/stopwatch) — `createCallbackLogic(...)` invoked by the `running` state sends a tick every 10ms; leaving the state clears the interval.
19
- - [timer](https://github.com/statelyai/xstate/tree/next/examples/timer) — the same ticking pattern, plus events that adjust the duration while the timer is stopped.
20
- - [tiles](https://github.com/statelyai/xstate/tree/next/examples/tiles) — a sliding tile puzzle with named guards deciding whether two tiles are adjacent.
21
- - [snake-react](https://github.com/statelyai/xstate/tree/next/examples/snake-react) — a game loop as an invoked callback actor, with the whole board in context.
22
- - [tic-tac-toe-react](https://github.com/statelyai/xstate/tree/next/examples/tic-tac-toe-react) — turn taking and win detection in a `playing` state, with a `RESET` transition on the machine root.
23
- - [todomvc-react](https://github.com/statelyai/xstate/tree/next/examples/todomvc-react) — the TodoMVC spec, with the todo list and the active filter held in context.
24
- - [friends-list-react](https://github.com/statelyai/xstate/tree/next/examples/friends-list-react) — a parent machine that spawns one child actor per friend and keeps their refs in context.
25
- - [trivia-game-example](https://github.com/statelyai/xstate/tree/next/examples/trivia-game-example) — a quiz whose navigation actions are supplied with `machine.provide(...)` so the machine stays free of router imports.
18
+ - [stopwatch](https://github.com/statelyai/xstate/tree/next/examples/stopwatch): `createCallbackLogic(...)` invoked by the `running` state sends a tick every 10ms; leaving the state clears the interval.
19
+ - [timer](https://github.com/statelyai/xstate/tree/next/examples/timer): the same ticking pattern, plus events that adjust the duration while the timer is stopped.
20
+ - [tiles](https://github.com/statelyai/xstate/tree/next/examples/tiles): a sliding tile puzzle with named guards deciding whether two tiles are adjacent.
21
+ - [snake-react](https://github.com/statelyai/xstate/tree/next/examples/snake-react): a game loop as an invoked callback actor, with the whole board in context.
22
+ - [tic-tac-toe-react](https://github.com/statelyai/xstate/tree/next/examples/tic-tac-toe-react): turn taking and win detection in a `playing` state, with a `RESET` transition on the machine root.
23
+ - [todomvc-react](https://github.com/statelyai/xstate/tree/next/examples/todomvc-react): the TodoMVC spec, with the todo list and the active filter held in context.
24
+ - [friends-list-react](https://github.com/statelyai/xstate/tree/next/examples/friends-list-react): a parent machine that spawns one child actor per friend and keeps their refs in context.
25
+ - [trivia-game-example](https://github.com/statelyai/xstate/tree/next/examples/trivia-game-example): a quiz whose navigation actions are supplied with `machine.provide(...)` so the machine stays free of router imports.
26
26
 
27
27
  ## 7GUIs
28
28
 
29
29
  Ports of the [7GUIs](https://eugenkiss.github.io/7guis/) benchmark tasks, in React and Vue:
30
30
 
31
- - [7guis-counter-react](https://github.com/statelyai/xstate/tree/next/examples/7guis-counter-react) and [7guis-1-counter-vue](https://github.com/statelyai/xstate/tree/next/examples/7guis-1-counter-vue) — the counter task.
32
- - [7guis-temperature-react](https://github.com/statelyai/xstate/tree/next/examples/7guis-temperature-react) and [7guis-2-temperature-vue](https://github.com/statelyai/xstate/tree/next/examples/7guis-2-temperature-vue) — two fields that convert into each other without looping.
33
- - [7guis-flight-booker-react](https://github.com/statelyai/xstate/tree/next/examples/7guis-flight-booker-react) — trip type, date validation and a booking request as invoked async logic.
31
+ - [7guis-counter-react](https://github.com/statelyai/xstate/tree/next/examples/7guis-counter-react) and [7guis-1-counter-vue](https://github.com/statelyai/xstate/tree/next/examples/7guis-1-counter-vue): the counter task.
32
+ - [7guis-temperature-react](https://github.com/statelyai/xstate/tree/next/examples/7guis-temperature-react) and [7guis-2-temperature-vue](https://github.com/statelyai/xstate/tree/next/examples/7guis-2-temperature-vue): two fields that convert into each other without looping.
33
+ - [7guis-flight-booker-react](https://github.com/statelyai/xstate/tree/next/examples/7guis-flight-booker-react): trip type, date validation and a booking request as invoked async logic.
34
34
 
35
35
  ## XState Store
36
36
 
37
37
  [XState Store](choose-xstate.md) examples, for state that needs events but not states:
38
38
 
39
- - [store-counter-react](https://github.com/statelyai/xstate/tree/next/examples/store-counter-react) — a module-level store read with `useSelector`.
40
- - [local-store-counter-react](https://github.com/statelyai/xstate/tree/next/examples/local-store-counter-react) — the same store created per component with `useStore`.
41
- - [store-tic-tac-toe](https://github.com/statelyai/xstate/tree/next/examples/store-tic-tac-toe) — a full game in one store, including derived win and draw outcomes.
39
+ - [store-counter-react](https://github.com/statelyai/xstate/tree/next/examples/store-counter-react): a module-level store read with `useSelector`.
40
+ - [local-store-counter-react](https://github.com/statelyai/xstate/tree/next/examples/local-store-counter-react): the same store created per component with `useStore`.
41
+ - [store-tic-tac-toe](https://github.com/statelyai/xstate/tree/next/examples/store-tic-tac-toe): a full game in one store, including derived win and draw outcomes.
42
42
 
43
43
  ## Backends and persistence
44
44
 
45
- - [express-workflow](https://github.com/statelyai/xstate/tree/next/examples/express-workflow) — an Express API that starts a workflow, restores it from a persisted snapshot on each request, sends the event and persists the result. The pattern behind [backend workflows](backend-workflows.md).
46
- - [persisted-donut-maker](https://github.com/statelyai/xstate/tree/next/examples/persisted-donut-maker) — a multi-step recipe machine whose snapshot is written to disk and restored on the next run.
47
- - [mongodb-persisted-state](https://github.com/statelyai/xstate/tree/next/examples/mongodb-persisted-state) — the same machine with MongoDB as the snapshot store, plus a queue that serializes writes.
48
- - [mongodb-credit-check-api](https://github.com/statelyai/xstate/tree/next/examples/mongodb-credit-check-api) — a credit check API where each external service is `createAsyncLogic(...)` with a Zod input schema.
45
+ - [express-workflow](https://github.com/statelyai/xstate/tree/next/examples/express-workflow): an Express API that starts a workflow, restores it from a persisted snapshot on each request, sends the event and persists the result. The pattern behind [backend workflows](backend-workflows.md).
46
+ - [persisted-donut-maker](https://github.com/statelyai/xstate/tree/next/examples/persisted-donut-maker): a multi-step recipe machine whose snapshot is written to disk and restored on the next run.
47
+ - [mongodb-persisted-state](https://github.com/statelyai/xstate/tree/next/examples/mongodb-persisted-state): the same machine with MongoDB as the snapshot store, plus a queue that serializes writes.
48
+ - [mongodb-credit-check-api](https://github.com/statelyai/xstate/tree/next/examples/mongodb-credit-check-api): a credit check API where each external service is `createAsyncLogic(...)` with a Zod input schema.
49
49
 
50
50
  ## Serverless Workflow ports
51
51
 
52
52
  Ports of the [Serverless Workflow specification](https://github.com/serverlessworkflow/specification/tree/main/examples) examples, each a single runnable `main.ts`. They are the densest collection of workflow shapes in the repository. Use them to see how a particular orchestration pattern looks as a statechart.
53
53
 
54
- - [workflow-hello](https://github.com/statelyai/xstate/tree/next/examples/workflow-hello) — the minimum: one final state with `output`.
55
- - [workflow-async-function](https://github.com/statelyai/xstate/tree/next/examples/workflow-async-function) — one invoked async function with typed input taken from the workflow's own input.
56
- - [workflow-parallel](https://github.com/statelyai/xstate/tree/next/examples/workflow-parallel) — two branches running at once, joining when both are done.
57
- - [workflow-event-based](https://github.com/statelyai/xstate/tree/next/examples/workflow-event-based) — waiting for one of several events, with a named delay as the deadline.
58
- - [workflow-check-inbox](https://github.com/statelyai/xstate/tree/next/examples/workflow-check-inbox) — a root-level callback actor that sends a `reminder` event on an interval, driving a check-then-notify cycle back to `Idle`.
59
- - [workflow-credit-check](https://github.com/statelyai/xstate/tree/next/examples/workflow-credit-check) and [workflow-applicant-request](https://github.com/statelyai/xstate/tree/next/examples/workflow-applicant-request) — decision workflows that branch on the result of an invoked service.
60
- - [workflow-book-lending](https://github.com/statelyai/xstate/tree/next/examples/workflow-book-lending) — a long-running flow that waits on human events between service calls, with a delayed transition for the wait and a nested checkout region.
61
- - [workflow-provision-orders](https://github.com/statelyai/xstate/tree/next/examples/workflow-provision-orders) — order provisioning with a nested `Exception` state whose children handle each kind of missing field.
62
- - [workflow-purchase-order-deadline](https://github.com/statelyai/xstate/tree/next/examples/workflow-purchase-order-deadline) — a deadline on the whole order, with retries handled by an external retry policy library.
63
- - [workflow-car-vitals](https://github.com/statelyai/xstate/tree/next/examples/workflow-car-vitals) — several checks invoked together while the car is on, re-run on a delayed transition.
64
- - [workflow-accumulate-room-readings](https://github.com/statelyai/xstate/tree/next/examples/workflow-accumulate-room-readings) — readings collected into context until a delayed transition ends the window and reports them.
65
- - [workflow-monitor-patient](https://github.com/statelyai/xstate/tree/next/examples/workflow-monitor-patient) — a single monitoring state whose event handlers react to each kind of vital sign alert.
66
- - [workflow-media-scanner](https://github.com/statelyai/xstate/tree/next/examples/workflow-media-scanner) — a file scanning pipeline split across modules.
54
+ - [workflow-hello](https://github.com/statelyai/xstate/tree/next/examples/workflow-hello): the minimum: one final state with `output`.
55
+ - [workflow-async-function](https://github.com/statelyai/xstate/tree/next/examples/workflow-async-function): one invoked async function with typed input taken from the workflow's own input.
56
+ - [workflow-parallel](https://github.com/statelyai/xstate/tree/next/examples/workflow-parallel): two branches running at once, joining when both are done.
57
+ - [workflow-event-based](https://github.com/statelyai/xstate/tree/next/examples/workflow-event-based): waiting for one of several events, with a named delay as the deadline.
58
+ - [workflow-check-inbox](https://github.com/statelyai/xstate/tree/next/examples/workflow-check-inbox): a root-level callback actor that sends a `reminder` event on an interval, driving a check-then-notify cycle back to `Idle`.
59
+ - [workflow-credit-check](https://github.com/statelyai/xstate/tree/next/examples/workflow-credit-check) and [workflow-applicant-request](https://github.com/statelyai/xstate/tree/next/examples/workflow-applicant-request): decision workflows that branch on the result of an invoked service.
60
+ - [workflow-book-lending](https://github.com/statelyai/xstate/tree/next/examples/workflow-book-lending): a long-running flow that waits on human events between service calls, with a delayed transition for the wait and a nested checkout region.
61
+ - [workflow-provision-orders](https://github.com/statelyai/xstate/tree/next/examples/workflow-provision-orders): order provisioning with a nested `Exception` state whose children handle each kind of missing field.
62
+ - [workflow-purchase-order-deadline](https://github.com/statelyai/xstate/tree/next/examples/workflow-purchase-order-deadline): a deadline on the whole order, with retries handled by an external retry policy library.
63
+ - [workflow-car-vitals](https://github.com/statelyai/xstate/tree/next/examples/workflow-car-vitals): several checks invoked together while the car is on, re-run on a delayed transition.
64
+ - [workflow-accumulate-room-readings](https://github.com/statelyai/xstate/tree/next/examples/workflow-accumulate-room-readings): readings collected into context until a delayed transition ends the window and reports them.
65
+ - [workflow-monitor-patient](https://github.com/statelyai/xstate/tree/next/examples/workflow-monitor-patient): a single monitoring state whose event handlers react to each kind of vital sign alert.
66
+ - [workflow-media-scanner](https://github.com/statelyai/xstate/tree/next/examples/workflow-media-scanner): a file scanning pipeline split across modules.
67
67
 
68
68
  The remaining `workflow-*` directories cover the rest of the specification's examples: [workflow-async-subflow](https://github.com/statelyai/xstate/tree/next/examples/workflow-async-subflow), [workflow-car-auction-bids](https://github.com/statelyai/xstate/tree/next/examples/workflow-car-auction-bids), [workflow-event-based-service](https://github.com/statelyai/xstate/tree/next/examples/workflow-event-based-service), [workflow-event-greeting](https://github.com/statelyai/xstate/tree/next/examples/workflow-event-greeting), [workflow-filling-water](https://github.com/statelyai/xstate/tree/next/examples/workflow-filling-water), [workflow-finalize-college-app](https://github.com/statelyai/xstate/tree/next/examples/workflow-finalize-college-app), [workflow-greeting](https://github.com/statelyai/xstate/tree/next/examples/workflow-greeting), [workflow-math-problem](https://github.com/statelyai/xstate/tree/next/examples/workflow-math-problem), [workflow-monitor-job](https://github.com/statelyai/xstate/tree/next/examples/workflow-monitor-job), [workflow-new-patient-onboarding](https://github.com/statelyai/xstate/tree/next/examples/workflow-new-patient-onboarding), [workflow-reusing-functions](https://github.com/statelyai/xstate/tree/next/examples/workflow-reusing-functions) and [workflow-send-cloudevent](https://github.com/statelyai/xstate/tree/next/examples/workflow-send-cloudevent).
69
69
 
package/docs/fsm.md CHANGED
@@ -153,7 +153,8 @@ from `xstate` adds the runtime to your bundle.
153
153
  - A transition can be a state name, `{ target, context }`, or a function that
154
154
  receives `{ context, event }` and returns `{ target, context }`. Return
155
155
  `undefined` from a function for no transition.
156
- - Context updates are shallow patches.
156
+ - Only own entries in `states` and `on` define transitions. A state inherited from the `states` prototype has no outgoing transitions, like an undeclared state. Transition targets are not validated.
157
+ - Context updates are shallow patches. A patch adds its own enumerable string and symbol keys even when a new value is `undefined` or matches an inherited value. If the target is unchanged, a patch whose keys already exist with `Object.is`-equal values keeps the current snapshot.
157
158
  - `types<T>()` adds types without runtime validation. Setup schema values are
158
159
  ignored by the FSM runtime; state `schemas.input` is accepted for upgrade
159
160
  compatibility but no input is consumed by this pure API.
package/docs/guards.md CHANGED
@@ -38,7 +38,7 @@ Do not fetch data in a guard. Invoke an actor, store the result in context, then
38
38
 
39
39
  ## Named guards
40
40
 
41
- Define reusable guards on `setup(...)` or the machine's `guards`. A named guard is a plain predicate function: it receives only the arguments you pass it and knows nothing about the machine. The caller decides what the guard sees — pass values from `context` or the event explicitly.
41
+ Define reusable guards on `setup(...)` or the machine's `guards`. A named guard is a plain predicate function: it receives only the arguments you pass it and knows nothing about the machine. The caller decides what the guard sees: pass values from `context` or the event explicitly.
42
42
 
43
43
  ```ts
44
44
  const orderMachine = createMachine({
package/docs/index.md CHANGED
@@ -1,4 +1,4 @@
1
- # XState 6.0.0-beta.0
1
+ # XState 6.0.0-beta.2
2
2
 
3
3
  These docs match the installed xstate package. Start with the [quick start](quick-start.md) or the [agent guide](agents.md).
4
4
  For differences from v5, read the [migration guide](xstate-v5-to-v6.md).
package/docs/invoke.md CHANGED
@@ -112,7 +112,7 @@ const machine = s.createMachine({
112
112
 
113
113
  Keep the helper call inline: its enclosing state supplies narrowed context, ancestor context, state input and transition targets. In this example, `context.userId` is `string` and `event.output` is `{ name: string }`.
114
114
 
115
- For an async `src`, `schemas.input` types the actor input, `schemas.output` checks the function's resolved value and types `onDone.event.output`, and `schemas.error` types `onError.event.error`. Omit the output schema to infer output from the async return value. The function receives `{ input, signal, self, system }` and the async enqueue object, including `emit` and `step`, just like `createAsyncLogic`. Its input comes from the invoke's static value or input mapper; parent context stays in the mapper and transition handlers.
115
+ For an async `src`, `schemas.input` types the actor input, `schemas.output` checks the function's resolved value and types `onDone.event.output`, and `schemas.error` types `onError.event.error`. Omit the output schema to infer output from the async return value. The function receives `{ input, signal, self, system }` and the async enqueue object, including `emit` and `step`, as `createAsyncLogic` does. Its input comes from the invoke's static value or input mapper; parent context stays in the mapper and transition handlers.
116
116
 
117
117
  You can also pass an existing actor logic value, including machine and callback logic:
118
118
 
@@ -124,6 +124,14 @@ invoke: s.createInvoke({
124
124
  })
125
125
  ```
126
126
 
127
+ A bare inline invoke such as `{ src: loadUserLogic, onDone }` cannot correlate
128
+ that specific logic with its completion output. With no registered actor
129
+ sources, its `onDone.event.output` is `unknown`, so TypeScript requires
130
+ narrowing before it can be used. For a typed output, use
131
+ `s.createInvoke({ src: loadUserLogic, onDone })` as above, or register the logic
132
+ in `setup({ actors: { loadUserLogic } })` and invoke it with
133
+ `src: 'loadUserLogic'`. Prefer one of these typed forms when generating code.
134
+
127
135
  For a logic value, its own schemas determine input, output, error and snapshot types. Declare schemas on that logic rather than repeating them on the invoke.
128
136
 
129
137
  The helper requires `input` when the source's input type excludes `undefined`. Pass a static value or a mapper returning that input type. Logic that accepts `undefined` can omit `input`.
@@ -306,7 +314,31 @@ Invoke also is not the right tool when the child must outlive the state, or when
306
314
 
307
315
  Provide named actor logic through `setup({ actors })` or `createMachine({ actors })`. XState correlates `src` with `input`, so a static or mapped input is checked against that logic's input type, and `onDone` sees the output type of that logic.
308
316
 
309
- Declare child ids with `schemas.children` to type `children` and to check that each `id` receives compatible logic:
317
+ For a literal invoke `id` and a named or inline actor source, XState infers the
318
+ child reference in `snapshot.children`. The child may be absent when the invoke
319
+ state is inactive, so check for `undefined` before sending an event:
320
+
321
+ ```ts
322
+ import { createActor, setup, types } from 'xstate';
323
+
324
+ const paymentMachine = setup({
325
+ schemas: { events: { retry: types<{}>() } }
326
+ }).createMachine({ initial: 'idle', states: { idle: {} } });
327
+
328
+ const parent = setup({ actors: { paymentMachine } }).createMachine({
329
+ invoke: { id: 'payment', src: 'paymentMachine' }
330
+ });
331
+
332
+ const payment = createActor(parent).getSnapshot().children.payment;
333
+ payment?.send({ type: 'retry' });
334
+ ```
335
+
336
+ Declare child ids with `schemas.children` when the child contract must also be
337
+ available inside machine callbacks. This also checks that each declared `id`
338
+ receives compatible logic. For dynamic IDs, keep the returned child reference
339
+ from `enq.spawn(...)` in context instead of indexing `snapshot.children` with
340
+ an arbitrary string. Spawned children have a separately managed lifetime; see
341
+ [Spawn](spawn.md):
310
342
 
311
343
  ```ts
312
344
  setup({}).createMachine({
@@ -25,6 +25,12 @@ const restored = restoreActor(
25
25
  ).start();
26
26
  ```
27
27
 
28
+ In Vue, keep a machine passed to `restoreActor` out of deep reactive state.
29
+ Store it in `shallowRef(machine)` or create it with `markRaw(machine)`. XState
30
+ also accepts a reactive Proxy around a machine, but a raw machine avoids
31
+ wrapping its state nodes and is the recommended way to hold actor logic in Vue.
32
+ See [Vue input and restored snapshots](https://stately.ai/docs/xstate/v6/vue/input-and-snapshots).
33
+
28
34
  Pass `system` (a `createSystem()` result) to restore the actor into that system.
29
35
 
30
36
  A restored snapshot is opaque: restoring runs no transitions and re-executes no actions. `always` transitions and choice states in the restored configuration are not re-evaluated until the next event, even if their guards would now pass. Development builds warn when the restored state has eventless transitions.
@@ -47,11 +53,11 @@ By default a persisted snapshot embeds each child's persisted state, producing a
47
53
  const persisted = actor.getPersistedSnapshot({ embedChildren: false });
48
54
  ```
49
55
 
50
- The option applies to the whole tree, not to a single placement boundary. Persisting by address requires each child to have a registered source key (a string `src`); `spawn(actors.worker)`, `enq.spawn(actors.worker)` and `enq.spawn('worker')` retain that key, while inline actor logic cannot be referenced by address. The string form makes the exact identity explicit when aliases share logic. Restoring an address-only child produces a location-transparent handle: sends to it route through the [system runtime](durable-execution.md), and its snapshot exposes lifecycle only, since a full snapshot is the last value an actor published and only co-located actors observe it. Install the system runtime before sending to a restored handle — without one, there is no route to the actor it references.
56
+ The option applies to the whole tree, not to a single placement boundary. Persisting by address requires each child to have a registered source key (a string `src`); `spawn(actors.worker)`, `enq.spawn(actors.worker)` and `enq.spawn('worker')` retain that key, while inline actor logic cannot be referenced by address. The string form makes the exact identity explicit when aliases share logic. Restoring an address-only child produces a location-transparent handle: sends to it route through the [system runtime](durable-execution.md), and its snapshot exposes lifecycle only, since a full snapshot is the last value an actor published and only co-located actors observe it. Install the system runtime before sending to a restored handle. Without one, there is no route to the actor it references.
51
57
 
52
58
  Persisted children changed shape in v6: each entry carries an `address` field and either an embedded `snapshot` or a `remote: true` marker. A remote entry may also carry an opaque `incarnation` token, round-tripped verbatim: XState never stamps one, but a host that does gets stale-completion protection on the referencing side and the token on journaled `sendTo` descriptors. Snapshots persisted by v5 or by v6 alphas use other shapes and are not read on restore; discard them or convert them before restoring. See the [migration guide](xstate-v5-to-v6.md#29-names-changed-during-the-v6-prereleases).
53
59
 
54
- A timer persisted from a running actor carries its wall-clock start (`scheduledAt`), and restoring the snapshot schedules the remaining time toward the original deadline — a timer past due fires immediately. Snapshots produced only by pure transitions carry no timestamp, so restoring one restarts each timer with its declared delay. A [durable execution](durable-execution.md) records timer starts when scheduling effects succeed; await `executeEffects()` before persisting its checkpoint to preserve deadlines. A custom adapter clock must use the same time origin when restoring.
60
+ A timer persisted from a running actor carries its wall-clock start (`scheduledAt`), and restoring the snapshot schedules the remaining time toward the original deadline. A timer past due fires immediately. Snapshots produced only by pure transitions carry no timestamp, so restoring one restarts each timer with its declared delay. A [durable execution](durable-execution.md) records timer starts when scheduling effects succeed; await `executeEffects()` before persisting its checkpoint to preserve deadlines. A custom adapter clock must use the same time origin when restoring.
55
61
 
56
62
  For a host-driven durable loop, use `execution.restore(persistedSnapshot)` from
57
63
  `xstate/durable`, then `execution.executeEffects(effects)` to resume embedded
@@ -59,13 +65,13 @@ children and pending timers. Persist `execution.nextTransitionIndex` with the
59
65
  checkpoint and pass it to the next execution's adapter. See
60
66
  [durable checkpoint restoration](durable-execution.md).
61
67
 
62
- An actor's address is the `/`-joined path of actor ids from the root, such as `order/worker:0`. It is stable across persistence and restore, unlike `sessionId`, which identifies one incarnation. Generated child ids are recorded in each snapshot's `_nextActorIds`, so restored actors keep numbering where they left off.
68
+ An actor's address is the `/`-joined path of actor ids from the root, such as `order/worker:0`. It is stable across persistence and restore, unlike `sessionId`, which identifies one incarnation. Generated child ids are recorded in each snapshot's `nextActorIds`, so restored actors keep numbering where they left off.
63
69
 
64
70
  ## Compatibility
65
71
 
66
72
  A persisted snapshot is a plain JSON-shaped object. Restoring one is lenient: XState reads the fields it knows and does not check a format marker.
67
73
 
68
- These envelope fields are stable across 6.x releases: `status`, `value`, `context`, `output`, `error`, `historyValue`, `stateInputs`, `children`, `timers` and `machine`. The [persisted snapshot JSON Schema](https://github.com/statelyai/xstate/blob/next/packages/core/src/persistedSnapshot.schema.json) (draft 2020-12) describes them. Nested machine children carry their own envelope in `children[id].snapshot`. A child entry's `src` is the registered source key the child restores from. A child invoked with inline logic has no `src`; it restores through the invoke definition with its id among the restored active states, and restoring fails when no active state, or more than one, invokes that id. Fields prefixed with `_`, such as `_nextActorIds`, are private. They round-trip verbatim; do not read or write them.
74
+ These envelope fields are stable across 6.x releases: `status`, `value`, `context`, `output`, `error`, `historyValue`, `stateInputs`, `children`, `timers` and `machine`. The [persisted snapshot JSON Schema](https://github.com/statelyai/xstate/blob/next/packages/core/src/persistedSnapshot.schema.json) (draft 2020-12) describes them. Nested machine children carry their own envelope in `children[id].snapshot`. A child entry's `src` is the registered source key the child restores from. A child invoked with inline logic has no `src`; it restores through the invoke definition with its id among the restored active states, and restoring fails when no active state, or more than one, invokes that id. `nextTimerId` and `nextActorIds` are private. They round-trip verbatim; do not read or write them. No field name in the envelope starts with `$` or `_`, so it can be stored in document databases such as Convex and MongoDB that reserve those prefixes.
69
75
 
70
76
  `timers` is a list of pending timers in scheduling order. A delayed `enq.raise(...)` or `enq.sendTo(...)` entry carries its `id`, or a `seq` number when scheduled without one. Built-in `after` and `timeout` timers have neither; their `event` (`xstate.after`, `xstate.timeout.state` or `xstate.timeout.actor`) identifies them.
71
77
 
@@ -95,15 +101,15 @@ In development builds, `getPersistedSnapshot()` warns once per call with the pat
95
101
 
96
102
  `Date` values are not reported. `JSON.stringify` writes them as ISO strings, and `JSON.parse` does not turn them back into `Date` objects. Convert them on restore if your context needs `Date` instances.
97
103
 
98
- Actor refs in `context` persist as `{ $actor: address }` and are not reported. A ref that names one of the snapshot's children restores to that child.
104
+ Actor refs in `context` persist as `{ '@actor': address }` and are not reported. A ref that names one of the snapshot's children restores to that child.
99
105
 
100
- `$actor` is a reserved key in persisted context. On restore, any object in `context` whose keys are exactly `$actor` (and optionally `incarnation`) is read as a serialized actor ref: without an `incarnation`, it becomes the live child at that address, if there is one. Objects with other keys alongside `$actor` stay plain data. Do not store application data as an object with only an `$actor` key.
106
+ `@actor` is a reserved key in persisted context. On restore, any object in `context` whose keys are exactly `@actor` (and optionally `incarnation`) is read as a serialized actor ref: without an `incarnation`, it becomes the live child at that address, if there is one. Objects with other keys alongside `@actor` stay plain data. Do not store application data as an object with only an `@actor` key.
101
107
 
102
108
  ### Serialized actor refs
103
109
 
104
- An actor ref serializes with `JSON.stringify` as `{ $actor: address }` (the `SerializedActorRef` type, which also allows an optional string `incarnation`). This shape appears when a ref travels in an event payload, such as a `replyTo` field, and the event is persisted or crosses a process boundary. A serialized ref in `context` keeps its address across persistence and restore.
110
+ An actor ref serializes with `JSON.stringify` as `{ '@actor': address }` (the `SerializedActorRef` type, which also allows an optional string `incarnation`). This shape appears when a ref travels in an event payload, such as a `replyTo` field, and the event is persisted or crosses a process boundary. A serialized ref in `context` keeps its address across persistence and restore.
105
111
 
106
- A serialized ref is a valid `enq.sendTo(...)` target. The event is delivered to the live actor at the ref's `$actor` address, including an actor restored after the ref was serialized. If no live actor has that address, the event becomes a dead letter with reason `'missingTarget'`.
112
+ A serialized ref is a valid `enq.sendTo(...)` target. The event is delivered to the live actor at the ref's `@actor` address, including an actor restored after the ref was serialized. If no live actor has that address, the event becomes a dead letter with reason `'missingTarget'`.
107
113
 
108
114
  ```ts
109
115
  const server = createMachine({
@@ -154,8 +160,8 @@ const checkoutVersions = machineVersions([
154
160
  children: z.object({}),
155
161
  historyValue: z.object({}),
156
162
  timers: z.tuple([]),
157
- _nextActorIds: z.record(z.string(), z.number()).optional(),
158
- _nextTimerId: z.number(),
163
+ nextActorIds: z.record(z.string(), z.number()).optional(),
164
+ nextTimerId: z.number(),
159
165
  stateInputs: z.undefined().optional(),
160
166
  machine: z.object({
161
167
  id: z.literal('checkout'),