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
+5 -5
View File
@@ -1205,7 +1205,7 @@ async fn reset_case_clears_derived_and_unfails_audio() {
assert!(dir.join("2026-04-15T09-05-00Z.m4a").exists());
assert!(!dir.join("2026-04-15T09-05-00Z.m4a.failed").exists());
// Derived artefacts gone.
assert!(!dir.join("2026-04-15T09-00-00Z.transcript.txt").exists());
assert!(!dir.join("2026-04-15T09-00-00Z.json").exists());
assert!(!dir.join(ONELINER_FILENAME).exists());
assert!(!dir.join(DOCUMENT_FILE).exists());
assert!(!dir.join(ANALYSIS_INPUT_FILE).exists());
@@ -1235,7 +1235,7 @@ async fn reset_case_requires_admin() {
assert_eq!(resp.status(), StatusCode::FORBIDDEN);
// Nothing touched.
assert!(dir.join("2026-04-15T09-00-00Z.transcript.txt").exists());
assert!(dir.join("2026-04-15T09-00-00Z.json").exists());
assert!(dir.join(DOCUMENT_FILE).exists());
}
@@ -1282,8 +1282,8 @@ async fn bulk_reset_processes_multiple_cases_admin_only() {
assert_eq!(resp.status(), StatusCode::SEE_OTHER);
assert!(!dir_a.join(DOCUMENT_FILE).exists());
assert!(!dir_b.join(DOCUMENT_FILE).exists());
assert!(!dir_a.join("2026-04-15T10-00-00Z.transcript.txt").exists());
assert!(!dir_b.join("2026-04-15T10-05-00Z.transcript.txt").exists());
assert!(!dir_a.join("2026-04-15T10-00-00Z.json").exists());
assert!(!dir_b.join("2026-04-15T10-05-00Z.json").exists());
// Non-admin: bulk reset is rejected, dr_b's case untouched.
let (cookie_doctor, csrf_doctor) = login_with_csrf(&app, &store, "dr_b", "s").await;
@@ -1299,7 +1299,7 @@ async fn bulk_reset_processes_multiple_cases_admin_only() {
.unwrap();
assert_eq!(resp.status(), StatusCode::FORBIDDEN);
assert!(dir_c.join(DOCUMENT_FILE).exists());
assert!(dir_c.join("2026-04-15T11-00-00Z.transcript.txt").exists());
assert!(dir_c.join("2026-04-15T11-00-00Z.json").exists());
}
/// Regression guard: the bulk route handles *all* actions (including