Context
packages/loopover-miner/lib/local-store.js (#4272) exists specifically to be "shared path-resolution + DB-open boilerplate for the package's local SQLite stores": it resolves the DB path, does the mkdirSync(0o700) + DatabaseSync open + chmodSync(0o600) + PRAGMA busy_timeout, and — critically — registers the opened handle via registerCleanupResource (packages/loopover-miner/lib/process-lifecycle.js, #4826) so installCliSignalHandlers closes it cleanly on SIGINT/SIGTERM/uncaughtException/unhandledRejection instead of leaving it mid-write.
packages/loopover-miner/lib/attempt-log.js, claim-ledger.js, event-ledger.js, run-state.js, portfolio-queue.js, policy-doc-cache.js, policy-verdict-cache.js, ranked-candidates.js, replay-snapshot.js, and governor-state.js all already open their SQLite handle through openLocalStoreDb.
Three of the seven "canonical" local stores that packages/loopover-miner/lib/status.js's storeIntegrityChecks and packages/loopover-miner/lib/migrate-cli.js's STORES list both track — governor-ledger.js, prediction-ledger.js, and plan-store.js — never migrated. Each still hand-rolls the exact same mkdirSync/new DatabaseSync(...)/chmodSync/PRAGMA busy_timeout sequence local-store.js was built to replace (see governor-ledger.js's initGovernorLedger, prediction-ledger.js's initPredictionLedger, and plan-store.js's DB-open block). Because none of the three call openLocalStoreDb, none of them call registerCleanupResource — a SIGINT/SIGTERM/crash mid-write to any of these three stores is never flushed/closed by the process-lifecycle cleanup chokepoint, unlike every sibling store.
Requirements
packages/loopover-miner/lib/governor-ledger.js's initGovernorLedger MUST open its database handle by calling openLocalStoreDb (imported from ./local-store.js) instead of hand-rolling mkdirSync/new DatabaseSync(...)/chmodSync/PRAGMA busy_timeout.
packages/loopover-miner/lib/prediction-ledger.js's initPredictionLedger MUST open its database handle by calling openLocalStoreDb instead of hand-rolling the same sequence.
packages/loopover-miner/lib/plan-store.js's openPlanStore function MUST open its database handle by calling openLocalStoreDb instead of hand-rolling the same sequence (including its existing :memory: special-case, which openLocalStoreDb already handles generically).
- Each of the three files MUST also switch its
resolve*DbPath/path-normalization helpers to resolveLocalStoreDbPath/normalizeLocalStoreDbPath from ./local-store.js, mirroring claim-ledger.js's and event-ledger.js's existing usage, rather than keeping a second hand-written copy of that logic.
- No behavior change to any store's public API, schema, or the env vars it honors (
LOOPOVER_MINER_GOVERNOR_LEDGER_DB, LOOPOVER_MINER_PREDICTION_LEDGER_DB, LOOPOVER_MINER_PLAN_STORE_DB, LOOPOVER_MINER_CONFIG_DIR, XDG_CONFIG_HOME must all still resolve identically).
- After the change, opening any of the three stores MUST increase
cleanupResourceCount() (from packages/loopover-miner/lib/process-lifecycle.js) by one, and closing the store via its own close() MUST decrease it back. Add this assertion to each store's own existing unit test file (test/unit/miner-governor-ledger.test.ts, test/unit/miner-prediction-ledger.test.ts, test/unit/miner-plan-store.test.ts).
Deliverables
Test Coverage Requirements
All touched paths are under packages/loopover-miner/lib/**, which is covered by this repo's 99%+ Codecov patch gate. Every changed line (the new openLocalStoreDb/resolveLocalStoreDbPath call sites and the removed hand-rolled open code) must be exercised by the existing or newly-added unit tests — do not rely on integration tests alone to cover the diff.
Expected Outcome
governor-ledger.js, prediction-ledger.js, and plan-store.js open their SQLite handles identically to every other local store in the package, and a SIGINT/SIGTERM/uncaught exception during a write to any of the three is flushed/closed by installCliSignalHandlers exactly as it already is for claim-ledger.js, event-ledger.js, run-state.js, and portfolio-queue.js.
Links & Resources
Context
packages/loopover-miner/lib/local-store.js(#4272) exists specifically to be "shared path-resolution + DB-open boilerplate for the package's local SQLite stores": it resolves the DB path, does themkdirSync(0o700)+DatabaseSyncopen +chmodSync(0o600)+PRAGMA busy_timeout, and — critically — registers the opened handle viaregisterCleanupResource(packages/loopover-miner/lib/process-lifecycle.js, #4826) soinstallCliSignalHandlerscloses it cleanly on SIGINT/SIGTERM/uncaughtException/unhandledRejection instead of leaving it mid-write.packages/loopover-miner/lib/attempt-log.js,claim-ledger.js,event-ledger.js,run-state.js,portfolio-queue.js,policy-doc-cache.js,policy-verdict-cache.js,ranked-candidates.js,replay-snapshot.js, andgovernor-state.jsall already open their SQLite handle throughopenLocalStoreDb.Three of the seven "canonical" local stores that
packages/loopover-miner/lib/status.js'sstoreIntegrityChecksandpackages/loopover-miner/lib/migrate-cli.js'sSTORESlist both track —governor-ledger.js,prediction-ledger.js, andplan-store.js— never migrated. Each still hand-rolls the exact samemkdirSync/new DatabaseSync(...)/chmodSync/PRAGMA busy_timeoutsequencelocal-store.jswas built to replace (seegovernor-ledger.js'sinitGovernorLedger,prediction-ledger.js'sinitPredictionLedger, andplan-store.js's DB-open block). Because none of the three callopenLocalStoreDb, none of them callregisterCleanupResource— a SIGINT/SIGTERM/crash mid-write to any of these three stores is never flushed/closed by the process-lifecycle cleanup chokepoint, unlike every sibling store.Requirements
packages/loopover-miner/lib/governor-ledger.js'sinitGovernorLedgerMUST open its database handle by callingopenLocalStoreDb(imported from./local-store.js) instead of hand-rollingmkdirSync/new DatabaseSync(...)/chmodSync/PRAGMA busy_timeout.packages/loopover-miner/lib/prediction-ledger.js'sinitPredictionLedgerMUST open its database handle by callingopenLocalStoreDbinstead of hand-rolling the same sequence.packages/loopover-miner/lib/plan-store.js'sopenPlanStorefunction MUST open its database handle by callingopenLocalStoreDbinstead of hand-rolling the same sequence (including its existing:memory:special-case, whichopenLocalStoreDbalready handles generically).resolve*DbPath/path-normalization helpers toresolveLocalStoreDbPath/normalizeLocalStoreDbPathfrom./local-store.js, mirroringclaim-ledger.js's andevent-ledger.js's existing usage, rather than keeping a second hand-written copy of that logic.LOOPOVER_MINER_GOVERNOR_LEDGER_DB,LOOPOVER_MINER_PREDICTION_LEDGER_DB,LOOPOVER_MINER_PLAN_STORE_DB,LOOPOVER_MINER_CONFIG_DIR,XDG_CONFIG_HOMEmust all still resolve identically).cleanupResourceCount()(frompackages/loopover-miner/lib/process-lifecycle.js) by one, and closing the store via its ownclose()MUST decrease it back. Add this assertion to each store's own existing unit test file (test/unit/miner-governor-ledger.test.ts,test/unit/miner-prediction-ledger.test.ts,test/unit/miner-plan-store.test.ts).Deliverables
packages/loopover-miner/lib/governor-ledger.jsmigrated ontoopenLocalStoreDb/resolveLocalStoreDbPath/normalizeLocalStoreDbPath.packages/loopover-miner/lib/prediction-ledger.jsmigrated ontoopenLocalStoreDb/resolveLocalStoreDbPath/normalizeLocalStoreDbPath.packages/loopover-miner/lib/plan-store.jsmigrated ontoopenLocalStoreDb/resolveLocalStoreDbPath/normalizeLocalStoreDbPath.test/unit/miner-governor-ledger.test.ts,test/unit/miner-prediction-ledger.test.ts, and the plan-store test file) asserting the opened store is registered as a cleanup resource and unregistered onclose().Test Coverage Requirements
All touched paths are under
packages/loopover-miner/lib/**, which is covered by this repo's 99%+ Codecov patch gate. Every changed line (the newopenLocalStoreDb/resolveLocalStoreDbPathcall sites and the removed hand-rolled open code) must be exercised by the existing or newly-added unit tests — do not rely on integration tests alone to cover the diff.Expected Outcome
governor-ledger.js,prediction-ledger.js, andplan-store.jsopen their SQLite handles identically to every other local store in the package, and a SIGINT/SIGTERM/uncaught exception during a write to any of the three is flushed/closed byinstallCliSignalHandlersexactly as it already is forclaim-ledger.js,event-ledger.js,run-state.js, andportfolio-queue.js.Links & Resources
packages/loopover-miner/lib/local-store.js(feat(miner-manage): local SQLite schema for run-state, claim ledger, and PR portfolio #4272)packages/loopover-miner/lib/process-lifecycle.js(Add signal/crash handling to the miner CLI #4826)packages/loopover-miner/lib/claim-ledger.js(reference implementation already usingopenLocalStoreDb)packages/loopover-miner/lib/status.js'sstoreIntegrityChecksandpackages/loopover-miner/lib/migrate-cli.js'sSTORES(the canonical seven-store list)