docs(#38): reconnect wiring design — slot manager + resume-first-with-grace#139
Merged
Conversation
…-grace Design for lighting up the DARK controller_proxy (#38 seamless reconnect), grounded in this session's on-Deck #112 evdev captures and a full code trace. Core structure (Scott's call): a slot_manager owns the slot record schema and every transition, so CONTROLLER_ADD/REMOVE share one get/set/release surface and the intent policy lives in exactly one function (slot_claim). Handlers become thin — they translate an event to a manager verb and execute the returned outcome (SPAWN/RESUME/ADOPT/REJECT). The manager is pure state+policy, so the whole reconnect decision matrix is a hardware-free unit test. Intent policy (Scott): resume-first-with-grace. Known MAC always resumes its slot; an unknown MAC adopts an orphaned slot when the session is full or within a grace window, else starts a new instance, else is rejected. Key hardware-derived decisions (see #112): - Identity anchor = the physical pad's MAC (uniq); eventN and the UHID instance are per-connection and must never be cached. - Role = "has a js handler" — but select physical pads by excluding the 28de Steam virtuals (which also carry js, with an empty uniq); the js-owner event node is the #112 crash fix (evsieve reopens a pad, not the touchpad). - RESUME (same pad) re-points the proxy; ADOPT (different pad, device-id changes) restarts it. The one open verification — whether evsieve persist=reopen honors a moved symlink — only decides seamless-vs-blip; the restart fallback is correct either way. Deferred to a Deck session (#112 step-2). Incremental rollout keeps raw-binding the working default behind MCSS_PROXY_ENABLE until that on-Deck verification passes. Includes a unit-test decision matrix and open questions for review (grace default, ADOPT device-id flip vs #36, proxy-off reconnect behavior, Steam-grab interaction, #125 full-session message). Refs #38, #112. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UZuoMpX28CbtGJbigVTv5H
…y-off no-op; full=log-only) Scott's review calls: Q1 180s, Q2 A (device-id follows the physical pad), Q3 A (proxy-off reconnect is a no-op), Q4 verify-only on-Deck, Q5 log-only (4-player limit documented in README, not a message surface). Refs #38. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UZuoMpX28CbtGJbigVTv5H
The earlier '3 DS4s share a MAC' was a misread of our own capture: all four uniqs were distinct; what repeats across same-model pads is the UHID vendor:product (054C:09CC), never the MAC. MAC is the device-agnostic identity key, present and unique on DS4 AND Xbox pads — corroborated by Steam telling lightbar-less Xbox pads apart (Steam keys on the MAC, not the DS4 lightbar; we use the same key, so no Steam dependency for identity). Downgrades the 'identical-MAC floor' to a theoretical counterfeit-only safety net, not a real constraint for this all-Bluetooth hardware. Refs #38. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UZuoMpX28CbtGJbigVTv5H
aradanmn
marked this pull request as ready for review
July 25, 2026 02:16
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Draft for design review — no code, just the design doc (
docs/DESIGN-38-RECONNECT-WIRING.md). Grounded in this session's on-Deck #112 evdev captures + a full code trace of the reconnect path.The problem
controller_proxy.sh— the whole of #38's seamless-reconnect mechanism — is DARK (zero callers). The live path raw-binds a pad'sjsNinto bwrap at launch; a reconnected pad is a new node the sandbox can't see (#61/#62). Sticky slots keep the instance alive but input-dead — exactly HW-2's "P2 dropped, session persisted, un-controllable."Core structure (the manager)
Event handlers hold no policy. A
slot_managerowns the slot record schema and every transition, soCONTROLLER_ADD/REMOVEshare one get/set/release surface and the intent policy lives in exactly one verb,slot_claim, which returnsSPAWN/RESUME/ADOPT/REJECT. Handlers just execute the outcome. The manager is pure state+policy → the whole reconnect decision matrix is a hardware-free unit test.Intent policy — resume-first-with-grace
Known MAC → always resume its slot. Unknown MAC → adopt an orphaned slot when the session is full (4-up ceiling, no 5th instance) or within a grace window, else spawn a new instance, else reject.
Hardware-derived decisions (#112)
uniq).eventN(lowest-free, not monotonic) and the UHID instance are per-connection — never cache them.jshandler", but pick physical pads by excluding the28deSteam virtuals (they carryjstoo, with an emptyuniq). The js-owner event node is the #38 D2 blocker: evsieve dies ~2min after BT battery-death reopen — 'capabilities of the reconnected device are different than expected' #112 crash fix — evsieve reopens a pad, not the touchpad.persist=reopenhonor a moved symlink — only decides seamless-vs-sub-second-blip; the restart fallback is correct either way. Deferred to a Deck session (#38 D2 blocker: evsieve dies ~2min after BT battery-death reopen — 'capabilities of the reconnected device are different than expected' #112 step-2).Rollout
Incremental, behind
MCSS_PROXY_ENABLE, raw-binding stays the working default until the on-Deck verification passes. PR-a (manager + state, behavior-neutral) → PR-b (launch wiring) → PR-c (policy) → PR-d (watchdog + verify → flip default).Please weigh in on §14 open questions
grabmay starve Steam's own28devirtual (guide button/overlay?) — verify before default-ON.🤖 Generated with Claude Code