# Teleop Session Safety and Authority

> The teleop session state machine, motion-authority epochs and sequence numbers, command-frame authentication counters, divergence, and supervisor take-over.

A teleop session binds one operator to one robot for a bounded period.
Everything the session is allowed to do is derived from two things: the
**state** it is in, and the **epoch** of motion authority it currently
holds. ClutchCall treats both as first-class, auditable facts — the
ops session screen shows the live value of each, and every transition
between them is attributed to an operator.

This page defines those values so that the numbers on the session detail
screen mean something specific.

## The session state machine

Each live session reports exactly one state. The console renders it as a
pill (`StatePill`) with a per-state colour, and repeats it in the session
list so that a supervisor can scan many sessions at once.

| State      | Motion authority | Meaning                                                                                  |
| ---------- | ---------------- | ---------------------------------------------------------------------------------------- |
| `SAFE`     | None             | The session is attached and streaming telemetry, but command frames do not drive the arm. |
| `ARMED`    | Held             | The operator holds motion authority for the current epoch. Accepted frames move the robot. |
| `DIVERGED` | Suspect          | The reported pose no longer tracks the commanded pose. The console draws the twin/ghost trace in its diverged styling. |
| Faulted    | None             | A fault is latched on the session. The `fault` field names it; the console tones it by severity. |

The transitions the platform records are **arm**, **disarm**, **e-stop**,
and **fault**. Each one is written to the audit stream with the operator
who caused it and the epoch it applied to — see
[The transition timeline and attribution](#the-transition-timeline-and-attribution).

A session in `SAFE` is not a broken session. `SAFE` is the resting state:
telemetry keeps flowing, the twin keeps updating, and the operator can
watch the robot without any command path being live.

## Motion authority: epochs and sequence numbers

Motion authority is not a boolean. It is a **grant**, and each grant has
an **epoch** number. The session detail screen shows the epoch and, as a
sub-label, the last accepted sequence:

```
Epoch  7
seq 1,284,109
```

- **Epoch** identifies the current grant of motion authority. It changes
  when authority changes hands — when a session arms, when it is dropped
  to `SAFE`, when a supervisor takes over. A command frame stamped with
  an epoch that is no longer current cannot move the robot, no matter how
  well-formed or well-authenticated it is.
- **Last accepted sequence** is the highest command-frame sequence number
  the robot has accepted and applied within the current epoch. It is the
  watermark. A frame that arrives with a sequence at or below the
  watermark has been overtaken by a newer command and is dropped.

The pairing matters. Sequence numbers give ordering *inside* an epoch;
epochs give ordering *between* grants of authority. Together they make
stale commands unapplyable rather than merely unlikely — an operator
whose link stalls and then recovers cannot have queued motion replayed
into a robot that has since been taken over by someone else.

Frames rejected on either ground are counted as **superseded**.

## Command frame authentication and rejection counters

Every command frame carries a message authentication code keyed to the
session. The receiving side verifies the MAC before it looks at the
payload, so an unauthenticated frame never reaches the motion path. The
console surfaces the outcome of that check as four counters in the
**Counters** card:

| Counter        | What it counts                                                                                       | Expected on a healthy session |
| -------------- | ---------------------------------------------------------------------------------------------------- | ----------------------------- |
| **Accepted**   | Frames that verified, belonged to the current epoch, advanced the sequence watermark, and were applied. | Grows continuously while `ARMED`. |
| **Superseded** | Frames that verified but were stale — an old sequence number, or an epoch that is no longer current.    | Small and sporadic. Reordering and recovered links produce a few. |
| **Bad MAC**    | Frames whose authentication code did not verify against the session key.                              | Zero. |
| **Bad frame**  | Frames that could not be parsed — truncated, malformed, or otherwise undecodable.                     | Zero, or near it. |

The console highlights **Bad MAC** in the error colour as soon as it is
non-zero, because it is the only counter of the four with no benign
explanation. Superseded frames are a timing artefact. Bad frames are a
bug or a corrupted transport. A non-zero Bad MAC means something is
emitting command frames onto the session that does not hold the session
key, and the session should be dropped to `SAFE` and investigated rather
than nursed along.

Treat these counters as monotonic per session. Compare them against
**Accepted** rather than reading them in isolation: a handful of
superseded frames against a million accepted ones is noise; the same
count against a few hundred accepted frames is a broken link.

## Divergence: commanded pose vs reported pose

The session screen draws two arms on top of each other in the
**Twin / ghost trace** panel:

- The **twin** pose — where the commanded state says the arm should be.
- The **ghost** pose — the joint angles the robot itself reports.

`Divergence` is the angular difference between them, reported in degrees
as a single scalar. The console shows it beside RTT and tones it with the
diverged colour once it passes the threshold at which the trace is worth
a second look (above 15°), and the `ArmView` switches to its diverged
rendering when the session state is `DIVERGED`.

Divergence and round-trip time read together. High RTT with low
divergence is a slow but faithful link. Low RTT with rising divergence
points at the robot side — a joint under load, a limit being clamped, a
controller refusing part of the commanded pose. The console tones RTT as
a warning above 320 ms.

Both poses arrive over the relay telemetry track, not with the session
record. Until that transport is subscribed, the screen has no live joint
data and falls back to a neutral six-joint pose; if the reported pose is
missing specifically, the ghost is drawn on top of the twin. In both
cases the trace shows zero apparent divergence because there is nothing
to compare, not because the arm is tracking perfectly. Check that
telemetry is subscribed before you read a flat trace as good news.

## Supervisor take-over: Force SAFE and E-STOP

The **Take-over** card gives a supervisor two distinct interventions.
They are not two strengths of the same button.

**Force SAFE** is scoped to one session. It holds the arm and drops that
operator to `SAFE`, ending their motion authority. The operator is
notified. The session stays attached, telemetry keeps flowing, and the
robot remains under the platform's control — it is a revocation of
authority, not a shutdown. Because authority has changed hands, command
frames stamped with the previous epoch are superseded from that moment
on, so nothing the operator had in flight can land afterwards. Recovery
is ordinary: the cause is addressed and the session arms again, taking a
new epoch.

**Supervisor E-STOP** is a latching stop and it applies to everyone, not
just the selected session. Latching is the important word: it does not
lapse when the link recovers, when the operator reconnects, or when the
supervisor navigates away from the screen. It stays asserted until it is
explicitly cleared. Nothing re-arms while the latch is set, so a
re-arm attempt that appears to do nothing is the expected behaviour of an
uncleared E-STOP rather than a failed request.

Pick between them on scope and on what you want afterwards. Force SAFE
when one operator should stop driving and you want the cell to keep
running. E-STOP when the cell itself is unsafe and every session on it
should stop until a human clears the condition.

## The transition timeline and attribution

The **Transition timeline** panel is the audit view of the state machine.
It renders the audit stream filtered to the selected session, plus
unscoped events — records with no session id are platform- or cell-wide
and are shown against every session, because a supervisor E-STOP or a
cell-level fault is context that a single-session view must not hide.

Each entry is attributable. Arm, disarm, e-stop, and fault transitions
carry the operator responsible and the epoch they applied to, so a
sequence of grants can be reconstructed after the fact: who armed, under
which epoch, what the counters and divergence looked like at that point,
who took authority away, and whether the stop was scoped or latching.
The epoch is the join key — it is what lets you tie a run of accepted
frames and a divergence excursion to the specific grant of authority that
produced them.

## What the session detail screen reads

The screen composes two calls and one live transport.

| Source                | Supplies                                                                                                                    |
| --------------------- | --------------------------------------------------------------------------------------------------------------------------- |
| `liveSessions`        | The session list and, per session: `id`, `robot`, `operatorName`, `state`, `startedAt`, `rttMs`, `divergenceDeg`, `fault`, `epoch`, `lastAcceptedSeq`, and the `counters` object (`accepted`, `superseded`, `badMac`, `badFrame`). |
| `audit`               | Transition events. Entries carry an optional `session` id; those without one are unscoped and render on every session.       |
| Relay telemetry track | The live twin and reported joint poses that drive the twin/ghost trace and the per-joint bars.                               |

`liveSessions` carries the operator, the robot, the start time, and the
**last known** safety state and counters. It is the authority record. It
is not the pose. A session detail view that shows sensible counters and a
neutral arm is a session whose telemetry transport has not been
subscribed — the safety state on screen is still correct.

## Related

- [Telemetry](/platform/telemetry) — metrics, traces, and the streams that carry them
- [Authentication](/concepts/authentication) — relay tokens and the namespace scope a session presents
