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

   ## Problem / Evidence
   
   Reloading the page while a run is still being admitted leaves the answer 
streaming invisibly, and the next send is refused with 409 until the operator 
navigates away and back.
   
   - Admission writes the run row as `QUEUED` (`AiRunService.java:438`) and 
only registers the worker later in `sendMessage` — after `workspace.prepare`, 
whose CLI availability probe has a 5s budget (`CliBinaryProbe.java:51,86-114`), 
so the window is wide.
   - Attaching inside that window cannot register an observer: 
`AiRunService.attach` (`:287-299`) gets `false` from `registry.attach`, the 
row's `QUEUED` status is non-terminal so no `RunFinished` is synthesised, and 
`session.complete()` sends only `done`. The stream therefore ends **cleanly** — 
no error state for the client to react to.
   - The client's guard is per run id for the whole visit 
(`useActiveRunAttach.ts:53-59` pre-fix): `requestedRunRef.current = 
activeRun.id` and never cleared, so the timeline's next fetch — which reports 
the same run as `RUNNING` — cannot attach. The run then generates with no 
observer, and admission refuses the next send (`AiRunService.java:191-195`, 
409).
   
   ## Root cause / Fix
   
   "A dead attach surfaces through the run's error state" (the hook's own 
docstring) does not hold for this path: the attach ended successfully with 
nothing to show. Record **what the attach observed** — the run id *and* the 
status — and allow one more attach when the run has moved on:
   
   ```ts
   const requestedRunRef = useRef<{ id: number; status: string } | null>(null);
   ...
   if (requested !== null && requested.id === activeRun.id && requested.status 
=== activeRun.status) return;
   ```
   
   A repeated identical status still returns early, so the anti-hot-loop 
guarantee the ref exists for is unchanged; only a genuine state change (QUEUED 
→ RUNNING) triggers the second attach.
   
   ## Priority & scoring
   
   PRIORITY 59 = 影响 26 (the answer is invisible and the conversation is locked 
with 409s until the operator leaves and returns) + 波及 9 (AI run re-attach) + 
可复现 12 (needs a reload inside the admission window) + 维护价值 12 (closes a hole in 
the hook's documented invariant).
   
   FIX_CONFIDENCE 72: a small, typed change to the existing guard, pinned by a 
regression that also asserts no loop on a repeated status.
   
   ## Tests
   
   ```
   cd web && npx vitest run src/pages/ai
   ```
   
   - Red before the fix: 
`reattachesWhenTheRunAdvancesPastTheAdmissionWindowItWasAttachedInTest` fails — 
`expected "vi.fn()" to be called 2 times, but got 1 times`.
   - Green after: `Test Files 17 passed (17) | Tests 195 passed (195)`.
   - The new test asserts: the QUEUED attach happens, a RUNNING transition 
attaches again, and re-rendering the same RUNNING state does not attach a third 
time.
   - `npx tsc -b` and `npx eslint` on both files: clean.
   
   ## Risk
   
   Low. The extra attach can only happen when the timeline reports a status the 
visit has not attached under, and a real attach flips `isStreaming`, which 
stops the effect from running again.


-- 
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