Repository navigation
Conversation
The all-keyboards fleet found new keyboards only on its two-second inventory check, so a Caps press right after plugging in could reach macOS unmapped. SessionCapsKeyboardArrivalMonitor now listens for IOHIDEventService registrations whose usage pairs include keyboard (including a board's shared NKRO interface) and makes the next fleet check due at once; checks then run every 0.25 s for three seconds while hidutil catches up. The two-second check stays as the backstop and covers everything if the notification can't be registered. The worker logs the arrival-to-mapped time. Eligibility, exclusions and the all-keys-up rule are unchanged: an arrival only schedules the same reconcile. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The arrival monitor classifies services with SessionCapsHIDUtilTransport.serviceEmitsKeyboardUsages (usage pairs, then the report descriptor) and fails open: an unreadable capability still schedules a check. The predicate is injectable for tests. - Arrival attribution waits up to 30 s (e.g. keys held at plug-in), ignores keyboards recovering from failure backoff, and every newly covered keyboard is logged. - Fleet check spacing is measured from completion, so fast arrival checks leave the HID queue idle between them. - Fix a notification-port leak on an unreachable failure path; guide no longer claims an unmeasured instant setup. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A wake re-registers every keyboard and now costs one check. An unreadable service gets one immediate check but no fast window. The fast window ends once no eligible keyboard is waiting, and lasts at most 30 s. The backstop is measured between check starts. Stale retry entries are pruned. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The fast window used to close on the first check of an ordinary hot-plug, because hidutil had not listed the new service yet. The monitor now passes on each arrived keyboard's registry ID and LocationID. Checks stay fast until a listing accounts for the keyboard, or until it is gone, so a wake still costs one check. Also: - a stale arrival no longer times a new one - backoffs are pruned only for keyboards that are gone - the live predicate test fails rather than skips when hidutil lists a keyboard - the monitor stops itself on deinit Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A virtual keyboard no longer starts the arrival-to-mapped clock, so the log a replug test reads is not inflated. Classification walks the registry and can parse report descriptors, so it now runs on a background queue rather than on the run loop the event tap uses. A failed check hands over to the backstop instead of retrying hidutil every 0.25 s. The design doc no longer describes the arrival path as deployed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each check spawns several hidutil processes. A 30 s fast window could keep that near-continuous, and a service hidutil never lists held it open the whole time. An arrival now earns up to six fast checks while its keyboard is unlisted, then the backstop takes over. Checks no longer stay fast while keys are held. Also: - the backoff-pruning rule is extracted and tested - the live predicate test is opt-in (KEYPATH_LIVE_CAPS_FLEET=1) - the guide no longer claims Settings shows a reason Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every check ahead of the backstop, including the immediate check an arrival asks for, now draws on one budget of six per 10 s and waits 0.25 s after the previous check. A device that keeps re-registering falls back to the backstop instead of spawning hidutil continuously. The arrival log now times each newly covered keyboard from its own service's arrival, matched by registry ID or LocationID. A keyboard covered for another reason is no longer timed. Also: - a check that can't run counts as failed - registry lookups are memoised per check - the doc's fast-check count is corrected - the guide hedges for when notifications are unavailable Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The off-main classification and its delivery on the main run loop are now tested, along with a result that lands after stop(). - The early-check budget is now twelve per 10 s, so a second keyboard plugged in just after the first isn't left for the backstop. - Lease contention keeps an arrival's fast checks. - Arrival timestamps are pruned on failed checks too. - LocationID is read as hidutil prints it, and an opt-in live test checks the two readings agree (they do on the Air). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The startup test now pumps the run loop before asserting. Reporting the startup inventory as arrivals makes it fail (checked by mutation). - A unit test checks that a high-bit LocationID reads the same from IOKit's signed CFNumber and from hidutil's unsigned JSON. - Lease contention keeps an arrival's fast checks for at most three busy results. - A replug at the same LocationID replaces an earlier arrival that was never mapped, so it is timed from the replug. - The docs state what "fails open" covers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Busy handling moves into the schedule (checkBusy) and is tested. - The monitor-to-schedule-and-timing wiring is one tested function. - An arrival that lands during a check keeps all six fast checks. - A sibling interface registering moments later keeps the keyboard's first arrival. Only a new service at that LocationID 2 s or more later counts as a replug. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A held key on a newly plugged-in keyboard is known only after its first report has been processed. A check right after the IOKit notification could read all keys as up and map the keyboard while one is held. Every check, the backstop included, now waits out a 0.25 s pause after the latest arrival. This is unmeasured; a fixture test with a held key would settle it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Timing it from the latest arrival let a device that re-registers faster than every 0.25 s block every check, the backstop included. The pause now runs from the first arrival since the last check started, so it delays a check by one pause at most. A tick-loop test at a 0.2 s re-registration rate pins this; it fails against the old rule. An arrival during a failed check now keeps its fast checks, as it already did on success. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
Review of PR 1343 (IOKit arrival-triggered Caps fleet check) I found no blocking correctness issues in the supplied diff. The diff was truncated at 60 KB, so I did not see the tail of What I checked:
Minor, non-blocking:
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e87589d303
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
The post-stop test no longer sleeps. It blocks classification, stops the monitor, releases classification, and waits for its result to be handled. It fails if the stop guard is removed. A wall-clock step backwards no longer holds off checks or uses up the early budget. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Review of PR 1343 (the supplied diff was truncated at 60 KB, so the rest of the docs and guide changes were not reviewed). I found no blocking correctness issues in the reviewed part (the arrival monitor, the check schedule, the worker changes and the tests).
|
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Review (diff truncated at 60k bytes; docs/guides tail not reviewed) Found two things worth addressing:
No blocking correctness issues found in the visible portion. 🤖 Generated with Claude Code |
Summary
With all-keyboards Caps Lock (#1339), a newly plugged-in keyboard was mapped by the worker's 2 s poll. Now an IOKit notification brings that check forward.
SessionCapsKeyboardArrivalMonitorlistens forIOHIDEventServicefirst-match notifications. Services already present at startup are ignored. It classifies each new service with the lease's ownserviceEmitsKeyboardUsages, on a background queue rather than the event tap's run loop, and fails open when a service's capability can't be read yet.SessionCapsCheckScheduledecides when the fleet reconcile runs:[SessionCaps] Keyboard coveredfor each newly covered keyboard, and[SessionCaps] Keyboard arrival mapped in N ms, timed from that keyboard's own arrival. A keyboard covered for another reason isn't timed. A sibling interface keeps the first arrival time; a replug at the same LocationID resets it.docs/architecture/all-keyboards-caps.mdis rewritten.guides/driverless-caps-lock.mddescribes plugging in a keyboard.Eligibility, exclusions, the all-keys-up gate and the existing-remap policy are unchanged. The monitor only schedules checks.
Testing
On the MacBook Air (Xcode 27.0):
./Scripts/run-tests-safe.sh: 5,860 tests passed.SessionCapsCheckScheduleTests(31 tests) cover:stop();KEYPATH_LIVE_CAPS_FLEET=1, passed on the Air: the lease predicate recognises an attached keyboard, and IOKit and hidutil agree on LocationIDs../Scripts/review-gate.sh: PASS, after nine failing rounds whose findings are all addressed in this branch.Not verified
arrival mapped in N mstime hasn't been measured. The Air is in single-keyboard mode; switch to all keyboards in Settings, then plug in an external keyboard and read the log.🤖 Generated with Claude Code