Repository navigation
Fix garbled Claude Code TUI in cmux ssh remote workspaces (#6352) - #6831
Conversation
The remote shell bootstrap installs the bundled xterm-ghostty terminfo in a background job while deciding TERM synchronously. On a host that lacks the entry, the first shell pass falls back to xterm-256color (and a later pass can pick xterm-ghostty while tic is still writing the database), so a full-screen TUI such as Claude Code renders against a missing/half-written terminfo entry and garbles its output. This test runs the generated terminal-setup lines against an isolated $HOME/terminfo search path so the host's own xterm-ghostty cannot mask the behavior, and asserts the install resolves xterm-ghostty before TERM is exported. It fails against the current background-install code. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ERM (#6352) Claude Code (and other full-screen TUIs) rendered garbled output inside a cmux ssh remote workspace because the remote shell bootstrap installed the bundled xterm-ghostty terminfo in a background job while deciding TERM up front. On a host lacking the entry, the first interactive shell fell back to xterm-256color, and a later shell pass could select xterm-ghostty while the background tic was still writing the (non-atomic) terminfo database — so the TUI started against a missing or half-written entry and scrambled its frame. Make the install synchronous and atomic, in both the app-side bootstrap builder and the CLI's interactive remote shell script: - Compile the bundled terminfo into a temp directory on the same filesystem as ~/.terminfo, then move each compiled entry into place with an atomic rename, so a concurrent reader in another cmux ssh session sharing $HOME never observes a partially written database. - Only select TERM=xterm-ghostty after re-confirming infocmp resolves the entry, so TERM is never xterm-ghostty against an absent/partial terminfo. - Fall back to a direct synchronous compile when mktemp is unavailable, and to xterm-256color when tic is missing — both safe, neither garbles. Validated across /bin/sh, /bin/zsh and bash --posix, with a 12-way concurrency stress (all sessions resolve xterm-ghostty, no corruption, no leftover temp dirs) and nested inside the generated .zshrc heredoc. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning Review limit reached
More reviews will be available in 1 minute and 53 seconds. Learn how PR review limits work. Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file). ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits. 🚦 How do rate limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (3)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
Address review feedback on the terminfo install fix: - Eliminate the duplicated terminfo-install shell generator. The CLI's interactiveRemoteTerminalSetupLines was a byte-for-byte copy of the app-side RemoteInteractiveShellBootstrapBuilder.terminalSetupLines (both compile into the CLI target), so a one-sided future edit could reintroduce #6352 on the CLI path with no test catching it. Delete the copy and delegate to the shared builder, leaving a single implementation covered by the existing regression test. This also gives the internal-visibility widening a production caller. - Remove the only non-atomic write path. When mktemp is unavailable the install now compiles into a per-process $HOME/.terminfo.cmux.$$ directory (unique among live processes) and uses the same atomic-rename move, instead of compiling directly into ~/.terminfo. No branch writes the terminfo database non-atomically, so concurrent cmux ssh sessions sharing $HOME can never observe a partial entry. Validated across /bin/sh, /bin/zsh and bash --posix, including a 12-way no-mktemp concurrency stress (all sessions resolve xterm-ghostty, no corruption, no leftover temp dirs) and nested inside the generated .zshrc. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Fixes #6352
Problem
Running the Claude Code CLI (
claude) — or any full-screen TUI — inside acmux sshremote workspace renders garbled output (stray escape sequences / scrambled frame). Runningclaudelocally is fine, so the bug is specific to the SSH remote path.Root cause
The remote shell bootstrap (
RemoteInteractiveShellBootstrapBuilder.terminalSetupLines, duplicated inCLI/cmux.swift'sinteractiveRemoteTerminalSetupLines) decidedTERMsynchronously but installed the bundledxterm-ghosttyterminfo in a background job (tic -x - … &):On a remote host that lacks the entry:
TERM=xterm-256color(the install hasn't happened yet), andcmux sshsession sharing$HOME) can selectTERM=xterm-ghosttywhile the backgroundticis still writing the non-atomic terminfo database — so the TUI starts against a missing or half-writtenxterm-ghosttyentry and scrambles its frame.Fix
Install the bundled terminfo synchronously and atomically, before deciding
TERM, in both the app-side bootstrap builder and the CLI script:~/.terminfo, then move each compiled entry into place with an atomic rename, so a concurrent reader in anothercmux sshsession never observes a partially-written database.TERM=xterm-ghosttyafter re-confirminginfocmpresolves the entry, soTERMis neverxterm-ghosttyagainst an absent/partial terminfo.mktempis unavailable;xterm-256colorwhenticis missing. Neither garbles.Testing
Two-commit red/green regression test in
cmuxTests/ShellStartupMatrixTests.swift(remoteTerminalSetupInstallsGhosttyTerminfoBeforeChoosingTerm):The test runs the generated setup lines against an isolated
$HOME/TERMINFO/TERMINFO_DIRSsearch path (so the host's ownxterm-ghosttycan't mask the behavior) and asserts the install resolvesxterm-ghosttybeforeTERMis exported.Validated locally across
/bin/sh,/bin/zsh, andbash --posix, including a 12-way concurrency stress (all sessions resolvexterm-ghostty, no corruption, no leftover temp dirs) and nested inside the generated.zshrcheredoc.Localization
No user-facing strings changed (the diff is shell-script generation only).
🤖 Generated with Claude Code
Need help on this PR? Tag
/codesmithwith what you need. Autofix is disabled.Summary by cubic
Fixes garbled TUI output in
cmux sshremote workspaces by installing the bundledxterm-ghosttyterminfo synchronously and atomically before choosingTERM. Unifies the setup so app and CLI use the same safe path.RemoteInteractiveShellBootstrapBuilder.terminalSetupLines; the CLI now delegates and the duplicate generator was removed.~/.terminfoand atomically rename into place; whenmktempis missing, use per‑process$HOME/.terminfo.cmux.$$with the same atomic move (no direct writes to~/.terminfo).TERM=xterm-ghosttyonly afterinfocmpconfirms it; fall back toxterm-256colorwhenticis unavailable.remoteTerminalSetupInstallsGhosttyTerminfoBeforeChoosingTermto ensure install completes beforeTERMis exported.Written for commit 40143e7. Summary will update on new commits.