From 6f4adae56c187fc7b6170a7bebf9f8668104104a Mon Sep 17 00:00:00 2001 From: Jake Date: Wed, 22 Apr 2026 04:54:15 +0100 Subject: [PATCH] fix(A.2 #19): only report audio_path when the WAV writer finalises cleanly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Review feedback: reported_audio_path was cached at writer-open time and survived a mid-session writer drop. If an append error cleared wav_writer (or hound's Drop ran instead of finalize), the Summary still reported the path — risking StopLiveTranscriptionResponse pointing at a file whose header did not reflect its data chunk. Moves the decision to end-of-session. wav_writer.take() is inspected: - writer present + finalize Ok → report the path, - writer present + finalize Err → None, emit a Warning, - writer absent (mid-session drop, or never opened) → None. StopLiveTranscriptionResponse.audio_path now means "the recording is known-good" rather than "a recording was attempted". Users can still recover partial files via filesystem if needed; the warning toasts already emitted by append_resampled_audio on write failure surface that path implicitly. --- src-tauri/src/commands/live.rs | 50 +++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/src-tauri/src/commands/live.rs b/src-tauri/src/commands/live.rs index a138fe5..6e04c57 100644 --- a/src-tauri/src/commands/live.rs +++ b/src-tauri/src/commands/live.rs @@ -402,15 +402,12 @@ fn run_live_session( }, None => None, }; - // Resolve the reported audio_path only if the writer actually - // opened; otherwise the returned Summary must not claim a path. - let reported_audio_path: Option = if wav_writer.is_some() { - audio_path - .as_ref() - .map(|p| p.to_string_lossy().to_string()) - } else { - None - }; + // `reported_audio_path` is decided at end-of-session based on + // whether the writer finalised successfully, not at open time. + // This way a writer that dies mid-session (append error clearing + // wav_writer, or finalise returning Err) does not leak a stale + // path back to the frontend that might point to a file whose + // header is out of sync with its data chunk. let mut buffer_start_sample: u64 = 0; let mut dropped_audio_ms: u64 = 0; @@ -552,23 +549,32 @@ fn run_live_session( thread::sleep(Duration::from_millis(10)); } - // Finalise the progressive WAV writer so the terminal header - // reflects every written sample. A failure here leaves the last - // flushed header state on disk — still a playable WAV — so we - // report a warning but don't fail the whole session. - if let Some(writer) = wav_writer.take() { - if let Err(e) = writer.finalize() { - let _ = status_channel.send(LiveStatusMessage::Warning { - session_id, - message: format!("WAV finalise failed: {e}; partial recording is still playable."), - }); - } - } + // Finalise the progressive WAV writer and decide whether to + // report a path to the frontend. Only a clean finalise produces a + // reported path: a writer that died mid-session (wav_writer was + // already None) or a finalise that itself errored both yield + // `None`, so `StopLiveTranscriptionResponse.audio_path` reflects + // "recording is known-good" rather than "recording was attempted". + let audio_path = match wav_writer.take() { + Some(writer) => match writer.finalize() { + Ok(()) => audio_path.as_ref().map(|p| p.to_string_lossy().to_string()), + Err(e) => { + let _ = status_channel.send(LiveStatusMessage::Warning { + session_id, + message: format!( + "WAV finalise failed: {e}. A partial recording may still be on disk from earlier flushes." + ), + }); + None + } + }, + None => None, + }; Ok(LiveSessionSummary { session_id, dropped_audio_ms, - audio_path: reported_audio_path, + audio_path, }) }