fix: guard every HID verb against Device-Hub-poisoned simulators#61
Merged
Conversation
A simulator booted while Device Hub is open loses legacy HID delivery (keyboard, and on CoreSimulator 1169.1+ touch as well): sends report success while nothing lands. Only `type` guarded against this, and its predicate — "dtuhidd currently running in the device's launchd_sim domain" — could not tell that poisoned state apart from a Device Hub attached to an already-booted simulator, where HID keeps working, so extending it to `tap` would have blocked healthy simulators. Live A/B on one device/runtime (issue #60) confirmed the boot-time rule and calibrated a predicate that can tell the states apart: process start times. Poisoned boots spawn dtuhidd together with launchd_sim (measured 1 s apart); a later Hub attach spawns it when the user opens the app (>= 34 s even scripted). The reworked guard — DeviceHubHIDSuppression, replacing KeyboardHIDSuppression — flags dtuhidd only when it is a child of the target's launchd_sim AND started within a 15 s boot-attach window, reusing LaunchdSimLocator's sysctl process table (ProcessRecord gains ppid; ~1-2 ms, no /bin/ps fork). The guard moves to HIDInteractor.makeSession, the single choke point every legacy-HID verb passes through, so tap/swipe/touch/gesture/ multi-touch/button/key/type all fail loudly with recovery steps; the type-level check is removed as redundant. The workaround message now explains that SIM_USE_SKIP_DTUHIDD_CHECK=1 must reach the daemon process (combine with SIM_USE_NO_DAEMON=1 or restart the daemon) — found live when the override silently failed against a daemon spawned without it. Verified live on Xcode 27 B4: poisoned boot -> loud error through the daemon path; clean boot -> taps land; Hub attached after boot -> taps land, no false positive; escape hatch bypasses the guard. Fixes #60 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: onevcat <onevcat@gmail.com>
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.
Summary
Fixes #60 — a simulator booted while Device Hub is open silently drops all legacy HID (
tapreported✓ completed successfullywith zero UI effect). Onlytypeguarded against dtuhidd, and its presence-based predicate would have false-positived on healthy simulators if extended totap. This PR replaces the predicate with one that can actually tell the two states apart, and moves the guard to the single choke point so every HID verb fails loudly.The predicate
Live A/B on one device/runtime (see #60) established:
Presence can't discriminate; start-time delta can.
DeviceHubHIDSuppression(replacingKeyboardHIDSuppression) flags dtuhidd only when it is a child of the target'slaunchd_simand started within a 15 s boot-attach window of it. The window leans generous deliberately: a false positive fails loudly with recovery steps + an escape hatch, a false negative would silently drop input. Implemented onLaunchdSimLocator's sysctl process table from #58 (ProcessRecordgainsppid; ~1–2 ms, no/bin/psfork — the old guard's 40 ms spawn is gone as a bonus).Changes
HIDInteractor.makeSession— coverstap/swipe/touch/gesture/multi-touch/button/key/type/batch; the redundanttype-level check is removed (and its documented attach-after-boot false positive disappears).SIM_USE_SKIP_DTUHIDD_CHECK=1must reach the process that runs the check (combine withSIM_USE_NO_DAEMON=1, orsim-use daemon stopfirst) — discovered live when the override silently failed against a daemon spawned without it.docs/ai/xxxx-xcode27-support/.Tests
Tests/DeviceHubHIDSuppressionTests.swift— pure decision table over injected process tables: measured poisoned/benign rows, inclusive window boundary, other-simulator dtuhidd, fail-open cases, command-name forms, message contract.make test: 1124 green.SIM_USE_SKIP_DTUHIDD_CHECK=1 SIM_USE_NO_DAEMON=1) bypasses ✅; clean boot → taps land ✅; Hub attached after boot → taps land, no false positive ✅.Superseded eventually by the idb DTUHID migration, same as the guard it replaces.
🤖 Generated with Claude Code