Why now
BS#1355 introduces trustedAlbumMatch and migrates three librarian-typed call sites of LookupResponse.results[0].artwork to it. apps/backend/services/metadata/metadata.service.ts:72 is the one ungated consumer 1355 explicitly left alone, because it sits on a track-context surface (flowsheet now-playing) rather than an album-context surface, and the strict search_type === 'direct' gate would null out a cohort where current behavior is correct UX.
This ticket exists to decide that policy on its own timeline, not under regression pressure from a broader migration.
The site
// apps/backend/services/metadata/metadata.service.ts:68-77
const lookupResponse = await lmlLookupCoordinator.lookup(artistName, albumTitle, trackTitle, {
caller: 'metadata-service',
budgetMs: METADATA_SERVICE_LML_BUDGET_MS,
});
const artwork: DiscogsMatchResult | null = lookupResponse.results?.[0]?.artwork ?? null;
if (artwork) {
result.album = extractAlbumMetadata(artwork);
result.artist = extractArtistMetadata(artwork) ?? undefined;
}
Called fire-and-forget on every flowsheet track insert (enrichment.service.ts, flowsheet-metadata-backfill). Persists into flowsheet.discogs_release_id, flowsheet.artwork_url, flowsheet.release_year, four streaming URLs on the flowsheet row, plus album_metadata (via Epic D #898). Read by dj-site flowsheet rendering and iOS now-playing surfaces.
Why the BS#1355 helper doesn't fit
The flowsheet entry is a track played by a DJ, not a librarian's typed album. Walking through search_type values from that frame:
search_type |
LML meaning |
Track-context correctness |
direct |
Discogs release matches the typed album |
Correct (all fields) |
compilation |
Track appears on a compilation; release ≠ typed album |
Correct — artwork = compilation cover, streaming URL = compilation page that plays the typed track, release_year = compilation year. The track is there. |
alternative |
Alternative release of the typed album (different country/format) |
Mostly correct — streaming URL plays the track, artwork is the alt release, release_year may diverge |
fallback |
Artist-only match; album not confirmed |
Wrong-album (Yenbett-class) |
song_as_artist |
Track title matched as artist field of an unrelated release |
Wrong-everything |
none |
No match |
No data |
direct and compilation produce data the now-playing surface should keep. fallback and song_as_artist and none produce Yenbett-class wrong-data. alternative is the edge.
trustedAlbumMatch (BS#1355) rejects everything except direct — correct for library.artwork_url (librarian typed a specific album) but a regression here for the compilation cohort.
Policy choices
A. Accept direct + compilation, reject the rest. Cleanest line. Compilation is the only non-direct type where LML explicitly confirms the track is on the release (driven by LML's SONG_AS_TRACK strategy, catalog-track-search plan §5.1). alternative confirms only the album shape, not the specific track. Add a third helper trustedTrackContextMatch to lml/trust.ts and migrate this site.
B. Accept direct + compilation + alternative, reject fallback + song_as_artist + none. Wider. The streaming-URL value of alternative (Spotify still plays the track from the alt release) is real, and the release_year discrepancy is a UX nit. Risk: alternative-release artwork showing up under a typed album occasionally misleads viewers.
C. Accept anything with a non-null track_match_hint. Read from the matched_via field on LookupResultItem (catalog-track-search §5.1) instead of search_type. Most precise but requires confirming LML populates matched_via on the relevant search-type branches.
D. Status quo + per-field policy. Keep accepting everything for streaming URLs (the "where to hear it" answer survives most match shapes), strict-gate only discogs_release_id and release_year (the "what is this release" claims). Splits the row, but matches what the data actually represents.
Recommendation
A. Mirrors the "specific track is confirmed on this release" axis cleanly, doesn't require touching matched_via shape, and the alternative cohort is small relative to compilation. D is the most accurate model of the data but the cost is non-trivial — split-helper API, two write paths through extractAlbumMetadata. Defer D unless A is shown to cut a meaningful real cohort.
Helper sketch for A:
/**
* Track-context gate. Releases artwork when LML confirms the typed track
* appears on the returned release. Use for flowsheet now-playing metadata
* and similar track-played-by-DJ surfaces — NOT for librarian-typed album
* surfaces (use trustedAlbumMatch for those).
*
* Accepts `direct` (release matches typed album) and `compilation` (track
* confirmed on a compilation that is not the typed album, per LML's
* SONG_AS_TRACK strategy). Rejects `alternative`, `fallback`,
* `song_as_artist`, `none`.
*/
export function trustedTrackContextMatch(
response: LookupResponse,
opts: TrustOpts,
): DiscogsMatchResult | null { /* ... */ }
Out of scope
- The artist-side fields (
artist_bio, wikipedia_url) survive album-mismatch and don't need a track-context gate. Worth a follow-up helper if the artist cohort gets a different policy, but no current data-quality issue forces it.
- Streaming-URL nuance per platform (Spotify/Apple/YT/Bandcamp/SoundCloud each have their own match strictness inside LML). Out of scope here; this ticket gates the BS-side consumption only.
Related
Why now
BS#1355 introduces
trustedAlbumMatchand migrates three librarian-typed call sites ofLookupResponse.results[0].artworkto it.apps/backend/services/metadata/metadata.service.ts:72is the one ungated consumer 1355 explicitly left alone, because it sits on a track-context surface (flowsheet now-playing) rather than an album-context surface, and the strictsearch_type === 'direct'gate would null out a cohort where current behavior is correct UX.This ticket exists to decide that policy on its own timeline, not under regression pressure from a broader migration.
The site
Called fire-and-forget on every flowsheet track insert (
enrichment.service.ts,flowsheet-metadata-backfill). Persists intoflowsheet.discogs_release_id,flowsheet.artwork_url,flowsheet.release_year, four streaming URLs on the flowsheet row, plusalbum_metadata(via Epic D #898). Read by dj-site flowsheet rendering and iOS now-playing surfaces.Why the BS#1355 helper doesn't fit
The flowsheet entry is a track played by a DJ, not a librarian's typed album. Walking through
search_typevalues from that frame:search_typedirectcompilationalternativefallbacksong_as_artistnonedirectandcompilationproduce data the now-playing surface should keep.fallbackandsong_as_artistandnoneproduce Yenbett-class wrong-data.alternativeis the edge.trustedAlbumMatch(BS#1355) rejects everything exceptdirect— correct forlibrary.artwork_url(librarian typed a specific album) but a regression here for the compilation cohort.Policy choices
A. Accept
direct+compilation, reject the rest. Cleanest line. Compilation is the only non-direct type where LML explicitly confirms the track is on the release (driven by LML's SONG_AS_TRACK strategy, catalog-track-search plan §5.1).alternativeconfirms only the album shape, not the specific track. Add a third helpertrustedTrackContextMatchtolml/trust.tsand migrate this site.B. Accept
direct+compilation+alternative, rejectfallback+song_as_artist+none. Wider. The streaming-URL value ofalternative(Spotify still plays the track from the alt release) is real, and the release_year discrepancy is a UX nit. Risk: alternative-release artwork showing up under a typed album occasionally misleads viewers.C. Accept anything with a non-null
track_match_hint. Read from thematched_viafield onLookupResultItem(catalog-track-search §5.1) instead ofsearch_type. Most precise but requires confirming LML populatesmatched_viaon the relevant search-type branches.D. Status quo + per-field policy. Keep accepting everything for streaming URLs (the "where to hear it" answer survives most match shapes), strict-gate only
discogs_release_idandrelease_year(the "what is this release" claims). Splits the row, but matches what the data actually represents.Recommendation
A. Mirrors the "specific track is confirmed on this release" axis cleanly, doesn't require touching
matched_viashape, and thealternativecohort is small relative tocompilation. D is the most accurate model of the data but the cost is non-trivial — split-helper API, two write paths throughextractAlbumMetadata. Defer D unless A is shown to cut a meaningful real cohort.Helper sketch for A:
Out of scope
artist_bio,wikipedia_url) survive album-mismatch and don't need a track-context gate. Worth a follow-up helper if the artist cohort gets a different policy, but no current data-quality issue forces it.Related
trustedAlbumMatchhelper + librarian-typed-site migration. This ticket is its track-context sibling.matched_viafield