Description
Issue #25 shipped the full backend cancel apparatus (StreamRegistry, CancellationToken, connection-drop-stops-billing, cancel_stream command), but the frontend never wired it up — there is no Stop button, and the frontend doesn't pass a stream_id to send_chat_message_streaming (the command comment explicitly calls this out as a pending follow-up). Users watching a long/wrong response stream in have no way to stop it, and abandoned streams bill to completion. Found during #112 manual QA (2026-07-09): the cancelled-audit-row path could not be exercised from the UI.
Current State
Suggested Fix
Verification
Automation Hints
scope: src/contexts/ConversationContext.tsx, src/lib/tauri-commands.ts, src/components/chat/
do-not-touch: src-tauri/ (backend is complete)
approach: extract-and-move
risk: low
max-files-changed: 6
blocked-by: none
bail-if: requires backend signature changes beyond making stream_id required
Priority
Medium — user-facing control gap + token cost on abandoned streams; also unblocks manual QA of the cancelled-row path.
Description
Issue #25 shipped the full backend cancel apparatus (StreamRegistry, CancellationToken, connection-drop-stops-billing,
cancel_streamcommand), but the frontend never wired it up — there is no Stop button, and the frontend doesn't pass astream_idtosend_chat_message_streaming(the command comment explicitly calls this out as a pending follow-up). Users watching a long/wrong response stream in have no way to stop it, and abandoned streams bill to completion. Found during #112 manual QA (2026-07-09): the cancelled-audit-row path could not be exercised from the UI.Current State
chat::StreamRegistry+cancel_streamcommand (src-tauri/src/commands/chat.rs) — cancelling drops the upstream connection and (post-Audit log has one write site (interactive chat, frontend fire-and-forget) — backend LLM egress writes zero audit rows #112) writes acancelledpartial audit rowsend_chat_message_streamingaccepts optionalstream_id; when absent it generates a UUID the UI can never learn (see comment in src-tauri/src/commands/chat.rs)sendChatMessageStreaming(src/lib/tauri-commands.ts) does not sendstreamId; no Stop affordance in the chat UItrial_stream_cancel_writes_cancelled_audit_row)Suggested Fix
sendChatMessageStreamingcancel_stream(stream_id)cancel_streamon conversation switch/unmount (the command is safe on unknown ids)chat-stream-cancelledevent (reset streaming UI state)cancelledaudit row landsVerification
cancelledrow with partial char countAutomation Hints
scope: src/contexts/ConversationContext.tsx, src/lib/tauri-commands.ts, src/components/chat/
do-not-touch: src-tauri/ (backend is complete)
approach: extract-and-move
risk: low
max-files-changed: 6
blocked-by: none
bail-if: requires backend signature changes beyond making stream_id required
Priority
Medium — user-facing control gap + token cost on abandoned streams; also unblocks manual QA of the cancelled-row path.