What the robot reports and how often
connectionTelemetry returns two arrays of per-minute aggregate rows for
the requested window. Each row is one minute of the robot’s relay session,
already reduced server-side — the screen never sees individual RTT samples or
individual packets.
Refresh behaviour:
- The telemetry query refetches every 30 seconds.
- The fleet list behind the picker refetches every 60 seconds and is considered stale after 30 seconds.
- The refresh button in the header forces a refetch and is disabled while one
is in flight. The header shows
Refreshing…during a fetch, otherwiseUpdated <relative time>based on when the telemetry query last resolved.
last 60s / −60 s … now; the underlying data is the 15-minute window at
one-minute resolution. Read the axis as “oldest sample → newest sample”, not
as literal seconds.
Choosing the focused robot
The focused robot comes from the route parameter/telemetry/:id. If the
route has no id and the fleet list has loaded, the screen redirects to the
first robot in the list. The header <select> navigates to
/telemetry/<id> on change.
Each option is labelled <name> · <id>, where the name falls back through
name → display_name → external_robot_id, and the id falls back from
id to external_robot_id.
If the id in the URL is not present in the fleet list, the picker adds a
synthetic option labelled <id> (not in active fleet) so the selection stays
visible. The telemetry query still runs for that id — a robot that has been
removed from the active fleet can still have telemetry rows in the window.
Telemetry is only queried when both an org id and a focused robot id are
present.
RTT percentiles: where they are measured
The RTT percentiles describe the QUIC connection between the robot and the relay, for the duration of the robot’s relay session. They are a property of that session, not of any single track or namespace: when there is no active relay session for the robot,rtt comes back empty.
Three percentiles are surfaced, and they are aggregated differently:
P50 and P95 are therefore “a typical minute”, while P99 is “the worst minute”.
A single bad minute moves P99 and leaves P50 and P95 alone. That is
intentional: the tail is the thing you want to see even if it happened once.
The tag in the panel corner is a display label derived from the worst P99:
above
1000 ms it reads tail expanded, otherwise within band. This is a
colouring rule for this screen only. It is not a service commitment, and it
says nothing about the other percentiles.
When rtt is empty the tag reads no data and all three percentages render
as — rather than 0. This matters: the medians of an empty array evaluate
to zero, and a zero RTT must never be read as a healthy result.
How the RTT histogram is built
The large histogram is not a distribution of RTT samples. It is a distribution of minutes, bucketed by that minute’s median RTT:- 20 buckets, 75 ms wide, covering 0–1500 ms.
- Each row in
rttincrements exactly one bucket, chosen byfloor(rtt_p50 / 75), clamped to the last bucket. - The bar count under the chart (
N minutes) is the sum of all buckets, which equals the number of rows returned. - The marker line is placed on the bucket containing the worst P99 of the
window, i.e.
floor(worst_p99 / 75), also clamped to the last bucket.
Congestion window and path migration
The congestion-window chart plotscwnd_avg from each cwnd row, converted
from bytes to KB (divided by 1024). The footer reports:
A path migration is counted by the relay whenever the robot’s QUIC
connection continues over a new network path — the connection ID survives the
handover, so there is no new TLS handshake and no session restart. A robot
moving between access points, or switching from Wi-Fi to cellular, produces
migrations without producing a reconnect. The counter is per-minute: a row
with
migrations: 2 means two migrations occurred during that minute.
On the chart, the dashed red MIG marker is drawn at the first bucket
where migrations > 0. Later migrations in the same window are counted in the
Migrations in window total but are not drawn. Read the marker as “migration
activity started here”, not as “the only migration”.
Expect the congestion window to collapse and re-grow around a migration: the
new path has a fresh estimate. A cwnd dip that lines up with the MIG marker
is the path change, not congestion on the old path.
The separate Path migrations panel is a placeholder — see
Panels with no data source yet.
Bytes in and bytes out
bytes_in and bytes_out are reported cumulatively per connection, not
as a per-minute delta. The screen therefore does not sum them. It walks the
rows in order and keeps the last non-zero value of each, which is the running
total for the connection as of the newest bucket in the window.
Consequences worth knowing:
- The figure is a connection lifetime total, so it can exceed what the robot actually transferred during the 15-minute window.
- If the connection is replaced, the counter restarts from the new connection’s baseline, and the tile will appear to drop.
- Zero and missing are treated identically: a non-finite or zero value never
overwrites the running total, and a total of zero renders as
—rather than0 B.
Per-track delivery and where drops come from
The Per-track frame ledger exists because a drop is usually a property of one track, not of the connection. The connection-level panels above it can look entirely healthy — steady cwnd, no migrations, flat RTT — while a single high-rate track is shedding frames. The ledger is the panel that attributes loss to a track. Its columns are:
Because
Late and FEC recovered are tracked separately from Dropped, they
do not contribute to the drop rate. A track can show a 0.00% drop rate and
still be unusable if its frames are consistently late; read all four counters
together.
This table currently renders no rows — see below.
Panels with no data source yet
Several panels are laid out but not wired toconnectionTelemetry. They
render placeholder text or — rather than being hidden, so that the shape of
the screen stays stable once the data lands. Do not read a placeholder as a
measurement of zero.
Reading an empty panel: no relay session vs no samples
Two different conditions produce a blank panel, and they call for different responses. No relay session.connectionTelemetry returns empty rtt and cwnd
arrays when the robot has no active relay session in the window. The RTT panel
shows the no data tag with — for all three percentiles, and the
congestion-window chart shows No congestion-window samples yet. This is a
connectivity question, not a performance question: the robot has not been
attached to the relay during the window. Check that the robot is online and
authenticated before reading anything else on the page.
No samples for a panel that has no source. The placeholders in the table
above look similar but mean only that ClutchCall does not yet populate that
panel. They will read empty even for a robot with a fully healthy, actively
transmitting session.
To tell them apart: if the RTT and congestion-window panels have data, the
session exists, and any other empty panel is an unwired panel. If those two
are also empty, there is no session.
The one trap to avoid in both cases is the zero. An empty rtt array produces
median and maximum values of 0. The screen suppresses them and renders —
instead, precisely so that “no session” is never displayed as “0 ms RTT”. If
you consume connectionTelemetry directly rather than through this screen,
apply the same rule: check the array length before reading percentiles, and
never treat a zero-length result as a pass.
