Skip to content

Remove Stack auth from mobile attach hot path - #6921

Closed
austinywang wants to merge 95 commits into
mainfrom
issue-6151-
Closed

austinywang wants to merge 95 commits into
mainfrom
issue-6151-

Conversation

@austinywang

@austinywang austinywang commented Jun 26, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #6151

Summary:

  • Use non-expired attach-ticket auth for ticket-covered mobile RPCs without fetching Stack auth.
  • Persist local-only attach ticket state for reconnect/session restoration.
  • Keep Stack auth as fallback for tokenless pairing and out-of-ticket requests.

Tests:

  • swift test --package-path Packages/iOS/CmuxMobileRPC
  • swift test --package-path Packages/iOS/CmuxMobilePairedMac
  • swift test --package-path Packages/iOS/CmuxMobileShell

Local note: swift test --package-path ios/cmuxPackage could not run in this checkout because GhosttyKit.xcframework is missing its binary artifact.


View with Codesmith Autofix with Codesmith
Need help on this PR? Tag /codesmith with what you need. Autofix is disabled.


Summary by cubic

Speeds up mobile launches and recovery by using scoped attach‑ticket auth on same‑account hot paths and keeping Stack Auth off the critical path; fixes #6151. Adds durable token persistence and strict scope/fallback rules with clear host-side errors.

  • New Features

    • Attach‑token auth: Use tickets for covered calls (incl. mobile.events.subscribe, terminal input/mouse/group ops); workspace.list accepts Mac‑wide or workspace tickets; workspace.create requires Mac‑wide; require workspace scope for terminal.list; terminal‑scoped tickets cannot mutate workspaces. Only use the attach‑token path on same‑account pairs; skip Stack waits when covered; retry with Stack only for legacy unauthorized variants or expired/stale tokens (no fallback on invalid_attach_token, accountless, or scope errors); surface attachTicketExpired. QR pairing carries no bearer token. Host rejects unknown tickets early with invalid_attach_token. Secondary clients reuse durable tickets and redact auth errors.
  • Refactors

    • Split the paired‑Mac store into focused files with migrations/SQLite helpers and a keychain secret store; keep secrets out of Codable; route updates prefer exact team rows. Networking/runtime/UI hardening: IPv6 literal preferred on device; add MobileShellComposite+SecondaryClients; move and guard compact glass helpers via CmuxMobileSupport/MobileGlassEffectContainer with #if compiler(>=6.2); add @MainActor where needed; safer gesture teardown and notification‑status isolation; terminal output‑queue surface handle; instance hardware‑key resolver.

Written for commit 19a14c6. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features

    • Paired Mac reconnects can now reuse a persisted attach token (when still valid), improving reconnect smoothness.
    • Attach-token and expiry are retained per paired Mac and survive store reopen and route refreshes.
  • Bug Fixes

    • Authentication selection for RPC requests is more consistent, avoiding unnecessary Stack access-token usage.
    • Expired or unauthorized persisted attach tokens now trigger the correct durable fallback behavior.
  • Tests

    • Added coverage for attach-token persistence across reopen/refresh and reconnect fallback, plus expanded auth-selection/workspace-related scenarios.

@vercel

vercel Bot commented Jun 26, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
cmux Ready Ready Preview, Comment Jul 5, 2026 3:53am
cmux-staging Building Building Preview, Comment Jul 5, 2026 3:53am

@coderabbitai

coderabbitai Bot commented Jun 26, 2026 •

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Attach-token state now persists through paired-Mac storage, reconnect, and RPC auth selection. SQLite migrations add the new columns, wrappers forward them, and tests assert the updated attach-token and Stack-auth behavior. A separate UI test and a zsh PATH test were also adjusted.

Changes

Durable attach-token flow

Layer / File(s) Summary
Paired-mac attach-token contract
Packages/iOS/CmuxMobilePairedMac/Sources/CmuxMobilePairedMac/MobilePairedMac.swift, Packages/iOS/CmuxMobilePairedMac/Sources/CmuxMobilePairedMac/MobilePairedMacStoring.swift, Packages/Shared/CmuxSyncStore/Tests/CmuxSyncStoreTests/CmuxSyncStoreTests.swift, Packages/iOS/CmuxMobilePairedMac/Tests/CmuxMobilePairedMacTests/MobilePairedMacCodingTests.swift, Packages/iOS/CmuxMobilePairedMac/Sources/CmuxMobilePairedMac/MobilePairedMacStoreMacRow.swift, Packages/iOS/CmuxMobilePairedMac/Sources/CmuxMobilePairedMac/MobilePairedMacStoreRouteKey.swift
MobilePairedMac stores attach-token fields, MobilePairedMacStoring.upsert accepts them, and the fake paired-store stub and coding test match the updated model contract.
SQLite schema and row writes
Packages/iOS/CmuxMobilePairedMac/Sources/CmuxMobilePairedMac/MobilePairedMacStore*.swift, Packages/iOS/CmuxMobilePairedMac/Tests/CmuxMobilePairedMacTests/MobilePairedMacStoreTests.swift
MobilePairedMacStore opens the database, bumps schema version 5, migrates the new columns, defines row helpers, and writes attach-token values through upserts and scope moves.
Wrapper forwarding and test doubles
Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/..., Packages/iOS/CmuxMobileShell/Tests/CmuxMobileShellTests/...
Wrapper upsert paths accept and forward attach-token fields, and the shell test doubles copy them into stored records while updating routes.
Durable reconnect wiring
Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite.swift, Packages/iOS/CmuxMobileShell/Tests/CmuxMobileShellTests/MobileShellCompositeDurableTicketFallbackTests.swift
MobileShellComposite persists durable attach tokens, reuses them for reconnect and secondary clients, and refreshes routes on tracked tasks before reconnecting.

RPC attach-token auth

Layer / File(s) Summary
RPC attach-token selection
Packages/iOS/CmuxMobileRPC/Sources/CmuxMobileRPC/..., Packages/iOS/CmuxMobileRPC/Tests/CmuxMobileRPCTests/...
requestDataWithAuth rejects expired attach tickets at use time, Stack fallback is adjusted for specific methods, and the RPC tests assert the updated attach-token behavior.
Ticket semantics and host authorization
Packages/Shared/CMUXMobileCore/Sources/CMUXMobileCore/..., Sources/Mobile/MobileHostService.swift, Sources/Mobile/MobileAttachTicketStore.swift, cmuxTests/MobileHostAuthorizationTests.swift, ios/cmuxPackage/Tests/cmuxFeatureTests/cmuxFeatureTests.swift
Ticket and QR comments are reworded, MobileHostService checks ticket authorization for token-only requests, and host tests cover covered and out-of-scope ticket access.

UI focus regression guard

Layer / File(s) Summary
Focus layout guard
cmuxTests/WorkspaceUnitTests.swift
The reparent-focus regression test sets the hosted surface frame before focus assertions.

zsh PATH diagnostics

Layer / File(s) Summary
zsh PATH validation
tests/test_claude_wrapper_user_binary_resolution.py
The zsh mismatch branch now checks PATH ordering and shim placement before skipping the generic failure.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • manaflow-ai/cmux#5543: Both PRs modify MobileShellComposite’s stored-Mac reconnect/persist flow.
  • manaflow-ai/cmux#6772: The IOSBuildScopedPairedMacStore upsert forwarding changes are directly related to the same decorator path.
  • manaflow-ai/cmux#5626: Shares the stored-Mac reconnect and route-refresh path that this PR extends.

Suggested reviewers

  • lawrencecchen

Poem

I tucked an attach token under my paw,
And hopped through reconnects without a flaw.
SQLite kept the trail,
While old Stack checks went stale,
And zsh sniffed the PATH with a straw. 🐇


Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (3 errors, 2 warnings)

Check name Status Explanation Resolution
Cmux Swift Blocking Runtime ❌ Error FAIL: MobileShellComposite.swift adds a production ContinuousClock().sleep restoring-gate deadline, which is sleep-based timing sync in non-test code. Replace the deadline sleep with a cancellable state transition/callback or explicit completion signal from the connect flow; keep timing only in test scaffolding.
Cmux Swift @Concurrent ❌ Error New @MainActor helpers (makeSecondaryClient, fetchSecondaryWorkspaces) perform network RPCs from UI isolation with no hop/@Concurrent boundary. Move ticket minting/workspace fetch into a nonisolated or detached helper (or actor) and keep the MainActor methods to state reads only.
Cmux Swift Logging ❌ Error Secondary-client runtime logs stringify auth/RPC errors with privacy: .public, exposing upstream details in production. Redact those error interpolations as .private (or remove them); also make any file-scoped Logger conform to the repo’s nonisolated private let shape if kept.
Out of Scope Changes check ⚠️ Warning Some changes appear unrelated to #6151, including the Python wrapper PATH test and Ghostty layout safeguard, which are not needed for mobile attach auth. Move unrelated CI/test tweaks to a separate PR or explain why they are required for the attach/reconnect fix.
Docstring Coverage ⚠️ Warning Docstring coverage is 19.16% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (20 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes match #6151: attach/reconnect use local attach tickets, durable state is persisted, security boundaries are tested, and latency is covered by no-wait tests.
Cmux Swift Actor Isolation ✅ Passed No new actor-isolation debt: added models are value types or actors, and the new shell/client helpers stay on @MainActor or use Sendable collaborators.
Cmux Browser Automation Off-Main ✅ Passed Waiting browser commands remain in socketWorkerMethods and route via socket-worker handlers; main-switch cases are direct UI commands, and policy tests cover the mapping.
Cmux Expensive Synchronous Load ✅ Passed Changed files are attach-ticket/store/RPC code; no added or moved agent-history loaders or heavy sync parsing on MainActor/interactive paths.
Cmux Cache Substitution Correctness ✅ Passed The diff persists attach-token state, but reconnect reads it from SQLite and durableAttachTicket rejects empty/expired tokens before use; no fresh read was replaced by an unchecked cache.
Cmux No Hacky Sleeps ✅ Passed PR changes are Swift/test-only; no changed non-Swift runtime files show fixed sleeps/delays/timers that would trigger the rule.
Cmux Algorithmic Complexity ✅ Passed Added lookups remain linear or dictionary-backed; no new nested scans, per-target batch rescans, or in-memory joins on scalable collections were introduced.
Cmux Swift Concurrency ✅ Passed PASS: diff adds async/await APIs and stored/cancelled Tasks, with no new DispatchQueue, Combine, or completion-handler async patterns in cmux-owned production code.
Cmux Swift File And Package Boundaries ✅ Passed No boundary violation: new prod files are focused and under 400 lines; the 6.9k-line app file grew by 246 lines, below the >250 threshold.
Cmux Swiftpm Lockfiles ✅ Passed Repo policy check reports OK, and the commit includes matching Package.resolved files for package/Xcode dependency edits.
Cmux User-Facing Error Privacy ✅ Passed User-facing errors and RPC error bodies stay generic; the only raw String(describing:) exposure is in private logs, outside this rule's scope.
Cmux Full Internationalization ✅ Passed Touched production files only change auth logic/comments; no new user-facing strings or locale resources were added.
Cmux Swiftui State Layout ✅ Passed Touched sources add model/RPC/store code only; scans found no new ObservableObject/@published, GeometryReader, lazy list-row store refs, or render-time state writes.
Cmux Architecture Rethink ✅ Passed Local attach-token state and route-refresh bookkeeping stay on existing store/composite owners; no new sleep/polling/observer/lock pattern was introduced.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed PR only changes RPC/store/test code; no user-visible NSWindow/NSPanel/WindowGroup/NSWindowController additions or cmuxAuxiliaryWindowIdentifiers edits.
Cmux Source Artifacts ✅ Passed Changed paths are source/tests/docs only; none are logs, temp dirs, caches, build output, or other checked-in artifacts under the rule.
Cmux No Test Or Debug Seam In Production Source ✅ Passed No new test/debug-only seam was added in production Sources; the new helpers are used by production call sites, and the only test-visible state is internal for @testable use.
Cmux No Ambient Global State ✅ Passed The new helpers are methods/structs inside owning types or extensions; I found no new top-level mutable vars or singleton/shared/default state in the touched Swift sources.
Title check ✅ Passed The title clearly matches the main change: removing Stack auth from the mobile attach hot path.
Description check ✅ Passed The description includes a clear summary and testing section and is mostly complete despite missing some optional template sections.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issue-6151-

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@greptile-apps

greptile-apps Bot commented Jun 26, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR removes Stack Auth from the mobile attach hot path by using non-expired attach-ticket credentials directly for ticket-covered RPCs, reserving Stack auth as a fallback for tokenless pairing, out-of-scope requests, and unauthorized rejections. Durable tokens are now persisted to the iOS keychain (schema v6 adds scope columns) and survive reconnect and session restoration without a Stack network round-trip.

  • Auth logic overhaul: MobileHostService.authorizationError now accepts a valid attach token as the primary credential; Stack auth is only verified when the token is absent, mismatched, expired, or the request is outside the ticket's scope. Client-side requestNeedsStackAuthFallback is aligned to the same rules, with structured RPC error code checks replacing all free-form message parsing.
  • Durable token persistence: MobilePairedMacStore schema advances to v6 (attach-token scope columns). The upsert path saves the bearer secret to keychain before the SQLite transaction, reads back the previous value, and restores it on SQLite failure. clearLegacySQLiteAttachTokenSecrets scrubs any plaintext tokens left by the v5 schema.
  • Reconnect retry logic: connectManualHost and the secondary-client builder both catch ticket-rejected errors and fall back to a freshly minted manual ticket; shouldRetryDurableAttachTicket allows retry on unauthorized/invalid_attach_token/auth-class codes but explicitly excludes account_mismatch and scope errors.

Confidence Score: 5/5

Safe to merge. The auth logic is carefully layered: structured error codes gate every retry decision, client and host scope checks are aligned, and the durable token path has an explicit fallback to Stack auth on any rejection.

The client-host auth contract is internally consistent: every RPC code the host can produce (unauthorized, forbidden, invalid_attach_token, account_mismatch) is handled by a distinct, deliberate branch on the client with no free-form string matching. Keychain/SQLite split atomicity is handled by read-before-write with rollback of the keychain value on SQLite failure. Test coverage spans auth selection, scope checks, durable fallback, workspace mutation auth, and store attach-token persistence.

No files require special attention.

Important Files Changed

Filename Overview
Sources/Mobile/MobileHostService.swift Core auth gate rewritten: attach-token is now a first-class credential for ticket-covered RPCs; Stack auth is the fallback. Structured error codes (forbidden, invalid_attach_token, account_mismatch) replace all free-form message checks.
Sources/Mobile/MobileAttachTicketStore.swift Adds size-bounded ticket cache (defaultMaximumStoredTickets=256) with LRU eviction via enforceRecordLimit. validAuthorization lazily removes the specific expired record on lookup.
Packages/iOS/CmuxMobileRPC/Sources/CmuxMobileRPC/MobileCoreRPCClient.swift requestDataWithAuth now sends attach_token as the primary credential; shouldRetryAttachTokenAuthorizationFailureWithStackAuth triggers fallback only for structured rpcError(unauthorized).
Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite.swift Adds durableAttachTicket(for:) for fast reconnect; Swift 6.1 deinit fallback uses nonisolated(unsafe) snapshots; registryRouteRefreshTasks are tracked and cancelled on sign-out/team-switch.
Packages/iOS/CmuxMobilePairedMac/Sources/CmuxMobilePairedMac/MobilePairedMacStore.swift Schema bumped to v6; upsert performs read-before-write keychain save with rollback on SQLite failure.
Packages/iOS/CmuxMobilePairedMac/Sources/CmuxMobilePairedMac/MobilePairedMacStore+Migrations.swift New file: migrations v1-v6 in single transactions; clearLegacySQLiteAttachTokenSecrets NULLs legacy tokens and VACUUMs.
Packages/iOS/CmuxMobilePairedMac/Sources/CmuxMobilePairedMac/MobilePairedMacKeychainAttachTokenSecretStore.swift New file: keychain store with kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly; idempotent write with duplicate-item retry.
Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite+SecondaryClients.swift New file: makeSecondaryClient accepts allowDurableTicket; fetchSecondaryWorkspacesThrowing split so callers can retry with allowDurableTicket=false.
Packages/iOS/CmuxMobileRPC/Sources/CmuxMobileRPC/MobileCoreRPCAttachTicketCoverage.swift Adds ticketCoversMacWideRequest, ticketCoversWorkspaceCreateRequest, ticketCoversTerminalCreateRequest; terminal-scoped tickets now fail ticketCoversWorkspaceRequest.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant iOS as iOS Client
    participant RPC as MobileCoreRPCClient
    participant Host as MobileHostService
    participant Ticket as MobileAttachTicketStore

    Note over iOS,Ticket: Happy path — durable attach token covers request
    iOS->>RPC: sendRequest(workspace.list)
    RPC->>RPC: requestNeedsStackAuthFallback? false
    RPC->>Host: "attach_token=tok, workspace.list"
    Host->>Ticket: validAuthorization(tok)
    Ticket-->>Host: authorization
    Host->>Host: ticketMatchesCurrentMacAccount → true
    Host-->>iOS: OK

    Note over iOS,Ticket: Fallback — token unknown/expired on host
    iOS->>RPC: sendRequest(terminal.input)
    RPC->>Host: "attach_token=stale, terminal.input"
    Host->>Ticket: validAuthorization(stale)
    Ticket-->>Host: nil
    Host-->>RPC: rpcError(unauthorized)
    RPC->>RPC: shouldRetryWithStackAuth → true
    RPC->>Host: "stack_access_token=…, terminal.input"
    Host-->>iOS: OK

    Note over iOS,Ticket: Reconnect — stale durable ticket rejected
    iOS->>RPC: connectManualHost(durableTicket)
    Host-->>RPC: rpcError(unauthorized)
    RPC->>iOS: shouldRetryDurableAttachTicket → true
    iOS->>Host: manualHostTicket via Stack auth
    iOS->>RPC: connectManualHost(freshTicket)
    Host-->>iOS: OK
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant iOS as iOS Client
    participant RPC as MobileCoreRPCClient
    participant Host as MobileHostService
    participant Ticket as MobileAttachTicketStore

    Note over iOS,Ticket: Happy path — durable attach token covers request
    iOS->>RPC: sendRequest(workspace.list)
    RPC->>RPC: requestNeedsStackAuthFallback? false
    RPC->>Host: "attach_token=tok, workspace.list"
    Host->>Ticket: validAuthorization(tok)
    Ticket-->>Host: authorization
    Host->>Host: ticketMatchesCurrentMacAccount → true
    Host-->>iOS: OK

    Note over iOS,Ticket: Fallback — token unknown/expired on host
    iOS->>RPC: sendRequest(terminal.input)
    RPC->>Host: "attach_token=stale, terminal.input"
    Host->>Ticket: validAuthorization(stale)
    Ticket-->>Host: nil
    Host-->>RPC: rpcError(unauthorized)
    RPC->>RPC: shouldRetryWithStackAuth → true
    RPC->>Host: "stack_access_token=…, terminal.input"
    Host-->>iOS: OK

    Note over iOS,Ticket: Reconnect — stale durable ticket rejected
    iOS->>RPC: connectManualHost(durableTicket)
    Host-->>RPC: rpcError(unauthorized)
    RPC->>iOS: shouldRetryDurableAttachTicket → true
    iOS->>Host: manualHostTicket via Stack auth
    iOS->>RPC: connectManualHost(freshTicket)
    Host-->>iOS: OK
Loading

Reviews (37): Last reviewed commit: "Guard compact mobile glass helpers" | Re-trigger Greptile

Comment thread Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite.swift Outdated
# Conflicts:
#	.github/swift-file-length-budget.tsv
@blacksmith-sh

This comment has been minimized.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
Packages/iOS/CmuxMobilePairedMac/Sources/CmuxMobilePairedMac/MobilePairedMacStoring.swift (1)

15-28: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Document nil as “preserve existing ticket” in the protocol contract.

The overload at Lines 95-116 relies on attachToken: nil / attachTokenExpiresAt: nil not clearing existing durable ticket state. Put that invariant on the required parameters so future conformers do not erase reconnect credentials during route refreshes.

Suggested contract clarification
-    ///   - attachToken: Local-only attach ticket secret for fast reconnect.
-    ///   - attachTokenExpiresAt: Expiration time for `attachToken`.
+    ///   - attachToken: Local-only attach ticket secret for fast reconnect.
+    ///     `nil` preserves the existing local ticket when updating an existing row.
+    ///   - attachTokenExpiresAt: Expiration time for `attachToken`. When
+    ///     `attachToken` is `nil`, implementations must preserve the existing
+    ///     expiration on updates.
🤖 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/CmuxMobilePairedMac/Sources/CmuxMobilePairedMac/MobilePairedMacStoring.swift`
around lines 15 - 28, Document the nil-handling contract on
MobilePairedMacStoring.upsert so future conformers know that attachToken: nil
and attachTokenExpiresAt: nil must preserve the existing durable reconnect
ticket instead of clearing it. Update the protocol comment near upsert and keep
the behavior consistent in the overload that refreshes routes, making the
invariant explicit alongside the attachToken and attachTokenExpiresAt
parameters.
🤖 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/CmuxMobilePairedMac/Sources/CmuxMobilePairedMac/MobilePairedMacStore.swift`:
- Around line 15-17: The MobilePairedMacStore actor has grown beyond the
800-line production Swift limit, so split out persistence responsibilities now
before adding more logic. Extract schema migration code and row mapping/SQL
helper methods from MobilePairedMacStore into separate types or files, keeping
the actor focused on orchestration while preserving the current
currentSchemaVersion behavior and public API.

In `@tests/test_claude_wrapper_user_binary_resolution.py`:
- Around line 222-235: The zsh branch in the PATH resolution test is no longer
enforcing the empty-component contract, so regressions that drop or move
`::...:` segments can slip through. Update the
`test_claude_wrapper_user_binary_resolution` assertions in the zsh-specific
branch to explicitly verify the empty PATH entries are still present in the
correct positions/count, using the existing `shell_name`, `entries`, and
`expected_path` checks. If zsh is intentionally allowed to weaken this behavior,
split or rename the test to match the new contract instead of keeping the
stronger test name.

---

Outside diff comments:
In
`@Packages/iOS/CmuxMobilePairedMac/Sources/CmuxMobilePairedMac/MobilePairedMacStoring.swift`:
- Around line 15-28: Document the nil-handling contract on
MobilePairedMacStoring.upsert so future conformers know that attachToken: nil
and attachTokenExpiresAt: nil must preserve the existing durable reconnect
ticket instead of clearing it. Update the protocol comment near upsert and keep
the behavior consistent in the overload that refreshes routes, making the
invariant explicit alongside the attachToken and attachTokenExpiresAt
parameters.
🪄 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

Run ID: 29342897-3084-4e32-89c0-c837cbbac36a

📥 Commits

Reviewing files that changed from the base of the PR and between e20f8ff and d4bcf71.

⛔ Files ignored due to path filters (1)
  • .github/swift-file-length-budget.tsv is excluded by !**/*.tsv
📒 Files selected for processing (18)
  • Packages/Shared/CmuxSyncStore/Tests/CmuxSyncStoreTests/CmuxSyncStoreTests.swift
  • Packages/iOS/CmuxMobilePairedMac/Sources/CmuxMobilePairedMac/MobilePairedMac.swift
  • Packages/iOS/CmuxMobilePairedMac/Sources/CmuxMobilePairedMac/MobilePairedMacStore.swift
  • Packages/iOS/CmuxMobilePairedMac/Sources/CmuxMobilePairedMac/MobilePairedMacStoring.swift
  • Packages/iOS/CmuxMobilePairedMac/Tests/CmuxMobilePairedMacTests/MobilePairedMacStoreTests.swift
  • Packages/iOS/CmuxMobileRPC/Sources/CmuxMobileRPC/MobileCoreRPCClient.swift
  • Packages/iOS/CmuxMobileRPC/Sources/CmuxMobileRPC/MobileSyncRuntime.swift
  • Packages/iOS/CmuxMobileRPC/Tests/CmuxMobileRPCTests/MobileCoreRPCClientTests.swift
  • Packages/iOS/CmuxMobileRPC/Tests/CmuxMobileRPCTests/MobileCoreRPCTokenTimeoutTests.swift
  • Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/BackingUpPairedMacStore.swift
  • Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/DeleteComputersVerifierPairedMacStore.swift
  • Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/IOSBuildScopedPairedMacStore.swift
  • Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite.swift
  • Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/TeamScopedPairedMacStore.swift
  • Packages/iOS/CmuxMobileShell/Tests/CmuxMobileShellTests/DelayedTeamPairedMacStore.swift
  • Packages/iOS/CmuxMobileShell/Tests/CmuxMobileShellTests/GatedUpsertStore.swift
  • ios/cmuxPackage/Tests/cmuxFeatureTests/cmuxFeatureTests.swift
  • tests/test_claude_wrapper_user_binary_resolution.py

Comment thread tests/test_claude_wrapper_user_binary_resolution.py

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🤖 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/CmuxMobilePairedMac/Sources/CmuxMobilePairedMac/MobilePairedMacStore.swift`:
- Around line 16-20: The SQLite handle in MobilePairedMacStore should remain
hidden behind the actor-isolated API; restore the access restriction on the db
property so only the store’s own methods can touch the unsafe OpaquePointer.
Update the declaration in MobilePairedMacStore to make db private again, while
leaving the deinit cleanup and actor-isolated access pattern unchanged.

In
`@Packages/iOS/CmuxMobilePairedMac/Sources/CmuxMobilePairedMac/MobilePairedMacStore`+Rows.swift:
- Around line 189-220: The SQLite row-loading loops in MobilePairedMacStore+Rows
are treating any non-`SQLITE_ROW` result from sqlite3_step as a clean ավարտ,
which can return partial data after an error. Update the row iteration logic in
the Mac and route fetch helpers to explicitly handle sqlite3_step failures (e.g.
SQLITE_BUSY, SQLITE_ERROR, and other non-ROW/non-DONE cases) by throwing instead
of falling through. Keep the existing row mapping code intact, but ensure the
functions that build the arrays surface the SQLite error rather than returning
incomplete results.

In
`@Packages/iOS/CmuxMobilePairedMac/Sources/CmuxMobilePairedMac/MobilePairedMacStoreLog.swift`:
- Line 3: The file-scoped PairedMacStore logger is currently exposed and subject
to default actor isolation; update the `pairedMacStoreLog` declaration in
`MobilePairedMacStoreLog.swift` to be a file-private, nonisolated constant so it
stays out of the module API and matches the logging guideline. Keep the same
`Logger(subsystem:category:)` setup, but change the declaration to use
`nonisolated private let` for `pairedMacStoreLog`.

In
`@Packages/iOS/CmuxMobilePairedMac/Sources/CmuxMobilePairedMac/MobilePairedMacStoreMacRow.swift`:
- Around line 3-17: Mark the MobilePairedMacStoreMacRow persistence model as
nonisolated so it does not inherit default actor isolation in Swift 6. Update
the struct declaration for MobilePairedMacStoreMacRow to explicitly opt out of
actor isolation since it is a pure value model with only stored properties and
no UI binding. Keep the model otherwise unchanged, and apply the annotation
directly on the struct definition.

In
`@Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite.swift`:
- Line 1004: The cancelled registry refresh path in MobileShellComposite still
allows an in-flight task to reach pairedMacStore.upsert after sign-out or team
change. Update the registry refresh flow around cancelRegistryRouteRefresh and
the related refresh/task methods at the other referenced call sites to either
keep task handles until they are drained or add a final current-generation/scope
check immediately before the store mutation so stale writes are blocked.
- Around line 1774-1782: The background registry refresh in MobileShellComposite
is incorrectly taking ownership of the active Mac state by calling
pairedMacStore.upsert with markActive set to true. Update this flow to
route-only behavior by using the store’s route-update path or by
preserving/re-reading the existing active flag inside the pairedMacStore
transaction, so the refresh updates routes without changing which Mac is active.
Keep the fix centered on the pairedMacStore.upsert call site and any related
store method that owns active-state persistence.
🪄 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

Run ID: 013832bf-e681-4345-84d4-01257ad99817

📥 Commits

Reviewing files that changed from the base of the PR and between d4bcf71 and 40b54d0.

⛔ Files ignored due to path filters (1)
  • .github/swift-file-length-budget.tsv is excluded by !**/*.tsv
📒 Files selected for processing (13)
  • Packages/iOS/CmuxMobilePairedMac/Sources/CmuxMobilePairedMac/MobilePairedMac.swift
  • Packages/iOS/CmuxMobilePairedMac/Sources/CmuxMobilePairedMac/MobilePairedMacStore+Connection.swift
  • Packages/iOS/CmuxMobilePairedMac/Sources/CmuxMobilePairedMac/MobilePairedMacStore+Migrations.swift
  • Packages/iOS/CmuxMobilePairedMac/Sources/CmuxMobilePairedMac/MobilePairedMacStore+Rows.swift
  • Packages/iOS/CmuxMobilePairedMac/Sources/CmuxMobilePairedMac/MobilePairedMacStore+SQLite.swift
  • Packages/iOS/CmuxMobilePairedMac/Sources/CmuxMobilePairedMac/MobilePairedMacStore.swift
  • Packages/iOS/CmuxMobilePairedMac/Sources/CmuxMobilePairedMac/MobilePairedMacStoreLog.swift
  • Packages/iOS/CmuxMobilePairedMac/Sources/CmuxMobilePairedMac/MobilePairedMacStoreMacRow.swift
  • Packages/iOS/CmuxMobilePairedMac/Sources/CmuxMobilePairedMac/MobilePairedMacStoring.swift
  • Packages/iOS/CmuxMobilePairedMac/Tests/CmuxMobilePairedMacTests/MobilePairedMacCodingTests.swift
  • Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite.swift
  • cmuxTests/WorkspaceUnitTests.swift
  • tests/test_claude_wrapper_user_binary_resolution.py

Comment thread Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite.swift Outdated
Comment thread Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite.swift Outdated
Comment thread Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite.swift Outdated
# Conflicts:
#	.github/swift-file-length-budget.tsv
#	Packages/iOS/CmuxAgentChatUI/Sources/CmuxAgentChatUI/Screen/ChatScrollEdgeCoordinator.swift
#	Packages/iOS/CmuxAgentChatUI/Sources/CmuxAgentChatUI/Transcript/ChatTranscriptTableView.swift
#	Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite.swift
#	Packages/iOS/CmuxMobileTerminal/Sources/CmuxMobileTerminal/GhosttySurfaceView.swift

This branch was successfully deployed

1 active deployment
Preview – cmux — 19a14c6c Deployed Jul 5, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

stale-revisit Closed after 30+ days without activity; preserved for possible revisit or reopening.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Remove Stack auth from mobile attach hot path

3 participants