feat: make hard deletion of disliked tracks real

The dislike lifecycle promised WARNED -> HIDDEN -> deleted, but nothing ever
removed a file: cleanup.service logged its intent behind
MUZICK_ALLOW_HARD_DELETE and returned, and the two backend delete paths
(hardDeleteTrack, permanentlyDeleteTrack) disagreed about what deletion
meant. The review recommended dropping hard deletion and making HIDDEN
terminal; the owner chose to make deletion real instead.

  - cleanup.service performs a true unlink() — no trash directory — for
    tracks that have been HIDDEN for a 7-day grace period, then settles the
    row. This is the single unlink() call site in the system.
  - permanentlyDeleteTrack is the one delete path; hardDeleteTrack is gone.
  - a deleted_permanent audit row records what was removed, and
    migration 20260730_hard_delete_audit_trail backs it.

MUZICK_ALLOW_HARD_DELETE remains OFF: the docker-compose entry is commented
out, there is no enabling default in code, and the worker's /music bind is
the only writable one. Deletion stays dry-run until the owner opts in
deliberately.

REVIEW-2026-07-30.md open decision: dislike lifecycle.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
kami
2026-07-30 23:56:49 +04:00
parent 27e8acc592
commit 963f845733
6 changed files with 450 additions and 103 deletions
+7
View File
@@ -211,8 +211,15 @@ CREATE TABLE IF NOT EXISTS track_genre (
CREATE TABLE IF NOT EXISTS dislikes (
track_id UUID PRIMARY KEY REFERENCES tracks(id) ON DELETE CASCADE,
disliked_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
-- When the track entered HIDDEN. The pre-deletion grace period
-- (MUZICK_HARD_DELETE_GRACE_DAYS, default 7 days) is measured from here —
-- separate from, and much longer than, grace_hours below. Recorded
-- explicitly rather than inferred from disliked_at, because an irreversible
-- clock must not rest on 'HIDDEN' merely being the state DEFAULT.
hidden_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
warned_at TIMESTAMP,
deleted_at TIMESTAMP,
-- HIDDEN -> WARNED delay (the ntfy warning), NOT the deletion delay.
grace_hours INTEGER DEFAULT 48,
state dislike_state DEFAULT 'HIDDEN'
);