You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
⚠️ Definition of Done: this issue must be completed in full, in a single PR. Do not split this
work across multiple PRs, and do not defer any Deliverable below to a follow-up issue. A PR that
satisfies only some of the Deliverables, stubs a required test, or leaves a checkbox
partially-done does NOT resolve this issue and will be closed.
Context
runDiscover in packages/loopover-miner/lib/discover-cli.ts documents the --dry-run contract in two places:
lines 537-540: "... it never opens any local store (portfolio queue, policy-doc cache, policy-verdict cache), since opening a not-yet-existing SQLite store file is itself a write."
The min-rank-override resolution block added later sits above the dry-run branch and is unconditional:
let minRankScore: number = AMS_MIN_RANK_SHIPPED;
{
let overrideLedger = null;
try {
const ledgerEnv = options.env ?? process.env;
overrideLedger = initEventLedger(resolveEventLedgerDbPath(ledgerEnv));
initEventLedger calls openLocalStoreDb (which mkdirSyncs the state dir and creates the DB file), runs CREATE TABLE IF NOT EXISTS, runs applySchemaMigrations, and calls pruneLedgerByRetention — so loopover-miner discover --dry-run today creates event-ledger.sqlite3 if absent, applies schema migrations to it, and can delete ledger rows when LOOPOVER_MINER_LEDGER_RETENTION_DAYS / _MAX_ROWS are set. That is three classes of write on a command whose entire point is to make none, and it is the only store the dry-run path touches (the portfolio queue is correctly stubbed with a no-op store).
The min-rank value itself is genuinely wanted on the dry-run path (the comment at lines 512-514 says it is "passed to BOTH the real enqueue and the dry-run preview, so a dry run shows the exact skip set a real run would produce"), so the fix is to read it without creating the store, not to skip it.
Requirements
On the --dry-run path, runDiscover must not create the event-ledger file, must not run its migrations, and must not run retention pruning.
The min-rank override must still be resolved and applied to the dry-run preview when the ledger file already exists: guard the initEventLedger(...) call with an existsSync check on resolveEventLedgerDbPath(ledgerEnv) when parsed.dryRun is true, falling back to AMS_MIN_RANK_SHIPPED when the file is absent.
The non-dry-run path must keep its current behaviour exactly (unconditional initEventLedger).
The existing try/catch/finally fail-open shape (minRankScore = AMS_MIN_RANK_SHIPPED on any read error, overrideLedger?.close() in finally) must be preserved.
⚠️ Required pattern: use the same "a store file that does not exist yet is skipped, not created" discipline packages/loopover-miner/lib/migrate-cli.ts:104 and packages/loopover-miner/lib/store-maintenance.ts:117 already implement with existsSync(dbPath). It does NOT satisfy this issue to move the min-rank block inside the non-dry-run branch only (that would silently change what the dry-run preview reports), to open the ledger read-only and hand-roll a second min-rank reader, or to add a new option flag for callers to disable the ledger read.
Deliverables
runDiscover skips the initEventLedger(...) call entirely when parsed.dryRun is true and existsSync(resolveEventLedgerDbPath(ledgerEnv)) is false.
A new named regression test in test/unit/miner-discover-cli.test.ts runs runDiscover with --dry-run against a temp LOOPOVER_MINER_CONFIG_DIR containing no event ledger, and asserts the event-ledger file does not exist afterwards.
A second new case asserts that when the event ledger DOES already exist and carries a min-rank override, --dry-run's reported below-min-rank skip set still reflects that override.
A third new case asserts the non-dry-run path still opens the ledger (unchanged behaviour).
All Deliverables above are required in a single PR. A PR that satisfies only some of them — for example adding the existsSync guard without the "override still applies when the file exists" case, which proves the dry-run preview was not silently degraded — does not resolve this issue.
Test Coverage Requirements
packages/loopover-miner/lib/**/*.ts IS inside Codecov's coverage.include in vitest.config.ts, so the 99%+ branch-counted codecov/patch gate applies exactly as for src/**. The new condition is a compound dryRun && !existsSync(...); every arm (dry-run with no file, dry-run with a file, non-dry-run) needs a test, and the fix needs a named regression test that fails against the current code.
Expected Outcome
loopover-miner discover --dry-run performs zero filesystem writes, matching the contract stated at discover-cli.ts:228 and :537-540, while still previewing the exact min-rank skip set a real run would produce when the ledger already exists.
Context
runDiscoverinpackages/loopover-miner/lib/discover-cli.tsdocuments the--dry-runcontract in two places:--dry-run/ simulate mode across the miner CLI #4847: fetches + ranks exactly as a real run, but skips opening any local store and makes zero writes."The min-rank-override resolution block added later sits above the dry-run branch and is unconditional:
initEventLedgercallsopenLocalStoreDb(whichmkdirSyncs the state dir and creates the DB file), runsCREATE TABLE IF NOT EXISTS, runsapplySchemaMigrations, and callspruneLedgerByRetention— soloopover-miner discover --dry-runtoday createsevent-ledger.sqlite3if absent, applies schema migrations to it, and can delete ledger rows whenLOOPOVER_MINER_LEDGER_RETENTION_DAYS/_MAX_ROWSare set. That is three classes of write on a command whose entire point is to make none, and it is the only store the dry-run path touches (the portfolio queue is correctly stubbed with a no-op store).The min-rank value itself is genuinely wanted on the dry-run path (the comment at lines 512-514 says it is "passed to BOTH the real enqueue and the dry-run preview, so a dry run shows the exact skip set a real run would produce"), so the fix is to read it without creating the store, not to skip it.
Requirements
--dry-runpath,runDiscovermust not create the event-ledger file, must not run its migrations, and must not run retention pruning.initEventLedger(...)call with anexistsSynccheck onresolveEventLedgerDbPath(ledgerEnv)whenparsed.dryRunis true, falling back toAMS_MIN_RANK_SHIPPEDwhen the file is absent.initEventLedger).try/catch/finallyfail-open shape (minRankScore = AMS_MIN_RANK_SHIPPEDon any read error,overrideLedger?.close()infinally) must be preserved.Deliverables
runDiscoverskips theinitEventLedger(...)call entirely whenparsed.dryRunis true andexistsSync(resolveEventLedgerDbPath(ledgerEnv))is false.test/unit/miner-discover-cli.test.tsrunsrunDiscoverwith--dry-runagainst a tempLOOPOVER_MINER_CONFIG_DIRcontaining no event ledger, and asserts the event-ledger file does not exist afterwards.--dry-run's reported below-min-rank skip set still reflects that override.All Deliverables above are required in a single PR. A PR that satisfies only some of them — for example adding the
existsSyncguard without the "override still applies when the file exists" case, which proves the dry-run preview was not silently degraded — does not resolve this issue.Test Coverage Requirements
packages/loopover-miner/lib/**/*.tsIS inside Codecov'scoverage.includeinvitest.config.ts, so the 99%+ branch-countedcodecov/patchgate applies exactly as forsrc/**. The new condition is a compounddryRun && !existsSync(...); every arm (dry-run with no file, dry-run with a file, non-dry-run) needs a test, and the fix needs a named regression test that fails against the current code.Expected Outcome
loopover-miner discover --dry-runperforms zero filesystem writes, matching the contract stated atdiscover-cli.ts:228and:537-540, while still previewing the exact min-rank skip set a real run would produce when the ledger already exists.Links & Resources
packages/loopover-miner/lib/discover-cli.ts:509-527,:536-542,packages/loopover-miner/lib/event-ledger.ts,packages/loopover-miner/lib/migrate-cli.ts:104,packages/loopover-miner/lib/store-maintenance.ts:117.