Skip to content

feat(state): #98 v1 node:sqlite workspace-durable driver + example migration (PR-2) - #149

Merged
ScriptedAlchemy merged 1 commit into
mainfrom
wave2/98-state-sqlite
Sep 1, 2026
Merged

feat(state): #98 v1 node:sqlite workspace-durable driver + example migration (PR-2)#149
ScriptedAlchemy merged 1 commit into
mainfrom
wave2/98-state-sqlite

Conversation

@ScriptedAlchemy

Copy link
Copy Markdown
Owner

Summary

Wave 2 Lane B, PR 2 of 2 for the #98 v1 state kernel (follows #142; plan #107 §6, G3 recorded).

  • node:sqlite workspace-durable driver behind the new @agent-bundle/runtime/state/sqlite subpath (G3: zero new dependencies; ExperimentalWarning documented in the module and both READMEs). WAL journal mode with synchronous = FULL; every commit is one BEGIN IMMEDIATE transaction (idempotency lookup, CAS, reducer, journal append, head update commit atomically); cross-process writers serialize on the database lock with a bounded busy timeout; explicit migrations run on open and rebase history; corruption fails closed with typed errors (SQLITE-level, head/journal mismatch, schema-invalid persisted state, unknown kernel format, foreign definition id).
  • Conformance + cross-process proofs: the sqlite driver passes the identical shared suite the memory driver runs (including the durable-only case), plus the two Provide an optional durable Agent state kernel #98 acceptance proofs as integration tests running the built dist in child processes — two independent processes safely update one workspace-durable instance, and a SIGKILLed writer cannot leave a successful-but-corrupt state (fresh reopen: head==journal, PRAGMA integrity_check ok, every retained revision replays, new commits accepted).
  • Packaging boundaries, verified by a new packaged-tree test: root/plugin entries contain no kernel or storage code, ./state never loads node:sqlite, and ./state/sqlite shares the ./state runtime chunk so instanceof AgentStateError holds across subpaths (the sqlite lib externalizes the kernel to ../state.js instead of re-bundling it). Package now declares "sideEffects": false.
  • Proof: examples/rsc-agent-runtime retires its 781-line JSONL kernel (state-file-core.ts, test support, lock fixtures, proper-lockfile dependency — net −1,996 lines). The example now declares schema/events/reducer via defineState and adapts the framework store to its unchanged provider-facing RuntimeKernel/RuntimeSnapshot contract. The event payload is exactly the four caller-owned fields (the retired kernel's canonical dedupe identity); eventId/recordedAt are derived from journal revisions and commit timestamps, so native tool-id retries replay instead of conflicting. No wire-DTO changes.
  • eval:spot stays green and now also proves cross-process idempotent replay (a second hook process replaying the same tool_use_id commits nothing). Tests that pinned raw JSONL bytes now read through the kernel; stderr-sensitive tests tolerate exactly the documented sqlite ExperimentalWarning and nothing else.

Test plan

  • pnpm --filter @agent-bundle/runtime test — 108 passed (18/18 conformance on sqlite incl. durable-only; cross-process two-writer + SIGKILL proofs; packaging boundaries)
  • pnpm --filter @agent-bundle/rsc-agent-runtime-demo check — validate/build/typecheck green; tests 174 total, 0 failed (6 pre-existing skip-gated host opt-ins)
  • pnpm eval:spot — green
  • pnpm lint, pnpm typecheck, pnpm check:runtime-topology — clean

Fixes #98. Refs #107.

@changeset-bot

changeset-bot Bot commented Sep 1, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 494f8fe

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@agent-bundle/runtime Minor

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

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 1, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-01T07:47:46.974537Z c86690a PR opened
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

… migrates off its JSONL kernel (#98 v1, PR-2)

The workspace-durable driver ships on node:sqlite (G3: zero new
dependencies; the ExperimentalWarning is documented in the module and both
READMEs): WAL with full synchronous durability, every commit in one
BEGIN IMMEDIATE transaction, bounded busy-timeout cross-process
serialization, explicit migrations on open, corruption fail-closed. It
passes the same conformance suite as the memory driver plus the two
cross-process acceptance proofs (two independent writers over one store; a
SIGKILLed writer cannot leave successful-but-corrupt state), and lives
behind its own ./state/sqlite subpath sharing one kernel runtime instance
with ./state so error identity holds across entries.

examples/rsc-agent-runtime retires its 781-line JSONL kernel and lock
machinery: the example now declares schema/events/reducer via defineState
and adapts the framework store to its provider-facing RuntimeKernel
contract, with eventId/recordedAt derived from journal revisions and commit
timestamps so whole-payload idempotency matches the retired kernel's
semantic dedupe identity. Tests and pnpm eval:spot stay green, with the
spot check now also proving cross-process idempotent replay.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c86690afab

ℹ️ 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".

Comment thread packages/rsc-runtime/src/state/sqlite.ts
Comment thread packages/rsc-runtime/src/state/sqlite.ts
Comment thread packages/rsc-runtime/src/state/sqlite.ts
@ScriptedAlchemy
ScriptedAlchemy merged commit f45ae75 into main Sep 1, 2026
@ScriptedAlchemy
ScriptedAlchemy deleted the wave2/98-state-sqlite branch September 1, 2026 07:49
ScriptedAlchemy added a commit that referenced this pull request Sep 1, 2026
…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.
ScriptedAlchemy added a commit that referenced this pull request Sep 1, 2026
…ots, workspace-free published manifest (#183)

Three independent breakages kept hosted CI red on main:

- scripts/run-examples-check.mjs launched npm_execpath through the current
  Node executable, but pnpm/setup on hosted runners exposes a native shim
  (bare command name), so `node pnpm` failed with MODULE_NOT_FOUND. The
  script now execs non-JavaScript entrypoints directly.
- The sqlite migration (#149) made the example kernel return the durable
  prefix at the original commit revision on idempotent replays; the retired
  JSONL kernel returned the current head. Re-running a fixture after a reset
  therefore reported the pre-reset state version and failed the runtime
  playground e2e on every Node line. Replays now read the head again.
- #151 added `"@agent-bundle/runtime": "workspace:*"` to the published
  agent-bundle devDependencies, tripping the release audit (npm refuses
  tarball manifests with workspace ranges). The optional peer is now
  satisfied through a pnpm-workspace override instead of a shipped range.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Provide an optional durable Agent state kernel

1 participant