Focus Mac pairing QR flow on Tailscale - #9493
Conversation
📝 WalkthroughWalkthroughThe PR makes Tailscale the sole Mac pairing transport, retains compatibility decoding for existing Iroh codes, updates iOS and macOS pairing guidance, and revises English and Japanese labels, errors, documentation, and regression tests. ChangesTailscale pairing flow
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant MacPairing
participant TailscaleRoute
participant MobileApp
MacPairing->>TailscaleRoute: select phone-reachable route
TailscaleRoute-->>MacPairing: return disclosure mode
MacPairing->>MobileApp: display Tailscale pairing QR
MobileApp->>MobileApp: scan QR and connect
Possibly related issues
Possibly related PRs
Suggested reviewers: Important Pre-merge checks failedPlease resolve all errors before merging. Addressing warnings is optional. ❌ Failed checks (2 errors, 2 warnings)
✅ Passed checks (21 passed)
✨ Finishing Touches 💡 1📝 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 |
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@cmuxTests/PostHogAnalyticsPropertiesTests.swift`:
- Around line 189-190: Rename the test method mobileConnectButtonDefaultsHidden
to tailscalePairingButtonDefaultsHidden so it matches the updated test title,
while leaving the isMobileConnectButtonEnabled compatibility identifier
unchanged.
In `@ios/cmux/Resources/Localizable.xcstrings`:
- Around line 435-441: Update the mobile.tailscale.pairingHelp localization and
its translations to remove the stale trusted-local-network host/port pairing
guidance, matching the simulator-loopback-only manual flow used by
mobile.addDevice.help.
In `@Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/PairingView.swift`:
- Line 102: Update PairingView.pair() and connectManualHost() so manual pairing
is accepted only for simulator loopback hosts, rejecting non-loopback hosts
before connection. Keep the QR/Tailscale pairing path unchanged, and ensure the
UI warning matches this enforced simulator-only contract.
In `@Resources/Localizable.xcstrings`:
- Around line 53692-53708: Add or update the translated values for every
supported locale in Resources/Localizable.xcstrings for all affected
pairing-related keys, including cli.workspaceAction.tailscalePairingUnavailable
and the additional referenced entries. Preserve the existing English and
Japanese translations while ensuring each of the catalog’s 20 supported locale
blocks has a translated value, updating stale Mobile/Iroh wording where
necessary.
In `@Sources/AppDelegate.swift`:
- Line 7555: Update the default title used when creating the pairing workspace
in the surrounding pairing-workspace creation flow to the new “Tailscale
Pairing” value, while leaving the reuse path and any user-defined custom titles
unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 3a33ab8c-5c23-46b4-a5cf-42969c9f0047
📒 Files selected for processing (34)
Packages/Shared/CMUXMobileCore/Sources/CMUXMobileCore/CmxPairingQRCode.swiftPackages/Shared/CMUXMobileCore/Sources/CMUXMobileCore/CmxPairingRouteDisclosureMode.swiftPackages/Shared/CMUXMobileCore/Sources/CMUXMobileCore/CompactAttachTicket.swiftPackages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobilePairingFailure.swiftPackages/iOS/CmuxMobileShell/Tests/CmuxMobileShellTests/MobilePairingFailureTests.swiftPackages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/MobileConnectionMethodSection.swiftPackages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/OnboardingConnectionView.swiftPackages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/PairingView.swiftPackages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/SetupHelpGateContent.swiftPackages/macOS/CmuxSettingsUI/Sources/CmuxSettingsUI/Navigation/CuratedSettingEntry+Default.swiftPackages/macOS/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/MobileSection.swiftResources/Localizable.xcstringsSources/AppDelegate.swiftSources/Auth/AccountSignInView.swiftSources/ClosedItemHistory+PanelTitle.swiftSources/CmuxSurfaceTabBarBuiltInAction.swiftSources/ContentView+CommandPaletteSurfaceMetadata.swiftSources/ContentView.swiftSources/FeatureFlags.swiftSources/Mobile/MobileAttachTicketStore.swiftSources/Mobile/Pairing/MobilePairingModel.swiftSources/Mobile/Pairing/MobilePairingView+Connected.swiftSources/Mobile/Pairing/MobilePairingView.swiftSources/Mobile/Pairing/MobilePairingWindowController.swiftSources/TerminalController.swiftSources/VerticalTabsSidebar+EmptyAreasAndFooter.swiftcmuxTests/AppDelegateShortcutRoutingTests.swiftcmuxTests/CmuxConfigContextMenuTests.swiftcmuxTests/CommandPaletteSearchEngineTests.swiftcmuxTests/MobilePairingConnectionTransitionTests.swiftcmuxTests/PostHogAnalyticsPropertiesTests.swiftdocs/iroh-app-transport-architecture.mddocs/pro-badge-options.htmlios/cmux/Resources/Localizable.xcstrings
Bugbot is paused — on-demand spend limit reachedBugbot uses usage-based billing for this team and has hit its on-demand spend limit. A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue. |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/PairingView.swift`:
- Around line 102-106: The manual-entry flow still permits non-loopback hosts
despite the simulator-only copy. Update the Pair button validation near the
manual host input and the connectManualHost() path to reject non-loopback
addresses before connecting, preserving loopback support; alternatively, revise
the displayed copy to accurately describe intentional arbitrary-host support.
In `@Resources/Localizable.xcstrings`:
- Line 134739: Update the English localization values associated with the
Tailscale pairing instructions at all referenced entries so they explicitly
state that Tailscale must be installed and connected on both the Mac and iPhone,
and that both devices must use the same Tailscale network. Preserve the existing
pairing-code context while replacing the ambiguous network wording consistently.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 10cb182c-8132-46d2-bb87-718934dfcfde
📒 Files selected for processing (10)
Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/MobileConnectionMethodSection.swiftPackages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/MobilePairingScannerPreview.swiftPackages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/MobilePairingScannerSheet.swiftPackages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/OnboardingConnectionView.swiftPackages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/PairingView.swiftPackages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/SetupHelpGateContent.swiftResources/Localizable.xcstringsSources/Mobile/Pairing/MobilePairingView.swiftios/cmux/Resources/Localizable.xcstringsios/cmuxUITests/cmuxUITests.swift
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (4)
Sources/AppDelegate.swift (1)
10939-11038: 📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy liftDo not expand the production target with a new UI-test seam.
Lines 10939-11038 add environment-controlled test setup and test-specific panel creation to
Sources/AppDelegate.swift.Move this four-panel setup to an isolated test or debug facility. Do not add more UI-test branches to
AppDelegate.As per path instructions, production Swift source under
**/Sources/**must not add test-only or debug-only seams. The coding guidelines require genuinely debug-only facilities to use a dedicated debug file or folder.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@Sources/AppDelegate.swift` around lines 10939 - 11038, Remove the environment-controlled Bonsplit UI-test setup, including runSetupWhenWindowReady and its four-panel creation logic, from AppDelegate. Move this behavior into an isolated UI-test or dedicated debug facility outside the production Sources target, preserving the existing setup behavior without adding further test-only branches to AppDelegate.Sources: Coding guidelines, Path instructions
ios/cmux/Resources/Localizable.xcstrings (1)
8572-8589: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winUpdate
mobile.tailscale.pairingHelpto match the simulator-only manual-entry policy.
mobile.tailscale.pairingHelpstill tells the user they can "pair by host and port on a trusted local network".mobile.addDevice.manualRouteWarningin this same catalog now states that manual credentials work only in the simulator, and physical devices must use Tailscale and scan the Mac QR. These two strings contradict each other. Updatemobile.tailscale.pairingHelp(English and Japanese) to remove the trusted-local-network host/port guidance, matching the simulator-only manual flow.💬 Proposed copy fix
- "value": "QR pairing usually needs both devices on the same Tailscale network. Turn Tailscale on first, or pair by host and port on a trusted local network." + "value": "QR pairing usually needs both devices on the same Tailscale network. Turn Tailscale on first. Host and port entry is an advanced fallback for reconnecting an already paired Mac, and works only in the simulator on a device."Update the matching
javalue to remove the trusted-local-network guidance as well.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@ios/cmux/Resources/Localizable.xcstrings` around lines 8572 - 8589, Update the English and Japanese values for mobile.tailscale.pairingHelp to remove guidance about pairing by host and port on a trusted local network, keeping the message consistent with the simulator-only manual-entry policy and Tailscale QR pairing flow.Resources/Localizable.xcstrings (1)
35814-35830: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winComplete locale coverage for all changed catalog entries.
The changed entries provide English and Japanese text, but the catalog supports additional locales. Add or update translations for every supported locale.
Resources/Localizable.xcstrings#L35814-L35830: add all supported locale entries forcli.surfaceResume.set.error.unknownFlag.Resources/Localizable.xcstrings#L110629-L110635: localize the sidebar Tailscale Pairing description in every supported locale.Resources/Localizable.xcstrings#L134756-L134762: localize the Tailscale pairing-code detail in every supported locale.Resources/Localizable.xcstrings#L135283-L135289: localize the setup requirement in every supported locale.Resources/Localizable.xcstrings#L135317-L135323: localize the Mac-connected guidance in every supported locale.As per coding guidelines, changed catalog keys must include translated values for every supported locale. As per path instructions,
.github/review-bot-rules/full-internationalization.mdis authoritative for this requirement. Based on learnings, this catalog currently supports 20 locales.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@Resources/Localizable.xcstrings` around lines 35814 - 35830, Complete translations for all 20 supported locales in Resources/Localizable.xcstrings: update cli.surfaceResume.set.error.unknownFlag at lines 35814-35830 and localize the sidebar Tailscale Pairing description at 110629-110635, pairing-code detail at 134756-134762, setup requirement at 135283-135289, and Mac-connected guidance at 135317-135323. Follow .github/review-bot-rules/full-internationalization.md and provide translated values for every supported locale at each listed entry.Sources: Coding guidelines, Path instructions, Learnings
Sources/ContentView.swift (1)
8409-8409: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winGate the command palette entry before invoking Tailscale Pairing.
palette.mobileConnectis registered unconditionally, butperformMobileConnectWorkspaceAction(..., enforceFeatureFlag: false)ignoresCmuxFeatureFlags.shared.isMobileConnectButtonEnabled. Use the shared flag here so the command is not visible/executable while the feature flag is disabled.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@Sources/ContentView.swift` at line 8409, Update the palette.mobileConnect registration and its performMobileConnectWorkspaceAction call to honor CmuxFeatureFlags.shared.isMobileConnectButtonEnabled, so the command is hidden or blocked when the flag is disabled while preserving the existing Tailscale Pairing behavior when enabled.
♻️ Duplicate comments (1)
Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/PairingView.swift (1)
100-108: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy liftManual host/port pairing restriction is inconsistent across code and copy. The intended policy is that manual host/port entry works only on the simulator, but the button logic does not enforce it and one catalog string still advertises host/port pairing "on a trusted local network".
Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/PairingView.swift#L100-L108,233-239: confirm whetherconnectManualHost()/pair()reject non-loopback hosts on a physical device, or gate the Pair button on loopback when running on device.ios/cmux/Resources/Localizable.xcstrings#L8572-L8589: updatemobile.tailscale.pairingHelp(English and Japanese) to drop the "pair by host and port on a trusted local network" guidance once the code-side policy is confirmed.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/PairingView.swift` around lines 100 - 108, Enforce the simulator-only manual host/port pairing policy in PairingView.swift by updating connectManualHost()/pair() or gating the Pair button so physical devices reject non-loopback hosts; preserve valid loopback behavior. Then update ios/cmux/Resources/Localizable.xcstrings at lines 8572-8589 for mobile.tailscale.pairingHelp in both English and Japanese to remove guidance about pairing by host and port on a trusted local network.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@ios/cmux/Resources/Localizable.xcstrings`:
- Around line 8572-8589: Update the English and Japanese values for
mobile.tailscale.pairingHelp to remove guidance about pairing by host and port
on a trusted local network, keeping the message consistent with the
simulator-only manual-entry policy and Tailscale QR pairing flow.
In `@Resources/Localizable.xcstrings`:
- Around line 35814-35830: Complete translations for all 20 supported locales in
Resources/Localizable.xcstrings: update cli.surfaceResume.set.error.unknownFlag
at lines 35814-35830 and localize the sidebar Tailscale Pairing description at
110629-110635, pairing-code detail at 134756-134762, setup requirement at
135283-135289, and Mac-connected guidance at 135317-135323. Follow
.github/review-bot-rules/full-internationalization.md and provide translated
values for every supported locale at each listed entry.
In `@Sources/AppDelegate.swift`:
- Around line 10939-11038: Remove the environment-controlled Bonsplit UI-test
setup, including runSetupWhenWindowReady and its four-panel creation logic, from
AppDelegate. Move this behavior into an isolated UI-test or dedicated debug
facility outside the production Sources target, preserving the existing setup
behavior without adding further test-only branches to AppDelegate.
In `@Sources/ContentView.swift`:
- Line 8409: Update the palette.mobileConnect registration and its
performMobileConnectWorkspaceAction call to honor
CmuxFeatureFlags.shared.isMobileConnectButtonEnabled, so the command is hidden
or blocked when the flag is disabled while preserving the existing Tailscale
Pairing behavior when enabled.
---
Duplicate comments:
In `@Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/PairingView.swift`:
- Around line 100-108: Enforce the simulator-only manual host/port pairing
policy in PairingView.swift by updating connectManualHost()/pair() or gating the
Pair button so physical devices reject non-loopback hosts; preserve valid
loopback behavior. Then update ios/cmux/Resources/Localizable.xcstrings at lines
8572-8589 for mobile.tailscale.pairingHelp in both English and Japanese to
remove guidance about pairing by host and port on a trusted local network.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 2ddab00b-c3e6-4557-b517-972d79d98432
📒 Files selected for processing (8)
Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/PairingView.swiftResources/Localizable.xcstringsSources/AppDelegate.swiftSources/ContentView.swiftSources/FeatureFlags.swiftSources/Mobile/Pairing/MobilePairingView.swiftcmuxTests/PostHogAnalyticsPropertiesTests.swiftios/cmux/Resources/Localizable.xcstrings
The caseless MobilePairingScannerGuidanceCopy enum (from #9493) trips the namespace-enum rule in scripts/lint-ios-package-conventions.sh, turning the package-conventions-lint job red for every branch that touches Packages/. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Add standalone iOS keyboard pinning lab * Test rapid iOS keyboard dock reversals * Unify iOS keyboard dock presentation * Fix CLI compile break from classify() tuple access #9804 landed `FeedEventClassifier.classify(...).0` while classify() already returned the named FeedEventClassification struct, so CLI/cmux.swift no longer compiles on main (every app-host and tests-build-and-lag CI job fails with "value of type 'FeedEventClassification' has no member '0'"). Use .hookEventName, matching the other call site. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Strengthen rapid keyboard dock coverage * test(panes): drop stale MobileInjectedAttachStartupTests referencing removed API The main merge replaced MobileStartupConnectionCoordinator's connectInjectedAttach with the claim/finish lifecycle, and DogfoodAttachPreparationTests already covers that lifecycle end to end. The stale file kept the whole CmuxMobileShellUITests target from compiling, so no package UI suite could run in CI. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Scope pairing scanner guidance copy onto MobilePairingScannerSheet The caseless MobilePairingScannerGuidanceCopy enum (from #9493) trips the namespace-enum rule in scripts/lint-ios-package-conventions.sh, turning the package-conventions-lint job red for every branch that touches Packages/. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Scope keyboard dock seam measurement to transitions * Scope dock seam metric to keyboard transitions * Test whole dock during keyboard reversal * Isolate keyboard dock from terminal layout * Animate hosted keyboard dock reflows * Localize keyboard pinning lab name --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Summary\n\n- Remove the visible Iroh QR from Mac pairing because it has no separate manual workflow. Same-account automatic Iroh connection and legacy decoding stay intact.\n- Rename every visible entrypoint and destination to Tailscale Pairing, including the command palette, sidebar, configurable tab action, Settings, workspace title, and iOS guidance.\n- Localize the new English and Japanese copy while preserving existing CLI and configuration identifiers.\n\n## Validation\n\n- Tagged macOS build: ./scripts/reload.sh --tag tsqr\n- Computer Use preflight verified the command palette, sidebar, Settings row, workspace title, page heading, and single Tailscale QR.\n- Swift parse checks, localization completeness, JSON validation, and git diff checks passed.\n- Focused remote tests could not start because the fleet Mac remained below its disk floor after cleanup.\n- Hosted UI run stopped before tests when the GhosttyKit artifact download failed: https://github.com/manaflow-ai/cmux/actions/runs/30864222895\n- iOS installation is pending because Aziz was unreachable and the approved local fallback ran out of disk while unpacking Sentry.
Summary by CodeRabbit
New Features
Bug Fixes
Documentation