cmux NEXT: connect the GitHub App from Settings Accounts

Merged
TE
2d ago
+930-16

Settings → Accounts gains one Connect GitHub action backed by the existing GitHub App connection API. A user chooses repositories and permissions in GitHub, then returns to Settings to see the connection, its granted permissions, and Disconnect. Pending browser authorization stays pending until the API confirms completion.

"one 'Connect GitHub' in Settings and onboarding"
"No gh auth login anywhere in the UI."

Depends on #26795 for typed result effects. The dashboard callback preserves the originating team through completion in merged #26804 (04f80b97).

Settings and onboarding share accounts.githubConnect (cmux accounts github-connect), which reports connected, pending, needs-action, or failed with localized recovery text. The Setup Guide button is carried by #26780. The Inbox preference is relabeled GitHub Inbox because it controls notifications, with its behavior and default preserved.

The native account/team scope binds every request and discards responses after switching accounts. Existing owner-session HTTPS transport is used; no credential-relay permission or backend behavior changes. Only a connection's creator sees an enabled Disconnect action. Connect retries retain the same idempotency key after a lost response; cached browser URLs expire with their signed authorization state, and a failed request returns a failed CLI action; repeated setup does not create another active connection. Showcase and isolated Accounts fixtures disable the live transport. Browser completion uses the existing dashboard callback; this does not claim access from other machines.

Validation at b497fafd2aa0df3e8f129a478eb9fd0793a14fcb: static pre-push checks passed. Fleet job 0a87afee31dec79469dc2d73 compiled the test targets and passed GitHubConnectionsTests (16 tests), ActionSurfaceParityTests (two skips), and ActionEffectTests. The same new regression cases on pre-fix b09afc6004cb715653a298dc21874cca00ef13c0 failed in fleet job 38662545ac0188fa553ba756: the policy retry reused its refused key, and the restored pending connection allowed two duplicate attempts. No test crash or incomplete run is counted as a pass.

Definitive connection refusals now discard their mutation key; a lost response or indeterminate transport/server failure keeps it for safe retry. A pending flow restored without its browser URL disables Connect; locally cached flows remain resumable, including renewal after state expiry. The GitHub App connection still uses the existing integration API; the daemon lease is not managed by Settings.

The current review repair at e004e898c1ebc879332acf9dbf483fe2d2670e75 isolates retry-key changes to the current account generation and refuses personal-team fallback under a managed-team restriction. Regression tests exercise an obsolete refusal arriving while a newer attempt is in flight, and missing/wrong managed teams making zero API calls. Exact-head focused verification and independent review are in progress.

Earlier focused web run 40652260308 passed types and rendered the fixture treatments. Its image-log transport lost bytes, so those renders are not claimed as retained screenshots; the fleet captures below replace them.

Size: this remains one Accounts connection state machine, its actions, localized copy, and regression/gallery coverage (about 880 changed lines). Splitting the state from its UI and behavior tests would leave an incomplete connection flow.

No new timer or observer is introduced; the existing Settings subscription and focus lifecycle refresh the connection state. HTTP requests use the existing 15-second bounded transport.

Design: existing Accounts rows and glass, secondary descriptions, and quiet actions. Three gallery treatments (balanced, compact, inline) plus pending, empty, and disconnect-confirmation states use synthetic data. Reference: Claude's Settings session-workflow rows; no private reference screenshot is published.

Captures

Settings > Accounts opened with accounts.show, signed out, on a fleet capture mini. Before is the merge base, after is the head build: the new GitHub group appears under the cmux account with Connect GitHub disabled until sign-in.

Before (9f6a7512cc7c, see marker below for the exact commit):

Capture steps-c31ce0cf51 at 940e57433514 (fleet build, capture mini, text checked for private data)

LightDark
lightdark

After:

Capture steps-c31ce0cf51 at c93b079685ba (fleet build, capture mini, text checked for private data)

LightDark
lightdark

Viewed after image: yes
Visual verdict: The new GitHub group sits under the cmux account in Settings Accounts with the same card, spacing and button style as its neighbours in light and dark, and Connect GitHub reads as disabled until sign-in.

Verification

  • At 6a1f1cb5 (fleet): GitHubConnectionsTests (with the new lost-connect pending regression), ActionEffectTests, ActionSurfaceParityTests pass.
  • The head 6976e112 merges feat-cmux-next into f218b875, resolving one conflict in ActionSurfaceCatalog+MenuExemptions.swift by keeping both accounts.githubConnect and settings.customizeMenus. CI at this head and the base-red classifier decide its reds.
  • Review at 6976e112 (Sol): no blocking finding. Its two P2s land as follow-ups under the 2026-10-30 severity rule: #28716 (reopen a cached authorization URL when the refresh fails) and #28717 (discard a response after an A→B→A account switch).

Agent session: agent session