feat: adopt peripherals when the other Mac sleeps or vanishes#54
Merged
Conversation
A Mac only ever chased peripherals it was holding before its own sleep, so the 'peripherals follow the awake Mac' story had a missing half: when the holder slept (lid close, shutdown), nothing made the other Mac pick them up — it only ever appeared to work via the peripherals' own bond-level reconnection, which a prior handoff destroys. Arm the existing auto-reconnect watcher in a new *adoption* flavour for every registered peripheral not connected locally, from two triggers: - on wake, alongside the existing reclaim of the pre-sleep set (covers 'both lids closed, open the other one'), and - when the reachability poll sees a pinned peer miss two consecutive pings (~a minute) — covers 'the holder slept while this Mac was awake'. Adoption is deliberately more polite than reclaim, since this Mac has no prior claim: it takes only from a provably absent peer (two consecutive HOLDS_ONE probes failing at the connect layer), stands down the moment a live peer answers at all — an explicit 'not holding' included, so the prior holder's reclaim or the user outranks it and a simultaneous dual wake can't fight — and caps its pair attempts at 3, because a free Magic peripheral pairs on the first try while one held by an unreachable-but- awake peer just hangs the pairing. An explicit claim (drop, failed handoff, wake reclaim) upgrades an adoption entry to a full reclaim; an adoption sweep never downgrades an existing reclaim. With no trusted peer to consult (none registered, or TOFU mismatch), adoption stands down instead of reclaiming locally.
The release-on-sleep gate keyed solely off Bonjour's isActive, which is event-driven and persisted, so it goes stale in both directions: a Bonjour Sleep Proxy keeps a sleeping peer's records alive (stale true), and a withdrawn record / missed goodbye leaves an awake peer inactive (stale false) — making the release silently environment- and lid-order-dependent. Accept either presence signal: isActive, or the 30s .ping reachability poll's verdict. Also require the peer's identity pin to be clean — a TOFU-mismatched peer can't be commanded, so it's no one to hand off to.
|
🎉 This PR is included in version 2.17.0 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
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.
Why
"Peripherals follow the awake Mac" only had one half implemented. The holder releases on sleep (gated on the peer looking present), but nothing ever made the other Mac pick the peripherals up: on wake a Mac only chases what it held before its own sleep (
connectedBeforeSleep), and the watcher only arms for drops of peripherals it was holding. The "it used to work" experience was the peripherals' own bond-level reconnection -- which stops working once a handoff hasremove()d the receiving Mac's bond.So: close the lid of the Mac holding the set, open the other one, and nothing happens. Order-dependence in the release gate (stale, persisted Bonjour
isActive) made the release half flaky too.What
1. Adoption -- a second, politer flavour of the auto-reconnect watcher (
armAdoptionOfUnheldPeripherals/continueAdoption, bookkept inadoptionProgress). Registered peripherals this Mac isn't holding get armed from two triggers:Adoption reuses the existing probe chain (RSSI-visible -> read-only
HOLDS_ONE-> pair locally) but, having no prior claim, is deliberately more cautious than a reclaim:HOLDS_ONEprobes failing at the connect layer (.connectionFailed/.connectTimeout); the second probe gives post-wake Wi-Fi a chance to come up before anything acts;.bodyFailed) included -- so the prior holder's reclaim (or the user) outranks it and a simultaneous dual wake can't fight;Reclaim behaviour is unchanged, and everything sits behind the existing default-on "Reconnect peripherals if they drop" toggle (help text updated).
2. Release-on-sleep gate freshening. The gate keyed solely off Bonjour's
isActive, which is event-driven and persisted, so it goes stale in both directions (sleep proxies keep a sleeping peer's records alive; a missed mDNS goodbye leaves a gone peer inactive) -- making the release silently environment- and lid-order-dependent. It now accepts either presence signal (isActiveor the 30s.pingpoll's verdict) and requires a clean fingerprint pin (a TOFU-mismatched peer can't be commanded, so it's no one to hand off to).Result
Close the lid on the Mac holding the peripherals, open the other one: ~5s wake settle + two 5s probes, peripherals are pairing locally within ~15-20s -- whether or not the sleeping Mac managed to release them first. When the first Mac wakes later, its reclaim's
HOLDS_ONEsees the peer using them and leaves them alone.Pre-existing edge deliberately untouched: a waking Mac's reclaim still treats "peer unreachable" as free-to-take, so a Mac waking while the holder is off-network can pull peripherals back. That ambiguity exists today; adoption's politeness rule intentionally doesn't apply to reclaims so current behaviour is preserved.
Built with
CODE_SIGNING_ALLOWED=NO,swift formatclean. No new opcodes -- adoption only uses the existing read-onlyHOLDS_ONE.