Add a way to force-disable synchronized output (counterpart to CLAUDE_CODE_FORCE_SYNC_OUTPUT)
What happened?
CLAUDE_CODE_FORCE_SYNC_OUTPUT=1 exists to force synchronized output on for terminals that auto-detection misses. There is no counterpart to force it off.
That asymmetry currently has no workaround for users hitting a terminal whose synchronized-output implementation misbehaves with Claude Code's rendering pattern. Concretely, on Ghostty the fullscreen renderer accumulates progressive corruption — stale fragments from earlier frames (stray ─, box-drawing remnants) overlapping live UI, worsening over a session, cleared temporarily by Ctrl+L. Ghostty has three open/unresolved reports of this (ghostty-org/ghostty#12062, #11001, #12685), including a ~60-line reproduction containing no Claude Code code at all that garbles Ghostty with synchronized output + incremental cursor-positioned writes and renders correctly both with sync disabled and in Kitty.
So the defect is arguably Ghostty's — but Claude Code is the only party that can currently give affected users an escape, and it doesn't expose one.
Measurements
Captured through a pty (TERM=xterm-ghostty, TERM_PROGRAM=ghostty, TMUX unset, 40-row terminal, "tui": "fullscreen"), full startup + 18s idle:
| Observation | Value |
|---|---|
| Balanced ESC[?2026h / ESC[?2026l pairs | 16–18 per startup render |
| Absolute ESC[row;colH inside sync blocks | 34 |
| Same, outside sync blocks | 0 |
| Target rows of those CUPs | 35, 36, 40 (of 40) — status/input area |
| ESC[<n>A cursor-up sequences | 0 |
| DECSTBM sets | 1, and it is ESC[r (reset) |
Two notes that may be useful upstream:
- Synchronized output is active on Ghostty outside tmux. The 2.1.200 changelog entry frames sync output as a tmux fix; per-terminal auto-detection has it on for Ghostty independently. Worth confirming that's intended.
- This does not require DECSTBM. The Ghostty report describes the bug as involving a reserved scroll region, but in fullscreen mode Claude Code resets the scroll region and still triggers it. The sufficient ingredients are sync output wrapping incremental cursor-positioned writes to the bottom rows.
Corruption rate scales with status-area update frequency — it is markedly worse with background agents running and with a frequently-refreshing custom statusline.
Why the existing options don't cover it
CLAUDE_CODE_ALT_SCREEN_FULL_REPAINT=1does fix the corruption, but full-screen repaint per frame also clamps the frame interval to a 480ms floor (vs the 16ms default). The result is constant visible flicker at idle, which several users find worse than the artifacts it cures.- Changing
TERM/TERM_PROGRAMso the terminal no longer matches the sync allowlist does work, but the same environment variables gate other behaviour — unsettingTERM_PROGRAMalso stops the kitty keyboard protocol / modifyOtherKeys push (CSI >1u,CSI >4;2m), breaking Shift+Enter and modifier chords, and it degrades hyperlink and notification support. Lying about the terminal to disable one feature is not a reasonable ask of users. - Downgrading isn't viable; this is present on the current release.
Request
An env var (and ideally a settings.json key) to force synchronized output off, mirroring the existing force-on:
CLAUDE_CODE_FORCE_SYNC_OUTPUT=0 # or CLAUDE_CODE_DISABLE_SYNC_OUTPUT=1
Today CLAUDE_CODE_FORCE_SYNC_OUTPUT is truthy-checked, so =0 falls through to auto-detection rather than disabling. A three-line change would give affected users a clean escape that costs nothing for everyone else, and would also let people test whether sync output is implicated in other rendering reports.
Related: #69619 (documents CLAUDE_CODE_ALT_SCREEN_FULL_REPAINT, which is otherwise undocumented).
Environment
- Claude Code 2.1.218
- Ghostty 1.3.1 (stable), macOS, Apple Silicon
"tui": "fullscreen",TERM=xterm-ghostty- Not running inside tmux