Skip to content

fix(cli): enable DEC synchronized output on Warp to reduce redraw flicker - #8832

Closed
chiga0 wants to merge 1 commit into
mainfrom
fix/warp-synchronized-output
Closed

fix(cli): enable DEC synchronized output on Warp to reduce redraw flicker#8832
chiga0 wants to merge 1 commit into
mainfrom
fix/warp-synchronized-output

Conversation

@chiga0

@chiga0 chiga0 commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator

What this PR does

Adds Warp and Ghostty to the allowlist of terminals that receive DEC 2026 synchronized updates around each frame write, so both get the same atomic erase-then-rewrite treatment as WezTerm and iTerm.

Why it's needed

The renderer's frame update erases the previous frame and rewrites it; terminals that paint intermediate states show that as flicker, most visible during resize drags (issue #8557). Warp answers a DECRQM 2026 probe with status 2 (mode recognized, currently reset) and Ghostty implements synchronized output natively, but both were outside the allowlist. With updates enabled, each erase+draw pair applies atomically and the drag flicker collapses to clean relayouts. The existing QWEN_CODE_DISABLE_SYNCHRONIZED_OUTPUT=1 escape hatch covers any regression.

Reviewer Test Plan

How to verify

In Warp or Ghostty, run npm run dev, send a prompt and watch streaming, then resize the window: expected frame updates without erase/rewrite tearing; with QWEN_CODE_DISABLE_SYNCHRONIZED_OUTPUT=1 the previous flicker returns. A DECRQM probe (printf '\e[?2026$p' in Warp) returns CSI ? 2026 ; 2 $ y. Non-allowlisted terminals are unaffected.

Evidence (Before & After)

Capability proven by the DECRQM probe (Warp) and Ghostty's documented DEC 2026 support; author-observed on Ghostty: drag flicker reduced to atomic relayouts when combined with the repaint fix in the companion PR.

Tested on

OS Status
🍏 macOS
🪟 Windows ⚠️
🐧 Linux ⚠️

Environment (optional)

npm run dev in Warp and Ghostty on macOS.

Risk & Scope

Linked Issues

References #8557. Does not close it.

中文说明

这个 PR 做了什么

把 Warp 与 Ghostty 加入 DEC 2026 synchronized updates 白名单,与 WezTerm/iTerm 同等获得原子化帧更新。

为什么需要

渲染器每帧先擦后写;会把中间态画出来的终端表现为闪烁,resize 拖拽时最明显(#8557)。Warp 的 DECRQM 2026 探针返回 status 2,Ghostty 原生支持 2026,但二者此前不在白名单。启用后擦+写原子应用,拖拽闪烁收敛为干净的重排。逃生门 QWEN_CODE_DISABLE_SYNCHRONIZED_OUTPUT=1 覆盖回归。

评审测试计划

Warp/Ghostty 中 npm run dev,流式输出并拖拽:期望无擦/写撕裂;设逃生门后旧闪烁复现。非白名单终端不受影响。

风险与范围

关联 issue

参考 #8557,不关闭。

@qwen-code-ci-bot

qwen-code-ci-bot commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator

Qwen Triage finishedview run. See the stage comments in this thread for the result.

Qwen Triage 已完成 —— 查看运行。结果见本线程中的各阶段评论。

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Thanks for the PR!

Template looks good ✓

Problem: Observed, not theoretical. The flicker half of #8557 was investigated directly on Warp (probe evidence recorded in the issue thread): a DECRQM 2026 probe in Warp answers CSI ? 2026 ; 2 $ y (mode recognized, currently reset), so synchronized updates are available there, and without them the renderer's erase-then-rewrite frame updates show as flicker. The resize-duplication half of the issue is explicitly scoped out here, with documented negative evidence that it is Warp platform behavior — honest scoping, and it says so in the description.

Direction: Aligned. DEC 2026 synchronized output is already qwen-code's established flicker mitigation (WezTerm and iTerm sit on this same allowlist), and the area is actively maintained in the same spirit elsewhere — Claude Code's changelog has several entries fixing rendering flicker exactly this way (enabling synchronized output for tmux 3.4+, and for JetBrains IDE terminals).

Size: Not applicable — packages/cli/src/ui/utils/ is not a core path; 9 production lines (3 of them the why-comment) + 1 test line.

Approach: The scope is exactly right. One allowlist entry, one parameterized test row, one why-comment; the existing QWEN_CODE_DISABLE_SYNCHRONIZED_OUTPUT=1 escape hatch covers any regression. Nothing to cut, nothing unrelated in the diff.

Risk: No elevated risk signals — none of the changed files match the high-risk paths from the revert-history analysis. Non-Warp terminals are untouched by construction: allowlist addition only, exact-match TERM_PROGRAM, and the existing tmux/SSH guard runs before it.

Moving on to code review. 🔍

中文说明

感谢贡献!

模板完整 ✓

问题:真实观测到的问题,不是理论加固。#8557 的闪烁半边已在 Warp 上直接调查过(issue 线程里记录了探针证据):Warp 中 DECRQM 2026 探针返回 CSI ? 2026 ; 2 $ y(识别该模式、当前为 reset),说明 synchronized updates 可用;不启用时,渲染器"先擦后写"的帧更新就表现为闪烁。resize 内容重复的半边被明确排除在本 PR 之外,并有负证据记录为 Warp 平台行为——范围划分诚实,描述里也写明了。

方向:对齐。DEC 2026 synchronized output 本来就是 qwen-code 既有的闪烁缓解手段(WezTerm、iTerm 已在同一白名单),这个方向在别处也在持续维护——Claude Code 的 changelog 里就有多条用同样方式(为 tmux 3.4+、JetBrains IDE 终端启用 synchronized output)修复渲染闪烁的记录。

规模:不适用——packages/cli/src/ui/utils/ 不是核心路径;9 行生产代码(其中 3 行是 why 注释)+ 1 行测试。

方案:范围恰好。一个白名单条目、一行参数化测试、一条 why 注释;既有逃生门 QWEN_CODE_DISABLE_SYNCHRONIZED_OUTPUT=1 兜底回归。没有可砍的,diff 里也没有无关改动。

风险:无升级风险信号——改动文件均未命中 revert 历史分析出的高风险路径。非 Warp 终端按构造不受影响:仅白名单新增、TERM_PROGRAM 精确匹配,且既有的 tmux/SSH 守卫在其之前生效。

进入代码审查 🔍

Qwen Code · qwen3.8-max

Reviewed at b6eac11aba9e42185b11fdd95bf439e24f6ac624 · re-run with @qwen-code /triage

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Code review

Independent take before reading the diff: "flicker on Warp because frame updates erase-then-rewrite; DEC 2026 available per probe" → add Warp's TERM_PROGRAM value to the allowlist in terminalSupportsSynchronizedOutput(), add one row to the parameterized test. That is exactly what this PR does — no simpler path exists, and nothing was missed.

Findings: no blockers, no convention violations.

  • The WarpTerminal value is the right one — the repo already keys on it elsewhere (terminal-image-renderer.ts, osc8 tests), and the exact-match style mirrors the neighboring WezTerm / iTerm.app entries.
  • Semantics for every other terminal are untouched by construction: the disable/force env checks and the tmux/SSH guard all run before the allowlist, and the diff only adds one disjunct. Downstream there is a single consumer (installSynchronizedOutput in startInteractiveUI.tsx), already live for WezTerm/iTerm on TTYs.
  • The why-comment earns its place (probe result + issue reference); worst case on a hypothetical Warp build without DEC 2026 is today's non-atomic behavior, matching the PR's risk note.
  • Reuse check: extends the existing mechanism, adds no parallel utility.

Testing evidence — the PR's own CI, read via API

⛔ Per the unattended-CI rules, no PR code was built or executed here; this section quotes the check results GitHub recorded for the reviewed commit.

Classification of the one red check — stale-branch CI infrastructure failure, not caused by this PR's code: the ubuntu test job died in job setup, before any test ran, with Can't find 'action.yml' ... under '.github/actions/verify-checkout-head'. That composite action exists on main (added 2026-08-05, c73b5ed) but not in this branch's tree — the branch sits 132 commits behind main (merge base da37110e). Any branch cut before Aug 5 fails identically. Consequence: the suite has not actually run on this head yet — including the one test line this PR adds. The macOS/Windows test jobs never ran (skipped). One reassurance for the rebase: synchronizedOutput.ts has not changed on main since 2026-04-25, so the diff applies onto current main exactly as reviewed.

Check Conclusion
Test (ubuntu-latest, Node 22.x) ❌ failure — job setup only (stale branch, see above)
Test (macos-latest, Node 22.x) ⏭️ skipped — never ran
Test (windows-latest, Node 22.x) ⏭️ skipped — never ran
Integration Tests (CLI, No Sandbox) ⏭️ skipped
web-shell E2E Smoke (ubuntu-latest, Node 22.x) ⏭️ skipped
Desktop Shell (ubuntu-22.04) ✅ success
Desktop Shell (windows-2022) ✅ success
Classify PR / label / authorize ✅ success

Sandboxed lanes: once the branch is rebased and CI can actually run, the unit suite pins the mechanism itself (drop the allowlist line and the new test row fails). Sandboxed verification would settle the remaining regression half: @qwen-code /tmux — driving the real TUI confirms the allowlist addition leaves non-Warp rendering untouched. The central claim itself — visibly less flicker on a real Warp terminal — cannot be settled by /verify or /tmux, since neither lane runs Warp; that last mile is the human-on-Warp visual check the PR explicitly asks for (author probe-verified the capability, but visual before/after is ⚠️ author-untested, left to reviewer).

Real-scenario testing: N/A — unattended CI run; live-behavior signal comes from the lanes above, not from anything executed here.

Not verified: any Warp visual behavior (no Warp in CI); the unit suite on this head (job failed in setup before tests ran).

中文说明

代码审查

读 diff 之前的独立判断:既然"Warp 上帧更新先擦后写导致闪烁、探针证实 DEC 2026 可用",做法就是在 terminalSupportsSynchronizedOutput() 的白名单里加上 Warp 的 TERM_PROGRAM 值,并补一行参数化测试。PR 的实现与此完全一致——没有更简的路径,也没有遗漏。

结论:无阻塞项,无规范违规。

  • WarpTerminal 取值正确——仓库其他地方(terminal-image-renderer.tsosc8 测试)已以它为键;精确匹配风格与相邻的 WezTerm / iTerm.app 条目一致。
  • 其他终端的语义按构造不受影响:禁用/强制环境变量检查与 tmux/SSH 守卫都在白名单之前执行,diff 只增加了一个析取项。下游唯一消费者是 startInteractiveUI.tsx 里的 installSynchronizedOutput,WezTerm/iTerm 在 TTY 上早已走这条路径。
  • why 注释(探针结果 + issue 引用)有其价值;假设某 Warp 版本不支持 DEC 2026,最坏情况就是现状(更新不原子),与 PR 风险说明一致。
  • 复用检查:扩展现有机制,未新增平行工具。

测试证据——经 API 读取的 PR 自身 CI

⛔ 按无人值守 CI 规则,此处未构建或执行任何 PR 代码;本节引用 GitHub 对被审 commit 记录的检查结果。

唯一红色检查的定性——分支过旧导致的 CI 基础设施失败,与本 PR 代码无关:ubuntu 测试 job 在任何测试运行之前就死于 job setup,报错 Can't find 'action.yml' ... under '.github/actions/verify-checkout-head'。该复合 action 存在于 main(2026-08-05 由 c73b5ed 引入),但不在此分支的树中——分支落后 main 132 个 commit(merge base da37110e)。任何在 8 月 5 日之前切出的分支都会同样失败。后果:测试套件实际上尚未在这个 head 上跑过——包括本 PR 新增的那一行测试。macOS/Windows 测试 job 未运行(skipped)。一点安心信息:synchronizedOutput.ts 自 2026-04-25 起在 main 上未再变动,rebase 到最新 main 后 diff 与本次审查所见完全一致。

沙箱验证通道:分支 rebase、CI 能真正跑起来之后,单测即可钉住机制本身(删掉白名单那一行,新增测试就会失败)。@qwen-code /tmux 可钉住剩余的回归半边——驱动真实 TUI 确认白名单新增不影响非 Warp 渲染。而核心主张本身——真实 Warp 终端上肉眼可见的闪烁减少——/verify/tmux 都无法验证(两条通道都不运行 Warp);这最后一步正是 PR 明确请求的人工目视(作者已用探针验证能力,但视觉 before/after ⚠️ 作者未测、留给评审)。

真实场景测试:不适用——无人值守 CI 运行;实行为信号来自上述通道,此处未执行任何代码。

未验证:Warp 上的任何视觉行为(CI 无 Warp);该 head 上的单测套件(job 在测试运行前的 setup 阶段失败)。

Qwen Code · qwen3.8-max

Reviewed at b6eac11aba9e42185b11fdd95bf439e24f6ac624 · re-run with @qwen-code /triage

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Confidence: 4/5 — clean, minimal, evidence-backed change; docking the fifth because CI has never actually run on this head (stale branch) and the Warp visual confirmation is still owed.

This one is easy to say yes to. The author didn't guess: the #8557 thread shows a probe-based investigation on a real Warp install (DECRQM 2026 → status 2), and the change itself is the smallest thing that could work — one allowlist entry mirroring the existing WezTerm/iTerm ones, one test row, one honest why-comment, and the scope stated truthfully (flicker half only; the resize-duplication half is documented as Warp platform behavior and deliberately left out). My independent proposal for this problem was identical to the diff, which is about as good as it gets for a fix like this.

The only thing standing between this PR and merge is mechanical, not substantive: the branch sits 132 commits behind main, so the ubuntu test job dies in CI setup (the composite checkout action added on main Aug 5 isn't in the branch's tree) and nothing downstream of it has run. @chiga0 please rebase onto current main — the touched file hasn't changed there since April, so it should apply cleanly, and CI will finally exercise the new test row.

The approval below is pinned to the reviewed commit; pushing the rebase dismisses it by design, and triage will take a fresh look at the green head. Approving while CI is red on this head is safe exactly because of that pinning — branch protection still requires green checks to merge.

中文说明

这个 PR 很容易让人点头。作者不是在猜:#8557 线程里记录了在真实 Warp 环境下的探针调查(DECRQM 2026 → status 2),而改动本身就是能起作用的最小形态——一个镜像既有 WezTerm/iTerm 条目的白名单项、一行测试、一条诚实的 why 注释,范围也说得实话实说(只处理闪烁半边;resize 重复的半边已记录为 Warp 平台行为、有意排除在外)。我对这个问题的独立方案与这个 diff 完全一致——对这类修复来说,这已经是最好的结果。

横在 PR 与合并之间的唯一问题机械而非实质:分支落后 main 132 个 commit,ubuntu 测试 job 死于 CI setup(8 月 5 日加入 main 的复合 checkout action 不在分支树里),其后的所有步骤都没跑。@chiga0 请 rebase 到最新 main——所改文件自 4 月起在 main 上未变,应能干净应用,CI 也才能真正跑到新增的那行测试。

下面的 approve 钉在被审 commit 上;推送 rebase 会按设计将其作废,triage 会对变绿的 head 重新审一遍。之所以敢在 CI 红着的时候 approve,正是因为这种钉定——分支保护仍然要求绿色检查通过才能合并。

Qwen Code · qwen3.8-max

Reviewed at b6eac11aba9e42185b11fdd95bf439e24f6ac624 · re-run with @qwen-code /triage

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM, looks ready to ship. ✅

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

⚠️ Downgraded from Approve to Comment: CI failing: Test (ubuntu-latest, Node 22.x). Reviewed.

中文说明

⚠️ 已从批准降级为评论:CI failing: Test (ubuntu-latest, Node 22.x)。 已审查。

— qwen3.8-max via Qwen Code /review (v0.21.8)

…cker

Warp answers the DECRQM 2026 probe with status 2 (recognized, reset), so
synchronized updates are available there. Without them Warp renders ink's
erase-then-rewrite frame pattern as visible flicker (issue #8557). Add
WarpTerminal to the synchronized-output allowlist; the existing
QWEN_CODE_DISABLE_SYNCHRONIZED_OUTPUT escape hatch covers regressions.
@github-actions

Copy link
Copy Markdown
Contributor

Please do not rebase or force-push to an active PR as it invalidates existing review comments. Note for future reference, the bots always squash all changes into a single commit automatically as part of the integration.

中文

请勿对活跃的 PR 执行 rebase 或 force-push,因为这会使已有的评审评论失效。另外,供日后参考:作为集成流程的一部分,机器人始终会自动将所有改动压缩(squash)为单个提交。

@chiga0

chiga0 commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator Author

Consolidated into #8831 per maintainer preference (single PR for the resize/wake repaint fix + DEC 2026 allowlist). The branch is merged into fix/wake-repaint-vp-clear.

@chiga0 chiga0 closed this Aug 10, 2026
@chiga0
chiga0 deleted the fix/warp-synchronized-output branch August 10, 2026 08:12
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.

2 participants