Skip to content

fix(desktop): use live busy state for voice submits - #105509

Open
ishuowang wants to merge 1 commit into
NousResearch:mainfrom
ishuowang:fix/voice-submit-live-busy
Open

ishuowang wants to merge 1 commit into
NousResearch:mainfrom
ishuowang:fix/voice-submit-live-busy

Conversation

@ishuowang

Copy link
Copy Markdown

What does this PR do?

Prevents a Desktop Voice Conversation barge-in transcript from being silently dropped when its retained submit callback was created while the interrupted agent turn was still busy.

useVoiceConversation() already waits on live busy state before submitting a captured interruption. The parent submitVoiceTurn() callback, however, could still read busy=true from the older render retained by the barge monitor and return without reaching the composer. This keeps the existing safety gate but makes it read the current busy value through a synchronously updated ref.

The reporter's independent RED → GREEN follow-up confirms the causal sequence at unit level: the retained callback produced zero semantic submissions before the fix, exactly one after it, and remained blocked while live busy was true. A patched live Windows Voice canary remains a separate follow-up.

Draft #95180 is adjacent but not a duplicate: it changes turn and capture lifetimes inside useVoiceConversation() and does not update this parent submit guard.

Related Issue

Fixes #105497

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)
  • ✨ New feature (non-breaking change that adds functionality)
  • 🔒 Security fix
  • 📝 Documentation update
  • ✅ Tests (adding or improving test coverage)
  • ♻️ Refactor (no behavior change)
  • 🎯 New skill (bundled or hub)

Changes Made

  • apps/desktop/src/app/chat/composer/hooks/use-composer-voice.ts: read the live busy gate from a ref when a Voice callback submits.
  • apps/desktop/src/app/chat/composer/hooks/use-composer-voice.test.tsx: retain the callback from a busy=true render, prove it submits after live busy clears, and preserve the live-busy block.

How to Test

  1. From apps/desktop, run the focused and adjacent Voice suites:

    npx vitest run --project ui \
      src/app/chat/composer/hooks/use-composer-voice.test.tsx \
      src/app/chat/composer/hooks/use-voice-conversation.test.tsx \
      src/app/chat/composer/hooks/use-voice-conversation-rearm.test.tsx
  2. Verify all 12 tests pass.

  3. Run npm run typecheck.

  4. Run ESLint and Prettier against the two changed files.

The new regression was also run against the unfixed base first and failed with zero calls to the semantic onSubmit; the identical test passes after this patch.

The full UI suite was attempted: 7,334 tests passed and two existing src/store/voice-prefs.test.ts localStorage assertions failed. Those two failures reproduce when that unchanged file is run alone; its source, test, and vitest.setup.ts have no diff from main.

Checklist

Code

  • I've read the Contributing Guide
  • My commit messages follow Conventional Commits (fix(scope):, feat(scope):, etc.)
  • I searched for existing PRs to make sure this isn't a duplicate
  • My PR contains only changes related to this fix/feature (no unrelated commits)
  • I've run pytest tests/ -q and all tests pass — N/A for this Desktop TypeScript-only change
  • I've added tests for my changes (required for bug fixes, strongly encouraged for features)
  • I've tested on my platform: Ubuntu Linux (x86_64), Node.js 24.11.0

Documentation & Housekeeping

  • I've updated relevant documentation (README, docs/, docstrings) — N/A
  • I've updated cli-config.yaml.example if I added/changed config keys — N/A
  • I've updated CONTRIBUTING.md or AGENTS.md if I changed architecture or workflows — N/A
  • I've considered cross-platform impact (Windows, macOS) per the compatibility guide — renderer-only state fix; no platform API changes
  • I've updated tool descriptions/schemas if I changed tool behavior — N/A

Screenshots / Logs

N/A — this async coordination bug is covered by the retained-callback regression and the reporter's independent RED → GREEN verification.

@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/desktop Electron desktop app (apps/desktop/*) tool/tts Text-to-speech and transcription labels Sep 8, 2026
@Enough1122

Copy link
Copy Markdown

AI code review — automated review for reference; please use your judgment.

Summary

Fixes dropped voice turns after barge-in: submitVoiceTurn previously closed over a stale busy value from the render where the callback was retained, so a transcript arriving after the turn ended was silently dropped. A busyRef mirror makes the gate read live state. Minimal, well-tested change.

Findings (all Non-blocking)

  • apps/desktop/src/app/chat/composer/hooks/use-composer-voice.ts:67-70 — busyRef.current = busy is assigned during render, which React docs discourage in concurrent mode (should ideally be in an effect/event). In practice this is the standard latest-ref pattern and the write is idempotent, so no real risk here. Non-blocking.
  • Test (use-composer-voice.test.tsx) covers both directions: retained callback submits after busy flips true→false, and does not double-submit after false→true. Good coverage of the stale-closure scenario. No gaps noted.

Verdict

Looks good — small, correct fix with a targeted regression test.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/desktop Electron desktop app (apps/desktop/*) P2 Medium — degraded but workaround exists tool/tts Text-to-speech and transcription type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Desktop barge-in transcript can be silently dropped by stale busy closure in submitVoiceTurn

3 participants