fix: hotplug-driven inventory and phantom device-card suppression#346
fix: hotplug-driven inventory and phantom device-card suppression#346davidbudnick wants to merge 5 commits into
Conversation
Greptile SummaryThis PR wires up OS hotplug events from
Confidence Score: 5/5Safe to merge once hardware validation is confirmed — all logic paths are unit-tested and the fallback to pure polling on any hotplug failure keeps the existing behaviour intact. The hotplug integration, confirming re-apply, and phantom-card suppression are all independently well-tested. The No files require special attention — the draft status is about pending hardware validation, not code correctness. Important Files Changed
Sequence Diagram%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant OS as OS HID Layer
participant HP as hotplug stream
participant IW as InventoryWatcher (thread)
participant ORC as Orchestrator
participant GUI as GUI DeviceList
OS->>HP: DeviceEvent::Connected(_)
HP->>IW: HotplugEvent::Connected
IW->>IW: "sleep(HOTPLUG_SETTLE=400ms)"
IW->>IW: drain burst (poll_once loop)
IW->>IW: enumerator.enumerate()
IW->>ORC: InventoryEvent::Snapshot(devices)
ORC->>ORC: plan_reapply() → targets + next_followup
ORC->>ORC: reapply_volatile_settings(first-sighting device)
Note over ORC: next_followup queued for confirming re-apply
Note over IW: ~2 s periodic tick (or next hotplug)
IW->>ORC: InventoryEvent::Snapshot(devices)
ORC->>ORC: followup fires → second reapply_volatile_settings
ORC->>GUI: refresh_inventory → build_device_list
GUI->>GUI: collect present_receivers UIDs
GUI->>GUI: append_offline_known (3 gates)
Note over GUI: Gate 1: key/model already live
Note over GUI: Gate 2: receiver UID absent
Note over GUI: Gate 3: wire PID already in blocked_pids
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant OS as OS HID Layer
participant HP as hotplug stream
participant IW as InventoryWatcher (thread)
participant ORC as Orchestrator
participant GUI as GUI DeviceList
OS->>HP: DeviceEvent::Connected(_)
HP->>IW: HotplugEvent::Connected
IW->>IW: "sleep(HOTPLUG_SETTLE=400ms)"
IW->>IW: drain burst (poll_once loop)
IW->>IW: enumerator.enumerate()
IW->>ORC: InventoryEvent::Snapshot(devices)
ORC->>ORC: plan_reapply() → targets + next_followup
ORC->>ORC: reapply_volatile_settings(first-sighting device)
Note over ORC: next_followup queued for confirming re-apply
Note over IW: ~2 s periodic tick (or next hotplug)
IW->>ORC: InventoryEvent::Snapshot(devices)
ORC->>ORC: followup fires → second reapply_volatile_settings
ORC->>GUI: refresh_inventory → build_device_list
GUI->>GUI: collect present_receivers UIDs
GUI->>GUI: append_offline_known (3 gates)
Note over GUI: Gate 1: key/model already live
Note over GUI: Gate 2: receiver UID absent
Note over GUI: Gate 3: wire PID already in blocked_pids
Reviews (2): Last reviewed commit: "fix(agent): run hotplug watch under runt..." | Re-trigger Greptile |
Context
Stale device cards pile up when the same models are used at two locations (work + home G513 / MX Master 3S), and a just-plugged keyboard waits seconds for its configured lighting. Root causes: persisted
known_identitiesresurrect unreachable devices as placeholders with model-key dedup defeated by the flaky extended-model byte (0b034vs2b034, #271/#280), and the inventory watcher is a pure 2 s poll — the "async-hid has no listener API" rationale predates async-hid 0.5'swatch().Demo
cargo test --workspacepasses; clippy/fmt clean.Changes
hotplug.rs,transport.rs(openlogi-hid)watchers/inventory.rsorchestrator.rsstate/devices.rsNotes