feat(server,web/m7-cover-sources): scan-orchestrator 4th stage

Adds an artist-art-enrich stage after cover-enrich in RunScan. The
stage drains EnrichArtistBatch with a configurable cap and the
operator's data_dir, persists processed/succeeded/failed tallies to
the scan_runs.artist_art_enrich jsonb column added in migration
0018.

The admin /admin Library Scan card grid expands from 3 to 4 columns
with the new "Artist art" card mirroring the cover-enrichment one
(Processed / Succeeded / Failed). The TS ScanStatus type gains an
optional artist_art_enrich field. The Go scanStatusResp gains the
matching ArtistArtEnrich json.RawMessage field.

cmd/minstrel/main.go (and admin scan-trigger handler) wire the new
RunScanConfig fields: ArtistEnrichCap reuses cfg.Library.CoverArtBackfillCap
for now (can be split if separate budgets are useful), DataDir is
cfg.Storage.DataDir.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-06 13:03:36 -04:00
parent 295d9da361
commit 00cd28e79b
7 changed files with 108 additions and 26 deletions
+27 -8
View File
@@ -53,20 +53,21 @@ func (q *Queries) GetInFlightScanRun(ctx context.Context) (GetInFlightScanRunRow
}
const getLatestScanRun = `-- name: GetLatestScanRun :one
SELECT id, started_at, finished_at, library, mbid_backfill, cover_enrich, error_message
SELECT id, started_at, finished_at, library, mbid_backfill, cover_enrich, artist_art_enrich, error_message
FROM scan_runs
ORDER BY started_at DESC
LIMIT 1
`
type GetLatestScanRunRow struct {
ID pgtype.UUID
StartedAt pgtype.Timestamptz
FinishedAt pgtype.Timestamptz
Library []byte
MbidBackfill []byte
CoverEnrich []byte
ErrorMessage *string
ID pgtype.UUID
StartedAt pgtype.Timestamptz
FinishedAt pgtype.Timestamptz
Library []byte
MbidBackfill []byte
CoverEnrich []byte
ArtistArtEnrich []byte
ErrorMessage *string
}
// Powers GET /api/admin/scan/status. Returns NoRows when no scan has
@@ -81,6 +82,7 @@ func (q *Queries) GetLatestScanRun(ctx context.Context) (GetLatestScanRunRow, er
&i.Library,
&i.MbidBackfill,
&i.CoverEnrich,
&i.ArtistArtEnrich,
&i.ErrorMessage,
)
return i, err
@@ -106,6 +108,23 @@ func (q *Queries) InsertScanRun(ctx context.Context) (InsertScanRunRow, error) {
return i, err
}
const updateScanRunArtistArtEnrich = `-- name: UpdateScanRunArtistArtEnrich :exec
UPDATE scan_runs
SET artist_art_enrich = $2
WHERE id = $1
`
type UpdateScanRunArtistArtEnrichParams struct {
ID pgtype.UUID
ArtistArtEnrich []byte
}
// M7 cover-sources: persists the artist-art enrich stage tally JSON.
func (q *Queries) UpdateScanRunArtistArtEnrich(ctx context.Context, arg UpdateScanRunArtistArtEnrichParams) error {
_, err := q.db.Exec(ctx, updateScanRunArtistArtEnrich, arg.ID, arg.ArtistArtEnrich)
return err
}
const updateScanRunCoverEnrich = `-- name: UpdateScanRunCoverEnrich :exec
UPDATE scan_runs SET cover_enrich = $2 WHERE id = $1
`
+7 -1
View File
@@ -27,11 +27,17 @@ UPDATE scan_runs
-- name: GetLatestScanRun :one
-- Powers GET /api/admin/scan/status. Returns NoRows when no scan has
-- ever run; the API layer maps that to a 200 with empty payload.
SELECT id, started_at, finished_at, library, mbid_backfill, cover_enrich, error_message
SELECT id, started_at, finished_at, library, mbid_backfill, cover_enrich, artist_art_enrich, error_message
FROM scan_runs
ORDER BY started_at DESC
LIMIT 1;
-- name: UpdateScanRunArtistArtEnrich :exec
-- M7 cover-sources: persists the artist-art enrich stage tally JSON.
UPDATE scan_runs
SET artist_art_enrich = $2
WHERE id = $1;
-- name: GetInFlightScanRun :one
-- Used by POST /api/admin/scan/run to 409 when a scan is already running.
-- "In flight" = finished_at IS NULL.