The session log is the teleop equivalent of a Call Detail Record. Where the voice gateway writes one CDR row per call, ClutchCall writes one session record per operator attach to a robot. Every attach produces a row, whether it ended cleanly, was taken over, or faulted out. The screen loads the whole set through a single read (sessionLog()) and renders it as a filterable table. It performs no mutations: nothing on this screen starts, stops, or re-attributes a session. It is a record of what already happened, plus the sessions that are still open.

What a session record contains

Each row is one session. These are the fields the log carries and the column each one renders into: Three of these columns are display transforms rather than stored values, which matters if you are reconciling the screen against exported data:
  • Session is truncated for width. The record holds the full id.
  • Duration is computed from startedAt as time elapsed. For a session that is still open this is the live elapsed time; the record itself stores only the start instant.
  • Fault and Bad MAC are tinted. The fault colour comes from a shared tone function keyed on the fault value, and the bad-MAC count is tinted only when it is non-zero. The colour carries no information that the text does not.

Session states and terminal faults

State and fault are two different columns and answer two different questions. State is where the session sits in its lifecycle — it distinguishes a session that is live and holding control from one that has finished. Live and past sessions share the table, so state is how you tell them apart, and it is the field the state pill renders. Fault is why the session is in that state. It is populated on every row, not only on failures: a session that ran to completion and detached normally still reports a fault value, and the tone function renders that value differently from a genuine fault so the two do not read alike at a glance. When a session is terminal, the fault is the terminal fault — the last condition observed before control was released, and the single field you want when someone asks why a robot stopped taking commands. A fault on a row does not by itself say whether the operator, the link, or the robot was at fault. Pair it with the counters in the same row.

Command counters: accepted, superseded, and bad MAC

Every command sent inside a session lands in exactly one of three counters. Reading them together tells you more than any one alone. Accepted counts commands the robot took and acted on. This is the session’s useful work, and it is the only counter the table formats with thousands separators — it is normally orders of magnitude larger than the other two, because a teleop session streams commands continuously rather than sending discrete requests. Superseded counts commands that were displaced by a newer command before they took effect. In a continuous control stream this is expected and not an error: when a fresher setpoint arrives, the stale one is dropped rather than queued, because executing a stale setpoint is worse than skipping it. A small superseded count alongside a large accepted count is the normal shape of a healthy session. A superseded count that grows toward the accepted count is the signal worth chasing — it means commands are arriving faster than they are being consumed, or arriving out of order. Bad MAC counts commands rejected because their message authentication code did not verify. These are not dropped for timing; they are refused because the robot could not prove the command came from the holder of the session’s key. This counter is the reason the column is tinted: on a correctly configured session it stays at zero, and any non-zero value means commands reached the robot carrying signatures it could not validate. Investigate a non-zero bad-MAC count as a security or key-distribution question, not a performance one.

Epochs and attribution

An epoch is the control-authority generation in force for a session. It is the value commands are stamped with, and it is what makes a command attributable to one specific grant of control rather than merely to a robot and a moment in time. Authority changes hands. An operator detaches and another attaches; a session is taken over; a robot is re-attached after a fault. Each of those produces a new session record, and the epoch on the new row differs from the epoch on the old one. That is the purpose of putting the column on this screen: two rows for the same robot, adjacent in time, are unambiguously separate authority periods rather than one continuous one, and a command carrying an epoch can be matched back to exactly one row in this table. The log records the epoch that was in force. It does not assign or roll epochs — nothing on this screen changes the authority of a session. Together, operatorName, region, and epoch are the attribution triple for a session: who held control, where they connected from, and under which grant of authority. Every attach is recorded with all three, which is what makes the log usable as evidence that a given robot motion is traceable to a named operator.

Exporting the log

The screen offers a CSV export of the session log. Export carries the session record, so the exported data is the field set in the table above — including the values the table only shows in transformed form. Expect the full id rather than the shortened display form, and startedAt as a timestamp rather than the elapsed-time string the Duration column renders. The colour applied to the fault and bad-MAC columns is a display concern and has no representation in the export. Export is a read of the same data the table shows. It does not archive, expire, or remove records, and this screen exposes no retention controls — how long session records are kept is configured outside it.
  • Telemetry — the voice-side CDR schema this record parallels
  • Authentication — relay tokens and namespace scope, which govern which robotics topics a session may command
  • Telephony metrics — metric definitions for the voice modality