Skip to content

Fix Cmd+N nightly crash: avoid local Workspace refs in ARC hotpath - #2204

Merged
austinywang merged 5 commits into
mainfrom
issue-2180-cmd-n-retain-crash
Mar 26, 2026
Merged

austinywang merged 5 commits into
mainfrom
issue-2180-cmd-n-retain-crash

Conversation

@austinywang

@austinywang austinywang commented Mar 26, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Extract preferredWorkingDirectory and inheritedTerminalFontPoints through self (always retained) before capturing locals, instead of navigating workspace → panel → surface through local variables inside the snapshot
  • Xcode 16.4's -O ARC optimizer aggressively elides retains on local Workspace references through inlined call chains, causing use-after-free on every Cmd+N in CI-built nightlies
  • The snapshot is now purely value-typed — no Workspace references held in locals that the optimizer can release prematurely
  • Removes withExtendedLifetime wrapper which was insufficient against the inlining optimizer

Context

This crash reproduced on every Cmd+N in the CI nightly (Xcode 16.4, macOS 15) but not in local builds (Xcode 26.4, macOS 26). The root cause was c1998e34 introducing workspaceCreationSnapshot() which held local Workspace refs across deep call chains.

Test plan

  • Updated regression test for mid-creation workspace close
  • CI nightly build on Xcode 16.4 — Cmd+N should no longer crash

🤖 Generated with Claude Code


Summary by cubic

Fixes Cmd+N crashes in CI nightlies by removing local Workspace refs from the creation path and making the snapshot value-only. Addresses issue 2180; config is pulled via self to avoid Xcode 16.4 ARC retain elision.

  • Bug Fixes
    • Pre-extract preferred working directory and inherited terminal font points via self before capturing locals; no local Workspace refs in the hot path.
    • Replace workspaceCreationSnapshot with workspaceCreationSnapshotLite(...) that takes value params; remove withExtendedLifetime as ineffective under inlining.
    • Update regression test to ensure Cmd+N survives a mid-creation close with correct tab order and selection; remove brittle lifetime assertion.
    • Preserve the hardened addWorkspace implementation after updating to the latest main.

Written for commit 71d89b2. Summary will update on new commits.

Summary by CodeRabbit

  • Refactor

    • Improved workspace snapshot logic for better efficiency.
  • Tests

    • Added test coverage for workspace closure during creation operations.

austinywang and others added 4 commits March 25, 2026 18:53
The snapshot approach (c1998e3) navigated workspace → panel → surface
through local variables. Xcode 16.4's -O ARC optimizer aggressively
elides retains on these locals through inlined call chains, causing
use-after-free on every Cmd+N in CI-built nightlies.

Fix: extract preferredWorkingDirectory and inheritedTerminalFontPoints
through self (always retained) BEFORE capturing locals. The snapshot
is now purely value-typed with no Workspace references held in locals.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@vercel

vercel Bot commented Mar 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 Mar 26, 2026 9:19pm

@coderabbitai

coderabbitai Bot commented Mar 26, 2026 •

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

The workspace creation snapshot logic in TabManager was refactored to pre-extract preferredWorkingDirectory and inheritedTerminalFontPoints before building snapshots, introducing a new lightweight workspaceCreationSnapshotLite(...) that operates on value-type inputs rather than live workspace references. A new test validates snapshot behavior when workspaces are closed mid-creation.

Changes

Cohort / File(s) Summary
Workspace Snapshot Refactoring
Sources/TabManager.swift
Pre-extracts preferredWorkingDirectory and inheritedTerminalFontPoints in addWorkspace(...); introduces workspaceCreationSnapshotLite(...) accepting value-type inputs; adds inheritedTerminalFontPointsForNewWorkspace() helper; original workspaceCreationSnapshot() now delegates to the lite version.
Snapshot Lifecycle Testing
cmuxTests/WorkspaceUnitTests.swift
Added testAddWorkspaceSurvivesMidCreationClose to verify workspace insertion behavior when a workspace is closed during the creation snapshot lifecycle.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 Snapshots captured, values blessed,
Pre-extracted from the test,
No live reads during creation's dance—
Safe, stable, sealed at first glance! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Description check ❓ Inconclusive PR description covers the what and why comprehensively, but testing section lacks detail on manual verification and the Demo Video and Checklist sections are incomplete. Complete the Testing section with specific manual verification steps, provide a Demo Video link if applicable, and ensure the Checklist section is fully addressed before merge.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately captures the main fix: addressing a Cmd+N crash by removing local Workspace references from the ARC hotpath, which aligns with the core technical change.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ 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 issue-2180-cmd-n-retain-crash

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 and usage tips.

@cubic-dev-ai cubic-dev-ai 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.

No issues found across 2 files

@greptile-apps

greptile-apps Bot commented Mar 26, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a use-after-free crash on every Cmd+N in CI-built nightlies (Xcode 16.4, macOS 15) by eliminating local Workspace references from the addWorkspace() hotpath. The Xcode 16.x -O ARC optimizer was aggressively eliding retains on standalone local Workspace variables across inlined call chains (workspace → panel → surface → C pointer), causing premature deallocation.\n\nKey changes:\n- addWorkspace() now calls preferredWorkingDirectoryForNewTab() and inheritedTerminalFontPointsForNewWorkspace() through self (always retained) before capturing tabs/selectedTabId as value copies — so no local Workspace references are held when navigating into panels/surfaces.\n- A new workspaceCreationSnapshotLite(currentTabs:currentSelectedTabId:preferredWorkingDirectory:inheritedTerminalFontPoints:) accepts only already-extracted value-typed data, removing the last selectedWorkspace local from the snapshot construction path.\n- workspaceCreationSnapshot() (used by newTabInsertIndex) is preserved but also updated to use the no-arg self-routed accessors instead of a local selectedWorkspace.\n- A new no-arg inheritedTerminalFontPointsForNewWorkspace() overload is added to mirror the existing preferredWorkingDirectoryForNewTab() pattern.\n- A new unit test covers the behavioral case of closing a non-selected workspace mid-creation and verifies the insertion order is correct.\n\nThe fix is targeted and correct. The snapshot is now purely value-typed from the caller's perspective, the withExtendedLifetime wrapper that was insufficient against the inlining optimizer has been removed, and all changes are confined to the workspace-creation hotpath with no impact on other code paths.

Confidence Score: 5/5

Safe to merge — targeted ARC fix with no behavioral regressions and new behavioral test coverage.

The fix is logically sound: extracting values through retained self before capturing locals is the correct way to avoid ARC elision on standalone Workspace references. All call sites use synchronous @MainActor code, so there are no ordering or concurrency concerns introduced by the reordering. The old snapshot function is preserved for other callers and is also updated to be safe. The one P2 comment (two-commit policy) is a process note, not a functional issue, and does not block merge.

No files require special attention. Both changed files are clean.

Important Files Changed

Filename Overview
Sources/TabManager.swift Refactors addWorkspace() to extract Workspace-dependent values through self before building the snapshot, eliminating local Workspace references vulnerable to Xcode 16.x ARC optimizations. Adds workspaceCreationSnapshotLite() for the value-typed hotpath and a no-arg inheritedTerminalFontPointsForNewWorkspace() overload; preserves workspaceCreationSnapshot() for non-critical callers.
cmuxTests/WorkspaceUnitTests.swift Adds testAddWorkspaceSurvivesMidCreationClose() covering the scenario where a non-selected workspace is closed mid-creation; complements the existing test that covers closing the selected workspace.

Sequence Diagram

sequenceDiagram
    participant C as Caller (Cmd+N)
    participant A as addWorkspace()
    participant S as self (retained)
    participant L as workspaceCreationSnapshotLite()
    participant D as didCaptureWorkspaceCreationSnapshot()
    participant N as newTabInsertIndex()

    C->>A: addWorkspace(placementOverride:)
    Note over A: self is always retained for duration
    A->>S: preferredWorkingDirectoryForNewTab()
    S-->>A: preferredDir (String?)
    A->>S: inheritedTerminalFontPointsForNewWorkspace()
    S-->>A: inheritedFontPoints (Float?)
    Note over A: Capture value types: tabs, selectedTabId
    A->>L: workspaceCreationSnapshotLite(currentTabs, currentSelectedTabId, preferredDir, inheritedFontPoints)
    L-->>A: WorkspaceCreationSnapshot (pure value type)
    A->>D: didCaptureWorkspaceCreationSnapshot()
    Note over D: Mid-creation mutations allowed here (close/reorder)
    A->>N: newTabInsertIndex(snapshot, placementOverride)
    N-->>A: insertIndex
    A->>A: insert newWorkspace at insertIndex into live tabs
    A-->>C: Workspace
Loading

Reviews (1): Last reviewed commit: "fix: extract workspace config through se..." | Re-trigger Greptile

Resolve conflicts in TabManager.swift and WorkspaceUnitTests.swift,
keeping the hardened addWorkspace() that extracts config data through
self before capturing locals (Xcode 16.x ARC optimizer fix).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Comment on lines +451 to +478
func testAddWorkspaceSurvivesMidCreationClose() {
let manager = SnapshotMutatingTabManager()
guard let first = manager.tabs.first else {
XCTFail("Expected initial workspace")
return
}

let closingWorkspace = manager.addWorkspace()
let third = manager.addWorkspace()
manager.selectWorkspace(third)

let closingWorkspaceId = closingWorkspace.id
XCTAssertEqual(manager.tabs.map(\.id), [first.id, closingWorkspaceId, third.id])

manager.afterCaptureWorkspaceCreationSnapshot = {
guard let liveWorkspace = manager.tabs.first(where: { $0.id == closingWorkspaceId }) else {
XCTFail("Expected captured workspace to still be present when closing after snapshot")
return
}
manager.closeWorkspace(liveWorkspace)
}

let inserted = manager.addWorkspace(placementOverride: .afterCurrent)

XCTAssertFalse(manager.tabs.contains(where: { $0.id == closingWorkspaceId }))
XCTAssertEqual(manager.tabs.map(\.id), [first.id, third.id, inserted.id])
XCTAssertEqual(manager.selectedTabId, inserted.id)
}

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 Regression test commit policy: test and fix landed in same commit

Per the CLAUDE.md regression test policy, new tests for a bug fix should be committed separately before the fix so that CI goes red first (proving the test catches the bug), then green after the fix lands.

This new test (testAddWorkspaceSurvivesMidCreationClose) and the production fix were committed together as c5837dbd. If the test actually fails on the unfixed code, it should have been split into two commits to demonstrate that on CI.

That said, because the underlying bug is an ARC optimizer use-after-free that manifests only in Release/CI builds (not easily provable in unit tests), and this test appears to exercise the behavioral outcome of a mid-creation close of a non-selected workspace (rather than the crash path directly), it may well pass on the unfixed code — in which case there is nothing to "prove red" and the single-commit structure is fine. If this test doesn't actually fail on the pre-fix code, a short comment on the test explaining that would be helpful context for future readers.

Context Used: CLAUDE.md (source)

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!

@austinywang
austinywang merged commit fe0443f into main Mar 26, 2026
14 checks passed
bn-l pushed a commit to bn-l/cmux that referenced this pull request Apr 3, 2026
…anaflow-ai#2204)

* test: reproduce Cmd+N snapshot workspace lifetime race

* fix: retain snapshot workspaces through Cmd+N creation

* fix: repair workspace lifetime regression test

* fix: extract workspace config through self to avoid Xcode 16.x ARC crash

The snapshot approach (f2e5091) navigated workspace → panel → surface
through local variables. Xcode 16.4's -O ARC optimizer aggressively
elides retains on these locals through inlined call chains, causing
use-after-free on every Cmd+N in CI-built nightlies.

Fix: extract preferredWorkingDirectory and inheritedTerminalFontPoints
through self (always retained) BEFORE capturing locals. The snapshot
is now purely value-typed with no Workspace references held in locals.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

This branch was successfully deployed

1 active deployment
Preview — 71d89b25 Deployed Mar 26, 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