fix: guard the integrity sweep against wiping the library on a dead mount

The sweep stats every track path and marks unreadable files missing, with no
check that /music is mounted. An unmounted or misbehaving bind would fail
every stat and mark the entire library missing in one pass; the ratio is only
recoverable by a full rescan.

Three guards, cheapest first:
  - liveness: probe a sample of existing track paths before doing anything;
    abort if none are readable
  - ratio: abort mid-sweep if the missing fraction crosses a threshold,
    leaving already-marked rows alone rather than rolling back a partial pass
  - progress: keyset pagination over id with the cursor persisted in
    integrity_sweep_state, so a sweep aborted or restarted mid-run resumes
    instead of re-walking from the top and re-marking

The repair-corrupted-metadata script shares the same failure mode and gets
the same abort path.

REVIEW-2026-07-30.md secondary finding: integrity sweep has no mount check.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
kami
2026-07-30 23:56:49 +04:00
parent ee43995e96
commit 27e8acc592
3 changed files with 167 additions and 12 deletions
@@ -29,9 +29,13 @@ async function main() {
const integrity = new IntegrityService(pgClient, queue);
const summary = await integrity.runSweep();
console.log(
`[Repair] Done: detected=${summary.detected} fixed=${summary.fixed} needsReview=${summary.needsReview}`
);
if (summary.aborted) {
console.error(`[Repair] Sweep ABORTED by safety guard: ${summary.abortReason}`);
} else {
console.log(
`[Repair] Done: detected=${summary.detected} fixed=${summary.fixed} needsReview=${summary.needsReview}`
);
}
await pgClient.end();
console.log('[Repair] Connection closed.');