The dashboard becomes a runtime observability surface rather than a telemetry
viewer, and one real defect in the tool accounting is fixed. Nine peer tabs
become four destinations; nothing about the collectors, the store or the MCP
tool surface changed.
Fixed - the tool accounting was fabricating a zero
- A tool call whose handler threw was never counted. The cost meter wrapped
registration and recorded after the handler returned, so the SDK's conversion
of a throw into an error result took the call out of the accounting
altogether. Measured against a live app: five tools called with no isolate
attached, four of them erroring, all four missing — the session reported
0 calls, 0 errors. A dashboard rendering that would have shown a confident
zero while nothing worked. There is a mutation-checked regression test: remove
thecatchand it fails.
Fixed - readings that were not what they claimed
- The frame and heap charts printed the drawing ceiling under the word
"max". The plotting area is padded 15% above the data so the series does not
touch the top edge, and that padded number was the caption. A frame chart
whose real peak was 41.0ms captioned47.1; the heap chart captioned a
maximum above the heap capacity it reported four inches higher, which is
physically impossible in a memory tool. The caption now gives the observed
maximum, the last value and the sample count. Pinned by a check that runs the
shipped drawing code against known data. 0was printed where nothing could have been measured. With no runtime
attached,ExceptionsandNetworkshowed a zero rather than naming which
kind of nothing applied.- Readings taken before a disconnect looked current. Frame, heap and network
figures kept their live typography beside aNOT CONNECTEDbadge; the
staleness stamp reached two of six statistics and now reaches all of them. Pauseasserted the stream wasRECEIVINGwhile every reading behind it
was frozen — byte-identical output measured 19.5 seconds apart while the
server processed 32 further tool calls.STREAMandVIEWare now separate
states, because both are true at once.- The dashboard claimed seven collectors were
ACTIVEwith the VM
unreachable. Collector health is last-known and was never invalidated. - "last event never" appeared beside a list of events, because the
last-event time was set on live pushes only and never seeded from the
snapshot.
Fixed - the stream was unusable under load
- The row you were reading moved 2,416px down the page in six seconds.
Entries are prepended newest-first, so every arrival pushed the thing being
read further away, at roughly 400px/s. The stream now anchors on a row rather
than on scroll position: at the top it follows, scrolled away it pins and
reports what arrived (N new entries — jump to latest). Measured after: 0px
drift following, 0.2px pinned. The browser's ownoverflow-anchorhad to be
disabled, because it was fighting the explicit anchor and silently pushing a
following view off the top. - A typo could not be corrected in the search field. The input is rebuilt on
every keystroke and a refocus put the caret at the end of the string; caret
and selection are now preserved exactly. - Over half the visible stream was the dashboard's own heap poller — 52-67%
of the newest 400 rows, with zero frame, log or network rows visible while
hundreds of frames sat captured. Consecutive samples now roll up the way
reconnect churn already did, every original one disclosure away. Errorsnamed three unrelated things at once — MCP tool failures, janky
frames and app exceptions — and a 41.8ms frame rendered in the same red as a
crash. It is gone; the filters now each return exactly what they say.
Added - what the process knows about itself
- Tool-call telemetry reaches the browser. The cost meter gains per-tool
total duration and last-called time plus a window of recent calls, and the
dashboard broadcasts it. All of it was already collected and none of it was
reachable outsideruntime_status. - The connected agent is named.
src/core/mcpClient.tsreads the
clientInfothe client sends atinitialize, so the dashboard can say
"Claude Code 2.1.0" instead of asserting that "an agent" is present. Shown as
self-reported, because that is what it is. - A
telemetrymessage on the dashboard WebSocket carrying server identity,
tool accounting, collector health, unobservable categories and retention.
Diffed before sending, so an idle dashboard stays silent.
Changed - the dashboard
- Four destinations instead of nine. Overview (a session report), Activity
(one stream with source and category filters), Performance (frame analysis),
Diagnostics (topology, health, capability, blind spots). Logs, Network,
Exceptions, Timeline and the MCP call log became filters; three of them had
been rendering the identical empty sentence in a 120px panel. Inspector became
a section of Diagnostics, because it is a capability list. - The header carries three independent states — runtime, MCP, stream —
because a single badge could not express "the app is gone but MCP is serving
an agent", which is a real and common state and is why the old header said
"app not connected" while 239 tool calls were being served. - Findings state the evidence and, where the evidence contains one, the
remedy. The VM Service diagnosis event carries concrete transport advice;
the finding renders it verbatim. Remediation is never synthesised. Findings
are ordered most severe first, from the severity already assigned. - Keyboard operable throughout. 418 interactive elements previously had no
keyboard path. The tab strip is one tab stop with arrow-key movement; the
activity stream is one tab stop with a roving cursor, so several hundred rows
never become several hundred tab stops. Focus survives the re-render. - Tab, filter and search live in the URL, so a filtered view can be reloaded
or shared, and a drill-through is reversed by the browser's own Back button. - Non-text contrast. Every boundary was one token at 1.33:1. Control
boundaries now clear 3:1 (WCAG 1.4.11) while structural edges and row
separators stay quiet, because a card edge is not a component boundary. Export JSONis nowExport events, with its scope stated: this browser's
buffer, not the session. The full export remains theexport_sessiontool.
Added - documentation
PRODUCT.mdrecords the product truth every design pass had been
re-deriving: two audiences of equal weight, the positioning, what is
observable, what is not, and the principles — including "the dashboard must
never imply a fact that the runtime cannot actually observe".docs/Data-Path.mdtraces the topology and proves it rather than
asserting it: an MCP tool call's own event was observed arriving on the
dashboard socket with the same event id.docs/DESIGN.mdreplacesdocs/Design.md, which documented the
seven-tab dashboard, Google Fonts and tokens that no longer exist.
Known limitation, unchanged in this release
The frame budget is a fixed 16.7ms (60fps). On a faster display the jank count
is conservative, and both the finding and the Performance view say so rather
than presenting it as absolute.