CONTROL SAFETY / ACTION FRESHNESS

A disconnected robot must not continue the last command

A socket can remain open while action responses stop. If the control loop keeps reading a persistent buffer, the robot may replay a stale base, arm or whole-body target. Reconnection is secondary; the first contract is deterministic hold on stale data.

01 / FAILURE

The fail-open path

In the reviewed PSI0 revision, the WebSocket message handler replaces a global action buffer but does not store a monotonic receive time with the action. The control loop reads the same buffer at control frequency; it does not consume or clear it. A normal close clears the run state, but a half-open connection can stop receiving without producing that close event.

OBSERVED IN SOURCEENGINEERING IMPLICATIONPUBLIC EVIDENCE
Message handler writes the latest action only.No action age can be enforced without a receive timestamp.psi-inference_rtc.py L149–164
Control loop repeatedly reads the persistent buffer.The last action remains eligible after response flow stops.psi-inference_rtc.py L272–342
Close callback clears running state.Explicit disconnect is handled differently from a silent receive stall.psi-inference_rtc.py L169–177
Public G1 issue reports sends continuing while receives stop.The symptom is consistent with a stale-action path, but does not alone prove the network root cause.PSI0 issue #95

Boundary: this is a review of one public revision and issue trace. It is not a claim that every Unitree controller or PSI0 version has the same behavior.

02 / CONTRACT

Fresh → stale → hold → re-arm

Fresh

Accept the action only when its receive age, server version and echoed observation sequence are within the agreed contract.

age ≤ budget

Stale

Once the monotonic age exceeds the budget, invalidate the action atomically. Do not extend freshness merely because the socket remains connected.

age > budget

Hold

Command zero base velocity and the controller-defined whole-body hold or standing path. Preserve a visible reason code and the last accepted sequence.

motion = safe_hold

Re-arm

Reconnect with bounded backoff, discard pre-disconnect actions and resume only after a fresh sequence passes validation plus the agreed arming rule.

fresh_seq + arm
03 / MINIMUM

Implementation requirements

  1. 01Store action payload, server version, echoed observation sequence and `received_at_monotonic` in one atomic record.
  2. 02Set the freshness budget from measured normal action intervals and the platform's safe stopping requirement; do not copy an arbitrary long timeout.
  3. 03Use a latest-only observation slot or one request in flight so network recovery cannot replay a long queue of obsolete observations.
  4. 04Define hold through the actual locomotion and arm controller APIs. “No new command” is not automatically the same as a safe stop.
  5. 05Log action age, observation/action sequence, commanded velocity, controller state and the exact reason for every hold transition.
  6. 06Require a fresh post-reconnect action and an explicit arming condition before non-zero motion resumes.
04 / HIL

Hardware-in-the-loop acceptance

FAULTMEASUREMENTPASS CONDITION
Normal run for 60 secondsAction interval, age, queue depth, base/arm commandNo false stale transition; bounded action age.
100 / 250 / 500 ms response delayFirst stale detection and hold timestampsBehavior matches the configured budget and produces one reasoned transition.
Silent response drop with socket left openLast fresh action versus first holdHold occurs before the stale-action deadline; last action is not replayed indefinitely.
Hard disconnect during non-zero motionCommanded velocity and controller modeDeterministic zero/hold independent of reconnect success.
Reconnect after an old response is delayedObservation and action sequenceNo pre-disconnect action is accepted; motion stays held until re-armed.

Run fault injection only with a cleared area, physical emergency stop or remote stop available, conservative motion limits and an agreed hold API. The matrix is a proposed acceptance method, not a completed test result.

Need this boundary tested on a G1 or Go2-W?

Start with the exact controller API, expected action period, allowed stop latency and one bounded non-zero motion. The deliverable is the recorded state trace, not a generic “works on hardware” claim.