You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Surfaced by the #541 search→queue consistency audit. The same "best fuzzy match" decision is implemented twice, and the copies have drifted:
app/services/sync/enrichment_pipeline.py::_find_best_match (the request/master-store path) — has a min_artist_score floor (0.35), a BPM-consensus tiebreaker (+0.01), version bonus/penalty (±0.1, min_score 0.4), and structured logging.
The inline matcher in app/services/recommendation/enrichment.py (enrich_from_beatport / enrich_from_tidal, ~:31/:121) — same core scoring (fuzzy_match_score + artist_match_score + version bonus) but missing the min_artist_score floor, the BPM-consensus tiebreaker, and the logging.
Same decision in 3+ call sites with behavioral drift → meets the DRY-extraction bar. The recommendation copy can accept a worse-artist match the request path would reject.
What
Extract ONE shared best-match function (e.g. app/services/track_normalizer.py or a small match.py) with the request-path behavior (incl. min_artist_score + BPM-consensus tiebreaker), and route both the enrichment pipeline and the recommendation engine through it.
Why
Surfaced by the #541 search→queue consistency audit. The same "best fuzzy match" decision is implemented twice, and the copies have drifted:
app/services/sync/enrichment_pipeline.py::_find_best_match(the request/master-store path) — has amin_artist_scorefloor (0.35), a BPM-consensus tiebreaker (+0.01), version bonus/penalty (±0.1, min_score 0.4), and structured logging.app/services/recommendation/enrichment.py(enrich_from_beatport/enrich_from_tidal, ~:31/:121) — same core scoring (fuzzy_match_score + artist_match_score + version bonus) but missing themin_artist_scorefloor, the BPM-consensus tiebreaker, and the logging.Same decision in 3+ call sites with behavioral drift → meets the DRY-extraction bar. The recommendation copy can accept a worse-artist match the request path would reject.
What
app/services/track_normalizer.pyor a smallmatch.py) with the request-path behavior (incl.min_artist_score+ BPM-consensus tiebreaker), and route both the enrichment pipeline and the recommendation engine through it.tracksrow.Notes / context
musical_keyvskey/camelot, bpm int vs float acrossSearchResult/TrackProfile/BeatportSearchResult) should be reconciled when feat(setbuilder): pool reads global store + pool→builder contract & build coverage gate #542 funnels pool writes intotracks.Source: #541 search→queue consistency audit (MEDIUM-3).