Skip to content

Fix cmux terminal environment injection - #4728

Merged
austinywang merged 2 commits into
mainfrom
issue-4714-cmux-env-vars
May 25, 2026
Merged

austinywang merged 2 commits into
mainfrom
issue-4714-cmux-env-vars

Conversation

@austinywang

@austinywang austinywang commented May 25, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Add a regression test covering all five managed CMUX terminal context variables.
  • Centralize CMUX terminal context env assembly so runtime surface creation and startup environment lookup share the same path.

Fixes #4714.

Verification

  • Not run locally: user explicitly prohibited xcodebuild and reload.sh in this workspace; CI is expected to run the regression test.

Commit structure

  • Commit 1: failing regression test only.
  • Commit 2: fix that makes the regression pass.

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


Note

Low Risk
Scoped to terminal startup environment assembly and protected keys; no auth or data-path changes, with regression test coverage.

Overview
CMUX terminal context env vars (CMUX_WORKSPACE_ID, CMUX_SURFACE_ID, CMUX_PANEL_ID, CMUX_TAB_ID, CMUX_SOCKET_PATH) are now built in one place via applyManagedCmuxContextEnvironment and used both when a Ghostty surface is created and in pre-spawn startupEnvironmentValue lookups (which previously only merged overrides and could miss managed context keys).

A small CmuxContextEnvironment type carries workspace/surface IDs and the active socket path. A new regression test asserts all five keys are present and correct on TerminalSurface.startupEnvironmentValue.

Reviewed by Cursor Bugbot for commit 25eaca4. Bugbot is set up for automated code reviews on this repo. Configure here.

Summary by CodeRabbit

  • Refactor

    • Consolidated CMUX context handling for terminal surfaces: workspace/surface/panel/tab IDs and socket path are computed once and applied as managed, protected environment values to prevent accidental overrides and ensure consistent startup environments.
  • Tests

    • Added test coverage verifying CMUX-related environment variables and socket path are populated and match expected values.

Review Change Stack

@vercel

vercel Bot commented May 25, 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 May 25, 2026 8:03pm
cmux-staging Building Building Preview, Comment May 25, 2026 8:03pm

@coderabbitai

coderabbitai Bot commented May 25, 2026 •

Copy link
Copy Markdown

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: c217cb4b-452d-4f27-a8a4-d77888f15f11

📥 Commits

Reviewing files that changed from the base of the PR and between b0106ea and 25eaca4.

📒 Files selected for processing (2)
  • Sources/GhosttyTerminalView.swift
  • Sources/TerminalStartupEnvironment.swift

📝 Walkthrough

Walkthrough

Refactors TerminalSurface startup environment assembly to build and apply a per-surface CmuxContextEnvironment (workspaceId, surfaceId, socketPath) into a protected environment, consolidates surface creation to use the helper, and adds a test validating CMUX_* environment variables are present.

Changes

CMUX Context Environment Injection

Layer / File(s) Summary
CmuxContextEnvironment contract and application
Sources/TerminalStartupEnvironment.swift
New TerminalSurface.CmuxContextEnvironment struct carries workspaceId, surfaceId, and socketPath; applyManagedCmuxContextEnvironment populates CMUX_SURFACE_ID, CMUX_WORKSPACE_ID, CMUX_PANEL_ID, CMUX_TAB_ID, and CMUX_SOCKET_PATH, marking them protected.
TerminalSurface startup environment value refactoring
Sources/GhosttyTerminalView.swift
Adds cmuxContextEnvironment(workspaceId:surfaceId:socketPath:) helper and refactors startupEnvironmentValue(_:) to compute active socketPath, apply managed CMUX context into a protected environment, then merge additionalEnvironment and initialEnvironmentOverrides when returning the requested key.
Surface creation environment setup consolidation
Sources/GhosttyTerminalView.swift
In createSurface(...), replaces multiple setManagedEnvironmentValue calls with a single Self.applyManagedCmuxContextEnvironment(...) call using the computed cmuxContextEnvironment and active socketPath.
CMUX context environment value test
cmuxTests/GhosttyTerminalStartupEnvironmentTests.swift
Adds testTerminalSurfaceStartupEnvironmentIncludesCmuxContextValues verifying CMUX_WORKSPACE_ID, CMUX_SURFACE_ID, CMUX_TAB_ID, CMUX_PANEL_ID match expected UUID-derived strings and CMUX_SOCKET_PATH is present and non-empty.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related PRs

  • manaflow-ai/cmux#4458: Related changes affecting how CMUX_SOCKET_PATH is derived/injected for new terminal/ghostty surfaces.

Suggested reviewers

  • jesstelford

Poem

🐰 In cmux shells where context flows,
The lost IDs are found at last,
Protected keys now guard the way,
From socket path to tab so fast,
The rabbit hops — environment restored.

🚥 Pre-merge checks | ✅ 16 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 11.11% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (16 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: fixing the injection of CMUX terminal environment variables.
Description check ✅ Passed Description includes summary with what/why, verification notes, and commit structure; lacks demo video (N/A for non-UI change) and testing checklist completion.
Linked Issues check ✅ Passed Changes fully address #4714 objectives: centralizes CMUX context assembly, ensures all five CMUX variables (CMUX_WORKSPACE_ID, CMUX_SURFACE_ID, CMUX_TAB_ID, CMUX_PANEL_ID, CMUX_SOCKET_PATH) are injected via new applyManagedCmuxContextEnvironment method, and includes regression test coverage.
Out of Scope Changes check ✅ Passed All changes are scoped to terminal startup environment assembly and CMUX context management; no unrelated modifications detected.
Cmux Swift Actor Isolation ✅ Passed New CmuxContextEnvironment struct is properly Sendable. Static helper methods are pure utilities with no shared mutable state. Test is @MainActor. Existing TerminalSurface isolation debt not worsened.
Cmux Swift Blocking Runtime ✅ Passed The PR introduces environment variable assembly logic with no blocking synchronization primitives, semaphores, sleeps, locks, or timing-based dispatch in production code.
Cmux No Hacky Sleeps ✅ Passed Rule scope is TypeScript, JavaScript, shell, and non-Swift scripts. All PR changes are Swift files (.swift), which are explicitly excluded and covered by swift-blocking-runtime.md.
Cmux Swift Concurrency ✅ Passed New code uses synchronous functions and value manipulation only. Proper Sendable marking on CmuxContextEnvironment. No legacy async patterns introduced in TerminalStartupEnvironment.swift or tests.
Cmux Swift @Concurrent ✅ Passed All changes are synchronous functions; CmuxContextEnvironment is correctly marked Sendable; @MainActor correctly isolates UI-bound work. No async or @concurrent violations.
Cmux Swift File And Package Boundaries ✅ Passed New file is 85 lines (under 400 threshold), oversized file grows by only 38 lines (under 250 threshold), focused bug fix with no responsibility mixing or policy violations.
Cmux Swift Logging ✅ Passed No logging violations. New code adds environment assembly (CmuxContextEnvironment, applyManagedCmuxContextEnvironment) with no print/NSLog statements, and a test file using XCTest assertions.
Cmux User-Facing Error Privacy ✅ Passed PR contains internal environment variable refactoring and tests with no user-facing error messages, alerts, or sensitive data exposure. Changes are developer/test code only.
Cmux Full Internationalization ✅ Passed No new user-facing strings added. Changes are internal implementation code for environment variable injection with no UI/UX modifications requiring localization.
Cmux Swiftui State Layout ✅ Passed PR adds only value structs and static helper functions for environment setup; introduces no SwiftUI state violations.
Cmux Architecture Rethink ✅ Passed PR consolidates env var assembly into single shared code path with protected keys, adds no timing/locks/observers/side channels, and fixes correctness bug without split lifecycle.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed PR does not add or materially change any NSWindow, NSPanel, NSWindowController, or SwiftUI Window/WindowGroup code; all changes are environment variable refactoring unrelated to window management.

✏️ 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-4714-cmux-env-vars

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.

@greptile-apps

greptile-apps Bot commented May 25, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a regression where startupEnvironmentValue could not return any of the five managed CMUX terminal context variables (CMUX_WORKSPACE_ID, CMUX_SURFACE_ID, CMUX_TAB_ID, CMUX_PANEL_ID, CMUX_SOCKET_PATH), because it only inspected additionalEnvironment and initialEnvironmentOverrides. The fix centralizes context assembly into applyManagedCmuxContextEnvironment / CmuxContextEnvironment, shared by both the surface-creation path and the pre-spawn lookup.

  • TerminalStartupEnvironment.swift: New CmuxContextEnvironment value type (Sendable) and applyManagedCmuxContextEnvironment static helper; inline setManagedEnvironmentValue calls at surface creation are replaced with this shared helper.
  • GhosttyTerminalView.swift: startupEnvironmentValue gains @MainActor isolation and now builds the managed context env via the shared helper before merging with additionalEnvironment / initialEnvironmentOverrides.
  • GhosttyTerminalStartupEnvironmentTests.swift: @MainActor regression test asserting all five context keys are present and non-empty via startupEnvironmentValue.

Confidence Score: 5/5

Safe to merge — the change correctly aligns the pre-spawn lookup with the surface-creation path, actor isolation is properly declared, and the regression test covers all five affected keys.

The refactor is narrow: it replaces five individual managed-env assignments with a shared static helper, adds @mainactor to the lookup method, and contributes a regression test. No new mutable shared state is introduced, the CmuxContextEnvironment struct is a plain Sendable value type, and the merge logic in mergedStartupEnvironment was already well-tested.

No files require special attention.

Important Files Changed

Filename Overview
Sources/TerminalStartupEnvironment.swift Adds CmuxContextEnvironment struct and applyManagedCmuxContextEnvironment helper; clean, pure static logic with no actor isolation concerns.
Sources/GhosttyTerminalView.swift Refactors startupEnvironmentValue to use shared env helper and adds @mainactor; removes duplicated setManagedEnvironmentValue calls at surface creation.
cmuxTests/GhosttyTerminalStartupEnvironmentTests.swift Adds @mainactor regression test covering all five CMUX context keys plus non-empty socket path via startupEnvironmentValue.

Sequence Diagram

sequenceDiagram
    participant Caller
    participant TS as TerminalSurface
    participant TSE as TerminalStartupEnvironment
    participant TC as TerminalController

    Note over TS,TSE: Pre-spawn lookup (startupEnvironmentValue)
    Caller->>TS: "startupEnvironmentValue(key) [@MainActor]"
    TS->>TC: activeSocketPath(preferredPath:)
    TC-->>TS: socketPath
    TS->>TSE: "applyManagedCmuxContextEnvironment(context, &env, &protectedKeys)"
    TSE-->>TS: env populated with 5 CMUX keys
    TS->>TSE: mergedStartupEnvironment(base:protectedKeys:additionalEnv:overrides:)
    TSE-->>TS: merged[String:String]
    TS-->>Caller: merged[key]

    Note over TS,TSE: Surface creation (createSurface)
    Caller->>TS: createSurface()
    TS->>TC: activeSocketPath(preferredPath:)
    TC-->>TS: socketPath
    TS->>TSE: "applyManagedCmuxContextEnvironment(context, &env, &protectedKeys)"
    TSE-->>TS: env populated with 5 CMUX keys (same path)
Loading

Reviews (3): Last reviewed commit: "fix: centralize cmux terminal context en..." | Re-trigger Greptile

Comment thread Sources/GhosttyTerminalView.swift Outdated
Comment on lines +5299 to +5305
private func cmuxContextEnvironment(socketPath: String) -> CmuxContextEnvironment {
CmuxContextEnvironment(
workspaceId: tabId,
surfaceId: id,
socketPath: socketPath
)
}

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 Missing @MainActor on cmuxContextEnvironment accessing mutable tabId

cmuxContextEnvironment reads tabId, which is a private(set) var on a class that is not globally @MainActor-isolated. Without the annotation, the method is nonisolated by default in Swift 6 strict concurrency, and any future caller outside @MainActor would compile cleanly while producing a data race on the mutable tabId. Adding @MainActor here makes the isolation contract explicit and matches every call site.

Rule Used: Flag new or materially worsened Swift 6 actor isol... (source)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed by making the cmux context helper a pure static constructor that receives workspace ID, surface ID, and socket path from the caller, so it no longer reads mutable surface state outside the caller's actor context.

— Claude Code

Comment on lines +5307 to 5325
@MainActor
func startupEnvironmentValue(_ key: String) -> String? {
additionalEnvironment[key] ?? initialEnvironmentOverrides[key]
let socketPath = TerminalController.shared.activeSocketPath(
preferredPath: SocketControlSettings.socketPath()
)
var environment: [String: String] = [:]
var protectedKeys: Set<String> = []
Self.applyManagedCmuxContextEnvironment(
cmuxContextEnvironment(socketPath: socketPath),
to: &environment,
protectedKeys: &protectedKeys
)
return Self.mergedStartupEnvironment(
base: environment,
protectedKeys: protectedKeys,
additionalEnvironment: additionalEnvironment,
initialEnvironmentOverrides: initialEnvironmentOverrides
)[key]
}

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 startupEnvironmentValue is a partial and undocumented view of the startup environment

The method is named as a generic key lookup over the startup environment, but it only materialises five CMUX context keys plus the additionalEnvironment/initialEnvironmentOverrides dicts. Managed keys applied in createSurface — TERM, COLORTERM, CMUX_BUNDLED_CLI_PATH, CMUX_PORT, etc. — return nil here. A future call like startupEnvironmentValue("CMUX_BUNDLED_CLI_PATH") will silently return nil even though the running terminal has it set. The fix centralises the CMUX context path correctly, but without a comment or a narrower name (e.g. cmuxContextEnvironmentValue) the method's scope is still easy to misread.

Rule Used: Flag Swift fixes that patch symptoms while leaving... (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!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed by documenting startupEnvironmentValue as a pre-spawn lookup for managed context keys and explicit startup overrides, with full runtime-only environment assembly still owned by createSurface.

— Claude Code

This branch was successfully deployed

1 active deployment
Preview – cmux — 25eaca48 Deployed May 25, 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.

v0.64.10: CMUX_* environment variables not injected into terminal shells

1 participant