f51b7c05ae
Replaces the M2 stub /api/radio with real weighted-shuffle scoring: LikeBoost + recency_decay - skip_ratio penalty + jitter, hard suppression of last-hour plays. Wide candidate pool (whole library) for v1; M4 adds similarity-based pool refinement. Pure scoring function in internal/recommendation; live-DB candidate loader; HTTP handler is a thin shim. No web changes — existing playRadio action already calls /api/radio. Sub-plan #1 of 3 in M3 (Fable #340). Session vectors and contextual match score land in the next two sub-plans.