Decisions › D08
Whoever owns the port does the talking: RobotDx never competes with a robot stack or a person's tool
Context
On 21 October a configuration read collided with an open tview on the same adapter. That is the failure the rule below prevents. The tool must be safe to leave running next to an experiment.
The decision
While a robot stack runs, its drivers own the ports and publish diagnostics; RobotDx only listens. When nothing runs, RobotDx may start a driver's own check as a short-lived owner, but only after a holder check (is any process using this port?) and the rack bus lock, taken without blocking.
“Blocked: bus busy”, with the holder named, is a state the page shows; it is not an error. On 24 October a person's own click was changed to wait its turn: the run shows as waiting, the host asks again every half second for up to 60 seconds, and a routine check never starves a person's run. Routine checks still yield to people and experiments, and never the reverse.
Why
“If commanding its more complicated beacuse now you need to deal with like permissions... so you don't intefer with the thing...”
Zach Yamaoka, notes, 22 Oct 2026
The reason a retry belongs to the host and not to a blocking lock inside the plugin is written beside the code: it works for every plugin that refuses this way, and a plugin keeps its non-blocking lock for diagnose and probe, so routine checks give way to people.
Options considered
| Option | Verdict | Reason |
|---|---|---|
| The dashboard talks to devices whenever it likes | Rejected | Collides with tview, an experiment or the robot stack. |
| A blocking lock inside every plugin | Rejected | Routine checks would block people; every plugin would have to implement it. |
| Holder check, a non-blocking bus lock, and a host retry for a person's click | Chosen | Yields to others by default; a person's press still gets through. |
Consequences
- Detection is passive by default (sysfs, serial by-id, USB descriptors, /proc). An active check runs only when no process holds the port and the bus lock is free, is query-only, and has an outer timeout.
- Tests never open a live CAN adapter or a camera. Mutation tests that touch how a command line is built run only behind the passive guard.
Evidence
- IO diagnostics: the converged designReport, 2026-10-22. Evidence for this decision.
- IO diagnostics v1: the first end-to-end buildReport, 2026-10-22. Evidence for this decision.
- IO diagnostics: converged designContract doc, 2026-10-22. Scope, layout, the two planes, port ownership, the contract and the open question about reusing Lichtblick.
- Runs v1: a live terminal for every utilityContract doc, 2026-10-24. The seam for runs: a real action on a pseudo-terminal, where records go, the output ring and recordings, Stop and the plugin's own stop.
- IO diagnostics v1: build planContract doc, 2026-10-22. The decisions, the plugin boundary (a subprocess speaking JSON lines), the lanes, and the interfaces frozen between them.
Commits in the app's repository (private):
3283bcf2026-10-22 Build plan v1: hard rules (no lane opens an adapter)4ae5e572026-10-24 Runs: a real act waits its turn for the rack bus lock