Fresh
Accept the action only when its receive age, server version and echoed observation sequence are within the agreed contract.
age ≤ budgetA 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.
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 SOURCE | ENGINEERING IMPLICATION | PUBLIC 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.
Accept the action only when its receive age, server version and echoed observation sequence are within the agreed contract.
age ≤ budgetOnce the monotonic age exceeds the budget, invalidate the action atomically. Do not extend freshness merely because the socket remains connected.
age > budgetCommand 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_holdReconnect with bounded backoff, discard pre-disconnect actions and resume only after a fresh sequence passes validation plus the agreed arming rule.
fresh_seq + arm| FAULT | MEASUREMENT | PASS CONDITION |
|---|---|---|
| Normal run for 60 seconds | Action interval, age, queue depth, base/arm command | No false stale transition; bounded action age. |
| 100 / 250 / 500 ms response delay | First stale detection and hold timestamps | Behavior matches the configured budget and produces one reasoned transition. |
| Silent response drop with socket left open | Last fresh action versus first hold | Hold occurs before the stale-action deadline; last action is not replayed indefinitely. |
| Hard disconnect during non-zero motion | Commanded velocity and controller mode | Deterministic zero/hold independent of reconnect success. |
| Reconnect after an old response is delayed | Observation and action sequence | No 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.
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.