Snapshot
The snapshot is the single read model for a live journey machine.
If you only inspect one value to understand what is true right now, inspect the snapshot.
Mental Model
Section titled “Mental Model”snapshot├─ currentStepId -> where the machine is now├─ history -> realized path + current pointer├─ context -> shared runtime data├─ visited -> whether each step was ever entered├─ status -> idled / running / completed / terminated└─ async -> loading phases and last async error by stepSnapshot Shape
Section titled “Snapshot Shape”type JourneySnapshot<TContext, TStepId extends string> = { currentStepId: TStepId; history: { timeline: readonly TStepId[]; index: number; }; context: TContext; visited: Record<TStepId, boolean>; status: "idled" | "running" | "completed" | "terminated"; async: JourneyAsyncState<TStepId>;};Field Guide
Section titled “Field Guide”| Field | Answers | Related docs |
|---|---|---|
currentStepId | Which step is active right now? | History |
history.timeline | Which realized path did the user actually take? | History |
history.index | Which timeline entry is considered “now”? | History |
context | What shared data do guards, transition updates, and UI read from? | Async Behavior |
visited | Which steps have ever been entered at least once? | History |
status | Is the machine idled, active, complete, or terminated? | Lifecycle |
async | Is async work in flight, and which step owns the last async error? | Async Behavior |
Treat the snapshot as a read model. Rendering, debugging, persistence, and selector subscriptions should all be able to explain themselves from this one object.
The value returned from getSnapshot() is immutable runtime output. Read it, derive from it, and discard it. Do not
try to mutate it in place.
Example Snapshot
Section titled “Example Snapshot”const snapshot = machine.getSnapshot();
const exampleSnapshot = { currentStepId: "payment", history: { timeline: ["start", "details", "payment"], index: 2 }, context: { isVip: false }, visited: { start: true, details: true, payment: true, review: false }, status: "running", async: { isLoading: false, byStep: { start: { phase: "idle", eventType: null, transitionId: null, error: null }, details: { phase: "idle", eventType: null, transitionId: null, error: null }, payment: { phase: "idle", eventType: null, transitionId: null, error: null }, review: { phase: "idle", eventType: null, transitionId: null, error: null } } }};Invariants You Can Trust
Section titled “Invariants You Can Trust”These stay true while a machine exists:
history.timeline.length >= 10 <= history.index < history.timeline.lengthcurrentStepId === history.timeline[history.index]
Those invariants are what make history navigation, snapshot selectors, and persistence safer to reason about.
How Snapshot Writes Happen
Section titled “How Snapshot Writes Happen”Different runtime actions update the snapshot for different reasons:
| Reason | Typical source | See implementation |
|---|---|---|
transition | step-to-step sends, terminal sends, headless goToStepById, declared goToStepById transitions | Send Pipeline, Navigation Commits |
navigation | goToPreviousStep(...), goToLastVisitedStep() | Navigation Commits |
async | guard loading, idle, or error updates | Async State |
context | updateContext(...) | Controls |
start | startJourney() | Controls |
reset | resetJourney() | Controls |
This is mainly visible to plugins and advanced instrumentation, but it is also a useful debugging frame: not every snapshot write means “a transition happened”.
Practical Notes
Section titled “Practical Notes”history.timelineis realized history, not authored step order.- A fresh machine snapshot is
idleduntilstartJourney()is called. - Pointer moves such as
goToPreviousStep(...)andgoToLastVisitedStep()do not rewritevisited. contextandasyncare immutable read branches in the returned snapshot. Change runtime state throughupdateContext(...), transition updates, or reset/start APIs instead of mutating the snapshot object.async.isLoadingis machine-wide, whileasync.byStep[stepId]gives you the step-level detail for UI.- Step definition metadata lives outside the snapshot. Read it through
machine.getStepMeta(stepId)when needed.
Useful Reads
Section titled “Useful Reads”const snapshot = machine.getSnapshot();
const currentStep = snapshot.currentStepId;const currentAsync = snapshot.async.byStep[currentStep];const atHistoryTail = snapshot.history.index === snapshot.history.timeline.length - 1;const canRenderNormally = snapshot.status === "running" && currentAsync.phase === "idle";Recommended Reading
Section titled “Recommended Reading”- Read Runtime Reference Overview for the section map.
- Read Lifecycle for the events that explain how this snapshot changed.
- Read Async Behavior for the
asyncbranch of the snapshot. - Read Timeline Navigation for
history,visited, and pointer behavior. - Read Runtime Queue and Navigation Commits if you want the implementation side.