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]