# 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? '<Track Name>' 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;`
