feat(server/m7-scan-progress): wire stagePublisher into 4 scan stages

Each of the 4 stage workers (library walk, MBID backfill, cover
enrich, artist art enrich) gains a progressCb parameter that
receives a snapshot of the current running state per item processed.
The orchestrator stores the snapshot in a local var that the
publisher's marshal closure reads at flush time.

scanrun.go's 4 stage blocks are rewritten to construct one publisher
per stage, pass a Tick-firing closure to the worker as progressCb,
and call Flush() after the worker returns (routing any persist error
to captureErr — preserves today's diagnostic behavior).

The previous post-stage UpdateScanRun* calls are removed; the
publisher's Flush handles the final write. Cadence: every 500 items
OR every 1s, whichever fires first.

Outside-orchestrator callers (scanner_test.go, enricher_test.go)
pass nil for progressCb — no-op behavior preserved for tests that
don't need progress observability.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-06 21:14:50 -04:00
parent 7b9ea131a2
commit 3e9e46f190
7 changed files with 141 additions and 45 deletions
+2 -2
View File
@@ -82,7 +82,7 @@ func TestScanner_Integration(t *testing.T) {
})
scanner := New(pool, logger, []string{root})
stats, err := scanner.Scan(ctx)
stats, err := scanner.Scan(ctx, nil)
if err != nil {
t.Fatalf("first scan: %v", err)
}
@@ -103,7 +103,7 @@ func TestScanner_Integration(t *testing.T) {
t.Errorf("artist sort = [%q, %q], want [Artist X, Artist Y]", artists[0].SortName, artists[1].SortName)
}
stats2, err := scanner.Scan(ctx)
stats2, err := scanner.Scan(ctx, nil)
if err != nil {
t.Fatalf("second scan: %v", err)
}