Repository navigation
Surface a browser fallback when Safari sign-in hangs (#6015) - #6113
Conversation
macOS sign-in runs through ASWebAuthenticationSession, which is always Safari-backed regardless of the user's default browser. When the hosted page never redirects to cmux://auth-callback, the popup just sits there and the only backstop is a 10-minute silent cancel — the user stares at a dead Safari window with no feedback or recovery path (#6015). Add an inert `signInIsSlow` flag + `slowSignInThreshold` knob on HostBrowserSignInFlow plus a regression test that drives a popup which never delivers a callback and asserts the flow surfaces the slow state. The flag is not yet wired, so the test fails here on purpose; the fix follows in the next commit (two-commit red/green per the repo's regression test policy). Issue: #6015 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
macOS sign-in always runs through ASWebAuthenticationSession, which is Safari-backed regardless of the user's default browser. When the hosted auth page never redirects to cmux://auth-callback, the popup hangs and the user is left on an indefinite spinner — the reported Founder's Edition repro (#6015). HostBrowserSignInFlow now flips an observable `signInIsSlow` flag after `slowSignInThreshold` (30s) of an attempt still waiting on the browser, without cancelling the popup (a user who is simply slow can still finish in it). It also exposes `activeAttemptSignInURL`, which reuses the active attempt's callback state so a sign-in completed in the user's real default browser deep-links cmux://auth-callback back into the in-flight attempt via handleCallbackURL. The Settings -> Account card surfaces this: when sign-in is slow and the user is still signed out, it shows a hint plus an "Open in Browser" button that opens activeAttemptSignInURL via NSWorkspace. AccountFlow gains signInIsSlow + openSignInInDefaultBrowser; HostAccountFlow wires them to the shared flow, so the command palette, pairing, and CLI sign-in entrypoints all drive the same single model path. Localized slowHint + openInBrowser in en/ja/ko/uk (the account section's locale set). This is the GREEN half of the two-commit regression test. Issue: #6015 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthroughAdds slow-sign-in detection to ChangesSlow Sign-In Detection and Default-Browser Fallback
Sequence DiagramsequenceDiagram
actor User
participant AccountIdentityCard
participant HostAccountFlow
participant HostBrowserSignInFlow
participant NSWorkspace
User->>AccountIdentityCard: initiates sign-in
AccountIdentityCard->>HostAccountFlow: signIn()
HostAccountFlow->>HostBrowserSignInFlow: startSignIn()
HostBrowserSignInFlow->>HostBrowserSignInFlow: scheduleSlowSignInHint(attempt)
Note over HostBrowserSignInFlow: threshold (30s) elapses with no completion
HostBrowserSignInFlow-->>HostAccountFlow: signInIsSlow = true
HostAccountFlow-->>AccountIdentityCard: signInIsSlow = true
AccountIdentityCard-->>User: shows "Open in Browser" fallback row
User->>AccountIdentityCard: taps "Open in Browser"
AccountIdentityCard->>HostAccountFlow: openSignInInDefaultBrowser()
HostAccountFlow->>HostBrowserSignInFlow: read activeAttemptSignInURL
HostBrowserSignInFlow-->>HostAccountFlow: URL with cmux_auth_state
HostAccountFlow->>NSWorkspace: open(url)
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related issues
Poem
Important Pre-merge checks failedPlease resolve all errors before merging. Addressing warnings is optional. ❌ Failed checks (1 error, 1 warning)
✅ Passed checks (19 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Greptile SummarySurfaces a "slow sign-in" fallback in the Settings Account card when macOS
Confidence Score: 5/5Safe to merge — the new slow-sign-in hint and browser-fallback path are non-destructive, all cancel/reset paths are symmetric, and four targeted regression tests validate the key scenarios. The fallback routing in handleCallbackURL correctly differentiates the active-session, issued-fallback, and stateless paths. The pendingFallbackCallbackState lifecycle is properly maintained across cancelActiveAttempt, completeCallback, and the routeToIssuedFallback eager-clear. The showSlowSignInFallback guard (signInIsSlow AND currentIdentity == nil) prevents any stale-flag visual artifacts. Localization matches the established en/ja/ko/uk pattern for settings.account.* keys. No files require special attention. Important Files Changed
Sequence DiagramsequenceDiagram
participant UI as AccountIdentityCard
participant AF as HostAccountFlow
participant Flow as HostBrowserSignInFlow
participant Popup as ASWebAuthSession
participant Browser as Default Browser
participant App as AppDelegate (cmux://)
UI->>AF: startSignIn()
AF->>Flow: beginSignIn()
Flow->>Popup: start() [scheduleSlowSignInHint T+30s]
Note over Popup: Popup hangs
Flow-->>Flow: "signInIsSlow = true (T+30s)"
Flow-->>AF: signInIsSlow observable
AF-->>UI: "signInIsSlow = true"
UI->>UI: showSlowSignInFallback
UI->>AF: openSignInInDefaultBrowser()
AF->>Flow: activeAttemptSignInURL
AF->>Browser: NSWorkspace.open(url)
Browser->>App: "cmux://auth-callback?cmux_auth_state=STATE"
App->>Flow: handleCallbackURL(url)
alt Popup still has active continuation
Flow->>Flow: routeToActive + cancelSlowSignInHint()
Flow->>Flow: completeCallback(attemptID)
Flow->>Popup: resumeContinuation(nil)
else Popup already closed
Flow->>Flow: routeToIssuedFallback
Flow->>Flow: completeCallback(acceptedExternalState)
end
Flow-->>AF: "isSigningIn=false, signInIsSlow=false"
AF-->>UI: currentIdentity updated
Reviews (8): Last reviewed commit: "fix: preserve issued fallback callback s..." | Re-trigger Greptile |
| Text(String( | ||
| localized: "settings.account.signIn.slowHint", | ||
| defaultValue: "The sign-in window opens in Safari and may hang. If nothing happens, open sign-in in your default browser instead." | ||
| )) |
There was a problem hiding this comment.
Vendor name in user-facing hint copy. The string
"The sign-in window opens in Safari and may hang…" names "Safari" — an upstream vendor product — directly in user-facing copy. The cmux copy rule prohibits upstream vendor or service names in hints, alerts, and recovery text. Replacing it with something like "The system sign-in window may take a moment or stop responding…" conveys the same information without surfacing the implementation detail, and holds up correctly if Apple ever changes the backing browser.
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
There was a problem hiding this comment.
Fixed in 98107a8: the slow sign-in hint copy no longer names a browser vendor, and the localized catalog entries were updated to match.
— Claude Code
There was a problem hiding this comment.
Removed the vendor-specific implementation detail from the sign-in fallback code path. The localized user-facing hint remains generic: system sign-in window plus default browser fallback.
— Claude Code
Strategy: -X theirs + cherry-pick fork features back Upstream changes pulled in (highlights): - Stagger restored terminal surface spawns (manaflow-ai#6149) - Configurable Dock max width (manaflow-ai#4385) - Polish canvas minimap navigation (manaflow-ai#6105) - Opt-in AI auto-naming workspaces (manaflow-ai#6071) - Dissolve namespace-enums into value types (manaflow-ai#6126) - Fix stale remote connected state after proxy disconnect (manaflow-ai#4513) - Surface browser Safari (manaflow-ai#6113) - Fix terminal top-row mouse (manaflow-ai#4391) - iOS Shift key support (manaflow-ai#6104) Fork-only features verified intact post-merge: - cmux_term socket handlers: surface.snapshot, screen_text, screen_hash, wait_for_text, wait_for_idle, wait_for_screen_change, wait_for_kind, wait_for_cursor, tui_probe, expect, screen_region (6 handler funcs) - agent-bus: notification.create with $bus dispatch - TerminalSurface.visibleSnapshot() + processHasExited() helpers - skills/cmux-terminal-control/* (13 Python lib files + ORCHESTRATOR_TEMPLATE.md) - HerdrWorkspaceSync + CustomTitleSource.herdrInbound Adapter changes (fork-side): - Drop InternalImportsByDefault from CmuxFeedback/CmuxFeedbackUI packages (default-arg parameter types remained internal-only across modules, blocking FeedbackComposerBridge() construction from cmux app target) - Strip .rawValue from SurfaceKind cases (became plain String) - Add herdrInbound to CustomTitleSource enum - Delete shadowing local enums in ContentView (CommandPaletteOverlayPromotionPolicy, ExtensionSidebarBrowserStackDropPlanner, SidebarDragFailsafePolicy, SidebarDragLifecycleNotification, SidebarMarkdownRenderer, SidebarOutsideDropResetPolicy, SidebarShortcutHintFreezePolicy, SidebarTrailingAccessoryWidthPolicy, SidebarWorkspaceSelectionSyncPolicy, ShortcutHintDebugSettings, ShortcutHintModifierPolicy, FeedbackComposerBridge, FeedbackComposerBridgeError, ExtensionSidebarBrowserStackDropRow, SidebarTabDropIndicatorPredicate, SidebarDropEdge, SidebarDropIndicator) - Delete local TerminalSurfaceClaudeCommandShim (use package's via typealias) - Delete local TerminalSurfaceRuntimeTeardownCoordinator + Request stubs (use package's enqueueRuntimeTeardown public API directly) - Adapt static→instance: CmuxGhosttyConfigSettingEditor.X → ().X, CmuxApplicationSupportDirectories.userDirectories → init(env:).userDirectories - newTerminalSurface/TerminalPanel: thread externalIo: parameter for herdr - Add createReplacementTerminalPanel(in:) overload - Add attachToViewForInputDemand/requestInputDemandSurfaceStartIfNeeded stubs - TerminalSurfaceRuntimeFilesystem.live(): wrap installClaudeCommandShim sync→async - AppDelegate.sortedMainWindowContextsForSessionSnapshot: add includeQuickTerminal param - Wire BackgroundSessionStore + BackgroundSessionsSidebarSection + SidebarSectionDivider in pbxproj (PBXFileReference + PBXBuildFile entries were missing post-merge) - Add SidebarSectionDivider stub View (was referenced but never created) Rebuilt clean against /tmp/cmux-p52 derivedData.
Problem
macOS sign-in (Settings → Account → Sign In) hangs: a Safari window opens — even when Safari is not the default browser — then nothing. No page progress, no callback, the app stays "Not signed in". Reported by a Founder's Edition customer; blocking them from signing in at all. (#6015)
Root cause map (from the code, not yet a confirmed web repro):
ASWebAuthenticationSession, which on macOS is always Safari-backed regardless of the user's default browser — that part is Apple-by-design, not the bug. The hang is when the hosted page never redirects tocmux://auth-callback.browserAttemptTimeout(10 minutes), after which the attempt is silently cancelled. The user gets zero feedback and no recovery path in the meantime.What this PR does
This is the UX + recovery fix the issue's "Expected" calls for ("surface an actionable error promptly instead of hanging silently"). It does not attempt to fix the web-side root cause (why the hosted redirect never fires for some users) — that genuinely needs a clean, not-signed-in macOS repro, and the cloud-mac repro infra is currently down.
HostBrowserSignInFlowflips a new observablesignInIsSlowafterslowSignInThreshold(30s) of an attempt still waiting on the browser. It is non-destructive — the popup keeps running, so a user who is simply taking their time can still finish in it. Resets on finish / cancel / replacement.activeAttemptSignInURLreuses the active attempt's callback state, so a sign-in completed in the user's real default browser deep-linkscmux://auth-callbackback into the in-flight attempt via the existinghandleCallbackURLpath (thecmux://scheme is already registered and routed inAppDelegate). This is a genuine recovery route, not just a nag.NSWorkspace.open(activeAttemptSignInURL)).AccountFlowgainssignInIsSlow+openSignInInDefaultBrowser;HostAccountFlowwires them to the shared flow. One model path — the command palette, mobile pairing, and CLI (auth.begin_sign_inalready returns a manual URL) sign-in entrypoints all drive the sameHostBrowserSignInFlow.Regression test (two-commit red/green per repo policy)
fe5194618(RED): inertsignInIsSlowstub +slowSignInSurfacesBrowserFallbacktest that drives a popup which never delivers a callback. Fails on exactlyExpectation failed: becameSlow.72bf8fe14(GREEN): wires the slow-hint scheduling; the test passes. AddsactiveAttemptSignInURLCarriesActiveAttemptState(asserts the fallback URL carries the active attempt'scmux_auth_stateso the deep link routes back).Test plan
swift test --package-path Packages/CmuxAuthRuntime→ 94/94 pass (was 92; +2 new). Verified the regression test fails on commit 1 and passes on commit 2.swift buildofCmuxSettingsUI→ clean.reload.sh --tag fix-6015-signin-hang) → BUILD SUCCEEDED.Localization audit
New user-facing strings
settings.account.signIn.slowHintandsettings.account.signIn.openInBrowseradded toResources/Localizable.xcstringsin en / ja / ko / uk — the exact locale set every othersettings.account.*key uses (verified against the catalog). No bare English literals introduced (Text/ButtonuseString(localized:)).Issue: #6015
🤖 Generated with Claude Code
Need help on this PR? Tag
/codesmithwith what you need. Autofix is disabled.Summary by cubic
Adds a default-browser fallback when macOS sign-in via the system sign-in window hangs; addresses #6015. After 30s, Settings shows “Open in Browser” using the in‑flight attempt state; the hint clears on callback/finish/cancel, and the fallback callback is accepted even if the popup was closed.
New Features
CmuxAuthRuntime:HostBrowserSignInFlowexposessignInIsSlow(30s, configurable) andactiveAttemptSignInURLthat reuses the activecmux_auth_state.CmuxSettingsUI: Account card shows a slow-sign-in hint only when still signed out and adds “Open in Browser”;AccountFlowaddssignInIsSlowandopenSignInInDefaultBrowser;HostAccountFlowopens viaNSWorkspace.Bug Fixes
Written for commit a067a07. Summary will update on new commits.
Summary by CodeRabbit
New Features
Localization