Context (corrected 2026-07-19)
This issue's original framing assumed a full sync-to-async cutover was the only path forward. That's
wrong in one important way: #7175 already shipped a working, proven incremental pattern, and this
issue should track finishing that pattern's rollout, not re-derive a new approach.
What actually exists today (packages/loopover-miner/lib/store-db-adapter.js, shipped via #7194,
closing #7175 -- though only as a first vertical slice, not the full migration its own body described):
SqliteDriver interface (query/exec), currently backed by a synchronous nodeSqliteDriver
wrapping node:sqlite's DatabaseSync -- deliberately still sync so callers need no async cascade
yet.
createD1Adapter(driver) -- an async D1-shaped wrapper over any SqliteDriver, ready for when a
genuinely async (Postgres) driver exists.
local-store.js's openLocalStoreAdapter() -- opens a store through this seam, returning
{ db, driver }.
- One store fully migrated onto the seam:
run-state.js. Its public API stayed synchronous
(CRUD routed through driver.query(sql, params) directly, bypassing the async createD1Adapter
entirely) -- proving the pattern is genuinely additive and low-risk.
What #7175 itself never finished (despite closing): ~12 more stores still call DatabaseSync
directly via openLocalStoreDb instead of the seam (event-ledger.js, claim-ledger.js,
portfolio-queue.js, governor-state.js, plan-store.js, attempt-log.js, governor-ledger.js,
prediction-ledger.js, contribution-profile-cache.js, ranked-candidates.js, replay-snapshot.js,
worktree-allocator.js), the actual Postgres driver (createPgAdapter-equivalent), interactive-
transaction support (batchClaim's BEGIN IMMEDIATE-style read-then-conditional-write, needs a
runOn(client) pattern per pg-adapter.ts's own shape), and lease-expiry moving from local-only
staleness checks to time-based (since a real Postgres backend means concurrent workers, not one
machine).
policy-doc-cache.js (79 lines, no transactions) has also now been migrated onto the seam as a
second proof point -- see the linked PR.
Corrected scope
This issue now tracks finishing the seam rollout only (still-synchronous driver.query
migration, matching run-state.js/policy-doc-cache.js's exact pattern) for the remaining stores
that have no interactive-transaction complexity. Stores with real transactions
(claim-ledger.js's claimIssueWithinCap, portfolio-queue.js's batchClaim,
governor-state.js's withTransaction) need the runOn(client) pattern designed first --
tracked separately, since that's real new design work, not a mechanical migration.
The actual Postgres driver, and the eventual async cutover once it exists, are also tracked
separately -- they're the genuinely risky, cascading-through-54-files step this issue originally
conflated with the (much safer) seam rollout.
Deliverables
Links & Resources
Context (corrected 2026-07-19)
This issue's original framing assumed a full sync-to-async cutover was the only path forward. That's
wrong in one important way: #7175 already shipped a working, proven incremental pattern, and this
issue should track finishing that pattern's rollout, not re-derive a new approach.
What actually exists today (
packages/loopover-miner/lib/store-db-adapter.js, shipped via #7194,closing #7175 -- though only as a first vertical slice, not the full migration its own body described):
SqliteDriverinterface (query/exec), currently backed by a synchronousnodeSqliteDriverwrapping
node:sqlite'sDatabaseSync-- deliberately still sync so callers need no async cascadeyet.
createD1Adapter(driver)-- an async D1-shaped wrapper over anySqliteDriver, ready for when agenuinely async (Postgres) driver exists.
local-store.js'sopenLocalStoreAdapter()-- opens a store through this seam, returning{ db, driver }.run-state.js. Its public API stayed synchronous(CRUD routed through
driver.query(sql, params)directly, bypassing the asynccreateD1Adapterentirely) -- proving the pattern is genuinely additive and low-risk.
What #7175 itself never finished (despite closing): ~12 more stores still call
DatabaseSyncdirectly via
openLocalStoreDbinstead of the seam (event-ledger.js,claim-ledger.js,portfolio-queue.js,governor-state.js,plan-store.js,attempt-log.js,governor-ledger.js,prediction-ledger.js,contribution-profile-cache.js,ranked-candidates.js,replay-snapshot.js,worktree-allocator.js), the actual Postgres driver (createPgAdapter-equivalent), interactive-transaction support (
batchClaim'sBEGIN IMMEDIATE-style read-then-conditional-write, needs arunOn(client)pattern perpg-adapter.ts's own shape), and lease-expiry moving from local-onlystaleness checks to time-based (since a real Postgres backend means concurrent workers, not one
machine).
policy-doc-cache.js(79 lines, no transactions) has also now been migrated onto the seam as asecond proof point -- see the linked PR.
Corrected scope
This issue now tracks finishing the seam rollout only (still-synchronous
driver.querymigration, matching
run-state.js/policy-doc-cache.js's exact pattern) for the remaining storesthat have no interactive-transaction complexity. Stores with real transactions
(
claim-ledger.js'sclaimIssueWithinCap,portfolio-queue.js'sbatchClaim,governor-state.js'swithTransaction) need therunOn(client)pattern designed first --tracked separately, since that's real new design work, not a mechanical migration.
The actual Postgres driver, and the eventual async cutover once it exists, are also tracked
separately -- they're the genuinely risky, cascading-through-54-files step this issue originally
conflated with the (much safer) seam rollout.
Deliverables
policy-doc-cache.jsmigrated onto the seam (see linked PR)contribution-profile-cache.js,policy-verdict-cache.js,prediction-ledger.js,ranked-candidates.js,replay-snapshot.js,worktree-allocator.js(verify each individually for hiddentransaction/RETURNING usage before assuming it's mechanical --
store-db-adapter.js's owndoc comment flags a RETURNING-statement heuristic limitation to check against)
runOn(client)interactive-transaction pattern neededby
claim-ledger.js/portfolio-queue.js/governor-state.js/event-ledger.js/attempt-log.jsbefore those migrate
the eventual cutover trigger)
Links & Resources
run-state.jsmigration, the pattern to mirror)packages/loopover-miner/lib/store-db-adapter.js-- the seam itselfpackages/loopover-miner/lib/run-state.js,policy-doc-cache.js-- worked examples