Skip to content

feat(interactive): M3 durable SQLite Checkpoint Store and hash-chained Journal - #4

Merged
rockyshi1993 merged 7 commits into
mainfrom
feat/interactive-m3-sqlite-checkpoint-journal
Nov 10, 2026
Merged

rockyshi1993 merged 7 commits into
mainfrom
feat/interactive-m3-sqlite-checkpoint-journal

Conversation

@rockyshi1993

@rockyshi1993 rockyshi1993 commented Nov 10, 2026 •

Copy link
Copy Markdown
Contributor

Scope — confirmed P0-A / M3 only

  • Host-owned SQLite DatabaseSync persistence with WAL, synchronous FULL, busy timeout, FK integrity, and explicit namespace-scoped run identity.
  • Exact-byte immutable Program artifacts; prevents deletion while referenced by stored runs.
  • Canonical 2 MiB JSON checkpoint snapshots; bounded depth, strict numbers, no proxy/getter/cycle/surrogate; reject before DB mutation.
  • Transactional start deduplication by namespace/startKey/startIntentHash, program and initial state; atomic Checkpoint revision CAS + Journal hash-chain append.
  • Linearizable snapshot reads and journal integrity verification. All writes occur before return; Store makes no external model/skill/host calls.
  • Internal source only. No public Interactive API or M4 Coordinator. Standard mode unchanged.

Evidence gate (do NOT merge before passing)

  • New tests/interactive/store.test.mjs: canonicalization adversarial cases, namespace isolation, immutable programs, start conflicts, retries, rollback after uniqueness conflict, durable reopen, two real OS processes racing same CAS, tampered rows.
  • npm run verify in Ubuntu 24.04 / Windows 2025 × Node 22.13.0 / 24.22.1; docs consumers and packaging checks.
  • Independent code/contract review and fixes until no P0/P1 findings.
  • Known boundary: node:sqlite in Node 22 is experimental; this is a host-owned sync CLI Store, NOT a model-facing sandbox, and M4/M5/M6 have not been implemented.

Note

Medium Risk
New durable persistence and concurrency-critical CAS/journal logic on experimental node:sqlite, with strict validation paths that must not regress or accept corrupt data.

Overview
Adds P0-A/M3 host-owned persistence for interactive state-machine runs: a strict canonical JSON layer (canonical.mjs) for bounded, hash-stable checkpoint/journal bytes, and a synchronous SQLite checkpoint store (sqlite-store.mjs) built on Node’s DatabaseSync with WAL, full sync, and foreign keys.

The store registers immutable program artifacts by content hash, idempotently starts runs per namespace/startKey/intent fingerprint, and advances state via revision CAS with a hash-chained journal. Reads verify checkpoint and journal integrity and reject corruption, tenant mismatch, or unsafe DB configuration. package-files.mjs ships the two new interactive modules; tests/interactive/store.test.mjs covers canonicalization edge cases, isolation, CAS races across processes, tampering, and rollback behavior.

Reviewed by Cursor Bugbot for commit c879166. Bugbot is set up for automated code reviews on this repo. Configure here.

Copy link
Copy Markdown
Contributor Author

M3 implementation gate — independent source review + actual CI evidence (2026-11-10 UTC)

Reviewed commit: c879166346c4e989761b57e73198bc49cdf30a60; base bc5572f607421e43cb226aa58cb6a2827eb88c7c.

Source review scope: src/interactive/canonical.mjs, src/interactive/sqlite-store.mjs, tests/interactive/store.test.mjs, exact package manifest. Verified namespace in keys and start intent, original hash-identified Program bytes retained, no modification of Standard root exports, one SQLite transaction for checkpoint CAS + Journal, revision monotonicity, immutable artifact/start/policy identities, checkpoint and journal hash checks, bounded canonical JSON, strict rejection of adversarial inputs, host-only execution boundary.

Findings closed before merge:

  1. Windows SQLite cleanup attempted to unlink still-open DB (EBUSY): fixed cleanup hook ordering; rerun verified.
  2. Durability downgrade was not explicitly detected: assert WAL / synchronous FULL / foreign keys at Store opening.
  3. Checkpoint policy identity could drift and Program bytes were copied before checking 4 MiB size: pin policy in CAS and reject oversize pre-copy. Added negative regression test.

Test evidence: GitHub Actions run 42824870369 SUCCESS, 5/5:

  • ubuntu-24.04 × Node 22.13.0 — npm run verify success; 293/293 test cases, docs consumer success.
  • ubuntu-24.04 × Node 24.22.1 — same.
  • windows-2025 × Node 22.13.0 — same.
  • windows-2025 × Node 24.22.1 — same.
  • website verification — success.
    Packaging and TypeScript tests are part of each actual npm run verify run. No omitted cross-platform execution or reuse of older M2 logs.

Review decision: no remaining P0/P1 findings within M3's persistence-only implementation. M4 Coordinator, M5 trusted Host, and M6 integrated end-to-end proofs are not claimed complete. No public Interactive SDK, npm publish, deployment, or P0-B+ scope in this PR. node:sqlite stays experimental in Node 22; the Store is a host-owned synchronous persistence adapter, not a JS sandbox. The original V0.6-R5 design text was not available in this session, so this review covers the established M3 persistence invariants and actual code/tests, not a line-by-line reconstruction of the full design document.

@rockyshi1993
rockyshi1993 marked this pull request as ready for review November 10, 2026 13:33
@rockyshi1993
rockyshi1993 merged commit 5cb1b74 into main Nov 10, 2026
8 checks passed

@cursor cursor 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.

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit c879166. Configure here.

const startIntentHash = sha256Bytes(canonicalBytes({
namespace, startKey, artifactHash:base.artifactHash, state:base.state,
policyHash:base.policyHash ?? null, intent,
}));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Canonical cap rejects wrapped payloads

Medium Severity

createRun fingerprints state together with startIntent under the same 2 MiB canonicalBytes cap that already applies to each piece separately, so two legal inputs can still throw RangeError. journalDigest has the same wrapping issue, so a journal entry near MAX_JOURNAL_BYTES can fail compareAndSwap even after the payload itself was accepted.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit c879166. Configure here.

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.

1 participant