From d40b08ab447eb588251177ed8abafd98d1ee026b Mon Sep 17 00:00:00 2001 From: baiqing Date: Sun, 10 May 2026 08:42:23 +0800 Subject: [PATCH] fix(coordinator): post-audit logic-review hotfixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two real bugs surfaced by the 2026-05-10 end-to-end logic review of beta after the 8-PR audit-fix wave landed. 1. coordinator.rs:2313 — `acquire_recording_mute(inner, "qa");` was missing `.await`. PR #391 (`6171df61`) made the function `async fn` and updated the dictation call site at coordinator/dictation.rs:451, but the QA call site was missed. Effect: when a user has `mute_during_recording = true` and triggers QA via Right Option, the returned Future is dropped on the next line, `spawn_blocking` is never scheduled, holders never increments, and system audio is NOT muted (e.g. YouTube playback continues into the QA recording). Both the matching `release_recording_mute(inner, "qa")` calls become no-ops (early return at holders == 0). The compiler was emitting `unused_must_use` for this site (verified before this commit). Fix: add `.await`. 2. coordinator/dictation.rs:843-849 — PR #387 (`ce82fcd9`) was framed as "clear focus_target on cancel regardless of phase", but the only code path that gained the unconditional clear was `finish_cancel_session_state`. cancel_session deliberately skips that helper for Processing (so end_session can drive its own teardown), and end_session's "ASR-finished, cancelled" exit at dictation.rs:843-849 set phase=Idle but never touched focus_target. Result: cancel-during-Processing leaves a stale `usize` focus slot in state.focus_target until the next begin_session_state overwrites it. Today the leak is bounded (no documented reader between cancel and next begin), but it violates PR #387's stated contract and is a silent footgun for any future reader of focus_target on that interval. Fix: in the cancelled-after-ASR exit branch, take the state lock once and clear both phase and focus_target together. Source: docs/logic-review-2026-05-10.md (subagent end-to-end review). 185/185 lib tests pass; cargo check clean (unused_must_use gone). Manual verification checklist for both fixes in the review doc. --- openless-all/app/src-tauri/src/coordinator.rs | 2 +- .../app/src-tauri/src/coordinator/dictation.rs | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/openless-all/app/src-tauri/src/coordinator.rs b/openless-all/app/src-tauri/src/coordinator.rs index c666c0d5..4428a8f7 100644 --- a/openless-all/app/src-tauri/src/coordinator.rs +++ b/openless-all/app/src-tauri/src/coordinator.rs @@ -2310,7 +2310,7 @@ async fn begin_qa_session(inner: &Arc) -> Result<(), String> { let microphone_device_name = selected_microphone_device_name(inner); stop_microphone_preview_monitor(inner, "QA recorder"); - acquire_recording_mute(inner, "qa"); + acquire_recording_mute(inner, "qa").await; match Recorder::start(microphone_device_name, consumer, level_handler) { Ok((rec, runtime_errors)) => { *inner.qa_recorder.lock() = Some(rec); diff --git a/openless-all/app/src-tauri/src/coordinator/dictation.rs b/openless-all/app/src-tauri/src/coordinator/dictation.rs index 1daa7efd..51512fba 100644 --- a/openless-all/app/src-tauri/src/coordinator/dictation.rs +++ b/openless-all/app/src-tauri/src/coordinator/dictation.rs @@ -845,7 +845,15 @@ pub(super) async fn end_session(inner: &Arc) -> Result<(), String> { if inner.state.lock().cancelled { log::info!("[coord] cancel detected after ASR — discarding transcript"); restore_prepared_windows_ime_session(inner, current_session_id); - inner.state.lock().phase = SessionPhase::Idle; + // PR #387 的「cancel 后清 focus_target」契约要在 Processing 路径上也成立。 + // cancel_session 在 Processing 阶段故意跳过 finish_cancel_session_state(让 + // 这里收尾),但此前的 end_session 没把 focus_target 清掉。logic-review + // 2026-05-10 P3 (🚩) 把这条补完。 + { + let mut state = inner.state.lock(); + state.phase = SessionPhase::Idle; + state.focus_target = None; + } return Ok(()); }