fix(recording): make saver startup, drain, and finalization reliable
Problem
Three pre-existing failures in the live-recording persistence lifecycle can lose audio or report success after persistence failed. They were identified while reviewing PR #767 and were explicitly deferred because they were neither introduced nor demonstrably amplified by that PR.
These belong together because RecordingSaver owns all three boundaries: storage initialization, queue consumption, and finalization.
1. Auto-save initialization failure silently degrades into total audio loss
RecordingSaver::start_accumulation returns () and only logs when initialize_meeting_folder fails. incremental_saver remains None, but the recording continues and the accumulation task still consumes incoming chunks. With auto_save = true, those chunks cannot be written. At stop, the missing saver is treated as though auto-save had been disabled and returns Ok(None).
Concrete triggers include an unavailable recording directory, permission changes, disk/filesystem errors, and invalid folder state.
Evidence:
- Initialization error is logged and ignored:
frontend/src-tauri/src/audio/recording_saver.rs:138-176 - Missing saver discards incoming auto-save chunks:
frontend/src-tauri/src/audio/recording_saver.rs:178-207 - Stop misclassifies the missing saver as disabled auto-save:
frontend/src-tauri/src/audio/recording_saver.rs:367-374 - Manager currently has no failure result to propagate:
frontend/src-tauri/src/audio/recording_manager.rs:225-280 - Immutable review evidence: https://github.com/Zackriya-Solutions/meetily/blob/0de4f7f9501179aedc6957fea1cc8237e394074f/frontend/src-tauri/src/audio/recording_saver.rs#L152-L157
2. Stop can discard audio chunks already queued for saving
stop_and_save clears is_saving before the receiver drains. The accumulation task checks that flag immediately after each receive and exits on the next queued chunk, dropping that chunk and everything behind it. The fixed 200 ms sleep does not establish queue completion, and the tokio::spawn handle is not retained or awaited before finalization.
This is timing-dependent: checkpoint I/O only needs to fall behind recording production when Stop is requested.
Evidence:
- Detached accumulation task and early-exit flag:
frontend/src-tauri/src/audio/recording_saver.rs:173-215 - Flag cleared before the sleep and finalization:
frontend/src-tauri/src/audio/recording_saver.rs:352-379 - Immutable review evidence: https://github.com/Zackriya-Solutions/meetily/blob/0de4f7f9501179aedc6957fea1cc8237e394074f/frontend/src-tauri/src/audio/recording_saver.rs#L359-L365
3. Final save failures are converted into successful shutdown
RecordingSaver::stop_and_save correctly returns errors for audio finalization and final transcript writes. RecordingManager::save_recording_only logs those errors and then returns Ok(()). The command layer therefore enters its success branch, emits recording-shutdown-progress as complete and emits recording-stopped, even when final audio or transcript persistence failed.
Concrete triggers include disk-full, permission changes, disconnected or synchronized folders, and Windows sharing/rename failures.
Evidence:
- Finalization and transcript errors originate correctly:
frontend/src-tauri/src/audio/recording_saver.rs:376-405 - Manager swallows the error:
frontend/src-tauri/src/audio/recording_manager.rs:384-407 - Command interprets the result as success and later emits completion:
frontend/src-tauri/src/audio/recording_commands.rs:973-1055 - Immutable review evidence: https://github.com/Zackriya-Solutions/meetily/blob/0de4f7f9501179aedc6957fea1cc8237e394074f/frontend/src-tauri/src/audio/recording_manager.rs#L400-L407
User impact
- An entire recording can be discarded despite auto-save being enabled.
- The saved recording can lose its queued ending audio.
- Meetily can tell the user that recording stopped successfully when final audio or transcript files are missing or incomplete.
- The user receives no actionable storage recovery guidance while the only remaining copy may be transient or already dropped.
Required behavior
- With auto-save enabled, meeting-folder and incremental-saver initialization must complete before capture starts or the recording is published as live.
- Initialization failure must abort startup, roll back constructed recording state/pipeline resources, and surface a storage-specific user-visible error. It must never fall back to discard mode.
- Auto-save disabled must retain its current transcript/metadata-only behavior and remain distinguishable from failed initialization.
- Stop must prevent new producer sends, close the producer side, drain every chunk already accepted by the saver channel in order, and await saver-task completion before finalizing audio.
- Queue completion must be synchronization-based; remove the fixed 200 ms timing assumption and do not use a flag that causes an accepted chunk to terminate draining.
- Audio finalization, final transcript write, and metadata completion failures must propagate out of
RecordingManagerafter native resources are safely cleaned up. - Do not emit an unqualified completion/
recording-stoppedsuccess when persistence failed. Emit a stable partial/failure outcome containing the meeting folder when available and actionable recovery guidance. - Successful recording, auto-save-disabled recording, pause/resume, and transcript persistence behavior must remain unchanged.
Verification
Add deterministic coverage for the observable contracts:
- Inject meeting-folder/incremental-saver initialization failure with auto-save enabled; startup fails before capture and no audio is silently consumed.
- Run auto-save disabled through start/stop; transcript/metadata-only completion remains successful.
- Queue multiple identifiable audio chunks behind a deliberately blocked saver, request Stop, release the saver, and assert every accepted sample reaches finalization exactly once and in order.
- Inject audio finalization failure, final transcript-write failure, and metadata-completion failure independently; cleanup completes, the caller receives failure/partial status, and no success event is emitted.
- Confirm a normal recording still emits one successful completion after the saver task drains and final files are verified.
Scope boundaries
- This issue covers chunks that already reached the recording-saver channel. Upstream capture/resampler/mixer tail draining is tracked separately in #773.
- Import/retranscription SQLite-to-sidecar reconciliation is tracked separately in #777.
- PR #767's ONNX loader, lifecycle-phase, tray recovery, and runtime-stage invalidation findings are separate and do not belong in this fix.
- Do not redesign audio mixing, ASR, or meeting storage formats as part of this work.
Provenance
- Original prioritized review comment: https://github.com/Zackriya-Solutions/meetily/pull/767#issuecomment-5568539071
- Attribution: all three findings are PRE-EXISTING relative to PR #767.
Source: Zackriya-Solutions/meetily