fix(vibe): let a dislike outlive the session that heard it

Disliking a track in a Vibe wrote one row to the session ledger and
nothing else. The ledger only excludes a track from the session it was
recorded in, so the same track came back the next evening, and the one
after that. A dislike in a Vibe is the same verdict as a dislike
anywhere else, so it now takes the same path.

Two more things undid a dislike that did land. The library scan rewrote
every track's state from the file on disk, which restored every HIDDEN
track to LIBRARY on every scan; finding a file again says nothing about
whether the listener wants to hear it. And hiding only matched tracks in
LIBRARY, so a disliked probation recommendation stayed eligible.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KENqSChfyqWnor6ud2WWH6
This commit is contained in:
kami
2026-08-10 13:59:16 +04:00
parent 0749f6ad10
commit 5a73a6a6f3
4 changed files with 55 additions and 3 deletions
+7 -2
View File
@@ -416,9 +416,14 @@ export class DbService {
*/
async dislikeTrack(userId: string, trackId: string): Promise<void> {
await this.withTransaction(async (client) => {
// Phase 1: hide the track in all active views
// Phase 1: hide the track in all active views. A probation recommendation
// is disliked the same way a library track is, and retires on the spot —
// the listener has answered the question probation exists to ask.
await client.query(
"UPDATE tracks SET state = 'HIDDEN' WHERE id = $1 AND state = 'LIBRARY'",
`UPDATE tracks
SET state = 'HIDDEN',
probation_status = CASE WHEN state = 'RECOMMENDED' THEN 'retired' ELSE probation_status END
WHERE id = $1 AND state IN ('LIBRARY', 'RECOMMENDED')`,
[trackId]
);