feat(state): #98 v1 kernel contract, memory driver, conformance suite (PR-1) - #142
Conversation
…formance suite (#98 v1, PR-1) defineState({ schema, initial, events, reduce }) with the explicit lifetime taxonomy (request | process | workspace-durable | external), typed AgentStateError codes, monotonic revisions, exact-revision reads, idempotency-key replay/conflict, CAS, explicit versioned migrations, and polling change cursors. Ships behind the new ./state subpath so stateless artifacts include none of it; the volatile in-memory driver (never durable) fills the request/process lifetimes; createAgentStateHandle fills the reserved `state` slot on AgentRequestContext; the exported conformance suite is the bar every driver, including external ones, must pass.
🦋 Changeset detectedLatest commit: a9b787e The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a9b787e1ce
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…head verification, replay across migrations (#171) Codex review follow-ups on the state kernel and drivers, re-verified against the Effect-native internals from #154: - Both drivers consult the idempotency key before running the reducer (#142/#149 P1): a committed key replays its stored result even when the reducer would now fail against the current head. Payload validation and canonicalization moved ahead of the key lookup; the reducer runs only on the append path. - Committed results replay across migrations (#142 P1): every journal record stores its post-commit state (event rows too, in the sqlite driver's existing nullable column), and migrations run the same chain over the stored results, so replay no longer depends on exact-revision history that migrations rebase. Legacy event rows without a stored state fall back to journal replay. - The sqlite driver verifies storage on open (#149 P1): journal continuity via expectConsistentJournal (a deleted intermediate row fails closed) and the materialized head against journal replay (a schema-valid hand-edited head fails closed); with a pending migration the head is checked against the last stored post-commit state instead. - sanitizedFileName hashes the complete definition id (sha-256) instead of hex-encoding its first six bytes (#149 P2), so ids sharing a sanitized prefix get isolated database files. - isJsonSafe rejects sparse arrays (#142 P2): holes no longer canonicalize like dense arrays under permissive schemas. The conformance suite pins the corrected semantics for both drivers: replay-before-reduce, replay across migrations, and prefix-colliding id isolation; sqlite corruption tests cover the two new fail-closed opens.
Summary
Wave 2 Lane B, PR 1 of 2 for the #98 v1 state kernel (plan #107 §6, G3 recorded).
@agent-bundle/runtime/statesubpath:defineState({ schema, initial, events, reduce })(zod schemas, typed event union, pure deterministic reducer), typedAgentStateErrorcodes, monotonic revisions, exact-revision snapshot reads, idempotency-key replay (duplicate key returns the committed result; conflicting payload under the same key is a typedidempotency-conflict), compare-and-swap viaexpectedRevision, explicit versioned migrations that rebase history, deterministic resets, and polling change cursors — nothing stronger promised.AgentStateLifetime = 'request' | 'process' | 'workspace-durable' | 'external'with exhaustive never-default switches; volatile vs durable is a typed property, never inferred from an MCP process.requestandprocesslifetimes — labeled test-only as a durability stand-in,durable: falsestructurally, refuses durable lifetimes.createAgentStateHandle(store, { signal })fills the reservedstateslot feat: framework request store and agent() (#95) #113 shipped onAgentRequestContext:(await agent()).state.dispatch(event, payload, { idempotencyKey }). Slot type goesundefined→AgentStateHandle | undefined; wiring stays additive.stateDriverConformanceCases,node:assert-based, runner-agnostic): 18 shared cases every driver must pass; runs here against the memory driver. PR-2 runs the same suite against thenode:sqliteworkspace-durable driver../stateis its own rslib entry + subpath export;dist/index.js/dist/plugin.jscontain none of the kernel (verified against the built tree; PR-2 adds the packaged-tree test alongside the sqlite entry). README +docs/framework-mode.mdpersistence statements flipped in-PR.PR-2 (same branch family) ships the
node:sqliteworkspace-durable driver (G3), cross-process proofs, and theexamples/rsc-agent-runtimemigration that retires the 781-line JSONL kernel.Test plan
pnpm --filter @agent-bundle/runtime test— 73 passed (17 conformance cases on memory, kernel/migration/handle units; 1 durable-only case skipped by design)pnpm --filter @agent-bundle/runtime typecheck+ rootpnpm typecheck— cleanpnpm lint— cleandist/state.jssmoke-tested; root entries free of state/kernel identifiersRefs #98, #107.