IO diagnostics v1
v1 runs end to end on this PC, passively: the real host builds one tree from the OS (25 devices across USB, PCI, network, serial and video), and the real moteus_servo and intel_rgbd plugins hang their rows under it without opening an adapter or a camera.
The dashboard, the chip, the table, the activity log, the gated action dialog and the GNOME tray all render from the
real host, and stock viewers (Lichtblick, ROS 2 Humble) show the same records. Not proved yet: the drivers'
active checks (CAN queries, the RealSense camera_info read). Those are your run to make, with the rack
powered and realsense-viewer closed (decision 4).
claude/traj-tracking-sim-to-real-a3da60, 4 untracked plugin files, not mergedno git writes anywhere
--passive from the runner. The live-passive test then ran moteus_servo's ACTIVE diagnose once, for about 3 s,
against fdcanusb XXXXXXXX.
- It went through the plugin's own gate: no port holder, the rack bus lock taken (LOCK_NB) and released. The checks are
query-only: id scan, reply-loss queries,
conf get,can status. No board changes mode. - The timing suggests it really talked to the bus. Neither outcome can be proved.
- The fix:
tests/fixtures/plugins-live-passive/passive_guard.pynow sits in front of both live-passive declarations. It refuses anydiagnosewithout--passiveand anyact, and it works independently of the host. Re-running the same mutation behind it, both drivers were refused in 0.011 s and never started. - The rule from now on: mutation tests that touch how argv is built only run behind that guard.
Live on this PC (passive)
- OS skeleton: 25 devices; chip IO ✓ with no plugin
- PCIe link health on every function: all clean (no false positives)
- moteus_servo: fdcanusb XXXXXXXX → CAN bus → CAN 1 / 14 / 15 from the rack inventory
- intel_rgbd: both D405s with identity, USB link and their colour/depth stream rows
- chip IO 3 ⚠ with the drivers passive (decision 2)
Replayed from recordings
- F1 18:11 (plug): XXXXXXXX's bus error-passive, CAN 15 fault 33, Port 1-7 flapping, D405 moved 28 px
- F2 18:25 (bus busy): tview held XXXXXXXX, so every check and button waits
- F3 19:02 (after a restart); recordings older than 3× their period show as STALE
- A fake rack PC: PCIe frame grabber, capture card x1 of x4, two EtherCAT NICs
Blocked: waits on you + the rack
- The ACTIVE driver checks (hard rule: your run)
- realsense-viewer (pid 2300632) holds both D405s, so intel_rgbd would say BusyHeldBy
- On the rack: bounded replies on a real fdcanusb (lane D), and the SDK path: physical_port, asic_serial vs USB serial (lane E)
- Lane D stays "unresolved" until that run
The dashboard, driven
A still from the middle of the video. The video is not in the public copy: its frames show the machine's name and device serials.
docs/record_v1_hero.js from the real host serving recorded frame F2 (18:25, tview holds
XXXXXXXX), in headless Chrome. It shows: the top-bar chip and its causes; a cause opening the adapter's row (its own
self-report is unknown: port held by tview, with a hint that CAN 15's driver below is in error); the driver-registered
Run comms check button, blocked by the port holder with its run button disabled; the table; the activity log; and a
search. 8/8 assertions passed during the recording. .Five tree drawings, side by side, same frame
Same host, same records, and the same row selected in all five (at 18:11 the XXXXXXXX CAN bus, at 18:25 the CAN 15 driver). Only the tree renderer changes; the bottom-right switch in the app flips between them live and remembers the choice. All five follow your rules: chevrons only on hover, the status shape on the right, nothing green, greyed when not connected, and a row draws its own level. Pick one below; v1 is the default if you say nothing.
VS Code explorer: hover chevrons in a left gutter, type icon + name, muted ids, a status shape at the far right only on problem rows; rollups are a quiet dot.
Hover a row to swap its icon for the chevron; thin indent guides; a folded parent with problems shows a count pill ("2 ▲").
Only the paths that hold a problem open by themselves; each level's healthy rows fold into one muted "N OK" line; a black box shows its own level with a quiet "child in error" hint.
Type icon + name + id on the left; a right-edge rail that is invisible when OK and takes the problem shape and colour otherwise.
The tree is also the table: one nested name column, then quiet aligned PORT · ID · RATE · LAST SEEN · FIRMWARE columns, the status shape + message last, only when the row is not OK.
The 100 measured checks behind the five (all green)
| check (hover a cell for the measured detail) | v1 | v2 | v3 | v4 | v5 |
|---|---|---|---|---|---|
switch | ✓ | ✓ | ✓ | ✓ | ✓ |
no-green | ✓ | ✓ | ✓ | ✓ | ✓ |
shapes | ✓ | ✓ | ✓ | ✓ | ✓ |
right | ✓ | ✓ | ✓ | ✓ | ✓ |
chevrons | ✓ | ✓ | ✓ | ✓ | ✓ |
greyed | ✓ | ✓ | ✓ | ✓ | ✓ |
select | ✓ | ✓ | ✓ | ✓ | ✓ |
search | ✓ | ✓ | ✓ | ✓ | ✓ |
tabs | ✓ | ✓ | ✓ | ✓ | ✓ |
errors | ✓ | ✓ | ✓ | ✓ | ✓ |
The whole dashboard
Live on this PC, with the real drivers
This PC, right now, through the real host with the real driver plugins in --passive mode (sysfs, by-id
links, /proc holders, the rack inventory and firmware cache files; no adapter or camera opened):
- intel_rgbd diagnose_args --passive:
usb:6-1ok (ran in 0.16 s · 8 records);usb:6-2ok (ran in 0.16 s · 8 records) - moteus_servo diagnose_args --passive:
usb:1-3ok (ran in 0.28 s · 10 records)
tools/shoot_dashboard.js): the D405 carries intel_rgbd's identity, USB link and stream rows, and its two
registered actions say who blocks them (realsense-viewer, pid 2300632); the fdcanusb carries moteus_servo's CAN bus.? rack-pc 25 devices — RealSense D405 (USB XXXXXXXXXXXX): passive run: the SDK was not asked (sysfs only)
? USB bus 1 · 480 Mb/s 2 devices — CAN bus: passive run: the bus was not queried
Port 1-3 12 Mb/s — 1 device · 12 Mb/s [below: unknown]
fdcanusb XXXXXXXX XXXXXXXX — on port 1-3 · USB full-speed · 12 Mb/s [below: unknown]
/dev/ttyACM0 — present
? CAN bus — passive run: the bus was not queried
? CAN 1 — passive run: the board was not queried (knock-on)
? CAN 14 — passive run: the board was not queried (knock-on)
? CAN 15 — passive run: the board was not queried (knock-on)
Port 1-6 12 Mb/s — 1 device · 12 Mb/s
ITE Device — on port 1-6 · USB full-speed · 12 Mb/s · no driver diagnostics
? USB bus 6 · 10 Gb/s 2 devices — RealSense D405 (USB XXXXXXXXXXXX): passive run: the SDK was not asked (sysfs only)
Port 6-1 5 Gb/s — 1 device · 5 Gb/s [below: unknown]
? RealSense D405 (USB XXXXXXXXXXXX) XXXXXXXXXXXX — passive run: the SDK was not asked (sysfs only)
/dev/video0 · capture — present
/dev/video1 — present
/dev/video2 · capture — present
/dev/video3 — present
/dev/video4 · capture — present
/dev/video5 — present
colour stream — configured 640x480 rgb8 at 30 Hz (intel_rgbd RgbdConfig default); diagnostics never opens a stream
depth stream — configured 848x480 z16 at 30 Hz (intel_rgbd RgbdConfig default); diagnostics never opens a stream
Port 6-2 5 Gb/s — 1 device · 5 Gb/s [below: unknown]
? RealSense D405 (USB XXXXXXXXXXXX) XXXXXXXXXXXX — passive run: the SDK was not asked (sysfs only)
/dev/video6 · capture — present
/dev/video7 — present
/dev/video8 · capture — present
/dev/video9 — present
/dev/video10 · capture — present
/dev/video11 — present
colour stream — configured 640x480 rgb8 at 30 Hz (intel_rgbd RgbdConfig default); diagnostics never opens a stream
depth stream — configured 848x480 z16 at 30 Hz (intel_rgbd RgbdConfig default); diagnostics never opens a stream
… (USB bus 7, PCI, network, tty: see the next section)Discovery beyond USB
The OS skeleton is passive and always there: 25 devices on this PC at capture time, chip IO ✓, with no driver plugin running. Every source is a file read (sysfs, by-id links, the udev database, the kernel log); nothing is opened.
| source | rows | on this PC right now | what it reads |
|---|---|---|---|
USBio_facts.usb | 9 | fdcanusb, ITE Device, Intel(R) RealSense(TM) Depth Camera 405, USB2.0 HUB, USB2.1 Hub, Logi Webcam C920e … | sysfs /sys/bus/usb + usb.ids, by-id links, kernel log (plug / re-enumerate history) |
PCI / PCIeio_facts.pci | 16 | IOMMU, FCH SMBus Controller, AD103 [GeForce RTX 4080 SUPER], Audio device, NVMe SSD Controller PM9A1/PM9A3/980PRO, MT7922 802.11ax PCI Express Wireless Network Adapter … | sysfs /sys/bus/pci: class, driver, link width/speed vs max, AER error counters |
Networkio_facts.netdev | 2 | wlp5s0 (link down), enp6s0 (1 Gb/s · link up) | /sys/class/net: link, speed, driver; an EtherCAT master is detected, the segment is not scanned |
Serial ttysio_facts.tty | 1 | /dev/ttyACM0 | /sys/class/tty + /dev/serial/by-id, hung under the USB or platform device |
video4linuxio_facts.v4l | 14 | /dev/video0 · capture, /dev/video1, /dev/video2 · capture, /dev/video3, /dev/video4 · capture, /dev/video5 … | /sys/class/video4linux + /dev/v4l/by-id, hung under the camera |
PCIe link health, this PC
| function | link | AER corr / nonfatal / fatal | level |
|---|---|---|---|
| AD103 [GeForce RTX 4080 SUPER] | x16 of x16 · 16.0 GT/s PCIe (max 16.0 GT/s PCIe) | 0 / 0 / 0 | OK |
| Audio device | x16 of x16 · 16.0 GT/s PCIe (max 16.0 GT/s PCIe) | 0 / 0 / 0 | OK |
| NVMe SSD Controller PM9A1/PM9A3/980PRO | x4 of x4 · 16.0 GT/s PCIe (max 16.0 GT/s PCIe) | 0 / 0 / 0 | OK |
| MT7922 802.11ax PCI Express Wireless Network Adapter | x1 of x1 · 5.0 GT/s PCIe (max 5.0 GT/s PCIe) | 0 / 0 / 0 | OK |
| RTL8111/8168/8211/8411 PCI Express Gigabit Ethernet Controller | x1 of x1 · 2.5 GT/s PCIe (max 2.5 GT/s PCIe) | 0 / 0 / 0 | OK |
| USB controller | x1 of x1 · 2.5 GT/s PCIe (max 2.5 GT/s PCIe) | 0 / 0 / 0 | OK |
| 600 Series Chipset SATA Controller | x1 of x1 · 2.5 GT/s PCIe (max 2.5 GT/s PCIe) | 0 / 0 / 0 | OK |
| Raphael | x16 of x16 · 16.0 GT/s PCIe (max 16.0 GT/s PCIe) | — / — / — | OK |
| Rembrandt Radeon High Definition Audio Controller | x16 of x16 · 16.0 GT/s PCIe (max 16.0 GT/s PCIe) | — / — / — | OK |
| Family 19h PSP/CCP | x16 of x16 · 16.0 GT/s PCIe (max 16.0 GT/s PCIe) | — / — / — | OK |
| USB controller | x16 of x16 · 16.0 GT/s PCIe (max 16.0 GT/s PCIe) | — / — / — | OK |
| USB controller | x16 of x16 · 16.0 GT/s PCIe (max 16.0 GT/s PCIe) | — / — / — | OK |
| Family 17h/19h HD Audio Controller | x16 of x16 · 16.0 GT/s PCIe (max 16.0 GT/s PCIe) | — / — / — | OK |
| USB controller | x16 of x16 · 16.0 GT/s PCIe (max 16.0 GT/s PCIe) | — / — / — | OK |
Hardware this PC does not have: the fake rack machine
Lane B's sysfs spec (tests/data/sysfs-rack-fixture.json) served through the same io_facts sources and the real
dashboard: a PCIe frame grabber with no kernel driver, a capture card that trained x1 of x4, two I210 NICs carrying
EtherCAT segments (detected, not scanned), and a USB-serial adapter.
▲ PCI root bus 0000:00 8 devices — Likely frame grabber · Coreco Inc: likely frame grabber / capture card with no kernel driver bound: it cannot capture until its vendor driver loads
▲ Likely frame grabber · Coreco Inc — likely frame grabber / capture card with no kernel driver bound: it cannot capture until its vendor driver loads
I210 Gigabit Network Connection — Network controller · kernel driver igb · no driver diagnostics
enp6s0 · EtherCAT segment (not scanned) — 100 Mb/s · link up
I210 Gigabit Network Connection — Network controller · kernel driver igb · no driver diagnostics
enp7s0 · EtherCAT segment (not scanned) — 100 Mb/s · link up
▲ Pro Capture Endpoint — trained narrower than the card can: x1 of x4 · 2.5 GT/s PCIe (max 2.5 GT/s PCIe) (reseat or another slot)
/dev/video20 · capture — present
USB controller — Serial bus controller · kernel driver xhci_hcd · no driver diagnostics
USB controller — Serial bus controller · kernel driver xhci_hcd · no driver diagnostics
ACP/ACP3X/ACP6x Audio Coprocessor — Multimedia controller · kernel driver snd_pci_acp6x · no driver diagnostics
USB controller — Serial bus controller · kernel driver xhci_hcd · no driver diagnosticsThe black-box rule, on a real tree
Zach's rule (2026-10-22): a black box reports for itself. A row draws its own entity's self-report and the host
never overrides it. What is worse further down is an advisory hint. A row with nothing to report itself (the host, a USB
bus) shows the worst thing below it. And when a parent has failed, the failures under it collapse into that one cause.
Below is the real host's output for recorded frame F1, row by row along the path to CAN 15's fault, from
io-diagnostics once --json --fixture F1:
| row (host → … → CAN 15 driver) | its own self-report | worst below (rollup) | the row shows | why |
|---|---|---|---|---|
| rack-pc host · root | none (grouping row) | ERROR XXXXXXXX CAN bus | ERROR | a pure grouping row has no self-report, so it shows the worst below |
| USB bus 1 · 480 Mb/s bus · via | none (grouping row) | ERROR XXXXXXXX CAN bus | ERROR | a pure grouping row has no self-report, so it shows the worst below |
| Port 1-3 port · via | OK 1 device · 12 Mb/s | ERROR CAN 15 driver | OK ▾ error below | its own report wins; the worse level below is only a quiet hint |
| fdcanusb XXXXXXXX device · via | OK on port 1-3 · USB full-speed · 12 Mb/s | ERROR CAN 15 driver | OK ▾ error below | its own report wins; the worse level below is only a quiet hint |
| XXXXXXXX CAN bus bus · via | OK error-active · err 0 · warn 0 · 3 of 3 answer | ERROR CAN 15 driver | OK ▾ error below | its own report wins; the worse level below is only a quiet hint |
| CAN 15 node · via | OK answers · 36.5 V · stopped | ERROR CAN 15 driver | OK ▾ error below | its own report wins; the worse level below is only a quiet hint |
| CAN 15 driver part · part_of | ERROR fault 33 (motor driver) on bridge enable · last trip 13:34:07 | — | ERROR | it reports this itself |
Port 1-3 12 Mb/s — 1 device · 12 Mb/s [below: error] fdcanusb XXXXXXXX XXXXXXXX — on port 1-3 · USB full-speed · 12 Mb/s [below: error] /dev/ttyACM0 — present XXXXXXXX CAN bus — error-active · err 0 · warn 0 · 3 of 3 answer [below: error] CAN 1 XXXXXXXXXXXXXXXX — answers · 36.5 V · stopped CAN 1 driver — 36.5 V · fault 0 CAN 1 motor — 10:1 (GIM4305-10; camera-measured 0.104 output/rotor) · output sign 1 · tag 0 CAN 14 XXXXXXXXXXXXXXXX — answers · 36.5 V · stopped CAN 14 driver — 36.5 V · fault 0 CAN 14 motor — 10:1 (GIM4305-10) · output sign -1 · tag 1, 21 CAN 15 XXXXXXXXXXXXXXXX — answers · 36.5 V · stopped [below: error] ✖ CAN 15 driver — fault 33 (motor driver) on bridge enable · last trip 13:34:07 CAN 15 motor — 10:1 (GIM4305-10) · output sign 1 · tag 2
--passive mode| row | own report | counted as |
|---|---|---|
| CAN bus | UNKNOWN passive run: the bus was not queried | the one cause (counted) |
| CAN 1 | UNKNOWN passive run: the board was not queried | knock-on of CAN bus (not counted) |
| CAN 14 | UNKNOWN passive run: the board was not queried | knock-on of CAN bus (not counted) |
| CAN 15 | UNKNOWN passive run: the board was not queried | knock-on of CAN bus (not counted) |
The plugin contract: what a manufacturer writes
A manufacturer writes one executable and one TOML file. The executable answers three verbs with JSON lines on
stdout; the host never imports it, so its dependencies, language and crashes stay its own (it runs in the driver's own
uv environment as a subprocess). The smallest real example, examples/acme_gripper/, run through the real host
by tests/test_integration.py (which also fails if the README drifts from these files):
acme_gripper.py — stdlib only"""The smallest IO diagnostics plugin: a made-up USB gripper (1234:5678) with one part, as JSON lines.
<python> acme_gripper.py describe
<python> acme_gripper.py diagnose --device usb:1-4
A real plugin reads its device here (inside its own driver's environment); this one only shows the shapes.
"""
import json
import os
import sys
import time
SCHEMA = "diagnostics-record/0"
def emit(**record):
print(json.dumps({"schema": SCHEMA, **record}))
if sys.argv[1] == "describe": # what it claims, and the buttons it registers (none here)
print(json.dumps({"name": "acme_gripper", "version": "1.0", "schema": SCHEMA,
"matches": ["usb:1234:5678"], "actions": []}))
elif sys.argv[1] == "diagnose": # one run for one device; paths are relative to the device's own row ("")
device = json.loads(os.environ.get("IO_DIAGNOSTICS_DEVICE", "{}")) # the host's skeleton dict for it
now = time.time()
emit(record="entity", path="", name=f"Acme gripper {device.get('serial') or ''}".strip(), kind="device",
identity={"manufacturer": "Acme", "model": "Gripper G1", "sw_version": "2.3.0"})
emit(record="entity", path="jaw", name="jaw", kind="part", part_of="")
emit(record="status", path="", name="gripper: comms", level="ok", message="answers in 2 ms",
stamp=now, expected_period_s=30)
emit(record="status", path="jaw", name="jaw: temperature", level="warn", code="JawHot",
message="jaw motor 71 °C (warn above 70 °C)", stamp=now, expected_period_s=30,
measurements=[{"key": "temperature", "value": 71.0, "unit": "Cel", "warn_high": 70.0}])
acme_gripper.toml — dropped into plugins.d/name = "acme_gripper"
command = ["{python}", "{here}/acme_gripper.py"]
timeout_s = 10
period_s = 30
matches = ["usb:1234:5678"]
diagnose --device usb:1-4 printed (schema key dropped){"record": "entity", "path": "", "name": "Acme gripper G1-0042", "kind": "device", "identity": {"manufacturer": "Acme", "model": "Gripper G1", "sw_version": "2.3.0"}}
{"record": "entity", "path": "jaw", "name": "jaw", "kind": "part", "part_of": ""}
{"record": "status", "path": "", "name": "gripper: comms", "level": "ok", "message": "answers in 2 ms", "stamp": 1792685757.684107, "expected_period_s": 30}
{"record": "status", "path": "jaw", "name": "jaw: temperature", "level": "warn", "code": "JawHot", "message": "jaw motor 71 °C (warn above 70 °C)", "stamp": 1792685757.684107, "expected_period_s": 30, "measurements": [{"key": "temperature", "value": 71.0, "unit": "Cel", "warn_high": 70.0}]}| verb | prints | who calls it, v1 |
|---|---|---|
describe | one object: name, version, schema diagnostics-record/0, matches, registered actions | the host, once (must be passive) |
diagnose --device <id> | JSON lines tagged entity / status / event (never fault: the host's FaultManager keeps faults) | the host, every period_s per matched device, killed past timeout_s |
act --device <id> --action <n> --confirm <token> | a result record | nobody in v1: buttons are shown with their gates, never executed |
The two real driver plugins, as they describe themselves
Captured for this report through passive_guard.py (which lets only describe and
diagnose --passive through). These are the bam_ws files on branch claude/traj-tracking-sim-to-real-a3da60.
moteus_servo — fdcanusb adapters + moteus boards
moteus_servo 0.1.0 · schema diagnostics-record/0 · matches usb:0483:5740, by-id:*fdcanusb*
| action | danger | scope | preconditions | what it does |
|---|---|---|---|---|
Run comms checkrun_comms_check | query | device | port_free; bus_lock_free | Addressed scan of every id, reply loss over a few queries, read-only config and identity, and the adapter's can status. Query frames and console reads only: no board changes mode. |
Clear faultsclear_faults | changes | node | port_free; bus_lock_free; board_answers | Send stop (mode 0) to one board; moteus clears a latched fault on stop. The motor stays unpowered. |
Reboot boardreboot_board | interrupts | node | port_free; bus_lock_free; board_answers | Send `d reset`: the board restarts and is back on the bus in about a second. Any session using it fails. |
raw describe output
{
"name": "moteus_servo",
"version": "0.1.0",
"schema": "diagnostics-record/0",
"description": "fdcanusb CAN-FD adapters and the moteus boards on their bus: ids, identity, firmware vs the latest release, read-only config, can status, reply loss",
"matches": [
"usb:0483:5740",
"by-id:*fdcanusb*"
],
"commands": [
"describe",
"diagnose",
"act"
],
"path_root": "--path, else $IO_DIAGNOSTICS_DEVICE_PATH, else fdcanusb-<serial>; the bus is <root>/can (part_of the adapter), each board <root>/can/<id> (via the bus)",
"global_options": [
"--inventory DIR",
"--firmware-cache DIR",
"--bus-lock FILE",
"--expect-ids LIST",
"--expected-period S",
"--timeout S"
],
"actions": [
{
"schema": "diagnostics-record/0",
"record": "action",
"name": "run_comms_check",
"title": "Run comms check",
"description": "Addressed scan of every id, reply loss over a few queries, read-only config and identity, and the adapter's can status. Query frames and console reads only: no board changes mode.",
"danger": "query",
"scope": "device",
"confirm": "Query every id on {name} and read bus health? No board changes mode.",
"preconditions": [
[
"port_free",
"No other process has the adapter open (tview, moteus_tool, a peer)"
],
[
"bus_lock_free",
"The rack bus lock is free (no experiment running)"
]
],
"kind": "touches"
},
{
"schema": "diagnostics-record/0",
"record": "action",
"name": "clear_faults",
"title": "Clear faults",
"description": "Send stop (mode 0) to one board; moteus clears a latched fault on stop. The motor stays unpowered.",
"danger": "changes",
"scope": "node",
"confirm": "Send stop to CAN {id} on {name}? A latched fault clears; the motor stays unpowered.",
"preconditions": [
[
"port_free",
"No other process has the adapter open (tview, moteus_tool, a peer)"
],
[
"bus_lock_free",
"The rack bus lock is free (no experiment running)"
],
[
"board_answers",
"CAN {id} answers a query on this bus"
]
]
},
{
"schema": "diagnostics-record/0",
"record": "action",
"name": "reboot_board",
"title": "Reboot board",
"description": "Send `d reset`: the board restarts and is back on the bus in about a second. Any session using it fails.",
"danger": "interrupts",
"scope": "node",
"confirm": "Reboot CAN {id} on {name}? It drops off the bus for about a second; any session using it fails.",
"preconditions": [
[
"port_free",
"No other process has the adapter open (tview, moteus_tool, a peer)"
],
[
"bus_lock_free",
"The rack bus lock is free (no experiment running)"
],
[
"board_answers",
"CAN {id} answers a query on this bus"
]
]
}
]
}intel_rgbd — Intel RealSense cameras
intel_rgbd 0.1.0 · schema diagnostics-record/0 · matches usb:8086:0b* (dict form)
| action | danger | scope | preconditions | what it does |
|---|---|---|---|---|
Check firmwarecheck_firmware | query | device | camera_free; bus_lock_free | Read the installed and the SDK's recommended firmware through camera_info (no stream, no option read), while no other process holds a RealSense and the bus lock is free. |
Hardware reset…hardware_reset | interrupts | device | camera_free; bus_lock_free | RealSenseCamera.hardware_reset(): the camera drops off USB for about 3 s, re-enumerates, and every option returns to its firmware default (measured on the D435: exposure 250 -> 166, auto-exposure back on). Anything streaming from it must start again. |
raw describe output
{
"name": "intel_rgbd",
"title": "RealSense camera",
"version": "0.1.0",
"schema": "diagnostics-record/0",
"matches": [
{
"bus": "usb",
"vid": "8086",
"pid": "0b*"
}
],
"actions": [
{
"schema": "diagnostics-record/0",
"record": "action",
"name": "check_firmware",
"title": "Check firmware",
"description": "Read the installed and the SDK's recommended firmware through camera_info (no stream, no option read), while no other process holds a RealSense and the bus lock is free.",
"danger": "query",
"scope": "device",
"confirm": "",
"preconditions": [
[
"camera_free",
"No other process holds a RealSense and realsense-viewer is closed"
],
[
"bus_lock_free",
"No rack experiment holds the bus lock"
]
],
"kind": "touches"
},
{
"schema": "diagnostics-record/0",
"record": "action",
"name": "hardware_reset",
"title": "Hardware reset…",
"description": "RealSenseCamera.hardware_reset(): the camera drops off USB for about 3 s, re-enumerates, and every option returns to its firmware default (measured on the D435: exposure 250 -> 166, auto-exposure back on). Anything streaming from it must start again.",
"danger": "interrupts",
"scope": "device",
"confirm": "Hardware-reset {name} ({serial})? It drops off USB for about 3 s and every option returns to its firmware default.",
"preconditions": [
[
"camera_free",
"No other process holds a RealSense and realsense-viewer is closed"
],
[
"bus_lock_free",
"No rack experiment holds the bus lock"
]
]
}
],
"icon": "camera",
"sdk": "pyrealsense2 2.58.3.10794",
"period_s": 60.0,
"device_ids": "RealSense serial, USB serial, USB port (6-2), usb:8086:<pid>:<USB serial>, /dev/videoN, a /dev/v4l/by-id link, a sysfs path or a host tree path (host/usb-6-2/...)",
"confirm_token": "hardware_reset takes --confirm <the camera's RealSense serial>: identity.serial_number, the {serial} in its confirm text"
}Interop: our records in stock viewers
Decision 1 of the build plan: own the dashboard, and stay backwards compatible through one pure adapter
(contract.record.to_diagnostic_status) that strips our added fields. A stock viewer then shows our records
with zero code of ours inside it.
A still from the middle of the video. The video is not in the public copy: its frames show the machine's name and device serials.
/diagnostics topic, served by our foxglove-sdk bridge. 10 stock rows, levels coloured, stale rows greyed (CAN 14).ROS 2 Humble (docker, no network): 5/5 checks
| ✓ decode | rclpy read 2337 bytes -> 10 statuses, equal to the bridge's JSON |
| ✓ encode | 2337 bytes, identical except 75 of 159 alignment padding bytes (rclpy/Fast-CDR leaves padding unwritten; ours is zero) |
| ✓ schema | 5 sections vs 5 installed .msg files; every field and constant matches |
| ✓ transport | received 10 statuses over DDS from the publisher process |
| ✓ contract | from_diagnostic_status read 10 rclpy statuses (level as bytes); to_diagnostic_status gave the same stock statuses back |
ros2 topic echo /diagnostics (first 40 lines)
header:
stamp:
sec: 1792677600
nanosec: 0
frame_id: ''
status:
- level: "\0"
name: 'fdcanusb XXXXXXXX: USB'
message: on port 1-3 as /dev/ttyACM0
hardware_id: usb:0483:5740:XXXXXXXX
values:
- key: port
value: 1-3
- key: tty
value: /dev/ttyACM0
- key: by-id
value: /dev/serial/by-id/usb-mjbots_fdcanusb_272813EF-if00
- key: link_speed
value: 12 Mbit/s
- level: "\0"
name: 'CAN bus: state'
message: error-active, no bus errors
hardware_id: usb:0483:5740:XXXXXXXX
values:
- key: state
value: error-active
- key: tx_error_count
value: '0'
- key: rx_error_count
value: '0'
- level: "\0"
name: 'CAN 1: comms'
message: answers · 36.5 V · stopped
hardware_id: moteus:32bb145a
values:
- key: serial
value: XXXXXXXXXXXXXXXX
- key: firmware
value: 1.1.2 (657b5c5)
- key: stationTests and evidence
| suite | result | notes |
|---|---|---|
| io-diagnostics pytest | 141 passed | re-run 2026-10-22 09:10 while building this report (10.5 s); was 119 + 1 xfail before integration. 22 new in tests/test_integration.py, two of them live on rack-pc |
| moteus_servo plugin tests | 35 passed | re-run 2026-10-22 09:12 for this report; fakes only |
| intel_rgbd package suite | 125 passed · 18 skipped | re-run 2026-10-22 09:12 for this report, through the device-less pyrealsense2 shadow (tools/safe_pytest_intel_rgbd.py) |
| tree proposals (node) | 100 / 100 | tests/test_tree_variants.js: 10 checks × 5 variants × 2 frames, measured in headless Chrome |
| dashboard proof shots (node) | 24 / 24 | tools/shoot_dashboard.js: F1, F2, live with real drivers passive, fake machine |
| hero recording | 8 / 8 | docs/record_v1_hero.js: the flow in the video, asserted while it was recorded |
| ROS 2 Humble | 5 / 5 | decode, encode, schema, transport, contract + ros2 topic echo |
| Lichtblick | 10 stock rows | colours checked per row; its own containers and ports cleaned up |
| mutation checks | red when broken | dropping diagnose_args, removing the intel alias, removing pci_statuses, changing the README example each turned a guarded test red |
Decisions: what needs you
Each one has a recommendation and the reversible default that happens if you say nothing. Your choices are kept in this browser only. Copy puts them on the clipboard as text you can paste into the chat.
Pick in the strip above. Recommendation: v1 Quiet IDE tree. Its main job is plug-and-see (which port, what it is), so every healthy row stays visible but quiet, and the chip already answers "what needs me". v3's healthy-row folding could become a toggle later.
default if silent: v1With the drivers in --passive mode, every driver row says unknown/PassiveOnly and the chip reads 'IO 3 ⚠' with nothing wrong. The contract ranks unknown above warn, so it is honest but noisy.
Today 'USB bus 1…8' are siblings of 'PCI root bus 0000:00'; the true wire is host → PCIe → xHCI function → USB bus. Lane B already records the controller in attrs.
The one run that talks to the hardware is yours (hard rule). realsense-viewer (pid 2300632) holds both D405s, so intel_rgbd would report BusyHeldBy. Still to prove on the rack: bounded replies on a real fdcanusb (lane D) and the SDK path (physical_port, asic_serial vs USB serial, lane E).
A mutation test ran moteus_servo's ACTIVE diagnose once (~3 s, query-only, behind its gate). passive_guard.py now refuses any diagnose without --passive and any act, independent of the host; re-verified with the same mutation (refused in 0.011 s).
The orchestrator commits after review. io-diagnostics goes on its
main: the code, tests/fixtures/plugins/v6_frames.json (271 KB), and the new examples/ and tools/
files. reports/media/ is gitignored and stays local. bam_ws gets two commits on claude/traj-tracking-sim-to-real-a3da60, one per
driver, so they can be cherry-picked. Merging that branch is your call.
Left, and deliberately not done
Done and proved: everything in the green column above, plus the integration fixes. intel_rgbd now accepts the
host's own skeleton id usb:6-1; before the fix, every host run ended in DeviceNotFound. once now
judges live records at the end of the runs, not at the start. And lane B's io_facts/usb.py fixes are in: a
1.5 Mb/s device no longer breaks the USB source, and hubs no longer list the camera's video nodes. The gaps closed
during the completeness check: PCIe link health (tree.pci_statuses), --machine SPEC.json for
hardware this PC lacks, --enable NAME and diagnose_args, one command through a hatchling build
backend, an end-to-end host → bridge → CDR test, and a README whose manufacturer example is tested against the files.
Not done, all non-blocking:
- Cosmetic:
--machinemode's header says 'Live · sysfs-rack-fixture (fake machine)' while the clock mode is still 'live'; the table's Status column truncates 'UNKNOWN'; moteus passive names its bus just 'CAN bus' (no adapter serial). - Lane B: move the sysfs helpers from
io_facts/skeleton.pytoio_facts/sysfs.py. - Lane C:
stock.pyimports the contract's private_stale_message(needs an additive contract change to make it public; the contract is frozen at c9784d0); foxglove-sdk accepts only thefoxglove.sdk.v1subprotocol;diagnostic_aggregatoris not proven (not in the image). - Lane D: status unresolved until the live run: bounded replies on a real fdcanusb (problem 3). Sibling moteus tools still carry moteus 1.0.0 traps (
can_bus_triage.scan_busbatches 8 ids;triage()andlist_fdcanusb.scan()open several Fdcanusb in one event loop); lane D's diff is in its handoff. - Lane F: the per-minute timeline strips were dropped (no API endpoint for them).
- bam_ws: two
bam_rack_twinlint tests fail on already-committed code (configcheck.py:148/151,inventory.py:36); the intel_rgbd README/DATASHEET do not mention the plugin yet. - After the bam_ws merge: switch the worktree paths to
~/bam_ws/src/core/mdp/agent/…inplugins.d/*.toml,tests/fixtures/plugins-live-passive/*.toml, the README commands andtools/safe_pytest_intel_rgbd.py's docstring.
Deliberately not done: no git writes (the orchestrator commits after review). No active hardware run (hard rule: yours). Actions are never executed in v1, by design. The tray was never launched on your desktop. EtherCAT segments are detected but not scanned (as the build plan says). The record contract was not edited, because it is frozen at c9784d0.
Run it
Fixture demo: the 18:25 bus-busy frame on http://127.0.0.1:8812/ (the CAN boards turn stale about 11 s in)
uv run --project ~/io-diagnostics io-diagnostics serve --fixture F2 --offset 330
Live, passive: this PC's real skeleton, no driver plugin runs
uv run --project ~/io-diagnostics io-diagnostics serve
Live with the real drivers in --passive mode (behind passive_guard.py)
uv run --project ~/io-diagnostics io-diagnostics serve --plugins ~/io-diagnostics/tests/fixtures/plugins-live-passive
Hardware this PC lacks (frame grabber, x1-of-x4 capture card, EtherCAT NICs)
uv run --project ~/io-diagnostics io-diagnostics serve --machine ~/io-diagnostics/tests/data/sysfs-rack-fixture.json
The top-bar chip (tray) + dashboard
uv run --project ~/io-diagnostics io-diagnostics tray
Your ACTIVE run: close realsense-viewer and tview first, with the rack powered
uv run --project ~/io-diagnostics io-diagnostics once --json --enable moteus_servo --enable intel_rgbd
Rebuild this report (add --recapture for fresh passive captures, --record-hero for a new video)
uv run --project ~/io-diagnostics --with pillow python ~/io-diagnostics/docs/build_v1_report.py
Built by docs/build_v1_report.py on 2026-10-22T09:21:22-07:00.
Host captures were taken 2026-10-22T09:15:56-07:00 on rack-pc through io-diagnostics once --json, and
F1 was judged at its own frame clock (2026-10-21T18:11:50-07:00). The images are WebP crops, cut from the proof
shots named in each caption.