Commit 534020a
feat(registry-dash): persist AI tool-call entries in chat scrollback (SRE-1963) (#141)
* feat(registry-dash): persist AI tool-call entries in chat scrollback (SRE-1963)
Tool-call indicators previously vanished the moment a tool returned, leaving
the user with no record of which tools the assistant called or how each one
resolved. Make each tool call a persistent pill in the chat timeline so the
chat surface mirrors how Claude Code's CLI renders tool calls.
Frontend-only — the SSE stream from RegistryDashAiAction already carries
tool name, args, structured status (SRE-1958), and diagnostic. No backend
change required.
Data model
- New ToolMessage variant + ChatTimelineEntry union in ai-analysis.models.ts.
ConversationMessage stays the wire shape; toWireHistory() strips tool
entries before sending follow-up turns to the backend.
- Drop now-unused ToolInFlight / ToolCompleted types.
Service lifecycle (ai-analysis.service.ts)
- conversationHistory becomes the single source of truth for the timeline,
typed ChatTimelineEntry[].
- On tool_use: flush any pending streamed text to an assistant entry first,
then push an IN_FLIGHT tool entry. Preserves chronological order across
text -> tool -> text streams.
- On tool_result: update the latest matching IN_FLIGHT entry in place.
- analyze() smart-merges incoming wire history (prefix-match append, falling
back to wholesale replace) so prior turns' tool entries survive across
follow-ups instead of being clobbered by the next request body.
- On cancel/abort/network drop: terminateInFlight() flips any leftover
IN_FLIGHT entries to INTERNAL_ERROR with diagnostic "Cancelled" so
pills stop pulsing.
- Drop toolsInFlight / toolsCompleted / toolsUsed signals — derivable
from conversationHistory and no external consumers.
Modal (ai-analysis-modal.component.{ts,html,scss})
- New tool branch in the persistent chat loop renders a single .tool-pill.
Variants: --in-flight (italic + pulsing dots), --ok (presence == success,
no chip suffix), --warn / --error (chip suffix carries the SRE-1958
status text + tooltip with the diagnostic).
- Streaming block trimmed to streamed text + progress bar; tool rendering
moves into the persistent timeline.
- chipFor() updated for ToolMessage; new ariaLabelFor() composes the
screen-reader description.
- runQueuedPrompt() filters via toWireHistory() before constructing the
request body so tool entries never round-trip to the backend.
Tests
- ai-analysis.service.spec.ts: rewrites the two existing tool tests to
assert on conversationHistory; adds 4 new SRE-1963 cases (multi-tool
ordering, text-tool-text interleave, IN_FLIGHT-resolved-on-cancel,
cross-turn preservation + wire-shape stripping).
- ai-analysis-modal.component.spec.ts: drops toolsInFlight from the stub
(no longer on the service); adds a persistent tool pills describe block
covering IN_FLIGHT/OK/non-OK rendering, multi-pill ordering, and the
chipFor matrix.
- All 115 ai-analysis specs pass. Pre-existing ExploreComponent /
RevenueBilling failures are unrelated (verified via stash).
Test plan (.claude/plugins/.../test-plan.md)
- Test 18 rewritten for persistence (pill stays visible through and past
the response).
- Test 65 updated: OK = pill present, no chip suffix; non-OK = pill +
chip suffix.
- New Test 68 covers multi-tool persistence, cross-turn survival, wire-
payload stripping, and cancel cleanup.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(registry-dash): FIFO match + accurate diagnostic for AI tool pills
Address two correctness issues in ai-analysis.service.ts surfaced during
review of #141 (SRE-1963):
1. updateLatestInFlight() iterated LIFO, so when the same tool was invoked
twice in a single turn the first tool_result (which arrives first per
the backend's FIFO ordering) would land on the second pill, swapping
diagnostics. Iterate from the start instead so oldest IN_FLIGHT pill
gets the first matching result.
2. terminateInFlight() unconditionally tagged leftover IN_FLIGHT entries
with diagnostic "Cancelled". This misrepresents network drops / 5xx /
unexpected stream ends as user-cancelled. Pass the diagnostic in from
the caller — "Cancelled" only when controller.signal.aborted, else
"Interrupted".
New tests:
- same-tool-twice → results match pills FIFO
- stream-end-without-result on a non-aborted controller → diagnostic
"Interrupted", not "Cancelled"
117/117 ai-analysis specs pass.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>1 parent 96a71b6 commit 534020a
8 files changed
Lines changed: 638 additions & 166 deletions
File tree
- .claude/plugins/ud-registry-dash/skills/test-registry-dash
- console-webapp/src/app/registry-dash/ai
Lines changed: 52 additions & 21 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
418 | 418 | | |
419 | 419 | | |
420 | 420 | | |
421 | | - | |
| 421 | + | |
422 | 422 | | |
423 | | - | |
| 423 | + | |
424 | 424 | | |
425 | 425 | | |
426 | 426 | | |
427 | 427 | | |
428 | 428 | | |
429 | 429 | | |
430 | 430 | | |
431 | | - | |
432 | | - | |
433 | | - | |
| 431 | + | |
| 432 | + | |
| 433 | + | |
| 434 | + | |
| 435 | + | |
434 | 436 | | |
435 | 437 | | |
436 | | - | |
437 | | - | |
438 | | - | |
| 438 | + | |
| 439 | + | |
| 440 | + | |
| 441 | + | |
| 442 | + | |
| 443 | + | |
439 | 444 | | |
440 | 445 | | |
441 | 446 | | |
| |||
1429 | 1434 | | |
1430 | 1435 | | |
1431 | 1436 | | |
1432 | | - | |
| 1437 | + | |
1433 | 1438 | | |
1434 | | - | |
| 1439 | + | |
1435 | 1440 | | |
1436 | 1441 | | |
1437 | 1442 | | |
1438 | 1443 | | |
1439 | 1444 | | |
1440 | | - | |
1441 | | - | |
1442 | | - | |
1443 | | - | |
1444 | | - | |
1445 | | - | |
1446 | | - | |
1447 | | - | |
1448 | | - | |
1449 | | - | |
1450 | | - | |
| 1445 | + | |
| 1446 | + | |
| 1447 | + | |
| 1448 | + | |
| 1449 | + | |
| 1450 | + | |
| 1451 | + | |
| 1452 | + | |
| 1453 | + | |
| 1454 | + | |
| 1455 | + | |
| 1456 | + | |
1451 | 1457 | | |
1452 | 1458 | | |
1453 | 1459 | | |
| |||
1591 | 1597 | | |
1592 | 1598 | | |
1593 | 1599 | | |
| 1600 | + | |
| 1601 | + | |
| 1602 | + | |
| 1603 | + | |
| 1604 | + | |
| 1605 | + | |
| 1606 | + | |
| 1607 | + | |
| 1608 | + | |
| 1609 | + | |
| 1610 | + | |
| 1611 | + | |
| 1612 | + | |
| 1613 | + | |
| 1614 | + | |
| 1615 | + | |
| 1616 | + | |
| 1617 | + | |
| 1618 | + | |
| 1619 | + | |
| 1620 | + | |
| 1621 | + | |
| 1622 | + | |
| 1623 | + | |
| 1624 | + | |
Lines changed: 25 additions & 17 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
51 | 51 | | |
52 | 52 | | |
53 | 53 | | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
54 | 76 | | |
55 | 77 | | |
56 | | - | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
57 | 81 | | |
58 | 82 | | |
59 | 83 | | |
60 | 84 | | |
61 | | - | |
62 | | - | |
63 | | - | |
64 | | - | |
65 | | - | |
66 | | - | |
67 | | - | |
68 | | - | |
69 | | - | |
70 | | - | |
71 | | - | |
72 | | - | |
73 | | - | |
74 | | - | |
75 | | - | |
76 | | - | |
77 | 85 | | |
78 | 86 | | |
79 | 87 | | |
| |||
Lines changed: 33 additions & 14 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
249 | 249 | | |
250 | 250 | | |
251 | 251 | | |
252 | | - | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
253 | 262 | | |
254 | 263 | | |
255 | 264 | | |
256 | 265 | | |
257 | 266 | | |
258 | 267 | | |
259 | 268 | | |
260 | | - | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
261 | 282 | | |
262 | 283 | | |
263 | | - | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
264 | 290 | | |
265 | 291 | | |
266 | | - | |
267 | | - | |
268 | | - | |
269 | | - | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
270 | 295 | | |
271 | 296 | | |
272 | | - | |
| 297 | + | |
273 | 298 | | |
274 | 299 | | |
275 | 300 | | |
| |||
284 | 309 | | |
285 | 310 | | |
286 | 311 | | |
287 | | - | |
288 | | - | |
289 | | - | |
290 | | - | |
291 | | - | |
292 | | - | |
293 | 312 | | |
294 | 313 | | |
295 | 314 | | |
| |||
0 commit comments