Context
packages/loopover-miner/lib/local-store.ts provides openLocalStoreDb, the shared crash-safe DB-open helper: it creates the parent directory (0700), opens the DatabaseSync handle, chmods the file (0600), sets PRAGMA busy_timeout, and — critically — calls registerCleanupResource(db) so a SIGINT/SIGTERM/uncaught-exception mid-run closes the handle instead of leaving the SQLite file half-written. Its own doc comment explains this crash-safety registration explicitly (#4826). Most stores in this package already route through it (e.g. plan-store.ts's openPlanStore and attempt-log.ts's initAttemptLog both call openLocalStoreDb directly, and claim-ledger.ts's real store-open path does too). Closed issue #6595 previously fixed this exact bypass for governor-ledger.js, prediction-ledger.js, and plan-store.js.
Three more stores still hand-roll the same open boilerplate directly against node:sqlite's DatabaseSync, bypassing openLocalStoreDb and its crash-safety registration entirely:
packages/loopover-miner/lib/orb-export.ts's openOrbExportStore: const db = new DatabaseSync(resolvedPath); chmodSync(resolvedPath, 0o600); db.exec("PRAGMA busy_timeout = 5000");
packages/loopover-miner/lib/deny-hook-synthesis.ts's initDenyHookSynthesisStore: the identical new DatabaseSync(resolvedPath); chmodSync(...); db.exec("PRAGMA busy_timeout = 5000") sequence.
packages/loopover-miner/lib/laptop-init.ts's initLaptopState: const db = new DatabaseSync(dbPath); (plus its own separate mkdirSync/chmodSync calls) with no busy-timeout and no crash-safety registration at all.
For all three, a SIGINT/SIGTERM/uncaught-exception during a write (e.g. mid-batch orb-export cursor update, mid-refresh deny-hook-synthesis proposal upsert, or during laptop-init's bootstrap insert) is not guaranteed to close the handle cleanly the way every openLocalStoreDb-routed store already is.
Requirements
⚠️ Required pattern. Replace each hand-rolled new DatabaseSync(...) + manual mkdirSync/chmodSync/PRAGMA busy_timeout sequence with a call to openLocalStoreDb(resolvedPath) from local-store.ts — the exact substitution plan-store.ts's openPlanStore and attempt-log.ts's initAttemptLog already demonstrate. openLocalStoreDb performs the mkdir/chmod/busy-timeout/cleanup-registration internally; do not duplicate any of those steps at the call site afterward.
orb-export.ts's openOrbExportStore: replace the new DatabaseSync/chmodSync/PRAGMA busy_timeout sequence with const db = openLocalStoreDb(resolvedPath);. Drop the now-redundant mkdirSync/chmodSync/PRAGMA lines (the mkdirSync(dirname(resolvedPath), ...) call already present can be dropped too — openLocalStoreDb does its own mkdirSync on the parent dir).
deny-hook-synthesis.ts's initDenyHookSynthesisStore: same substitution.
laptop-init.ts's initLaptopState: same substitution, importing openLocalStoreDb from ./local-store.js. Verify no import cycle: local-store.ts imports only from process-lifecycle.js and store-db-adapter.js, neither of which imports laptop-init.ts, so this import is safe (the file's existing "avoid import cycles" comment is about its own resolveMinerStateDir mirroring status.js's path-resolution helper, a separate concern from the DB-open helper — do not remove that comment or change the local resolveMinerStateDir duplication, which is out of scope here).
- Each store's
close() must keep working as before — openLocalStoreDb's returned handle already wraps close() to unregister the cleanup hook, so no additional change is needed at the call sites beyond the open substitution.
Deliverables
Test Coverage Requirements
packages/loopover-miner/** is not Codecov-gated, but npm run test:ci must stay green. test/unit/miner-orb-export.test.ts, the deny-hook-synthesis test file, and test/unit/miner-laptop-init.test.ts should keep passing unmodified where they test read/write behavior (the on-disk contract — table names, permissions, busy-timeout — is unchanged); add or extend a test in each file asserting the store is registered for crash-safe cleanup (e.g. asserting the returned handle's close() still tears down cleanly, or checking registerCleanupResource/process-lifecycle.js's test seam the way plan-store.ts's or attempt-log.ts's own tests already verify their openLocalStoreDb usage, if such a test precedent exists in this repo — otherwise a plain open/write/close round-trip test is sufficient since the behavior itself doesn't change, only the crash-safety wiring).
Expected Outcome
All local SQLite stores in packages/loopover-miner — not just most of them — get the same SIGINT/SIGTERM/uncaught-exception crash-safety guarantee, closing #6595's fix class for the three stores it missed.
Links & Resources
Context
packages/loopover-miner/lib/local-store.tsprovidesopenLocalStoreDb, the shared crash-safe DB-open helper: it creates the parent directory (0700), opens theDatabaseSynchandle, chmods the file (0600), setsPRAGMA busy_timeout, and — critically — callsregisterCleanupResource(db)so a SIGINT/SIGTERM/uncaught-exception mid-run closes the handle instead of leaving the SQLite file half-written. Its own doc comment explains this crash-safety registration explicitly (#4826). Most stores in this package already route through it (e.g.plan-store.ts'sopenPlanStoreandattempt-log.ts'sinitAttemptLogboth callopenLocalStoreDbdirectly, andclaim-ledger.ts's real store-open path does too). Closed issue #6595 previously fixed this exact bypass forgovernor-ledger.js,prediction-ledger.js, andplan-store.js.Three more stores still hand-roll the same open boilerplate directly against
node:sqlite'sDatabaseSync, bypassingopenLocalStoreDband its crash-safety registration entirely:packages/loopover-miner/lib/orb-export.ts'sopenOrbExportStore:const db = new DatabaseSync(resolvedPath); chmodSync(resolvedPath, 0o600); db.exec("PRAGMA busy_timeout = 5000");packages/loopover-miner/lib/deny-hook-synthesis.ts'sinitDenyHookSynthesisStore: the identicalnew DatabaseSync(resolvedPath); chmodSync(...); db.exec("PRAGMA busy_timeout = 5000")sequence.packages/loopover-miner/lib/laptop-init.ts'sinitLaptopState:const db = new DatabaseSync(dbPath);(plus its own separatemkdirSync/chmodSynccalls) with no busy-timeout and no crash-safety registration at all.For all three, a SIGINT/SIGTERM/uncaught-exception during a write (e.g. mid-batch
orb-exportcursor update, mid-refreshdeny-hook-synthesisproposal upsert, or duringlaptop-init's bootstrap insert) is not guaranteed to close the handle cleanly the way everyopenLocalStoreDb-routed store already is.Requirements
orb-export.ts'sopenOrbExportStore: replace thenew DatabaseSync/chmodSync/PRAGMA busy_timeoutsequence withconst db = openLocalStoreDb(resolvedPath);. Drop the now-redundantmkdirSync/chmodSync/PRAGMAlines (themkdirSync(dirname(resolvedPath), ...)call already present can be dropped too —openLocalStoreDbdoes its ownmkdirSyncon the parent dir).deny-hook-synthesis.ts'sinitDenyHookSynthesisStore: same substitution.laptop-init.ts'sinitLaptopState: same substitution, importingopenLocalStoreDbfrom./local-store.js. Verify no import cycle:local-store.tsimports only fromprocess-lifecycle.jsandstore-db-adapter.js, neither of which importslaptop-init.ts, so this import is safe (the file's existing "avoid import cycles" comment is about its ownresolveMinerStateDirmirroringstatus.js's path-resolution helper, a separate concern from the DB-open helper — do not remove that comment or change the localresolveMinerStateDirduplication, which is out of scope here).close()must keep working as before —openLocalStoreDb's returned handle already wrapsclose()to unregister the cleanup hook, so no additional change is needed at the call sites beyond the open substitution.Deliverables
orb-export.tsopens its store viaopenLocalStoreDb.deny-hook-synthesis.tsopens its store viaopenLocalStoreDb.laptop-init.tsopens its store viaopenLocalStoreDb.Test Coverage Requirements
packages/loopover-miner/**is not Codecov-gated, butnpm run test:cimust stay green.test/unit/miner-orb-export.test.ts, the deny-hook-synthesis test file, andtest/unit/miner-laptop-init.test.tsshould keep passing unmodified where they test read/write behavior (the on-disk contract — table names, permissions, busy-timeout — is unchanged); add or extend a test in each file asserting the store is registered for crash-safe cleanup (e.g. asserting the returned handle'sclose()still tears down cleanly, or checkingregisterCleanupResource/process-lifecycle.js's test seam the wayplan-store.ts's orattempt-log.ts's own tests already verify theiropenLocalStoreDbusage, if such a test precedent exists in this repo — otherwise a plain open/write/close round-trip test is sufficient since the behavior itself doesn't change, only the crash-safety wiring).Expected Outcome
All local SQLite stores in
packages/loopover-miner— not just most of them — get the same SIGINT/SIGTERM/uncaught-exception crash-safety guarantee, closing #6595's fix class for the three stores it missed.Links & Resources
packages/loopover-miner/lib/local-store.ts(openLocalStoreDb)packages/loopover-miner/lib/orb-export.ts,lib/deny-hook-synthesis.ts,lib/laptop-init.ts(the files to change)packages/loopover-miner/lib/plan-store.ts,lib/attempt-log.ts(existingopenLocalStoreDbcall sites to mirror)