Skip to content
In development. Nothing in Store 6 is published yet.

Inspection and observability

Two kinds of observability

Mutation inspection separates journal-backed facts from best-effort signals:

SurfaceLifetimeUse
pending(key), pendingWrites(), deadLetters()Truthful snapshots reconstructed from the journal. With durable journal storage, they survive process restart.Reconciliation, recovery, and settlement decisions.
eventsIn-process mutation lifecycle telemetry with no replay and bounded buffering.Logging, diagnostics, and live UI hints.
poisonedIn-process exact-throwable reports with a replay window of 16.Diagnose a projector that violated its contract.
keyEventsIn-process core writer notifications with no replay and bounded buffering.Observe delegated Store writes, invalidations, and deletions.

The default in-memory journal does not survive process death. Install durable storage before depending on inspection after restart. See Journal storage.

Durable truth

pending(key)

pending(key) returns the current nonterminal intents for the key's terminal identity in durable client-sequence FIFO order. Alias traversal uses durable identity pairs only. Inspection never reconstructs a K, never calls the resolver, and therefore cannot fail because a canonical key is unresolvable.

pendingWrites()

pendingWrites() returns every nonterminal active intent across all durable identities in durable client-sequence order. It is a snapshot, not a subscription. Retired history never appears.

deadLetters()

deadLetters() returns only durably PARKED intents. Parking is legal only before a successful acknowledgement is durably recorded. Once parked, that client sequence never re-enters the executable FIFO. Retired history and post-acknowledgement work do not appear here.

The five public pending states

Five public states cover the six nonterminal durable execution phases:

Public stateDurable phase
PENDINGUNPREPARED or READY
INFLIGHTINFLIGHT
REFRESHINGREFRESH_REQUIRED
ADOPTINGACKED
APPLYING_EFFECTSEFFECTS_PENDING

A parked execution appears only in deadLetters(). A retired execution appears in neither pending inspection nor dead letters.

PendingIntent fields

FieldMeaning
namespaceVerbatim effective namespace.
canonicalIdVerbatim effective canonical id.
mutationIdOpaque public id assigned at enqueue.
mutatorIdRegistered mutator storage identity.
statePublic mapping of the current active execution phase.
attemptCompleted network attempts for the current generation.
createdAtEpochMillisDurable enqueue time in Unix epoch milliseconds.

The failure taxonomy

MutationFailureKind is an append-only classification with nine broad kinds:

KindMeaning
IDENTITYThe key resolver returned null, threw, or returned a mismatched identity pair.
CODECStored argument or value bytes could not be decoded, or the stored mutator is not registered.
PROJECTIONA registered projection function threw before transport.
PROTOCOLThe backend violated the acknowledgement, alias, or retirement-checkpoint protocol.
CONFLICTA precondition conflict ended the intent because policy threw or the repeat bound was reached.
TRANSPORTMutationServer.push or MutationServer.retire threw a non-cancellation failure.
ADOPTIONWriting a durable acknowledgement into Store failed.
EFFECTApplying a durable invalidation effect to its target failed.
PERSISTENCEA journal-storage operation failed.

MutationFailure is normalized and restart-safe. A raw StoreError or Throwable is never persisted or carried by it. Store removes stack-trace lines and remaining ISO control characters, then truncates at a Unicode code-point boundary. detail is at most 128 UTF-8 bytes and message is at most 1,024 UTF-8 bytes. occurredAtEpochMillis records Unix epoch milliseconds.

Advisory events

events is a read-only SharedFlow<MutationEvent> with replay 0, extra buffer capacity 64, and DROP_OLDEST overflow. Store emits with non-blocking tryEmit only. A new collector receives no history, restart replays nothing, and no event carries a raw Throwable or StoreError.

Intent-scoped events carry a mutationId and durable identity pair:

EventObservation
MutationEnqueuedThe intent durably entered the journal.
MutationAttemptedA transport invocation began after INFLIGHT became durable.
MutationConflictObservedThe backend reported a precondition conflict.
MutationAcknowledgedA successful acknowledgement became durable.
MutationAdoptedThe durable acknowledgement was adopted into Store.
MutationEffectAppliedAn invalidation effect reached APPLIED.
MutationEffectSkippedConflict server-wins moved an invalidation effect to SKIPPED.
MutationFailedA retryable failure retained an active intent instead of parking it.
MutationParkedThe intent durably parked and left the executable FIFO.
MutationRetiredThe intent retired and advanced the contiguous local high-water.

Checkpoint events are client-scoped and do not include a mutation identity or generation:

EventObservation
MutationCheckpointConfirmedA validated retirement-checkpoint receipt was persisted.
MutationCheckpointFailedA non-cancellation checkpoint transport, protocol, or persistence failure occurred without creating an intent-owned failure row.

Collect for a lifetime your application owns. This example stops after 30 seconds and treats the output only as diagnostics:

kotlin
withTimeoutOrNull(30_000L) {
    mutations.events.collect { event ->
        println(event::class.simpleName ?: "MutationEvent")
    }
}
// Dropped events are expected under pressure. Reconcile with durable inspection.

The poisoned flow

poisoned reports a projector throw as a PoisonedIntent containing the exact local Throwable. For the durable execution path, a projector throw also parks the row with a normalized PROJECTION failure. The exact throwable is never persisted and does not cross restart.

The flow replays up to 16 in-process reports and drops the oldest under pressure. For the documented projector case, fix the projector rather than treating the report as retry policy; see Authoring mutators.

KeyEvents

MutationStore.keyEvents republishes the delegated core Store's writer-event flow. The mutations facade returns null from runtime(), while keeping this advisory flow available as a public member.

KeyEvents is deliberately open rather than sealed. Its current variants are Written, Invalidated, and Deleted; retain an else branch when matching so a future minor-version variant does not break the consumer. Written covers fetch commits and write-handle adoption, Invalidated covers successful stale marks, and Deleted covers successful destructive removals. There is no Rekeyed variant. Canonical-key changes are observed through the stream swap and inspection described in Aliases and canonical rekeying.

Delivery is a best-effort hot flow with replay 0 and a buffer of 64 that drops the oldest event. It never completes, including after Store.close, so scope collection to an application lifecycle. Purge sweeps, nonresident watermark coverage, external source-of-truth changes, and superseded fetches emit no key event. The extension guide covers the open hierarchy in more detail.

Relationship to store-level telemetry

telemetry(...) in the mutation-store configure lambda installs StoreTelemetry on the delegated core Store. Its callbacks observe fetches, public serves, invalidations, and clears without participating in correctness. The mutations-owned events flow is a separate vocabulary for the journalled write lifecycle, while keyEvents remains the core writer-event stream.

Use Devtools and the inspector for shipped core telemetry sinks and live inspection. None of these advisory surfaces replaces mutation inspection.


Source recorded 2026-08-12 ·main@539614c0· pre-6.0.0-alpha01