The teleop Ready screen is the operator’s front-of-house surface. It is the robotics analogue of a contact-centre agent’s ready/aux screen: instead of waiting for a call, the operator waits for a robot job matched to their skill certifications. This page documents what that screen publishes, how ClutchCall picks an operator for a job, and what the ring offer carries into the cockpit.

The operator model: certifications, levels and presence

The screen loads the signed-in operator with api.me(). The operator record it renders has these fields: Certifications are per skill, with a level. The screen renders each one as a skill · level chip and states the matching rule directly to the operator: a job offer only reaches you if you hold its required cert at level. Until api.me() resolves, the screen renders an empty operator (id: '', no certs, presence: 'ready'). No presence is broadcast while ME.id is empty — the broadcast effect returns early.

Ready vs Aux and what each broadcasts

Presence is local UI state with two values, toggled by the two buttons in the screen header:
  • Ready — the operator is in the dispatchable pool.
  • Aux — the operator is out of the pool, with a reason selected from a dropdown: Break, Training, Debrief, Meeting.
Whenever the operator identity or the presence toggle changes, the screen publishes a presence update on the ring channel:
Three details matter when you consume this pool:
  • available is the only presence signal. Aux is published as available: false, not as a separate state.
  • certs is flattened to skill names only. The level is not in the presence payload, so a dispatcher reading the pool sees which skills an operator holds, not at what level.
  • The selected Aux reason (Break, Training, …) is not part of the payload. It is displayed locally on this screen only.
Aux also changes the screen itself: the Available-jobs card’s status text reads paused — you are in Aux instead of listening, and every row’s Attach button is disabled.

How a job is matched: certification filter, then lowest RTT

Matching happens off this screen. The Ready screen’s job is to advertise the operator into the pool and to render whatever offer arrives. The order is:
  1. Certification filter. Only operators holding the job’s required cert are candidates. The screen states this to the operator as the reason an offer would or would not reach them.
  2. Lowest RTT. The dispatching ops console reads the presence pool of available operators and runs claim_lowest_rtt over it, so the candidate with the lowest round-trip time to that robot wins.
The resulting offer is delivered to one operator. The offer carries an operatorId, and the ring only belongs to an operator when offer.operatorId equals their own id.
In the current screen, the offer handler presents any offer that arrives on the channel rather than comparing offer.operatorId to ME.id — the demo channel is shared between tabs, and per-operator targeting on the client is noted in the source as a refinement. Server-side, the ACD has already picked the operator before it rings.

The ring offer: fields, accept and decline

An offer opens a modal over the whole screen. The offer object exposes: Accept acknowledges the offer and moves the operator straight into the cockpit on the assigned session:
Decline acknowledges the offer with the same two identifiers and dismisses the modal. The operator stays on the Ready screen and stays in the pool with their current presence:
Both paths clear the modal. The screen implements no client-side ring timeout — an unanswered modal stays up until the operator accepts or declines, or until the screen unmounts (at which point the ring channel is closed).

Where the session id comes from and how it reaches the cockpit

The operator console never mints a session id. The id is created by the match that produced the offer — a POST /v1/sessions match on the engine rings this console — and arrives as offer.sessionId. From there it travels by URL. Accepting navigates to /cockpit?robot=<robotId>&session=<sessionId>, both URL-encoded, so the cockpit attaches to the session the platform assigned rather than opening a new one. The Attach button on an Available-jobs row is a different path: it navigates to /cockpit?robot=<robot> with no session parameter. Only the accepted ring offer carries an assigned session id into the cockpit.

The two ring transports

RingChannel is created once per screen (held in a ref) and carries both presence and offers. The screen wires two delivery paths into the same modal:
  • BroadcastChannel. Works cross-tab with no engine running. This is the path used for local and demo flows.
  • Engine realtime. ring.connectRealtime(tenant, url) subscribes to the engine’s mod_realtime channel private-cti-<orgId>, so a real match on the engine rings this console.
The relay URL comes from VITE_TELEOP_REALTIME_URL, falling back to the platform’s default relay host. The tenant argument is the org id from getOrgId() — the same id every other teleop call sends — and not a build-time constant. The BFF’s teleopSign signs a private-cti-<orgId> subscribe only for an org the caller is a member of, and a compile-time constant cannot be checked against a membership. On unmount, the screen calls ring.close().

The available jobs queue

api.availableJobs() backs the queue table. Each row exposes: The queue is empty by default. An empty queue renders an explicit message rather than bare headers, because headers with nothing under them read as “stuck loading”: no robots need an operator right now, and offers appear the moment a robot you are certified for raises a distress call. While the operator is in Aux, the message appends that no offers will be made until they go Ready.

Shift metrics on this screen

The This shift card shows four counters, labelled: These four values are literals in the screen source, not results of any procedure the screen calls. api.me() and api.availableJobs() are the only data the screen fetches. Treat the tile as a placeholder layout for shift counters, and do not read the displayed numbers as live operator telemetry.
  • Authentication — how the org-scoped token that authorises the private-cti-<orgId> subscribe is minted
  • Telemetry — the metric and trace streams the gateway emits for sessions