unbridled-41 opened a new pull request, #5002:
URL: https://github.com/apache/rocketmq-dashboard/pull/5002

   ## Problem
   
   A prompt typed immediately after clicking 停止生成 was **silently lost**. The 
stop window works like this (base `1ef5d860`):
   
   1. The stop POST resolves; the terminal frames have not arrived. 
`useAgentRun` keeps `isStreaming=true` but sets `stopRequested=true` 
(`useAgentRun.ts:393`, deliberately not aborting the fetch so 
`run_status{STOPPED}` can still arrive).
   2. `Composer` derives `generating = isStreaming && !stopRequested` 
(`Composer.tsx:248`), so `generating` is now **false** and `canSend` (`:250`) 
becomes **true**: the composer accepts the send.
   3. `Composer.handleSend` (`:256-261`) calls `onSend(text)` and immediately 
clears the draft (`onChange('')`).
   4. `useAiSend.startRun`'s existing-conversation branch 
(`useAiSend.ts:109-111`, base) did `void optionsRef.current.send(...)` and 
returned the non-null id, so `AiPage.handleSend`'s only restore path — `target 
=== null` (`index.tsx:122-124`, base) — never fired.
   5. `useAgentRun.startStream`'s in-flight guard (`useAgentRun.ts:307`, base: 
`if (chatInFlightRef.current) return;`) silently no-oped — `chatInFlightRef` is 
released only in `finishStream` (`:257`) after the stream closes.
   
   ## Root cause
   
   `startStream`'s two refusal branches `return` instead of rejecting, and the 
only failure signal `AiPage.handleSend` checks (`startRun === null`) covers 
just the create-conversation path. The button state (`stopping`, 
`SendStopButton.tsx:62`) and the Enter-enabled state (`!generating`) disagree 
inside this window, so the in-flight guard is the last line of defence — and it 
was silent.
   
   ## Fix
   
   - `startStream` throws (with a reason) instead of silently returning on both 
guard branches.
   - `useAiSend` awaits the send in the existing-conversation branch so the 
refusal propagates to the caller instead of dying in a `void`.
   - `AiPage.handleSend` restores the cleared draft on a rejected send 
(mirroring the existing create-failure restore), so the operator sees their 
prompt again.
   
   The double-submit guarantee is unchanged: still one stream, first prompt 
wins (`opensOnlyOneStreamForADoubleSubmitTest` keeps its assertion).
   
   ## Scoring (AGENTS.md)
   
   PRIORITY **56** = impact 24 (a user's typed prompt irrecoverably dropped, 
zero feedback, on a common interaction: stop a long run, retype) + reach 10 (AI 
composer) + reproducibility 12 (deterministic inside the stop window, which is 
the normal way to abandon a long answer and continue) + maintainability value 
10 (removes a silent no-op guard). FIX_CONFIDENCE **88**: guard->reject is 
mechanical; the draft-restore path already existed for create failures and is 
now extended; tests pin both the refusal and the restore.
   
   ## Tests
   
   - New: 
`useAgentRunTest.reportsASendInTheStoppingWindowInsteadOfSilentlyDroppingItTest`
 — pre-fix: **`AssertionError: promise resolved "undefined" instead of 
rejecting`**; it also verifies the in-flight stream is not clobbered by the 
refused send.
   - Updated: `AiPage.doesNotAdmitASecondSendWhileOneIsInFlightTest` now also 
pins the draft being restored after the refused second send; 
`useAgentRunTest.opensOnlyOneStreamForADoubleSubmitTest` swallows the 
now-throwing second send the way production does.
   - Post-fix: `npx vitest run src/pages/ai/` -> **16 files, 192/192 passed**, 
no unhandled rejections; `npx tsc --noEmit` clean; `npx eslint` on the changed 
files clean; `src/pages/studio/` + `src/pages/home/` (the other `useAiSend` 
consumers) -> **101/101**.
   
   ## Risk
   
   Low. The refusal previously vanished; now it either restores the draft (page 
path) or surfaces as a handled rejection. Any caller that fire-and-forgets 
`run.send()` directly would now see an unhandled rejection instead of a silent 
no-op — the production callers are `AiPage.handleSend` and the draft-handoff, 
both covered.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to