Skip to content

Swift 6.3 concurrency warnings: RemoteSession captured-self + a test Sendable flag (slice 1) - #6623

Closed
azooz2003-bit wants to merge 2 commits into
mainfrom
feat-swift6-concurrency-warnings
Closed

azooz2003-bit wants to merge 2 commits into
mainfrom
feat-swift6-concurrency-warnings

Conversation

@azooz2003-bit

@azooz2003-bit azooz2003-bit commented Jun 22, 2026 •

Copy link
Copy Markdown
Collaborator

Follow-up to #6603 (which moved the macOS CI gates to Xcode 26.x / Swift 6.3). The 6.3 compiler surfaces concurrency/Sendable warnings the 6.1 gate did not. None are build errors (the main app + tests are SWIFT_VERSION = 5.0 with no strict-concurrency; RemoteSession is Swift 6 mode but @unchecked Sendable/closure-capture diagnostics there are warnings). This is the first, highest-confidence, behavior-preserving slice.

⚠️ Do not merge before review/dogfood — it touches production CmuxRemoteSession runtime code.

Fixed here (11 warnings)

CmuxRemoteSession (3, production) — reference to captured var 'self' in concurrently-executing code [#SendableClosureCaptures] in three Foundation callbacks (Process.terminationHandler, the stderr readabilityHandler, the proxy-broker update callback). Each dereferenced an optional [weak self] twice (outer callback + nested queue.async). Fix: bind self once with guard let self before touching queue. [weak self] release semantics and the serial-queue synchronization contract are unchanged; the .endOfFile handler-clearing path is preserved exactly (bind only inside .data).

cmuxTests/AppDelegateRenameShortcutContextTests (8) — capture of '…' with non-Sendable type 'ShortcutNotificationFlag' in a '@Sendable' closure. Make the flag @unchecked Sendable with an NSLock-guarded Bool (the repo's existing test-box idiom). Public API (wasPosted, markPosted()) unchanged, so no call sites move; one type change clears all 8.

Remaining (catalogued for follow-up)

A full survey classified ~48 deferred warnings: 0 are RISKY hot-path (none in hitTest/forceRefresh/TabItemView), but several need per-site judgment or carry submodule/vendor ceremony, so they're intentionally not in this slice:

  • Sources/AppDelegate.swift (~20) — main-actor-isolation / non-Sendable-capture warnings, all in CMUX_UI_TEST_*-gated diagnostic recorders (recordFocusedState, attemptResolve, attemptFocus, writeUITestDiagnosticsIfNeeded, feedSidebarUITestPush…, etc.) plus one production scheduleLaunchServicesBundleRegistration @convention(block) site. Uniform fix is MainActor.assumeIsolated { … } (observers register queue: .main) or Task { @MainActor in … }. Deferred because AppDelegate is typing/focus-sensitive and warrants its own reviewed+dogfooded PR.
  • More cmuxTests sites (~13) — hoist let id = x.id.uuidString before DispatchQueue.global (TerminalControllerSocketSecurityTests), convert capture-only helpers to static nonisolated (TerminalNotification{Caller,Queue}Tests), an NSLock counter box (BrowserConfigTests), and assumeIsolated for mid-flight-assertion observers (TabManagerSessionSnapshotTests, WorkspacePullRequestSidebarTests, WorkspaceUnitTests). Mechanical but per-site; held back so this PR's test-target delta stays trivially reviewable (the worktree can't resolve the test deps locally, so each is CI-verified).
  • Sparkle SUAppcastItem(dictionary:) deprecation (1, CmuxUpdater/TestSupport) — no public non-deprecated initializer in Sparkle 2.8.1 (the designated init needs a private SPUAppcastItemStateResolver). Needs a decision: suppress at the call site or refactor the fake to drop the Sparkle-private dependency.
  • vendor/bonsplit submodule (4) — onChange(of:perform:) deprecation ×3 (trivial: drop _ in, use the zero-arg macOS-14 form) + one nonisolated bounds read (mirror the in-file nonisolated(unsafe) cache sibling). Needs a submodule branch/push + parent-pointer bump, so it's its own PR.

Verification

Push runs swift-package-tests (covers the RemoteSession change) and the app-host/tests-build-and-lag jobs (cover the test-target change) on Xcode 26.x. Expect green with the 11 warnings gone.


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


Summary by cubic

Clears 11 Swift 6.3 concurrency/Sendable warnings in CmuxRemoteSession and tests by binding self once before dispatch and making a test flag Sendable. No behavior change; keeps Swift 6.3 builds quiet.

  • Bug Fixes

    • CmuxRemoteSession: In Process.terminationHandler, stderr readabilityHandler, and the proxy-broker update callback, bind self with guard let self before queue.async; preserves [weak self] semantics and .endOfFile behavior.
    • Tests: ShortcutNotificationFlag in AppDelegateRenameShortcutContextTests is now @unchecked Sendable via an NSLock-guarded Bool; API unchanged and clears 8 Sendable-capture warnings.
    • CI: Xcode 26.x/Swift 6.3 jobs pass with these warnings removed.
  • Refactors

    • RemoteSessionCoordinator: Collapsed the proxy-broker queue.async body to one line to stay within the Swift file-length budget; no behavior change.

Written for commit f544c12. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • Refactor
    • Improved session coordination so asynchronous callbacks safely stop when the coordinator is no longer available, avoiding unnecessary queued work.
    • Tightened reverse relay and stderr handling to ensure scheduled buffer processing only occurs when the relevant session object is still alive.
  • Tests
    • Strengthened concurrency correctness in shortcut context tests by using a lock-guarded flag to track notification state reliably under Swift concurrency diagnostics.

…ion + a test flag)

First slice of the Swift 6.3 concurrency-warning cleanup surfaced by the Xcode
26.x CI bump (#6603). These are warnings, not build errors; this clears the
highest-confidence, behavior-preserving subset.

CmuxRemoteSession (production, Swift 6 language mode) — "reference to captured
var 'self' in concurrently-executing code [#SendableClosureCaptures]" in three
Foundation callbacks (Process.terminationHandler, the stderr readabilityHandler,
and the proxy-broker update callback). Each double-dereferenced an optional
`[weak self]` across the outer callback and the nested `queue.async` block. Bind
`self` once with `guard let self` before touching `queue`; the `[weak self]`
release semantics and the serial-`queue` synchronization contract are unchanged.

cmuxTests AppDelegateRenameShortcutContextTests — "capture of '…' with
non-Sendable type 'ShortcutNotificationFlag' in a '@sendable' closure" (8 sites).
Make the flag `@unchecked Sendable` with an `NSLock`-guarded Bool (the repo's
existing test-box idiom), so it can be flipped from the `@Sendable`
NotificationCenter observer and read on the main actor. The public API
(`wasPosted` get, `markPosted()`) is unchanged, so no call sites move.

Remaining warnings (AppDelegate UI-test recorders, more cmuxTests sites, the
Sparkle SUAppcastItem deprecation, and the bonsplit submodule onChange/bounds)
are catalogued in the PR description for follow-up.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 22, 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 Jun 23, 2026 12:00am
cmux-staging Building Building Preview, Comment Jun 23, 2026 12:00am

@coderabbitai

coderabbitai Bot commented Jun 22, 2026 •

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 4ae06ba6-3464-45c7-93c4-c622676e8f86

📥 Commits

Reviewing files that changed from the base of the PR and between 11e7a30 and f544c12.

📒 Files selected for processing (1)
  • Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Session/RemoteSessionCoordinator.swift

📝 Walkthrough

Walkthrough

Three coordinator closures (terminationHandler, stderr readabilityHandler, proxyBroker.acquire callback) replace optional-chaining weak-self dispatch with an explicit guard let self else { return } before self.queue.async. A test helper class gains NSLock-guarded access to its boolean flag to satisfy Swift concurrency diagnostics.

Changes

Concurrency Safety Fixes

Layer / File(s) Summary
Explicit guard let self in coordinator closures
Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Session/RemoteSessionCoordinator+ReverseRelay.swift, Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Session/RemoteSessionCoordinator.swift
The terminationHandler, stderr readabilityHandler, and proxyBroker.acquire update closures each drop the self?.queue.async optional-chaining form in favor of guard let self else { return } before calling self.queue.async.
NSLock-guarded test flag
cmuxTests/AppDelegateRenameShortcutContextTests.swift
ShortcutNotificationFlag is rewritten as a @unchecked Sendable final class; _wasPosted is guarded by an NSLock in both the wasPosted getter and the markPosted() setter.

Estimated code review effort

🎯 1 (Trivial) | ⏱️ ~5 minutes

Suggested reviewers

  • lawrencecchen

Poem

🐇 A rabbit once chained through an optional door,
But nil could slip through — a concurrency snore.
Now guard let self stands firm at the gate,
And NSLock keeps the test flag straight.
No race conditions, no optional dismay —
The queue dispatches safely, hip-hip-hooray! 🎉

🚥 Pre-merge checks | ✅ 22 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (22 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and specifically describes the main changes: Swift 6.3 concurrency warnings fixed in RemoteSession (captured-self patterns) and a test Sendable flag, marked as the first slice of a multi-part effort.
Description check ✅ Passed The description is comprehensive and detailed, covering what changed and why, though it lacks explicit sections matching the template structure and omits the standard checklist completion status.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Cmux Swift Actor Isolation ✅ Passed PR fixes 11 Swift 6.3 concurrency warnings in production CmuxRemoteSession and tests without introducing actor isolation mistakes; changes preserve queue confinement safety and add NSLock-guarded S...
Cmux Swift Blocking Runtime ✅ Passed PR introduces no new blocking synchronization in production code. Changes are guard-let-self refactoring in production (no blocking pattern change) and NSLock in test-only ShortcutNotificationFlag...
Cmux Expensive Synchronous Load ✅ Passed PR adds no expensive synchronous agent-history loads; changes are Swift concurrency refactors in callbacks and test Sendable compliance without introducing file I/O, JSON parsing, or main-actor work.
Cmux Cache Substitution Correctness ✅ Passed PR only modifies weak-self captures in Swift 6.3 concurrency callbacks and test Sendability. No cache substitution or fresh-read replacement occurs.
Cmux No Hacky Sleeps ✅ Passed PR modifies only Swift files; the runtime-no-hacky-sleeps rule explicitly excludes Swift code (stating Swift is covered by swift-blocking-runtime.md). No new sleeps, delays, or timers introduced.
Cmux Algorithmic Complexity ✅ Passed PR contains refactorings to fix Swift 6.3 warnings with no new algorithmic complexity: no loops/filters/sorts introduced, string buffer has explicit 8192-byte cap, test-file changes are excluded by...
Cmux Swift Concurrency ✅ Passed PR refactors existing queue-confinement patterns in RemoteSessionCoordinator (binding self once before queue.async) and makes test flag @unchecked Sendable; does not introduce new legacy async patt...
Cmux Swift @Concurrent ✅ Passed No async functions, @concurrent annotations, or nonisolated async work introduced. Changes dispatch synchronous work to serial queue; not heavy operations from UI isolation.
Cmux Swift File And Package Boundaries ✅ Passed All three modified files comply with swift-file-package-boundaries.md: RemoteSessionCoordinator+ReverseRelay (442 lines, coherent extension), RemoteSessionCoordinator (654 lines, existing budgeted...
Cmux Swiftpm Lockfiles ✅ Passed All cmux-owned package .gitignore files correctly exclude Package.resolved patterns; all packages with external dependencies include matching Package.resolved diffs; root Xcode Package.resolved i...
Cmux Swift Logging ✅ Passed PR adds no logging changes to production or test code. Changes are pure concurrency/Sendable fixes using guard let self binding, with no print/debugPrint/dump/NSLog additions or MainActor-coupled L...
Cmux User-Facing Error Privacy ✅ Passed These are refactoring-only changes to callback patterns for Swift concurrency fixes. No new user-facing error messages are added, no error message content is changed, and the test modification expl...
Cmux Full Internationalization ✅ Passed PR contains only refactoring of weak-self capture patterns and test infrastructure changes with no new user-facing text added. Test file changes are explicitly allowed per the internationalization...
Cmux Swiftui State Layout ✅ Passed PR modifies Foundation callbacks and test infrastructure for concurrency diagnostics, with no SwiftUI state declarations, layout changes, or render-time mutations covered by the check.
Cmux Architecture Rethink ✅ Passed PR refactors three Foundation platform callbacks with guard-let for compiler diagnostics, and adds test-only NSLock-guarded flag following repo's LockedResult idiom. All changes are small, behavior...
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed PR contains no user-visible window/panel/controller creations or material window changes. Changes address Swift 6.3 concurrency warnings in RemoteSession callbacks and a test-only Sendable flag—nei...
Cmux Source Artifacts ✅ Passed All three changed files are legitimate hand-written source and test code intentionally part of the product; no local artifacts, generated files, or problematic directories are involved.
Cmux No Test Or Debug Seam In Production Source ✅ Passed No test/debug seams added to production source. Changes in RemoteSessionCoordinator files are legitimate concurrency patterns (guard let self before queue.async), not test observation accessors. Te...

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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 feat-swift6-concurrency-warnings

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 22, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR addresses 11 Swift 6.3 concurrency warnings by binding [weak self] once with guard let self before dispatching to the serial queue in three CmuxRemoteSession Foundation callbacks, and making ShortcutNotificationFlag in tests @unchecked Sendable via an NSLock-guarded bool.

  • RemoteSessionCoordinator (2 files): In terminationHandler, the stderr readabilityHandler, and the proxy-broker callback, the guard let self is hoisted to before the queue.async dispatch. The .endOfFile handler-clearing path in the stderr handler is unaffected — it does not reference self and runs unconditionally.
  • cmuxTests/AppDelegateRenameShortcutContextTests: ShortcutNotificationFlag gains NSLock-guarded accessors and conforms to @unchecked Sendable; public API (wasPosted, markPosted()) is unchanged, matching the repo's existing test-box idiom.

Confidence Score: 5/5

All three changes are mechanical, behavior-preserving refactors with no logic added or removed; the serial-queue synchronization contract and weak-reference semantics in CmuxRemoteSession are intact.

The production CmuxRemoteSession changes only hoist an existing guard let self from inside a queue.async block to just before it — the .endOfFile handler-clearing path remains unchanged, and the serial queue still guards all locked state. The test change adds NSLock protection to a flag type that was previously not thread-safe; the public API is identical. No new control flow, no new mutable state, no new concurrency paths.

No files require special attention; both production files touch only the capture binding in existing callbacks.

Important Files Changed

Filename Overview
Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Session/RemoteSessionCoordinator+ReverseRelay.swift Moves guard let self before queue.async in both terminationHandler and readabilityHandler; .endOfFile handler-clearing path is unaffected and still runs without requiring self.
Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Session/RemoteSessionCoordinator.swift Moves guard let self before queue.async in the proxy-broker update callback; behavior-preserving one-liner refactor.
cmuxTests/AppDelegateRenameShortcutContextTests.swift Makes ShortcutNotificationFlag @unchecked Sendable via an NSLock-guarded backing bool; public API unchanged, clears 8 Sendable-capture warnings in Swift 6.3.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant OS as OS/Foundation
    participant Closure as Callback Closure
    participant Self as RemoteSessionCoordinator
    participant Queue as serial queue

    note over Closure: [weak self] capture

    OS->>Closure: fire (terminationHandler / readabilityHandler / broker update)
    Closure->>Closure: guard let self (bind once — NEW)
    alt self was deallocated
        Closure-->>OS: return (no-op)
    else self is alive
        Closure->>Queue: "queue.async { self.handle...Locked(...) }"
        Queue->>Self: handle...Locked (self strongly retained for block lifetime)
    end

    note over Closure: .endOfFile path (readabilityHandler only)
    OS->>Closure: fire with .endOfFile
    Closure->>Closure: "handle.readabilityHandler = nil (no self needed)"
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 OS as OS/Foundation
    participant Closure as Callback Closure
    participant Self as RemoteSessionCoordinator
    participant Queue as serial queue

    note over Closure: [weak self] capture

    OS->>Closure: fire (terminationHandler / readabilityHandler / broker update)
    Closure->>Closure: guard let self (bind once — NEW)
    alt self was deallocated
        Closure-->>OS: return (no-op)
    else self is alive
        Closure->>Queue: "queue.async { self.handle...Locked(...) }"
        Queue->>Self: handle...Locked (self strongly retained for block lifetime)
    end

    note over Closure: .endOfFile path (readabilityHandler only)
    OS->>Closure: fire with .endOfFile
    Closure->>Closure: "handle.readabilityHandler = nil (no self needed)"
Loading

Reviews (2): Last reviewed commit: "Keep RemoteSessionCoordinator under its ..." | Re-trigger Greptile

Comment on lines +45 to 55
var wasPosted: Bool {
lock.lock()
defer { lock.unlock() }
return _wasPosted
}

func markPosted() {
wasPosted = true
lock.lock()
defer { lock.unlock() }
_wasPosted = true
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 The manual lock()/defer { unlock() } pair in both accessors is correct but can be replaced with NSLock.withLock (available since Swift 5.8 / macOS 13), which is shorter and eliminates the risk of forgetting the defer on future edits.

Suggested change
var wasPosted: Bool {
lock.lock()
defer { lock.unlock() }
return _wasPosted
}
func markPosted() {
wasPosted = true
lock.lock()
defer { lock.unlock() }
_wasPosted = true
}
var wasPosted: Bool {
lock.withLock { _wasPosted }
}
func markPosted() {
lock.withLock { _wasPosted = true }
}

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!

The captured-self fix added one line, tipping the file to 656 over its 655
budget. Collapse the proxy-broker queue.async body to a single line so the fix
lands without growing the file (now 654).

This branch was successfully deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant