← stitchkitDocumentation

ADR 0100 — The agent store reducer owns runtime transitions

Context

ADR 0098 put eight aggregate operations behind AgentRuntimeStore. That kept the runtime ORM-neutral, but every durable adapter still had to reproduce the reference state machine: revisions, ownership, collisions, idempotency, coalescing, terminal mapping and contiguous compaction. Storing the whole AgentSnapshot as JSON avoided that reducer only by duplicating canonical message history already present in application tables.

Separate state and history callbacks are not enough. Independent reads can assemble a torn snapshot, and a state CAS followed by an unrelated history write can commit half a transition.

Decision

createAgentRuntimeStore(driver) owns all runtime transitions. A driver owns:

  • an adapter-created transaction(work) boundary;
  • state and history loads using the same opaque transaction token;
  • one version-checked state CAS;
  • application-row mapping for a typed history mutation;
  • a bounded, paged recoverable-run scan.

The original persistence shape stored version, runs and complete admission identities in AgentStoredState. ADR 0101 replaces that lifetime aggregate with a bounded head and normalized records while retaining this reducer ownership. Messages remain canonical application rows. A winning CAS and its history mutation execute inside the same transaction callback; an exception rolls both back. The driver contains no run-state switch, revision arithmetic, coalescing, terminal or collision policy.

createMemoryAgentRuntimeStore() is implemented through this same factory. Third-party adapters can execute the public black-box conformance helper from stitchkit/testing. The repository carries an executable Prisma/PostgreSQL reference fixture, but Prisma is not a runtime dependency, peer or public adapter commitment.

Admission delivery uses a canonical committed input and assigned run plus a separate pending assistant projection. It does not pretend that a pending UI placeholder is already an AgentMessage in history. Publisher callbacks remain post-commit, at-most-once notifications: exactly-once delivery still requires an application outbox in the same database transaction.

Recovery scans are bounded. Queued work may resume by default; an acquired run is skipped unless application policy supplies replay-safe or stale-owner evidence. Abandoning a run atomically materializes its terminal assistant row.

Consequences

  • Applications write transaction plumbing and row codecs, not a second engine.
  • State/history consistency is part of the driver contract and can be tested.
  • Existing stores must persist the full admission identity, not only runId.
  • Full snapshots are assembled only from one coherent transactional read; no second durable history blob is required.
  • Post-commit events remove projection rereads but do not claim outbox delivery.