Join consent sheet: a real click on Allow grants (HQ#5121)

TE
6d ago
+134-33

Summary

On a Mac with no saved Computer Use consent, the join consent sheet appears, but a person's real click on Allow never grants (manaflow-ai/cmuxterm-hq#5121). The sheet answers Allow only for the person's own click: takePersonClick(on:) needs the person's mouse-down on Allow held by the sheet, with the person's mouse-up on Allow as the current event. The sheet held that mouse-down only for the length of the mouse-down's own sendEvent dispatch.

On the displayed sheet (a borderless, non-activating panel that is never key), AppKit dispatches the click's mouse-up as a separate event, and Allow's action runs in that dispatch. That dispatch cleared the mouse-down, so takePersonClick found none and refused every real click. The HQ#5121 diagnostic on a GUI fleet Mac showed this: at Allow's action, personDown was already nil, and eventInHand was never read. Deny has no such check, so it closed the sheet on the same kind of click. cmux#27842 fixed the Upstream/Legacy helper fallback, but not this path.

The fix: the sheet now reads Allow from the person's click itself.

  • It holds the person's mouse-down until the click ends: when the mouse-up's dispatch finishes, or when the mouse-down's finishes if a button's tracking loop took the mouse-up inside it. A drag or pressure change keeps the mouse-down; any other event clears it.
  • The held mouse-down carries the Allow button that was shown and enabled when it came. A click begun while Allow was disabled (a write in flight), or across a rebuild, holds nothing.
  • Where the click ends, the panel checks the person's mouse-down and mouse-up on that same enabled Allow, once per click (takePersonClick(on:up:), strict PersonInputCheck), and answers Allow there.
  • Allow's button action now answers nothing. An Accessibility press, a programmatic click or the Return key equivalent can never grant, inside a click's dispatch or out of one. Base allowed a reentrant press inside the person's mouse-down dispatch to grant; both review rounds flagged the same path on this PR's first head.

The grant is still gated by the device-owner confirm.

Testing

Red/green: thePersonsClickOnAllowGrantsWhenItsMouseUpIsDispatchedOnItsOwn(nested:) in JoinConsentTests. It dispatches the person's mouse-down, then the mouse-up with Allow's action inside it, both after the mouse-down's dispatch and nested in it, and needs a grant. It also presses Allow's action inside that click and still sees exactly one grant. Around that it checks that these still grant nothing:

  • a mouse-up the person check refuses
  • a click released on Deny
  • an Accessibility press while Allow is held
  • a press after the click ended
  • a click with another event between its two halves
  • a click pressed while Allow was disabled and released after it was enabled again

The existing allowAnswersOnlyThePersonsOwnClickOnAllow (the tracking-loop path and its refusals) is unchanged.

Command: swift test --filter JoinConsentTests (CmuxNext package) on a fleet Mac, via cmux-ci. Expected: the new test fails at the red commit and passes at the head.

Observed / Not run

  • Observed: scripts/cmux-next/prepush-gate.py reports no new violations.
  • Observed on the fleet at ac6c158 (same UI and logic as this head; since then only test compile fixes and the tuple type):
    • A fresh-home launch passes: Home in 10.0 s, up 30 s, no crash, no uncaught exception.
    • On a fresh home with Labs machineJoin on and This Mac joined, a refused screen request shows the sheet with Deny and Allow, in light and dark.
  • Reviews:
    • Sol (gpt-6.1-sol) blocked 6612e1a and Astra (gpt-6-astra) blocked f81f785, both on the reentrant-press P1.
    • Astra rounds 3 and 4 caught a wrong test expectation and a click begun while Allow was disabled. Both are fixed.
    • Astra SHIP at ac6c158. The fleet then found that the test target did not compile there, and Astra re-reviewed: SHIP at a16f7f1e9d40d6b6fc371e15a407c8a7c0469ae4.
  • Observed on fleet Macs at this head (a16f7f1), with CMUX_TEST_REQUIRE_GUI=1:
    • CmuxNextAppTests compiles.
    • JoinConsentTests at the red commit a313827: exactly one test fails, thePersonsClickOnAllowGrantsWhenItsMouseUpIsDispatchedOnItsOwn(nested:), in both cases (JoinConsentTests.swift:771, fake.writes == ["screen true"] false). 41 tests ran, no crash.
    • JoinConsentTests at the head: all 41 tests pass, including both cases of the new test.
    • Computer Use suites, 24 at the head: 23 pass. ComputerUseSetupWindowTests fails the same 6 tests with the same errors on base 898b1b0 (panel.dockedFrame(for:) nil). That is a base red. No crash, signal or abort at the head, the red commit or base.
  • Not shown, and the fleet can't show it today: a real hardware click on the displayed Allow granting, followed by a Computer Use step.
    • PersonInputCheck accepts only HID-system input, and cmux-ci capture has no HID-level click. Its clicks are in-app debug RPCs.
    • The GUI suite from HQ#5121 can't run either: its fleet Mac's test launcher has lost its Accessibility and post-event grant, which takes a person at the console to restore.
    • The unit tests drive the panel's own dispatch in both orders AppKit uses. HQ#5121's diagnostic showed the displayed sheet's mouse-up arrives on its own.

Changelog

Fixed: Clicking Allow on the Computer Use consent sheet now grants consent

Proof

No capture: the sheet looks the same. The proof is behavioural: a fresh-home fleet Mac run where a click on Allow grants (added here when it runs).

Risk: high (the consent gate's person check). Lands on an independent review at the exact head plus a green compile.

🤖 Generated with Claude Code

Agent session: agent session