# Lifecycle & Removal Specification This document defines the "Dislike $\rightarrow$ Delayed Deletion" lifecycle. This state machine ensures user intent is respected while preventing accidental permanent loss of music. ## 1. The Dislike State Machine A track follows this state transition to ensure a "Grace Period" before physical deletion. | Current State | Action | Next State | Side Effects | | :--- | :--- | :--- | :--- | | **LIBRARY** | User Dislikes | **PENDING_REMOVAL** | `dislikes` row created; Track becomes `HIDDEN`. | | **PENDING_REMOVAL** | User Restores | **LIBRARY** | `dislikes` row deleted; Track becomes `VISIBLE`. | | **PENDING_REMOVAL** | Grace Period Ends | **WARNING_SENT** | `ntfy` notification sent; `warned_at` timestamp set. | | **WARNING_SENT** | 24h Passes | **DELETED** | File deleted from FS; Track record removed from DB. | ## 2. Detailed Transitions ### **Phase 1: The Dislike (Immediate)** When a user triggers a dislike: 1. **DB Transaction:** * Update `tracks.state = 'HIDDEN'`. * Insert into `dislikes` table `{track_id, disliked_at: now}`. * Log `feedback(action='disliked')`. 2. **UI Update:** The track immediately disappears from all "active" views (Library, Playlists, Vibe Queue, Search). ### **Phase 2: The Grace Period (The "Safety Net")** * **Duration ($X$):** Configurable (default: 48 hours). * **Behavior:** The track remains on disk and in the database, but is filtered out of all user-facing discovery and playback. ### **Phase 3: The Warning (The "Nudge")** When `now > disliked_at + X`: 1. **Worker Action:** The `Cleanup/Sweep Worker` identifies the track. 2. **Notification:** Sends a message via `ntfy` (e.g., *"Are you sure? '' is scheduled for deletion in 24h"*). 3. **DB Update:** Set `dislikes.warned_at = now`. ### **Phase 4: The Finality (The "Cleanup")** When `now > warned_at + 24h`: 1. **FS Action:** Delete the physical file at `tracks.path`. 2. **DB Action:** * Cascade delete all related records (history, play_counts, etc.). * Remove the `tracks` record. 3. **Logging:** Log `feedback(action='deleted_permanent')`. ## 2. Safety Invariants - **No Immediate Deletion:** No user action (other than a "Hard Delete" admin command) can trigger immediate file deletion. - **State Consistency:** A track cannot be in `PENDING_REMOVAL` and `LIBRARY` simultaneously. - **Atomic Deletion:** The file deletion and the database removal must be treated as a single logical unit of work to prevent "Orphaned Files" (files on disk with no DB record) or "Ghost Records" (DB records with no file). ## 3. User Recovery The "Restore" action is a simple reversal: - `DELETE FROM dislikes WHERE track_id = X;` - `UPDATE tracks SET state = 'LIBRARY' WHERE id = X;`