Skip to content

feat(ocap-kernel): refuse a savepoint taken while the run loop runs - #1106

Open
sirtimid wants to merge 1 commit into
sirtimid/stop-run-loop-before-direct-writesfrom
sirtimid/refuse-out-of-loop-savepoint
Open

sirtimid wants to merge 1 commit into
sirtimid/stop-run-loop-before-direct-writesfrom
sirtimid/refuse-out-of-loop-savepoint

Conversation

@sirtimid

@sirtimid sirtimid commented Sep 15, 2026 •

Copy link
Copy Markdown
Contributor

Stacked on #1105. Review that one first; this branch's base is sirtimid/stop-run-loop-before-direct-writes.

The enforcement half of stopping the run loop before a direct write.

A savepoint taken outside the crank machinery is the outermost one on the connection, and so the transaction's commit point. Opened while the loop runs, it either nests inside whatever crank is open or opens a transaction the next crank nests inside — and either way somebody else decides whether this caller's writes survive. Ryan's #1039 claim 1 has all three orderings against real SQLite.

Nothing does that any more. After inbound remote messages (#1103) and peer incarnation changes (#1104) moved onto the run queue, no caller of createSavepoint, rollbackSavepoint or releaseSavepoint is left outside store/methods/crank.ts. This is what keeps it that way.

Changes

  • KernelStore.createSavepoint refuses a savepoint taken while the run loop is running. The crank machinery's own savepoints are unaffected — they are taken by the run loop, which is what the refusal is defined against.
  • RunLoopStatus's stopped state, added in feat(ocap-kernel): stop the run loop before the control plane writes #1105, is what the check reads, so the control plane's own direct writes still work: it stops the loop first, and only then is a savepoint legal again.

Testing

KernelQueue.run-loop-stop.test.ts gains the refusal cases alongside #1105's stop/resume ones. @metamask/ocap-kernel's KernelQueue, KernelQueue.run-loop-stop, store/index and get-status suites are green (102 tests), and the package type-checks.

Not done here, and the reason for the warning above: the code-reviewer / silent-failure-hunter / pr-test-analyzer round, the mutation checks, and the full-package and @ocap/kernel-test runs. Every other PR in this split had all of them.

🤖 Generated with Claude Code


Note

Medium Risk
Changes when kernel store transactions may be opened relative to the run loop; incorrect savepoint timing could affect persistence, though the change fails fast instead of allowing ambiguous commits.

Overview
KernelStore.createSavepoint now fails while the run loop is active, so embedders cannot open the outermost DB savepoint (the real commit boundary) during a crank. That nesting let the run loop decide whether direct writes survived, which conflicted with stopping the loop for control-plane storage updates (#1105).

KernelQueue.run toggles the guard via new setRunLoopRunning: set true when the loop starts and cleared in finally, so savepoints are allowed again once the loop is idle or stopped between cranks. Crank-internal savepoints (createCrankSavepoint) are unchanged.

Integration tests in KernelQueue.run-loop-stop.test.ts cover refusal during a running loop and success after stopRunLoop. The changelog documents the behavior change.

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

@sirtimid
sirtimid requested a review from a team as a code owner September 15, 2026 22:32
@sirtimid
sirtimid added this pull request to stack #1107 September 15, 2026 22:37
@sirtimid
sirtimid force-pushed the sirtimid/refuse-out-of-loop-savepoint branch from 7b2e0e6 to 1d6b159 Compare September 16, 2026 16:29
@sirtimid
sirtimid force-pushed the sirtimid/refuse-out-of-loop-savepoint branch from 1d6b159 to ee493f4 Compare September 23, 2026 16:57
@github-actions

github-actions Bot commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

Coverage Report

Status Category Percentage Covered / Total
🔵 Lines 73.38%
⬆️ +0.23%
9966 / 13580
🔵 Statements 73.18%
⬆️ +0.23%
10096 / 13795
🔵 Functions 73.86%
⬆️ +0.24%
2334 / 3160
🔵 Branches 67.8%
⬆️ +0.38%
4120 / 6076
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
packages/ocap-kernel/src/KernelQueue.ts 98.96%
⬆️ +0.24%
91.66%
⬆️ +1.42%
100%
🟰 ±0%
98.96%
⬆️ +0.24%
208, 761
packages/ocap-kernel/src/store/index.ts 97.8%
⬇️ -1.07%
95.65%
⬆️ +0.42%
96.77%
⬇️ -3.23%
97.77%
⬇️ -1.09%
362, 435
packages/ocap-kernel/src/store/types.ts 100%
🟰 ±0%
100%
🟰 ±0%
100%
🟰 ±0%
100%
🟰 ±0%
Generated in workflow #4970 for commit eff9659 by the Vitest Coverage Report Action

@sirtimid
sirtimid force-pushed the sirtimid/refuse-out-of-loop-savepoint branch 2 times, most recently from 80ef039 to d3fe3d4 Compare September 23, 2026 21:47
@sirtimid
sirtimid force-pushed the sirtimid/refuse-out-of-loop-savepoint branch from d3fe3d4 to 86739ec Compare September 23, 2026 22:26
@sirtimid
sirtimid force-pushed the sirtimid/refuse-out-of-loop-savepoint branch from 86739ec to eff9659 Compare September 24, 2026 13:18
The enforcement half of stopping the loop before a direct write. A
savepoint taken outside the crank machinery is the outermost one on the
connection, and so the transaction's commit point: opened while the loop
runs, it either nests inside whatever crank is open or opens a
transaction the next crank nests inside, and either way somebody else
decides whether this caller's writes survive.

Nothing does that any more — after inbound remote messages and peer
incarnation changes moved onto the run queue, no caller of
`createSavepoint`, `rollbackSavepoint` or `releaseSavepoint` is left
outside `store/methods/crank.ts`. This is what keeps it that way.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@sirtimid
sirtimid force-pushed the sirtimid/refuse-out-of-loop-savepoint branch from eff9659 to 5b9afe4 Compare September 24, 2026 15:10

This branch has not been deployed

No deployments
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