Skip to content

Fix ARC workspace inheritance crash and native Zig helper builds - #2283

Merged
austinywang merged 4 commits into
mainfrom
fix/cmd-n-arc-and-intel-crash
Mar 28, 2026
Merged

austinywang merged 4 commits into
mainfrom
fix/cmd-n-arc-and-intel-crash

Conversation

@austinywang

@austinywang austinywang commented Mar 28, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • pin selected workspace, panel, and terminal surface lifetimes while extracting inherited config so ARC does not release workspace-backed objects during optimized config inheritance
  • keep panel/surface wrappers alive around raw ghostty surface pointer reads for split and new-workspace inheritance paths
  • prefer native Zig compilation when the requested helper target already matches the Zig binary architecture, avoiding the macOS Tahoe cross-link failures seen in tagged dev builds

Verification

  • PATH="/opt/homebrew/bin:$PATH" ./scripts/reload.sh --tag cmd-n-intel-pr --launch
  • xcodebuild -project GhosttyTabs.xcodeproj -scheme cmux -configuration Debug -destination 'platform=macOS,arch=x86_64' -derivedDataPath ~/Library/Developer/Xcode/DerivedData/cmux-intel-mac-transfer build

Summary by cubic

Fixes Nightly Cmd+N and split-workspace crashes by pinning object lifetimes and switching config inheritance to a Swift-owned template instead of live terminal pointers. Also builds the ghostty helper natively with zig when possible, choosing native slices in universal builds to avoid macOS Tahoe cross-link failures.

  • Bug Fixes
    • Keep the selected workspace and tabs alive with withExtendedLifetime across creation/snapshot; capture the selected tab from the pinned workspace; build snapshots via an explicit loop to retain each workspace during copy.
    • Replace pointer-backed config with CmuxSurfaceConfigTemplate: seed only font size from the workspace’s cached lineage for new-workspace creation, pin panel/TerminalSurface wrappers while reading raw ghostty_surface_t for splits, pass a sanitized template (font size only) into new workspaces, and resolve working directory/command/env/initial input at surface creation.

Written for commit 256fce1. Summary will update on new commits.

Summary by CodeRabbit

  • Bug Fixes

    • Fixes terminal font/config inheritance during workspace creation to prevent unexpected font-size or configuration regressions.
  • Chores

    • Improved memory/lifetime handling during workspace initialization for greater stability.
    • Build helper now prefers native compilation when appropriate, improving build reliability and universal build handling.
  • Tests

    • Updated unit tests to align with the new terminal configuration representation and inheritance behavior.

@vercel

vercel Bot commented Mar 28, 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 28, 2026 9:56am

@coderabbitai

coderabbitai Bot commented Mar 28, 2026 •

Copy link
Copy Markdown

Caution

Review failed

Pull request was closed or merged during review

📝 Walkthrough

Walkthrough

Wraps Workspace/TerminalSurface/TerminalPanel lifetimes with withExtendedLifetime during workspace creation and snapshotting to avoid ARC premature deallocation of pointer-backed surface data; switches inheritance to cached font-point extraction and rebuilds configs from that; the Zig build helper skips explicit -Dtarget for native-arch builds. (50 words)

Changes

Cohort / File(s) Summary
TabManager workspace creation
Sources/TabManager.swift
Capture selectedWorkspace and tabs, compute derived placement/config values inside withExtendedLifetime((capturedTabs, sourceWorkspace)); move snapshot creation and early return into that lifetime scope; change workspace-creation helpers to use CmuxSurfaceConfigTemplate?.
Workspace surface access & inheritance
Sources/Workspace.swift, Sources/TabManager.swift (helpers)
Introduce CmuxSurfaceConfigTemplate and switch APIs to return/use it; pin terminalPanel + TerminalSurface with withExtendedLifetime while reading C-backed ghostty_surface_t; replace probing-based config derivation with cached font-point extraction and rebuild Swift-owned config template.
Terminal view & panel callsites
Sources/GhosttyTerminalView.swift, Sources/Panels/TerminalPanel.swift
Change TerminalSurface / TerminalPanel initializer parameter types to CmuxSurfaceConfigTemplate?; in surface creation, build a C-backed ghostty_surface_config_new() from the Swift template and source environmentVariables from the template instead of reading C struct env fields.
Snapshot creation loop
Sources/TabManager.swift (snapshotting)
Replace map-based snapshot construction with an explicit loop that constructs each WorkspaceCreationTabSnapshot inside withExtendedLifetime(workspace) to ensure source Workspace objects remain alive while fields are read.
Tests
cmuxTests/WorkspaceUnitTests.swift
Update test overrides to use CmuxSurfaceConfigTemplate?, remove manual unsafe C-string retention/deallocation, and assert against Swift template fields (fontSize, workingDirectory, command, environmentVariables).
Build helper script
scripts/build-ghostty-cli-helper.sh
Detect Zig binary architecture and omit -Dtarget for native-arch builds; for --universal, perform native/no-target build for matching arch and targeted build for the other arch; fallback to previous behavior if detection fails.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 I hop and hold each tiny thread,
I pin the panels, keep them fed,
Fonts remembered, pointers kept,
New tabs wake safe — no panic leapt,
I nibble Zig flags, then thump ahead.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 8.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Fix ARC workspace inheritance crash and native Zig helper builds' clearly and specifically summarizes the two main changes: ARC/inheritance crash fixes and Zig helper build improvements.
Description check ✅ Passed The description includes a detailed Summary section explaining what changed and why, but lacks Testing section (manual verification steps are present but not in the Testing format) and Demo Video section. Verification steps are provided in the description body rather than the Testing section.

✏️ 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 fix/cmd-n-arc-and-intel-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 3 files

@greptile-apps

greptile-apps Bot commented Mar 28, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes two distinct issues: (1) ARC-optimizer-induced use-after-free crashes when opening new workspaces under Xcode 16+ whole-module optimization, and (2) cross-linker failures when building the native Zig helper on macOS Tahoe.

ARC lifetime fixes (Swift)

  • addWorkspace now captures selectedWorkspace into a single strong local (sourceWorkspace) and wraps both helper calls inside one withExtendedLifetime block, preventing the optimizer from eliding the retain across inlined callees.
  • inheritedTerminalConfigForNewWorkspace (TabManager) and inheritedTerminalConfig (Workspace) both introduce a named surface local for panel.surface and add a trailing withExtendedLifetime((panel, surface)) {} after each raw ghostty_surface_t C call, keeping the TerminalSurface wrapper — and therefore the backing C pointer — alive through the call.
  • The pattern is the idiomatic Swift approach for suppressing aggressive ARC elision around C-interop pointer reads.

Native Zig build optimisation (shell)

  • build-ghostty-cli-helper.sh now detects the zig binary's output architecture via file | grep -oE and, when the requested target matches, drops -Dtarget to use native compilation. This avoids the cross-linker failures reported on macOS Tahoe with zig 0.15.x.
  • Error handling is sound: 2>/dev/null guards the file call and the else branch restores the previous cross-compilation fallback.

Confidence Score: 5/5

Safe to merge; the Swift ARC fixes are correct and the one shell-script concern is an edge case that degrades only to the pre-fix cross-compilation behaviour, never to a build failure.

All P0/P1 findings are absent. The single comment is P2: a fat/universal zig binary (uncommon in practice — Homebrew and the Zig project both ship arch-specific binaries) would cause the native-compilation optimisation to silently not apply, reverting to cross-compilation. No crash, no data loss, no build failure.

scripts/build-ghostty-cli-helper.sh — the ZIG_ARCH detection via grep|head -1 misidentifies the running arch for universal zig binaries.

Important Files Changed

Filename Overview
Sources/TabManager.swift Pins selectedWorkspace once before helper calls and wraps the extraction in withExtendedLifetime; also refactors inheritedTerminalConfigForNewWorkspace to pin workspace + panel + surface wrappers around raw C pointer access. Pattern is correct.
Sources/Workspace.swift Introduces a named surface local and a trailing withExtendedLifetime((terminalPanel, surface)){} to keep the TerminalSurface wrapper alive through both ghostty C calls in the split-inheritance loop. Correct use of the pattern.
scripts/build-ghostty-cli-helper.sh Adds native-compilation optimisation for matching arch to avoid macOS Tahoe cross-linker failures; error handling (2>/dev/null + fallback else) is sound, but the grep

Sequence Diagram

sequenceDiagram
    participant TM as TabManager.addWorkspace
    participant SW as selectedWorkspace (Workspace)
    participant TP as TerminalPanel
    participant TS as TerminalSurface
    participant C as ghostty C API

    TM->>SW: pin via withExtendedLifetime(sourceWorkspace)
    TM->>TM: preferredWorkingDirectoryForNewTab(workspace:)
    TM->>SW: read .currentDirectory / .panelDirectories
    TM->>TM: inheritedTerminalFontPointsForNewWorkspace(workspace:)
    TM->>TM: inheritedTerminalConfigForNewWorkspace(workspace:)
    note over TM,SW: withExtendedLifetime(workspace)
    TM->>TP: terminalPanelForWorkspaceConfigInheritanceSource
    TM->>TS: pin surface = panel.surface
    TM->>TS: read surface.surface → ghostty_surface_t*
    TM->>C: cmuxInheritedSurfaceConfig(sourceSurface)
    TM->>TS: withExtendedLifetime((panel, surface)) — keeps TS alive through C call
    TM-->>TM: return CmuxSurfaceConfigTemplate
    note over TM,SW: withExtendedLifetime block ends — SW may now be released
Loading

Reviews (1): Last reviewed commit: "Fix ARC workspace inheritance crash and ..." | Re-trigger Greptile

Comment on lines +86 to +90
ZIG_ARCH="$(file "$(command -v zig)" 2>/dev/null | grep -oE '(arm64|x86_64)' | head -1)"
case "$TARGET_TRIPLE" in
aarch64-macos) [[ "$ZIG_ARCH" == "arm64" ]] && TARGET_TRIPLE="" ;;
x86_64-macos) [[ "$ZIG_ARCH" == "x86_64" ]] && TARGET_TRIPLE="" ;;
esac

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 ZIG_ARCH detection breaks for universal/fat zig binaries

When zig is a universal binary, file outputs both architectures on the same line, e.g.:

/opt/homebrew/bin/zig: Mach-O universal binary with 2 architectures: [x86_64:...] [arm64:...]

grep -oE '(arm64|x86_64)' | head -1 will always return x86_64 because it appears first in that format. On an Apple Silicon host where zig would run natively as arm64, the native-compilation optimisation (clearing TARGET_TRIPLE for aarch64-macos) would silently never trigger, and the build would fall back to cross-compilation — defeating the purpose of this fix.

A more robust approach is to intersect the binary's slices with the host arch (uname -m), or to use lipo -archs to enumerate slices and pick the one that matches the current hardware:

# Prefer lipo so fat binaries are handled correctly
_ZIG_BIN="$(command -v zig)"
if [[ -x "$_ZIG_BIN" ]]; then
  _SLICES="$(lipo -archs "$_ZIG_BIN" 2>/dev/null || true)"
  _HOST_ARCH="$(uname -m)"   # arm64 or x86_64 on the metal
  if echo "$_SLICES" | grep -qw "$_HOST_ARCH"; then
    ZIG_ARCH="$_HOST_ARCH"
  else
    ZIG_ARCH="$(echo "$_SLICES" | awk '{print $1}')"
  fi
fi

The same concern applies to the duplicated detection in the --universal branch (line 135).

@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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@scripts/build-ghostty-cli-helper.sh`:
- Around line 82-90: The arch-detection code uses command -v zig and a grep
pipeline that can fail under set -euo pipefail, and it calls command -v zig
before the explicit existence check; fix by validating the zig binary once
before any use (capture command -v zig into a variable like zig_path and check
it is non-empty), and make the ZIG_ARCH assignment resilient to no-match by
ensuring the grep/file pipeline cannot cause a non-zero exit (e.g., use "command
-v zig || true" or capture file output into a variable and run grep with "||
true", or use awk to extract arm64|x86_64 safely), then set ZIG_ARCH to an empty
string when detection fails; apply the same robust pattern for both occurrences
of ZIG_ARCH and ensure TARGET_TRIPLE logic (case matching with TARGET_TRIPLE and
ZIG_ARCH) still runs after zig existence and arch detection.

In `@Sources/Workspace.swift`:
- Around line 7406-7408: The call to
rememberTerminalConfigInheritanceSource(terminalPanel) (and the subsequent if
config.fontSize > 0 { ... } block that reads terminalPanel.surface.surface and
calls cmuxCurrentSurfaceFontSizePoints()) is executed after the
withExtendedLifetime((terminalPanel, surface)) {} scope ends, risking ARC
deallocation and a use-after-free; move
rememberTerminalConfigInheritanceSource(terminalPanel) and the following
font-size handling into the body of withExtendedLifetime((terminalPanel,
surface)) { ... } so terminalPanel and surface remain pinned while their C
pointers are accessed.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 23d7d105-8176-47c8-a713-cd6f8b0caae3

📥 Commits

Reviewing files that changed from the base of the PR and between f0c3ccc and a9b37f7.

📒 Files selected for processing (3)
  • Sources/TabManager.swift
  • Sources/Workspace.swift
  • scripts/build-ghostty-cli-helper.sh

Comment on lines +82 to +90
# When the requested target matches zig's native output arch, drop -Dtarget
# so zig uses native compilation. This avoids cross-linker issues on newer
# SDKs (e.g., macOS Tahoe + zig 0.15.x). Note: zig may run under Rosetta,
# so we detect native output arch from the zig binary itself, not uname -m.
ZIG_ARCH="$(file "$(command -v zig)" 2>/dev/null | grep -oE '(arm64|x86_64)' | head -1)"
case "$TARGET_TRIPLE" in
aarch64-macos) [[ "$ZIG_ARCH" == "arm64" ]] && TARGET_TRIPLE="" ;;
x86_64-macos) [[ "$ZIG_ARCH" == "x86_64" ]] && TARGET_TRIPLE="" ;;
esac

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

f="$(fd -p 'build-ghostty-cli-helper.sh' | head -1)"
echo "Inspecting: $f"
nl -ba "$f" | sed -n '80,100p;132,148p'

echo
echo "Reproducing Bash behavior under set -euo pipefail (expect non-zero):"
if bash -c 'set -euo pipefail; x="$(printf "unknown\n" | grep -oE "(arm64|x86_64)" | head -1)"; echo survived'; then
  echo "Unexpected: command substitution survived."
  exit 1
else
  echo "Expected: shell exits when grep has no match."
fi

Repository: manaflow-ai/cmux

Length of output: 167


🏁 Script executed:

# Find and read the build script
fd -p 'build-ghostty-cli-helper.sh' | head -1 | xargs wc -l

Repository: manaflow-ai/cmux

Length of output: 99


🏁 Script executed:

# Read the critical sections (lines 75-100 and 130-150)
f="$(fd -p 'build-ghostty-cli-helper.sh' | head -1)"
echo "=== File: $f ==="
echo "Lines 75-100:"
sed -n '75,100p' "$f" | cat -n
echo ""
echo "Lines 130-150:"
sed -n '130,150p' "$f" | cat -n

Repository: manaflow-ai/cmux

Length of output: 2179


🏁 Script executed:

# Test the bash pipefail behavior with grep
bash -c 'set -euo pipefail; x="$(printf "unknown\n" | grep -oE "(arm64|x86_64)" | head -1)"; echo "survived: [$x]"' 2>&1 || echo "Exit code: $?"

Repository: manaflow-ai/cmux

Length of output: 72


🏁 Script executed:

# Test with || true to suppress
bash -c 'set -euo pipefail; x="$(printf "unknown\n" | grep -oE "(arm64|x86_64)" | head -1 || true)"; echo "survived: [$x]"' 2>&1

Repository: manaflow-ai/cmux

Length of output: 72


Prevent false hard-fail in arch detection and validate zig before first use.

At lines 86 and 135, ZIG_ARCH="$(... | grep ... | head -1)" terminates the script under set -euo pipefail when grep finds no match, so the intended fallback path is unreachable. Additionally, line 86 uses command -v zig before the explicit check at lines 93-96.

Proposed fix
@@
-if [[ -n "$TARGET_TRIPLE" ]]; then
+if ! command -v zig >/dev/null 2>&1; then
+  echo "error: zig is required to build the Ghostty CLI helper" >&2
+  exit 1
+fi
+
+detect_zig_arch() {
+  local zig_path file_out
+  zig_path="$(command -v zig 2>/dev/null || true)"
+  [[ -z "$zig_path" ]] && return 1
+
+  file_out="$(file "$zig_path" 2>/dev/null || true)"
+  case "$file_out" in
+    *arm64* )
+      [[ "$file_out" != *x86_64* ]] && { echo "arm64"; return 0; }
+      ;;
+    *x86_64* )
+      [[ "$file_out" != *arm64* ]] && { echo "x86_64"; return 0; }
+      ;;
+  esac
+  return 1
+}
+
+if [[ -n "$TARGET_TRIPLE" ]]; then
@@
-  ZIG_ARCH="$(file "$(command -v zig)" 2>/dev/null | grep -oE '(arm64|x86_64)' | head -1)"
+  ZIG_ARCH="$(detect_zig_arch || true)"
@@
-fi
-
-if ! command -v zig >/dev/null 2>&1; then
-  echo "error: zig is required to build the Ghostty CLI helper" >&2
-  exit 1
 fi
@@
-  ZIG_ARCH="$(file "$(command -v zig)" 2>/dev/null | grep -oE '(arm64|x86_64)' | head -1)"
+  ZIG_ARCH="$(detect_zig_arch || true)"

Also applies to: lines 93-96, 135-146

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/build-ghostty-cli-helper.sh` around lines 82 - 90, The arch-detection
code uses command -v zig and a grep pipeline that can fail under set -euo
pipefail, and it calls command -v zig before the explicit existence check; fix
by validating the zig binary once before any use (capture command -v zig into a
variable like zig_path and check it is non-empty), and make the ZIG_ARCH
assignment resilient to no-match by ensuring the grep/file pipeline cannot cause
a non-zero exit (e.g., use "command -v zig || true" or capture file output into
a variable and run grep with "|| true", or use awk to extract arm64|x86_64
safely), then set ZIG_ARCH to an empty string when detection fails; apply the
same robust pattern for both occurrences of ZIG_ARCH and ensure TARGET_TRIPLE
logic (case matching with TARGET_TRIPLE and ZIG_ARCH) still runs after zig
existence and arch detection.

Comment thread Sources/Workspace.swift
Comment on lines +7406 to 7408
// Prevent ARC from releasing panel/surface before the C calls above complete.
withExtendedLifetime((terminalPanel, surface)) {}
rememberTerminalConfigInheritanceSource(terminalPanel)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify that the lifetime guard currently ends before rememberTerminalConfigInheritanceSource(...)
# Expected (current code): one match showing withExtendedLifetime(...) followed by remember call.
rg -n -U 'withExtendedLifetime\(\(terminalPanel, surface\)\)\s*\{\s*\}\s*\n\s*rememberTerminalConfigInheritanceSource\(terminalPanel\)' Sources/Workspace.swift

Repository: manaflow-ai/cmux

Length of output: 198


🏁 Script executed:

# Read the full context around the flagged lines
sed -n '7395,7420p' Sources/Workspace.swift | cat -n

Repository: manaflow-ai/cmux

Length of output: 1486


🏁 Script executed:

# Find the definition of rememberTerminalConfigInheritanceSource to verify it accesses pointer-backed data
rg -n 'func rememberTerminalConfigInheritanceSource' Sources/Workspace.swift

Repository: manaflow-ai/cmux

Length of output: 155


🏁 Script executed:

# Look at the implementation of rememberTerminalConfigInheritanceSource
rg -A 20 'func rememberTerminalConfigInheritanceSource\(' Sources/Workspace.swift | head -40

Repository: manaflow-ai/cmux

Length of output: 1141


Keep rememberTerminalConfigInheritanceSource inside the same lifetime pin

At Line 7408, you call rememberTerminalConfigInheritanceSource(terminalPanel) after the withExtendedLifetime block ends. This function reads terminalPanel.surface.surface (a C pointer) and calls cmuxCurrentSurfaceFontSizePoints() with it. ARC can deallocate terminalPanel or surface between lines 7407 and 7408, causing use-after-free. Move both rememberTerminalConfigInheritanceSource(terminalPanel) and the subsequent if config.fontSize > 0 { ... } block into the same lifetime scope.

Suggested patch
-            // Prevent ARC from releasing panel/surface before the C calls above complete.
-            withExtendedLifetime((terminalPanel, surface)) {}
-            rememberTerminalConfigInheritanceSource(terminalPanel)
-            if config.fontSize > 0 {
-                lastTerminalConfigInheritanceFontPoints = config.fontSize
-            }
+            // Prevent ARC from releasing panel/surface before all pointer-backed reads complete.
+            withExtendedLifetime((terminalPanel, surface)) {
+                rememberTerminalConfigInheritanceSource(terminalPanel)
+                if config.fontSize > 0 {
+                    lastTerminalConfigInheritanceFontPoints = config.fontSize
+                }
+            }
             return config
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Sources/Workspace.swift` around lines 7406 - 7408, The call to
rememberTerminalConfigInheritanceSource(terminalPanel) (and the subsequent if
config.fontSize > 0 { ... } block that reads terminalPanel.surface.surface and
calls cmuxCurrentSurfaceFontSizePoints()) is executed after the
withExtendedLifetime((terminalPanel, surface)) {} scope ends, risking ARC
deallocation and a use-after-free; move
rememberTerminalConfigInheritanceSource(terminalPanel) and the following
font-size handling into the body of withExtendedLifetime((terminalPanel,
surface)) { ... } so terminalPanel and surface remain pinned while their C
pointers are accessed.

@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.

1 issue found across 1 file (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="Sources/TabManager.swift">

<violation number="1" location="Sources/TabManager.swift:2334">
P2: `inheritedTerminalFontPointsForNewWorkspace` now bypasses the overridable `inheritedTerminalConfigForNewWorkspace` path, so subclass/test-injected inherited config is ignored during `addWorkspace`. This breaks the existing config-sanitization seam and can cause regressions in workspace creation behavior that relies on overrides.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread Sources/TabManager.swift
return nil
}
return inheritedConfig.fontSize
cachedInheritedTerminalFontPointsForNewWorkspace(workspace: workspace)

@cubic-dev-ai cubic-dev-ai Bot Mar 28, 2026 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: inheritedTerminalFontPointsForNewWorkspace now bypasses the overridable inheritedTerminalConfigForNewWorkspace path, so subclass/test-injected inherited config is ignored during addWorkspace. This breaks the existing config-sanitization seam and can cause regressions in workspace creation behavior that relies on overrides.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At Sources/TabManager.swift, line 2334:

<comment>`inheritedTerminalFontPointsForNewWorkspace` now bypasses the overridable `inheritedTerminalConfigForNewWorkspace` path, so subclass/test-injected inherited config is ignored during `addWorkspace`. This breaks the existing config-sanitization seam and can cause regressions in workspace creation behavior that relies on overrides.</comment>

<file context>
@@ -2290,48 +2296,42 @@ class TabManager: ObservableObject {
-            return nil
-        }
-        return inheritedConfig.fontSize
+        cachedInheritedTerminalFontPointsForNewWorkspace(workspace: workspace)
     }
 
</file context>
Suggested change
cachedInheritedTerminalFontPointsForNewWorkspace(workspace: workspace)
guard let inheritedConfig = inheritedTerminalConfigForNewWorkspace(workspace: workspace),
inheritedConfig.fontSize > 0 else {
return nil
}
return inheritedConfig.fontSize
Fix with Cubic

@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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@Sources/TabManager.swift`:
- Around line 1246-1248: The default workspace title is currently hard-coded as
"Terminal \(nextTabCount)" when calling makeWorkspaceForCreation (see the title
argument), which must be localized; replace that literal with String(localized:
"workspace.defaultTerminalTitle", defaultValue: "Terminal \(nextTabCount)") (or
similar key) so the UI uses localized text, and add the corresponding key
"workspace.defaultTerminalTitle" with the English value to
Resources/Localizable.xcstrings (and provide translations as needed); ensure the
substitution of nextTabCount remains inside the defaultValue string so the
numeric suffix is preserved.
- Around line 2299-2324: The new logic reduces inheritance to only an optional
font size and drops other fields; restore full-template inheritance by either
(A) having Workspace persist a Swift-owned snapshot of the full
CmuxSurfaceConfigTemplate (not just lastTerminalConfigInheritanceFontPoints) and
expose it (e.g. add or use a method like
lastRememberedTerminalConfigForInheritance()), or (B) if you prefer to keep
Workspace storage minimal, populate and return a full CmuxSurfaceConfigTemplate
from inheritedTerminalConfigForNewWorkspace instead of only setting fontSize
(copy command, environmentVariables, initialInput, waitAfterCommand,
workingDirectory as appropriate) so that
cachedInheritedTerminalFontPointsForNewWorkspace and
inheritedTerminalConfigForNewWorkspace do not lose fields; update references to
cachedInheritedTerminalFontPointsForNewWorkspace,
inheritedTerminalConfigForNewWorkspace,
Workspace.lastTerminalConfigInheritanceFontPoints and addWorkspace to use the
full-template API to preserve parity with the previous behavior.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 55277227-4d34-4766-b01b-bc56ca627c44

📥 Commits

Reviewing files that changed from the base of the PR and between a9b37f7 and 570afe8.

📒 Files selected for processing (1)
  • Sources/TabManager.swift

Comment thread Sources/TabManager.swift
Comment on lines +1246 to +1248
let newWorkspace = makeWorkspaceForCreation(
title: title ?? "Terminal \(nextTabCount)",
workingDirectory: workingDirectory,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Localize the default workspace title.

Terminal \(nextTabCount) is user-visible, so this will show up untranslated in the sidebar/window title path. Please switch it to String(localized:..., defaultValue: ...) and add the xcstrings entry.

🌐 Suggested change
-                title: title ?? "Terminal \(nextTabCount)",
+                title: title ?? String(
+                    localized: "workspace.defaultTitle",
+                    defaultValue: "Terminal \(nextTabCount)"
+                ),

As per coding guidelines, "All user-facing strings must be localized using String(localized: "key.name", defaultValue: "English text") for every string shown in the UI. Keys must go in Resources/Localizable.xcstrings with translations for all supported languages."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Sources/TabManager.swift` around lines 1246 - 1248, The default workspace
title is currently hard-coded as "Terminal \(nextTabCount)" when calling
makeWorkspaceForCreation (see the title argument), which must be localized;
replace that literal with String(localized: "workspace.defaultTerminalTitle",
defaultValue: "Terminal \(nextTabCount)") (or similar key) so the UI uses
localized text, and add the corresponding key "workspace.defaultTerminalTitle"
with the English value to Resources/Localizable.xcstrings (and provide
translations as needed); ensure the substitution of nextTabCount remains inside
the defaultValue string so the numeric suffix is preserved.

Comment thread Sources/TabManager.swift
@austinywang
austinywang merged commit c4bc18d into main Mar 28, 2026
13 of 14 checks passed
bn-l pushed a commit to bn-l/cmux that referenced this pull request Apr 3, 2026
…aflow-ai#2283)

* Fix ARC workspace inheritance crash and native Zig helper builds

* Fix Nightly Cmd+N workspace creation crash

* Restore safe terminal config snapshots for Intel Nightly

This branch was successfully deployed

1 active deployment
Preview — 256fce17 Deployed Mar 28, 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