Skip to content

miner(discover): --dry-run opens, migrates and prunes the event ledger #9679

Description

@JSONbored

⚠️ 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:

  • line 228: "Add a --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."
  • 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.

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    gittensor:bugGittensor-scored bug fix — scores a 0.05x multiplier.help wantedExtra attention is needed

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions