Edge case found on-Deck (2026-07-26) right after #38 shipped. Single-pad reconnect is solid; this is the multi-pad-simultaneous-swap corner.
Repro
4-up, proxy on. Disconnect P4 then P2 (both down). Reconnect P4 then P2. Observed: P4's pad drives P2's instance, and P4's instance is uncontrollable.
The identity logic is CORRECT
The orchestrator matched by MAC perfectly despite the swap:
REMOVE event30 → slot 4 REMOVE event22 → slot 2
ADD event22 MAC=90:89 → RESUME slot 4 (P4 came back on P2's old node)
ADD event30 MAC=dc:0c → RESUME slot 2 (P2 came back on P4's old node)
Final state + symlinks are right: slot4→event22, slot2→event30.
Root cause — a race between the repoint and evsieve's own persist=reopen
When P4's pad reconnected on event22 (P2's old node), evsieve-slot2 was still waiting to reopen event22 (its symlink not yet repointed), so it grabbed P4's pad first (exclusive grab). By the time the orchestrator repointed slot4→event22, evsieve-slot4 can't grab it:
slot4 evsieve: failed to grab input device: libevdev status code -16 (EBUSY) — grabbed by another program
slot2 evsieve: "capabilities of the reconnected device are different than expected"
evsieve selects its input by symlink path, and it can't filter by uniq (DS4s share vendor:product), so on node-number reuse the wrong slot's evsieve wins the grab.
Fix directions
- Quiesce-then-repoint (preferred): when handling a reconnect batch, first point every affected slot's pads-symlink at a dead/nonexistent node (releasing stale grabs), then repoint each to the correct node by MAC. Removes the window where a stale symlink grabs the wrong pad.
- Detect the swap (target node already grabbed by another slot's evsieve) → stop that evsieve, repoint, restart.
- Debounce: process a burst of REMOVE/ADD together rather than one-at-a-time so repoints land before evsieve's next persist poll.
Severity
Low — needs ≥2 pads dropped simultaneously AND reconnecting into each other's freed numbers. Single-pad reconnect (the dominant real case, e.g. one battery dies) works. Recovery today: restart the affected instance(s).
Refs #38.
Edge case found on-Deck (2026-07-26) right after #38 shipped. Single-pad reconnect is solid; this is the multi-pad-simultaneous-swap corner.
Repro
4-up, proxy on. Disconnect P4 then P2 (both down). Reconnect P4 then P2. Observed: P4's pad drives P2's instance, and P4's instance is uncontrollable.
The identity logic is CORRECT
The orchestrator matched by MAC perfectly despite the swap:
Final state + symlinks are right: slot4→event22, slot2→event30.
Root cause — a race between the repoint and evsieve's own persist=reopen
When P4's pad reconnected on event22 (P2's old node),
evsieve-slot2was still waiting to reopenevent22(its symlink not yet repointed), so it grabbed P4's pad first (exclusivegrab). By the time the orchestrator repointed slot4→event22, evsieve-slot4 can't grab it:evsieve selects its input by symlink path, and it can't filter by
uniq(DS4s share vendor:product), so on node-number reuse the wrong slot's evsieve wins the grab.Fix directions
Severity
Low — needs ≥2 pads dropped simultaneously AND reconnecting into each other's freed numbers. Single-pad reconnect (the dominant real case, e.g. one battery dies) works. Recovery today: restart the affected instance(s).
Refs #38.