Application code usually reads a known instance through its typed client. Operators need another boundary: committed facts across instances, statuses, versions, and unfinished work—without accidentally becoming another command path.
Inspection is a separate authority
StatechartInspect exposes seven projections: headCheckpoint, parkJournal,
historyCheckpoints, steps, outboxPending, instancesByStatus, and getVersion. They share
committed-as-of-read semantics and typed storage/format failures, but each answers a different
operational question.
The walkthrough begins with one Ticket coordinate, expands to several projections, then resolves
every retained fact back to the exact chart version and causal authority that produced it.
Read committed storage without mutating it
Inspection carries the provenance of every fact.
Operators need committed facts without accidentally becoming another command path. Inspection projects storage-owned heads, history, pending work, and exact chart geometry with enough provenance to interpret every row.
1 · Read the stored head without activating it
Inspection is storage-owned and read-only.
headCheckpoint reads the committed row for one chart and instance coordinate. It does not start a runtime, enter the mailbox, fold an event, or turn a terminal checkpoint back into live authority.
import { State, Statechart, States, Transitions } from "@motive/motive";
import { StatechartInspect } from "@motive/motive-storage";
import { Effect, Schema } from "effect";
const Close = Schema.TaggedStruct("Close", {});
export class Ticket extends State.Compound<Ticket>()("Ticket") {
static states = States.make(() => [Open, Closed]);
}
class Open extends State.Atomic<Open>()("Open") {
static transitions = Transitions.make(this, ({ on }) => [
on(Close, Closed),
]);
}
class Closed extends State.Atomic<Closed>()("Closed") {}
export const TicketChart = Statechart.make(Ticket);
const key = {
chartName: TicketChart.name,
instanceId: Statechart.id("ticket-42"),
};
export const inspectHead = Effect.gen(function* () {
const inspect = yield* StatechartInspect.StatechartInspect;
return yield* inspect.headCheckpoint(key);
});
2 · Keep current, history, and pending work distinct
Each projection answers one operational question.
The head names current committed truth, steps return retained macrosteps, and outboxPending reports unsettled intent. Reading any of them neither settles work nor reconstructs rows removed by retention.
import { State, Statechart, States, Transitions } from "@motive/motive";
import { StatechartInspect } from "@motive/motive-storage";
import { Effect, Schema } from "effect";
const Close = Schema.TaggedStruct("Close", {});
export class Ticket extends State.Compound<Ticket>()("Ticket") {
static states = States.make(() => [Open, Closed]);
}
class Open extends State.Atomic<Open>()("Open") {
static transitions = Transitions.make(this, ({ on }) => [
on(Close, Closed),
]);
}
class Closed extends State.Atomic<Closed>()("Closed") {}
export const TicketChart = Statechart.make(Ticket);
const key = {
chartName: TicketChart.name,
instanceId: Statechart.id("ticket-42"),
};
export const inspectWork = Effect.gen(function* () {
const inspect = yield* StatechartInspect.StatechartInspect;
return yield* Effect.all({
head: inspect.headCheckpoint(key),
pending: inspect.outboxPending(key),
steps: inspect.steps({ ...key, limit: 10 }),
});
});
3 · Carry enough provenance to interpret the rows
Chart hash ties facts to exact geometry.
Every head and step carries chart name, instance id, sequence, and chart hash. getVersion resolves that hash to registered geometry so an operator never explains stored facts with whichever source happens to be open now.
import { State, Statechart, States, Transitions } from "@motive/motive";
import { StatechartInspect } from "@motive/motive-storage";
import { Effect, Schema } from "effect";
const Close = Schema.TaggedStruct("Close", {});
export class Ticket extends State.Compound<Ticket>()("Ticket") {
static states = States.make(() => [Open, Closed]);
}
class Open extends State.Atomic<Open>()("Open") {
static transitions = Transitions.make(this, ({ on }) => [
on(Close, Closed),
]);
}
class Closed extends State.Atomic<Closed>()("Closed") {}
export const TicketChart = Statechart.make(Ticket);
const key = {
chartName: TicketChart.name,
instanceId: Statechart.id("ticket-42"),
};
export const inspectWithGeometry = Effect.gen(function* () {
const inspect = yield* StatechartInspect.StatechartInspect;
return yield* Effect.all({
head: inspect.headCheckpoint(key),
steps: inspect.steps({ ...key, limit: 10 }),
version: inspect.getVersion({ chartHash: TicketChart.chartHash }),
});
});
Inspection does not enter the mailbox
The first frame reads headCheckpoint from StatechartInspect. This service projects committed
storage directly: it does not activate the instance, fold a chart, send a command, or mutate the row
it returns.
The live diagram keeps the chart coordinate tangible; the exact source beside it is the
storage-owned read boundary. headCheckpoint returns an Option. None means storage has no
current row for that chart and instance at the time of this read—it is not permission to infer a
phase or birth the instance to find out.
Inspection does not require chart registration or engine admission. Its typed failures are storage, artifact-format, and—across a browser generation boundary—terminal generation protocol failures. Those are read-authority failures, not chart command errors.
Keep projections separate
The second frame reads three different projections:
headCheckpointreturns the latest committed checkpoint;stepsreturns retained committed macrosteps in ascending sequence order; andoutboxPendingreturns planned work still pending or delivering.
The remaining projections preserve the same separation. historyCheckpoints reads retained
checkpoint history; instancesByStatus enumerates one chart's current stored status;
parkJournal exposes the exact incident-backed Park record; and getVersion resolves a chart hash
to registered geometry.
Each operation observes storage as of its own read. Effect.all can issue several reads together,
but that does not turn them into one cross-projection transaction. Compare chart, instance, and
sequence coordinates rather than assuming their completion order made them atomic.
No inspection operation settles an outbox row, redrives work, rewrites history, or locks the future
against a later commit. A gap in steps is a retention fact, not proof that no event ever occurred
there.
Chart hash determines geometry
The final frame adds getVersion. Checkpoints and steps carry chart name, instance id, sequence, and
chart hash; the version row resolves that hash to the exact registered geometry used by those facts.
That distinction matters during a deployment. The currently open source file may define a newer chart with the same name. Explaining an older checkpoint against that geometry can produce a convincing but false story. Hash-qualified geometry keeps stored history tied to its own authority.
Operational status follows the same rule. faulted or parked describes the engine's ability to
continue safely; it does not rewrite the retained domain configuration into a made-up chart state.
Step provenance explains why this step exists
Every retained step stores StepProvenance once beside the immutable macrostep. Its causation is
total and engine-owned:
ExternalAdmissioncarries the stable admission identity derived for a caller's stimulus; andCommittedIntentpoints to the exact parent chart hash, ownership coordinate, sequence, and intent index that manufactured a later stimulus.
Optional application correlation travels along those causal edges as a flat JSON envelope without entering the deterministic fold. Correlation groups work for the application; causation explains the specific durable edge that produced this step. Neither should be reconstructed from log timing.
Effect tracing can project safe coordinates from this durable provenance into spans, but tracing is an observation of the authority—not its owner. Provenance, Causation & Tracing carries this chain into operational search and redaction policy.
Read Park as its own projection
A parked checkpoint still carries its last domain Snapshot, while parkJournal carries the exact
incident, replayable stimulus, Cause, timestamps, and append-only dispositions that explain the
operational stop. Combining them in presentation is useful; collapsing them into one invented chart
state is not.
The later Incidents & Parking guide owns replay, interrupt, redrive, and operator decision flow. This page establishes the prerequisite: inspection reads both authorities without performing any of those operations.
Change the world
- Read head and steps on either side of another commit and compare their sequence coordinates.
- Remove an old step through retention while keeping the latest head, then describe both facts.
- Deploy a new chart hash under the same chart name and resolve each checkpoint to its own version.
- Follow one
CommittedIntentbackward to the parent step and intent index that produced it.
Next, Inspect an Instance chooses between this storage-owned evidence and a chart-typed current read.