Skip to content

Commit 0a94689

Browse files
fix(miner): include four durable stores in doctor and migrate sweeps
governor-state, attempt-log, replay-snapshot, and worktree-allocator were only migrated lazily; keep storeIntegrityChecks and migrate STORES in sync at eleven entries. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 2e2f588 commit 0a94689

4 files changed

Lines changed: 40 additions & 4 deletions

File tree

packages/loopover-miner/lib/migrate-cli.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
// whatever command happens to touch it first -- this command instead lets an operator PROACTIVELY bring every
44
// known store's EXISTING on-disk file up to date in one pass (e.g. right after upgrading, or before starting a
55
// fleet), without needing to guess which command happens to touch which store first. Mirrors status.js's
6-
// storeIntegrityChecks [name, resolve*DbPath(env)] store list exactly (same seven stores `doctor` already
7-
// covers), but actually OPENS each store (rather than a read-only integrity probe) so its real open/init
6+
// storeIntegrityChecks [name, resolve*DbPath(env)] store list exactly (same eleven stores `doctor` already
7+
// covers, #6768), but actually OPENS each store (rather than a read-only integrity probe) so its real open/init
88
// function's migration path runs for real. A store file that does not exist yet is skipped, not created --
99
// "migrate" brings existing files up to date; it is not another way to bootstrap fresh state (that's `init`).
1010
import { existsSync } from "node:fs";
@@ -18,6 +18,10 @@ import { initPredictionLedger, resolvePredictionLedgerDbPath } from "./predictio
1818
import { initPortfolioQueueStore, resolvePortfolioQueueDbPath } from "./portfolio-queue.js";
1919
import { initRunStateStore, resolveRunStateDbPath } from "./run-state.js";
2020
import { openPlanStore, resolvePlanStoreDbPath } from "./plan-store.js";
21+
import { openGovernorState, resolveGovernorStateDbPath } from "./governor-state.js";
22+
import { initAttemptLog, resolveAttemptLogDbPath } from "./attempt-log.js";
23+
import { openReplaySnapshotStore, resolveReplaySnapshotDbPath } from "./replay-snapshot.js";
24+
import { openWorktreeAllocator, resolveWorktreeAllocatorDbPath } from "./worktree-allocator.js";
2125

2226
const MIGRATE_USAGE = "Usage: loopover-miner migrate [--json]";
2327

@@ -29,6 +33,10 @@ const STORES = [
2933
{ name: "claim-ledger", resolveDbPath: resolveClaimLedgerDbPath, open: openClaimLedger },
3034
{ name: "run-state", resolveDbPath: resolveRunStateDbPath, open: initRunStateStore },
3135
{ name: "plan-store", resolveDbPath: resolvePlanStoreDbPath, open: openPlanStore },
36+
{ name: "governor-state", resolveDbPath: resolveGovernorStateDbPath, open: openGovernorState },
37+
{ name: "attempt-log", resolveDbPath: resolveAttemptLogDbPath, open: initAttemptLog },
38+
{ name: "replay-snapshot", resolveDbPath: resolveReplaySnapshotDbPath, open: openReplaySnapshotStore },
39+
{ name: "worktree-allocator", resolveDbPath: resolveWorktreeAllocatorDbPath, open: (dbPath) => openWorktreeAllocator({ dbPath }) },
3240
];
3341

3442
/** Read a store file's stamped schema version without ever creating it -- matches checkStoreIntegrity's

packages/loopover-miner/lib/status.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ import { resolvePortfolioQueueDbPath } from "./portfolio-queue.js";
2121
import { resolveClaimLedgerDbPath } from "./claim-ledger.js";
2222
import { resolveRunStateDbPath } from "./run-state.js";
2323
import { resolvePlanStoreDbPath } from "./plan-store.js";
24+
import { resolveGovernorStateDbPath } from "./governor-state.js";
25+
import { resolveAttemptLogDbPath } from "./attempt-log.js";
26+
import { resolveReplaySnapshotDbPath } from "./replay-snapshot.js";
27+
import { resolveWorktreeAllocatorDbPath } from "./worktree-allocator.js";
2428

2529
// Slim laptop-mode CLI commands (#2288): `status` (what's installed + where local state lives) and `doctor` (is
2630
// this laptop set up correctly). Both are read-only and 100% local — no repo-scanning, no coding-agent invocation,
@@ -297,7 +301,8 @@ function checkStateDirWritable(stateDir) {
297301
}
298302

299303
/** Per-store `PRAGMA integrity_check` sweep for `doctor` (#4834) — flags a corrupted store instead of probing
300-
* only one with `SELECT 1`. A store file that does not exist yet is healthy by absence. */
304+
* only one with `SELECT 1`. A store file that does not exist yet is healthy by absence. Keep in sync with
305+
* migrate-cli.js's `STORES` list (#6768): every durable local SQLite store using resolveLocalStoreDbPath. */
301306
function storeIntegrityChecks(env) {
302307
const stores = [
303308
["event-ledger", resolveEventLedgerDbPath(env)],
@@ -307,6 +312,10 @@ function storeIntegrityChecks(env) {
307312
["claim-ledger", resolveClaimLedgerDbPath(env)],
308313
["run-state", resolveRunStateDbPath(env)],
309314
["plan-store", resolvePlanStoreDbPath(env)],
315+
["governor-state", resolveGovernorStateDbPath(env)],
316+
["attempt-log", resolveAttemptLogDbPath(env)],
317+
["replay-snapshot", resolveReplaySnapshotDbPath(env)],
318+
["worktree-allocator", resolveWorktreeAllocatorDbPath(env)],
310319
];
311320
return stores.map(([name, dbPath]) => checkStoreIntegrity(`store-integrity:${name}`, dbPath));
312321
}

test/unit/miner-migrate-cli.test.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ const STORE_NAMES = [
2424
"claim-ledger",
2525
"run-state",
2626
"plan-store",
27+
"governor-state",
28+
"attempt-log",
29+
"replay-snapshot",
30+
"worktree-allocator",
2731
];
2832

2933
afterEach(() => {
@@ -32,11 +36,13 @@ afterEach(() => {
3236
});
3337

3438
describe("loopover-miner migrate (#4871)", () => {
35-
it("covers the exact same seven stores doctor's store-integrity sweep covers, in the same order, and skips every one when nothing has been created yet", () => {
39+
it("covers the exact same eleven stores doctor's store-integrity sweep covers, in the same order, and skips every one when nothing has been created yet", () => {
3640
const env = tempEnv();
3741
const results = runMigrateChecks(env);
3842

3943
expect(results.map((result) => result.name)).toEqual(STORE_NAMES);
44+
// REGRESSION (#6768): these four durable stores were previously omitted from both migrate and doctor.
45+
expect(STORE_NAMES).toEqual(expect.arrayContaining(["governor-state", "attempt-log", "replay-snapshot", "worktree-allocator"]));
4046
for (const result of results) {
4147
expect(result.ok).toBe(true);
4248
expect(result.status).toBe("skipped");

test/unit/miner-status.test.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,20 @@ describe("loopover-miner status/doctor (#2288)", () => {
125125
"store-integrity:claim-ledger",
126126
"store-integrity:run-state",
127127
"store-integrity:plan-store",
128+
"store-integrity:governor-state",
129+
"store-integrity:attempt-log",
130+
"store-integrity:replay-snapshot",
131+
"store-integrity:worktree-allocator",
128132
]);
133+
// REGRESSION (#6768): doctor previously omitted these four durable local stores from the integrity sweep.
134+
expect(checks.map((check) => check.name)).toEqual(
135+
expect.arrayContaining([
136+
"store-integrity:governor-state",
137+
"store-integrity:attempt-log",
138+
"store-integrity:replay-snapshot",
139+
"store-integrity:worktree-allocator",
140+
]),
141+
);
129142
expect(runDoctor([], env, cwd)).toBe(0);
130143
expect(log).toHaveBeenCalled();
131144
});

0 commit comments

Comments
 (0)