bug: stop button disappears between speech segments after switching chats
Describe the bug
The stop button sometimes disappears after the user switches chats during speech playback.
A local investigation reproduced a related timing window between TTS segments. The response remains active, but the button visibility condition becomes false.
Reproduction sequence
Preconditions: chat A owns an active response, both composers are empty, and no replacement submission is pending.
- Start a response with multiple speech segments in chat A.
- During the first audio segment, switch to chat B.
- Let the first segment finish before the next segment arrives.
- Observe the stop button during this gap.
- Let the next segment start.
The local state reproduction produced these results:
| State | Visible chat | Generation flag for visible chat | nowSpeaking |
Stop visibility condition |
|---|---|---|---|---|
| First segment plays | A | true | true | true |
| Switch during playback | B | false | true | true |
| First segment ends, next segment pending | B | false | false | false |
| Switch back during the gap | A | true | false | true |
| Next segment starts | B | false | true | true |
Expected behavior
With an empty composer, the stop button remains available while the response can still generate text or produce speech, including gaps between segments.
Switching the visible chat does not remove access to cancellation for that response.
Investigation
Inspected revision: 2e0e963670e5435a2909149fff1f83b93f8ffd11.
- InteractiveArea.vue restricts local generation state to the visible session.
- use-chat-interruption.ts computes response activity from that generation state or speech playback state.
- useStopSpeakingButton.ts uses
nowSpeakingfor speech visibility. - playback-speaking-state.ts clears
nowSpeakingat each playback end.
These conditions omit background generation and pending speech between segments after a chat switch.
Validation and limits
- A local script used the real
createPlaybackManagerandbindSpeakingStateToPlaybackManager, with deferred playback completion. - The script used the Stage playback policies and a source-equivalent visibility calculation for an empty composer.
- The state transitions in the table passed assertions. The script did not mount the composer or use real audio output.
pnpm -F @proj-airi/stage-layouts exec vitest run src/composables/use-chat-interruption.test.ts: 10 tests passed.- The existing tests cover cancellation ownership after a chat switch, but do not cover visibility during gaps between segments.
- A full UI reproduction remains pending. Persistent disappearance during uninterrupted audible playback remains unconfirmed.
- No production code changed during this investigation.
System Info
Local module reproduction in the repository revision above. The original report did not specify the app surface, browser, or TTS provider.
Acceptance criteria
- Add a regression test for A-to-B switching while A generates text and waits for its next speech segment.
- Keep the stop action available throughout that gap with an empty composer.
- Confirm that Stop cancels the response in A and prevents its pending speech from starting.
- Confirm the behavior in a mounted UI, including after text generation ends but speech remains pending.
Source: moeru-ai/airi