Refactor recording metadata to JSON sidecar

Replaces the `.transcript.txt` sidecar with a structured `.json` file
for recording metadata. This change consolidates transcript text,
duration, and other potential metadata into a single, extensible JSON
object.

This also refactors the `TranscriptState` enum to better represent the
on-disk state (absence of file means pending) and the in-memory
representation. The `Transcript` enum now specifically models the
terminal outcomes of the transcriber (`Silent` or `Content`).

The commit includes updates to documentation, data structures, path
handling, and various tests to align with the new metadata format.
This commit is contained in:
2026-04-27 12:48:25 +02:00
parent a510c20e75
commit 66b3b7e4c8
20 changed files with 637 additions and 335 deletions
+3 -3
View File
@@ -48,12 +48,12 @@ Ubuntu Server (RTX 3060, 12 GB VRAM)
### Filesystem ist Source of Truth (SoT)
Der Zustand eines Falls wird **ausschließlich aus dem Dateisystem** abgeleitet, nicht aus einer parallelen Sidecar- oder Metadaten-Datei. Das Vorhandensein bestimmter Dateimarker (`.m4a`, `.m4a.failed`, `.transcript.txt`, `oneliner.json`, `analysis_input.json`, `document.md`, `.deleted`) plus die `WorkerBusy`-Flags bestimmen jederzeit eindeutig, was als Nächstes zu tun ist.
Der Zustand eines Falls wird **ausschließlich aus dem Dateisystem** abgeleitet, nicht aus einer parallelen Sidecar- oder Metadaten-Datei. Das Vorhandensein bestimmter Dateimarker (`<stem>.m4a`, `<stem>.m4a.failed`, `<stem>.json` (Per-Recording-Metadaten — Transcript + Duration), `oneliner.json`, `analysis_input.json`, `document.md`, `.closed`) plus die `WorkerBusy`-Flags bestimmen jederzeit eindeutig, was als Nächstes zu tun ist.
**Konsequenzen**:
- Keine State-Duplizierung (kein `state.json`, keine DB). Der zu synchronisierende Zweitstand fehlt ersatzlos — also kann er auch nicht drift.
- Recovery-Scan nach Restart ist trivial: finde `.m4a` ohne `.transcript.txt` → transkribieren. Natürliche Idempotenz, kein Crash-Recovery-Protokoll.
- Crash mitten in Whisper = `.transcript.txt` fehlt = nächster Lauf macht's nochmal. Keine Intermediate-States, die rückwärts abgewickelt werden müssten.
- Recovery-Scan nach Restart ist trivial: finde `.m4a` ohne `<stem>.json` → transkribieren. Natürliche Idempotenz, kein Crash-Recovery-Protokoll.
- Crash mitten in Whisper = `<stem>.json` fehlt = nächster Lauf macht's nochmal. Keine Intermediate-States, die rückwärts abgewickelt werden müssten. Single atomic write (`tmp` + `rename`) am Ende der Pipeline garantiert: entweder gibt es ein vollständiges JSON oder keines — nie ein halbes.
- Manuelle Reparatur möglich: Datei löschen, neu triggern. Kein „state irgendwie auf Received setzen".
**Bewusst verzichtet** auf: retry-Budget, Failed-Kategorie mit Error-Details. Wenn ein Audio dauerhaft an Whisper scheitert, loggt der Server im Crash-Loop — das fällt sofort auf und wird manuell entfernt. Diese Kategorie wird erst eingeführt, wenn ein realer Bedarf entsteht (z.B. hochvolumiger Betrieb).