Skip to content

Fix cmux themes single-sided Ghostty values (#10068) - #10358

Closed
austinywang wants to merge 59 commits into
mainfrom
issue-10068-themes-single-sided
Closed

austinywang wants to merge 59 commits into
mainfrom
issue-10068-themes-single-sided

Conversation

@austinywang

@austinywang austinywang commented Aug 18, 2026 •

Copy link
Copy Markdown
Contributor

Closes #10068

Root cause

cmux themes set --light/--dark and the bundled Ghostty picker emitted single-sided conditional theme values (light:<name> or dark:<name>). Ghostty intentionally rejects those values because conditional syntax requires both sides, so the directive was dropped and terminals kept the default palette. The picker also chose a single-side target when the current value was unset or already single-sided.

Fix

  • Make the Swift CLI encoder emit a two-sided pair, preserving the known opposite side and duplicating the selected side when no opposite value exists.
  • Make the Ghostty/Zig picker encoder follow the same invariant for live preview and apply.
  • Default the picker target to both for fresh or one-sided state so Enter repairs stale values.
  • Repair legacy single-sided values in cmux-managed blocks during both value-config loading paths, while leaving unmarked user config semantics unchanged.
  • Add behavior tests for CLI writes, picker fallback/preservation, target selection, Ghostty config repair, and the Zig encoder.

Verification

  • Regression tests were committed before the production fix (e49d4cdb44); the test-only hosted attempt reached setup and stopped only because that intermediate Ghostty SHA had no artifact pin.
  • Real Zig-built bundled-helper regression script: PASS.
  • Focused Ghostty Zig test: 73/73 tests passed.
  • Swift parser validation, PBX wiring, checksum guards, workspace/package policy, and CI change-area routing: PASS.
  • arch -arm64 swift test in an isolated package copy: all 5 GhosttyConfigCmuxThemeRepairTests passed; SwiftPM also reports its existing ghostty-internal.a binary-name preflight warning for the downloaded artifact.
  • GhosttyKit ReleaseFast artifact for submodule 987780a132 is published and pinned in scripts/ghosttykit-checksums.txt; the submodule commit is merged into manaflow-ai/ghostty main (PR 201).
  • No local Xcode build, app launch, or local XCUITest was run.

Summary by CodeRabbit

  • Bug Fixes

    • Fixed theme selection when only a light or dark theme is configured.
    • Ensured managed themes include valid light and dark values for Ghostty.
    • Preserved existing opposite-side themes during updates.
    • Automatically repairs older cmux-managed configurations without changing user-defined settings.
  • Tests

    • Added regression coverage for theme picker behavior, configuration repair, fallback handling, and theme preservation.
  • Documentation

    • Updated Ghostty fork documentation with conditional theme encoding details.

Note

Medium Risk
Changes theme encoding, config load semantics for cmux-managed blocks, and pins a new GhosttyKit build—user-facing appearance behavior with careful precedence tests but broad touch across CLI, config, and embedded Ghostty.

Overview
Fixes Ghostty rejecting cmux theme values when only light: or dark: was written—conditional syntax requires both sides, so themes were dropped and terminals stayed on defaults.

Encoding and picker behavior: Swift CLI themes set and theme encoding now always emit light:…,dark:…, duplicating the selected side when the opposite is unknown and keeping the other side when it is already set. The interactive picker defaults to both targets when the current selection is fresh or single-sided so Enter repairs stale config. The pinned Ghostty fork documents the matching Zig picker change.

Load-time repair: New GhosttyConfig helpers detect single-sided theme lines inside # cmux themes start/end blocks and normalize them in memory during config load and appearance summary (unmarked user theme directives are unchanged). Theme parsing reuses a shared conditionalThemeComponents helper.

Other: cmux-skill-contract CI runs on GitHub-hosted ubuntu-24.04 for untrusted PRs; GhosttyKit checksum pins submodule 987780a132; minor web test timeout style tweaks and a socket self. fix.

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

@coderabbitai

coderabbitai Bot commented Aug 18, 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

cmux now writes symmetric Ghostty theme values, repairs legacy single-sided managed themes during loading, and selects both picker targets when a side is missing. Tests, fork metadata, checksums, and CI runner configuration cover the changes.

Changes

Conditional theme handling

Layer / File(s) Summary
Symmetric theme encoding and picker targeting
CLI/CMUXCLI+Themes.swift, cmuxTests/CMUXCLIErrorOutputRegressionTests.swift, tests/test_bundled_ghostty_theme_picker_helper.sh
Single-sided selections now duplicate the selected theme. The picker targets both sides when one side is missing. Regression tests cover fallback and opposite-side preservation.
Managed configuration repair
Packages/macOS/CmuxTerminalCore/Sources/CmuxTerminalCore/Config/GhosttyConfig+CmuxThemeRepair.swift, Packages/macOS/CmuxTerminalCore/Sources/CmuxTerminalCore/Config/GhosttyConfig.swift, Packages/macOS/CmuxTerminalCore/Tests/CmuxTerminalCoreTests/GhosttyConfigCmuxThemeRepairTests.swift
Marked cmux theme values are normalized to light/dark pairs during config loading. Tests cover managed markers, BOM-prefixed values, unmarked values, complete pairs, and include precedence.
Application startup integration
Sources/GhosttyTerminalView+ThemeRepair.swift, Sources/GhosttyTerminalView.swift, cmux.xcodeproj/project.pbxproj
GhosttyApp reads managed config files and loads a repair directive before recursive config files. The new source file is registered in the Xcode target.
Fork artifact and documentation updates
docs/ghostty-fork.md, scripts/ghosttykit-checksums.txt
Fork documentation records conditional theme encoding and artifact pins. The checksum manifest includes the new GhosttyKit artifact.

CI runner configuration

Layer / File(s) Summary
Hosted runner selection
.github/workflows/cmux-skill-contract.yml
The browser-skill job now uses the GitHub-hosted ubuntu-24.04 runner for untrusted pull-request code.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ThemePicker
  participant CmuxConfigFiles
  participant GhosttyConfig
  participant GhosttyApp
  participant GhosttyRuntime
  ThemePicker->>CmuxConfigFiles: write symmetric light/dark theme
  GhosttyApp->>CmuxConfigFiles: read managed configuration
  CmuxConfigFiles->>GhosttyConfig: normalize single-sided theme
  GhosttyConfig-->>GhosttyApp: return repaired theme value
  GhosttyApp->>GhosttyRuntime: load inline theme repair
  GhosttyRuntime->>GhosttyRuntime: load recursive config files
Loading

Merge Risk: 🔵 Low · up to c1b57

Theme updates generally produce valid two-sided values, but uncommon BOM-formatted managed files may remain unrepaired and an unmarked user theme matching an earlier managed value may be changed unexpectedly. These are bounded configuration edge cases that should be addressed before relying on repair behavior.

🚥 Pre-merge checks | ✅ 13 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning Most changes support issue #10068, but docs/ghostty-fork.md also removes the PR #183 regression-coverage block and an unrelated Fish integration note. These documentation deletions are not required fo… Revert the unrelated documentation deletions in docs/ghostty-fork.md, or provide explicit issue scope and justification for them. Keep documentation changes that describe the theme encoding fix and required artifact pinning.
Docstring Coverage ⚠️ Warning Docstring coverage is 39.29% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 28 functions across 7 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (13 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the main change: fixing single-sided Ghostty theme values for cmux.
Description check ✅ Passed The description explains the root cause, implementation, verification results, and testing limitations. It does not use every template heading, include a demo video, or include the checklist, but the …
Linked Issues check ✅ Passed The changes address issue #10068 by emitting valid two-sided theme values, preserving or duplicating the opposite side, defaulting picker targets to both sides, repairing legacy cmux-managed values, p…
Cmux Swift Actor Isolation ✅ Passed PASS. The production Swift diff adds value-only theme parsing and encoding in CMUXCLI and GhosttyConfig, plus file scanning in an extension of the existing GhosttyApp. It does not add an implici…
Cmux Swift Blocking Runtime ✅ Passed The production Swift changes add theme parsing, file scanning, and inline config repair. They do not add semaphores, blocking waits, sleeps, delayed dispatch, polling, main-queue sync, or manual locks…
Cmux Browser Automation Off-Main ✅ Passed PASS. The rule applies to browser socket automation routing, WebKit/AppKit access, and worker-lane policy tests. The branch-specific first-parent commits for this PR are theme, Ghostty configuration, …
Cmux Expensive Synchronous Load ✅ Passed The PR does not add an expensive synchronous agent-history load. Its new synchronous read is limited to a bounded list of cmux Ghostty configuration URLs and parses plain Ghostty theme directives. It …
Cmux Cache Substitution Correctness ✅ Passed PASS. The theme changes do not substitute a cache for an authoritative read in a persistence, history, undo, or snapshot path. currentThemeSelection and the new managed-theme scanner read configurat…
Cmux No Hacky Sleeps ✅ Passed PASS. The only changed shell/runtime file is tests/test_bundled_ghostty_theme_picker_helper.sh, which is deterministic test scaffolding. Its added lines only add theme scenarios and assertions. The …
Cmux Algorithmic Complexity ✅ Passed PASS. The changed production Swift uses linear scans over each config file and over the theme tokens. The new repair path performs a small constant number of passes; it does not nest a full scan insid…
Cmux Swift Concurrency ✅ Passed PASS. The theme-repair Swift changes add synchronous parsing, file reads, and a value-extractor closure. They do not add Dispatch queues, Combine state, completion-handler async APIs, or fire-and-forg…
Cmux Swift @Concurrent ✅ Passed The changed Swift theme-repair code adds no async, @concurrent, or nonisolated async functions. loadConfigFile, cmuxAppSupportConfigValue, and loadCmuxManagedThemeRepairIfNeeded are synchr…
Cmux Swift Package Boundaries ✅ Passed PASS: The reusable theme parsing and managed-theme repair logic is in the CmuxTerminalCore SwiftPM target (GhosttyConfig+CmuxThemeRepair.swift), with package tests in CmuxTerminalCoreTests. The …
Full details: Out of Scope Changes check

Explanation

Most changes support issue #10068, but docs/ghostty-fork.md also removes the PR #183 regression-coverage block and an unrelated Fish integration note. These documentation deletions are not required for the theme repair.

✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issue-10068-themes-single-sided

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.

@austinywang
austinywang marked this pull request as ready for review August 18, 2026 21:07
@cursor

cursor Bot commented Aug 18, 2026

Copy link
Copy Markdown

Bugbot is paused — on-demand spend limit reached

Bugbot uses usage-based billing for this team and has hit its on-demand spend limit.

A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue.

…ngle-sided

# Conflicts:
#	Sources/GhosttyTerminalView.swift
#	docs/ghostty-fork.md
#	ghostty
#	scripts/ghosttykit-checksums.txt
@greptile-apps

greptile-apps Bot commented Aug 24, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR makes cmux-generated Ghostty theme selections consistently use valid two-sided conditional values and repairs legacy single-sided managed values while preserving recursive-include precedence.

  • Updates the Swift CLI and bundled Ghostty picker encoders to emit both light and dark theme sides.
  • Defaults fresh or one-sided picker state to target both appearances.
  • Adds managed-config repair in both Ghostty configuration loading paths.
  • Updates the Ghostty submodule, artifact checksums, project wiring, documentation, and regression coverage.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
CLI/CMUXCLI+Themes.swift Encodes single-sided selections as valid two-sided Ghostty values and targets both sides for incomplete picker state.
Packages/macOS/CmuxTerminalCore/Sources/CmuxTerminalCore/Config/GhosttyConfig+CmuxThemeRepair.swift Adds focused normalization for legacy single-sided themes inside cmux-managed blocks.
Packages/macOS/CmuxTerminalCore/Sources/CmuxTerminalCore/Config/GhosttyConfig.swift Applies managed-theme normalization before recursive includes so later included configuration retains precedence.
Sources/GhosttyTerminalView+ThemeRepair.swift Reads the active cmux-managed configuration and derives an in-memory repair value when needed.
Sources/GhosttyTerminalView.swift Moves the repair before recursive config loading, resolving the previously reported include-precedence reversal.
cmux.xcodeproj/project.pbxproj Wires the new production source and retains the relevant runtime and test source memberships.
ghostty Advances the Ghostty fork pin to include matching two-sided picker encoding.
scripts/ghosttykit-checksums.txt Pins checksums for the theme-fix artifact and current fork-main artifact.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  A[Load Ghostty default files] --> B[Load cmux-managed config]
  B --> C{Managed theme is single-sided?}
  C -- Yes --> D[Inject valid light/dark repair]
  C -- No --> E[Keep parsed configuration]
  D --> F[Load recursive includes]
  E --> F
  F --> G[Apply final conditional override]
Loading

Reviews (2): Last reviewed commit: "fix: preserve included theme precedence ..." | Re-trigger Greptile

Comment thread Sources/GhosttyTerminalView.swift Outdated
@cursor

cursor Bot commented Aug 24, 2026

Copy link
Copy Markdown

Bugbot is paused — on-demand spend limit reached

Bugbot uses usage-based billing for this team and has hit its on-demand spend limit.

A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue.

…ngle-sided

# Conflicts:
#	cmux.xcodeproj/project.pbxproj
#	docs/ghostty-fork.md
#	ghostty
#	scripts/ghosttykit-checksums.txt
@vercel

vercel Bot commented Sep 1, 2026

Copy link
Copy Markdown

Deployment failed for project cmux166 with the following error:

Resource is limited - try again in 60 minutes (more than 450, code: "api-deployments-paid-per-hour").

Learn More: https://vercel.com/manaflow?upgradeToPro=build-rate-limit

@vercel

vercel Bot commented Sep 1, 2026

Copy link
Copy Markdown

Deployment failed for project cmux41 with the following error:

Resource is limited - try again in 60 minutes (more than 450, code: "api-deployments-paid-per-hour").

Learn More: https://vercel.com/manaflow?upgradeToPro=build-rate-limit

@blacksmith-sh

This comment has been minimized.

@austinywang

Copy link
Copy Markdown
Contributor Author

I have read the CLA Document and I hereby sign the CLA

@vercel

vercel Bot commented Sep 1, 2026 •

Copy link
Copy Markdown

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

Project Deployment Actions Updated
cmux166 Ready Ready Preview Sep 12, 2026 2:33am UTC
cmux41 Ready Ready Preview Sep 12, 2026 2:33am UTC

@austinywang

Copy link
Copy Markdown
Contributor Author

Re: Blacksmith report comment 5492325558 — fixed in 23e746b. The CLA policy guard intentionally remains on a GitHub-hosted runner because it executes the immutable base-controlled security validator; I documented it with the existing github-hosted-required exception recognized by test_ci_self_hosted_guard.sh. This keeps the security boundary intact while making the runner-policy check pass.

…ngle-sided

# Conflicts:
#	.github/workflows/cla-policy-guard.yml

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

Stale Bugbot comment from a previous run.

Comment thread docs/ghostty-fork.md Outdated

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

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 482d75d. Configure here.

@lawrencecchen

Copy link
Copy Markdown
Contributor

Mac fleet instructions for head 2ec1e0a83a9f06f0ab16bc314e757162b1b54df1. Planned tag: pr-10358-2ec1e0a8; this is not yet a published build.

JOB_JSON=$(~/.local/bin/cmux-ci submit --kind cmux --command 'CMUX_FLEET_BUILD_TAG=pr-10358-2ec1e0a8 /Users/Shared/cmux-build-fleet/recipes/cmux.sh https://github.com/manaflow-ai/cmux.git 2ec1e0a83a9f06f0ab16bc314e757162b1b54df1' --artifact artifacts/cmux.app.zip --workspace https://github.com/manaflow-ai/cmux/pull/10358 --source-digest 2ec1e0a83a9f06f0ab16bc314e757162b1b54df1 --cache-key cmux:pr-10358 --min-free-bytes 268435456000 --label cmux --label ram48)
JOB_ID=$(python3 -c 'import json,sys; print(json.load(sys.stdin)["id"])' <<<"$JOB_JSON")
~/.local/bin/cmux-ci wait "$JOB_ID" --receipt artifacts/fleet/$JOB_ID.json
~/.local/bin/cmux-ci publish-hq "$JOB_ID"

Use an existing campaign job ID if one is already posted; do not submit a duplicate. A wait timeout leaves the remote job running. Published results will include an exact-head artifact link and timing/disk receipt. This recipe validates the macOS app only, not iOS or tests. Never use maclease or put credentials in a PR comment.

@teamleaderleo teamleaderleo added area: appearance Themes, light and dark mode, window chrome S3: minor Wrong behavior with a workaround closing-soon Conflicting or red with no activity for 7+ days; closes 2026-10-06 unless the label is removed labels Sep 30, 2026
@teamleaderleo

Copy link
Copy Markdown
Collaborator

Closing; reopen if you still want it.

This branch was successfully deployed

2 active deployments
Preview – cmux41 — 2ec1e0a8 Deployed Sep 12, 2026 by vercel[bot]
Preview – cmux166 — 2ec1e0a8 Deployed Sep 12, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: appearance Themes, light and dark mode, window chrome closing-soon Conflicting or red with no activity for 7+ days; closes 2026-10-06 unless the label is removed S3: minor Wrong behavior with a workaround

Projects

None yet

Development

Successfully merging this pull request may close these issues.

cmux themes selections never apply: single-sided theme = light:<name> values are rejected by Ghostty's parser

3 participants