Repository navigation
Commit 9f73370
authored
Show startup progress before the agent's first message (#1692)
## Summary
After an investigation starts, the Activity pane showed only "Starting
investigation…" and a timer for the 20+ seconds until the agent's first
message. Three real signals exist before that message and none was
shown. This PR reports the two Radar can observe honestly, with no
timer-based labels:
- **Connected to Radar's tools** the moment the agent completes the MCP
handshake on the private investigation mount (agent-agnostic).
- **Claude Code ready · \<model\> · \<n\> Radar tools** from Claude
Code's stream-json `system`/`init` line. A non-connected MCP server
becomes a visible warning in the Activity pane instead of a silent
tool-less investigation. Cursor's init names the model only.
Also on the same Activity rows: sub-2s tool durations are hidden on the
collapsed row (whole seconds above that), the exact milliseconds stay in
the expanded result header, and a failed tool row shows the producer's
own failure reason inline.
## What changed
- `internal/investigationrefs`: a scope now carries a `Connected()`
channel; `Registry.MarkConnected(scope)` closes it once for a live
scope. This is the seam between the MCP mount and the runner, so
`internal/mcp` still does not import `internal/ai`.
- `internal/mcp/server.go`: the investigation middleware marks the scope
connected after `initialize` or `tools/list` returns **without error** —
a rejected handshake never reports as connected.
- `internal/ai/diagnoser.go`: `StreamEvent` gains optional `Model`,
`ToolCount`, `MCPServers` (phase events were already persisted in the
run log; no migration). The runner watches the scope and emits one
`phase: connected` per turn, serialized with the parser's events and
always before the turn returns — including when the handshake and the
stream's end coincide. The watcher stays alive until process exit.
`parseStream` handles `system`/`init` and emits `phase: ready` with the
model, the `mcp__radar__` tool count and every MCP server status. Cursor
emits the model.
- `web/src/api/diagnose.ts`, `parts.tsx`, `InvestigationView.tsx`: the
`phase` event folds into `turn.startup` (furthest phase wins, since the
handshake and init are reported by different goroutines). The pending
status line shows the startup label only while the turn is running with
no items, so it never renders on a replayed finished turn and never
becomes a transcript item. The MCP warning is attributed to startup and
survives replay, since it is a fact of the run; its copy is conditional
on the server's state and reads in past tense on a replayed run.
- Tool rows: `toolDurationLabel` (hidden under 2s, whole seconds above),
`toolErrorReason` (JSON `error` field or first non-empty line of the
producer text, with Radar's retry hints after the em dash dropped),
`middleTruncate` so a long reason keeps its identifier tail, arguments
shrink before the reason on the collapsed row, `PayloadBlock` header
detail for the exact ms.
- Running status: the elapsed counter is a row-level "Ns elapsed" figure
set apart from the label, so "Connected to Radar's tools" no longer
reads as if the handshake took that long.
## Testing
- `make tsc`, `make build`, `go test ./...` (both modules), `git diff
--check` — pass. New Go tests: the Claude init parser, the
handshake-emitted-once runner path including the short-turn race, the
registry handshake, the middleware's success-only marking, and Cursor's
init model.
- `packages/k8s-ui`: 3549 passed. `web`: 1023 passed across 87 files,
including the startup-signal merge (furthest phase wins, init facts
kept), the bare `ready` label with no init facts, replay suppression,
and the tool-row duration/reason rendering.
- Visual test against a live GKE cluster (Deployment
`dev/cloud-provisioner`, real Claude Code run). Observed from the click:
0.4s "Claude Code starting…", 0.9s "Connected to Radar's tools", 5.0s
"Claude Code ready · claude-sonnet-5 · 23 Radar tools", 23.2s first tool
row. Reloading a finished run shows no startup label. A follow-up turn
on the same run repeated the progression without special-casing.
- Tool rows on the same cluster: a call for a nonexistent Secret
rendered `resource not found: secret "…" not found` inline after the
arguments. Every live call finished under 2s, so the slow row was
verified against a saved run whose `search` took 3754ms — the collapsed
row reads `4s`, the expanded header `ORIGINAL RESULT · 3754ms`.
## Scope and tradeoffs
- Codex (`codex exec --json`) only emits `thread.started`; it carries no
model or MCP data, so it gets the agent-agnostic "connected" signal
only.
- The startup warning is hand-rolled rather than `AlertBanner`. That
component is the drawer-scale renderer surface (`mb-4 p-3`, 14px title
plus message); the Activity pane is an 11px transcript and every warning
in `parts.tsx` (consent, apply, truncation notes) is hand-rolled at that
scale, so the banner would be out of register there.
- Radar Hub's hosted transport forwards only the phase name for `phase`
events, so a hosted run shows the bare "\<agent\> ready" label with no
model, tool count or warning. That is the same code path with fields
absent — there is no version fallback.
- The "starting" label uses the agent's display name ("Claude Code
starting…") rather than the literal "Agent starting…"; the label is
right there and reads better.
- The 5s to 23s gap between "ready" and the first tool is the model's
first thinking; removing it needs `--include-partial-messages`, which is
the planned follow-up.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_0125QDX24TNuod7FKLwTDhvW
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **Medium Risk**
> Touches investigation streaming, MCP handshake signaling, and live UI
state; race handling is new but heavily tested—no auth or data-model
changes.
>
> **Overview**
> Investigation Activity now surfaces **real startup progress** before
the first agent message, instead of a generic “Starting investigation…”
line.
>
> The backend emits new **`phase` stream events**: `investigating`, then
**`connected`** when the agent completes MCP handshake on the private
investigation mount (via `investigationrefs.MarkConnected` + middleware
on successful `initialize`/`tools/list`), then **`ready`** from the
CLI’s `system/init` line with **model**, **Radar tool count**
(`mcp__radar__` prefix), and **MCP server statuses**. Events are
**mutex-serialized** so handshake and parser callbacks don’t interleave;
the handshake watcher still fires once on short turns. **Cursor** maps
init to `ready` with **model only**.
>
> The UI folds phases into **`turn.startup`** (`mergeStartupSignal`
keeps the furthest phase), shows agent-specific pending labels while the
turn is running and the transcript is empty, and **warns** when MCP
servers aren’t `connected` (warning persists on replay). **Tool rows**
hide sub-2s durations on the collapsed row (seconds + exact ms in
expanded header), show **inline failure reasons**, and move the elapsed
timer to a separate **“Ns elapsed”** figure beside the status label.
>
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
8d78513. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->1 parent cc95f0d commit 9f73370
12 files changed
Lines changed: 1023 additions & 23 deletions
File tree
- internal
- ai
- investigationrefs
- mcp
- web/src
- api
- components/diagnose
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
197 | 197 | | |
198 | 198 | | |
199 | 199 | | |
| 200 | + | |
200 | 201 | | |
201 | 202 | | |
202 | 203 | | |
| |||
292 | 293 | | |
293 | 294 | | |
294 | 295 | | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
295 | 301 | | |
296 | 302 | | |
297 | 303 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
96 | 96 | | |
97 | 97 | | |
98 | 98 | | |
99 | | - | |
| 99 | + | |
100 | 100 | | |
101 | 101 | | |
102 | 102 | | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
103 | 107 | | |
104 | 108 | | |
105 | 109 | | |
| |||
146 | 150 | | |
147 | 151 | | |
148 | 152 | | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
149 | 156 | | |
150 | 157 | | |
151 | 158 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
249 | 249 | | |
250 | 250 | | |
251 | 251 | | |
252 | | - | |
253 | | - | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
254 | 259 | | |
255 | 260 | | |
256 | 261 | | |
| |||
277 | 282 | | |
278 | 283 | | |
279 | 284 | | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
280 | 292 | | |
281 | 293 | | |
282 | 294 | | |
| |||
609 | 621 | | |
610 | 622 | | |
611 | 623 | | |
612 | | - | |
| 624 | + | |
| 625 | + | |
| 626 | + | |
| 627 | + | |
| 628 | + | |
| 629 | + | |
| 630 | + | |
| 631 | + | |
| 632 | + | |
613 | 633 | | |
614 | 634 | | |
615 | 635 | | |
616 | 636 | | |
617 | 637 | | |
618 | 638 | | |
619 | | - | |
| 639 | + | |
| 640 | + | |
| 641 | + | |
| 642 | + | |
| 643 | + | |
| 644 | + | |
| 645 | + | |
| 646 | + | |
| 647 | + | |
| 648 | + | |
| 649 | + | |
| 650 | + | |
| 651 | + | |
| 652 | + | |
| 653 | + | |
| 654 | + | |
| 655 | + | |
| 656 | + | |
| 657 | + | |
| 658 | + | |
| 659 | + | |
| 660 | + | |
| 661 | + | |
| 662 | + | |
620 | 663 | | |
621 | 664 | | |
622 | 665 | | |
623 | 666 | | |
624 | 667 | | |
| 668 | + | |
| 669 | + | |
| 670 | + | |
| 671 | + | |
625 | 672 | | |
626 | 673 | | |
627 | 674 | | |
| |||
944 | 991 | | |
945 | 992 | | |
946 | 993 | | |
| 994 | + | |
| 995 | + | |
| 996 | + | |
| 997 | + | |
| 998 | + | |
| 999 | + | |
| 1000 | + | |
| 1001 | + | |
| 1002 | + | |
| 1003 | + | |
| 1004 | + | |
| 1005 | + | |
| 1006 | + | |
| 1007 | + | |
| 1008 | + | |
| 1009 | + | |
| 1010 | + | |
| 1011 | + | |
| 1012 | + | |
| 1013 | + | |
947 | 1014 | | |
948 | 1015 | | |
949 | 1016 | | |
| |||
982 | 1049 | | |
983 | 1050 | | |
984 | 1051 | | |
| 1052 | + | |
| 1053 | + | |
| 1054 | + | |
| 1055 | + | |
985 | 1056 | | |
986 | 1057 | | |
987 | 1058 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
9 | 9 | | |
10 | 10 | | |
11 | 11 | | |
| 12 | + | |
12 | 13 | | |
| 14 | + | |
13 | 15 | | |
| 16 | + | |
14 | 17 | | |
15 | 18 | | |
16 | 19 | | |
| |||
922 | 925 | | |
923 | 926 | | |
924 | 927 | | |
| 928 | + | |
| 929 | + | |
| 930 | + | |
| 931 | + | |
| 932 | + | |
| 933 | + | |
| 934 | + | |
| 935 | + | |
| 936 | + | |
| 937 | + | |
| 938 | + | |
| 939 | + | |
| 940 | + | |
| 941 | + | |
| 942 | + | |
| 943 | + | |
| 944 | + | |
| 945 | + | |
| 946 | + | |
| 947 | + | |
| 948 | + | |
| 949 | + | |
| 950 | + | |
| 951 | + | |
| 952 | + | |
| 953 | + | |
| 954 | + | |
| 955 | + | |
| 956 | + | |
| 957 | + | |
| 958 | + | |
| 959 | + | |
| 960 | + | |
| 961 | + | |
| 962 | + | |
| 963 | + | |
| 964 | + | |
| 965 | + | |
| 966 | + | |
| 967 | + | |
| 968 | + | |
| 969 | + | |
| 970 | + | |
| 971 | + | |
| 972 | + | |
| 973 | + | |
| 974 | + | |
| 975 | + | |
| 976 | + | |
| 977 | + | |
| 978 | + | |
| 979 | + | |
| 980 | + | |
| 981 | + | |
| 982 | + | |
| 983 | + | |
| 984 | + | |
| 985 | + | |
| 986 | + | |
| 987 | + | |
| 988 | + | |
| 989 | + | |
| 990 | + | |
| 991 | + | |
| 992 | + | |
| 993 | + | |
| 994 | + | |
| 995 | + | |
| 996 | + | |
| 997 | + | |
| 998 | + | |
| 999 | + | |
| 1000 | + | |
| 1001 | + | |
| 1002 | + | |
| 1003 | + | |
| 1004 | + | |
| 1005 | + | |
| 1006 | + | |
| 1007 | + | |
| 1008 | + | |
| 1009 | + | |
| 1010 | + | |
| 1011 | + | |
| 1012 | + | |
| 1013 | + | |
| 1014 | + | |
| 1015 | + | |
| 1016 | + | |
| 1017 | + | |
| 1018 | + | |
| 1019 | + | |
| 1020 | + | |
| 1021 | + | |
| 1022 | + | |
| 1023 | + | |
| 1024 | + | |
| 1025 | + | |
| 1026 | + | |
| 1027 | + | |
| 1028 | + | |
| 1029 | + | |
| 1030 | + | |
| 1031 | + | |
| 1032 | + | |
| 1033 | + | |
| 1034 | + | |
| 1035 | + | |
| 1036 | + | |
| 1037 | + | |
| 1038 | + | |
| 1039 | + | |
| 1040 | + | |
| 1041 | + | |
| 1042 | + | |
| 1043 | + | |
| 1044 | + | |
| 1045 | + | |
| 1046 | + | |
| 1047 | + | |
| 1048 | + | |
| 1049 | + | |
| 1050 | + | |
| 1051 | + | |
| 1052 | + | |
| 1053 | + | |
| 1054 | + | |
| 1055 | + | |
| 1056 | + | |
| 1057 | + | |
| 1058 | + | |
| 1059 | + | |
| 1060 | + | |
| 1061 | + | |
| 1062 | + | |
| 1063 | + | |
| 1064 | + | |
| 1065 | + | |
| 1066 | + | |
| 1067 | + | |
| 1068 | + | |
| 1069 | + | |
| 1070 | + | |
| 1071 | + | |
| 1072 | + | |
| 1073 | + | |
| 1074 | + | |
| 1075 | + | |
| 1076 | + | |
| 1077 | + | |
| 1078 | + | |
| 1079 | + | |
| 1080 | + | |
| 1081 | + | |
| 1082 | + | |
| 1083 | + | |
| 1084 | + | |
| 1085 | + | |
| 1086 | + | |
| 1087 | + | |
| 1088 | + | |
| 1089 | + | |
| 1090 | + | |
| 1091 | + | |
| 1092 | + | |
| 1093 | + | |
| 1094 | + | |
| 1095 | + | |
| 1096 | + | |
| 1097 | + | |
| 1098 | + | |
| 1099 | + | |
| 1100 | + | |
| 1101 | + | |
| 1102 | + | |
| 1103 | + | |
| 1104 | + | |
| 1105 | + | |
| 1106 | + | |
| 1107 | + | |
| 1108 | + | |
| 1109 | + | |
| 1110 | + | |
0 commit comments