12 KiB
闪屏治理:Issue 驱动的 4-PR 实施方案
目标:把闪屏优化从“围绕某个大 PR 拆补丁”改成“围绕真实用户问题逐类关闭 issue”,同时把当前 8 条过细的 PR 计划压缩成 4 条主 PR,并补齐下一步可以直接进入编码的执行边界。 校准时间点:2026-04-24 使用方式:
09-pr-3013-gap-analysis.md只用于参考#3013里哪些 patch 被证明有价值;真正的实施排期、PR 粒度和验证矩阵以本文档为准。 口径约束:如果 00-overview.md、02-screen-flickering.md、08-execution-plan-and-test-matrix.md 中的技术阶段表或切片表与本文档的 PR 粒度看起来不同,以本文档为准;那些文档承担的是“技术演进顺序”和“文件/测试切片”视角。
1. 为什么从 8 条收成 4 条
8 条方案在“研究和拆因”层面是对的,但落到真实开发节奏上会有两个问题:
- PR 太碎,review 和回归成本偏高
- 太多前后依赖会让排期看起来像 8 次独立发布,执行上反而拖慢
但我也不建议再继续压到 2-3 条。原因是下面两类问题必须保持隔离:
- 窄屏 / interactive shell 问题:这是 shell serializer 和 viewport 语义问题,不能再混回主 UI PR
- 终端协议层问题:synchronized output / frame coalescing 需要按终端家族灰度,也不应和应用层修复绑死
因此,这一版把原来的 8 条收成 4 条主 PR,作为执行层的平衡点。
2. 证据边界
本文档只使用四类证据:
- 当前 qwen-code 源码
- 真实用户 issue
- Gemini CLI / Claude Code 的可借鉴实现
#3013中已被证明有价值的 patch
其中第 4 类只做交叉印证,不作为拆 PR 的主轴。按 2026-04-24 复核,#3013 仍是 OPEN + CHANGES_REQUESTED,最近更新时间仍为 2026-04-22T11:28:48Z。
3. 4 条主 PR 总览
| 主 PR | 关闭的问题类 | 覆盖旧拆分 | 代表 issue | 主要范围 | 明确不带 |
|---|---|---|---|---|---|
PR-1 |
主屏闪烁基础修复 | PR-Prep + PR-A1 + PR-B1 的安全子集 |
#1184 #1491 #3007 #938 #1861 #2924,以及 #2748 的 flicker 子问题 |
counters、回归 harness、content/thought throttle、已清屏路径重复 clear 削减 | 不带 narrow-shell、不带 synchronized output、不带大输出详情重构 |
PR-2 |
大输出与详情展开稳定性 | PR-D1 + PR-E1 |
#1479 #2424 #2624,以及 #1491 #1861 #2924 的展开子问题 |
pre-slicing、stable height、bounded detail panel、update cadence decoupling | 不带 synchronized output、不带 shell serializer、不带 refreshStatic() 主链改造、不带 core budgeting |
PR-3 |
窄屏 / interactive shell 专项 | PR-C1 |
#2912 #2972 #1591 #1778 |
shell serializer、live viewport vs transcript、interactive prompt 回归 | 不带 tool budgeting、不带 detail panel、不带终端协议层优化 |
PR-4 |
终端协议层残余闪烁收尾 | PR-A2 |
#3144,以及主屏闪烁在特定终端中的残余问题 |
synchronized output、frame write 合并、runtime probe、allowlist | 不带主 UI 语义改造、不带 shell serializer、不带大输出重构 |
非 flicker 主线 follow-up:
Follow-up-F1:通用 tool budgeting 与 summary/detail 语义,主要对应#2818#1008#355Follow-up-F2:markdown-heavy 大输出的 parser/block 级降峰,属于渲染层 follow-up,不纳入当前 4 条 flicker 主 PR
PR1-4 之后的彻底闭环口径:
当前 4 条 PR 是必要基础,但它们不应被描述为“已经彻底关闭所有 TUI 闪烁 / 重复输出 issue”。refreshStatic() 替换型整屏 clear、bounded detail / virtual scroll、JetBrains / Windows / cmux 终端矩阵、以及 Claude 式 renderer ownership 的取舍,统一在 15-complete-tui-flicker-closure-plan.md 中继续推进。后续不建议再拆成过多小 PR,而应按 Closure-A、Closure-B、Closure-C 三个闭环包组织。
4. 推荐顺序
推荐按下面顺序推进:
PR-1PR-2PR-3PR-4
排序理由:
PR-1先解决主路径上的“普通流式闪烁 + 整屏 clear”PR-2再解决“大输出 / 展开详情导致的布局风暴和不可读”PR-3继续单独收敛最复杂的窄屏 shell 问题PR-4最后上终端协议层,是为了避免应用层问题没收敛前就引入终端家族灰度复杂度
5. PR-1:主屏闪烁基础修复
5.1 关闭的问题类
主屏路径上最常见的两类闪烁:
- 普通 assistant 流式输出时的高频抖动
refreshStatic()导致的整屏 clear + redraw
Issue 边界:
#2748在这条 PR 中只作为 startup/view-switch flicker 样本,不代表这条 PR 会关闭其 slow startup 或 verbose output 全部诉求
5.2 合并后的范围
- 输出层 counters 与固定回归场景
useGeminiStream.ts的 content/thought throttle- 强制 flush 语义
refreshStatic()的安全子集拆分:- 已清屏路径使用
remountStaticHistory() - 替换旧 static output 的路径继续保留 clear,等待后续 renderer 策略
- 已清屏路径使用
- slash clear /
/clear重复清屏削减
5.3 为什么这几块适合并在一起
它们都属于主屏主路径的基础修复:
- 验证场景高度重合
- 同一组 counters 能同时衡量收益
- 都不依赖 shell serializer 或终端协议层 rollout
5.4 明确不带
- synchronized output
- narrow-shell / interactive shell 修复
- 大工具输出 pre-slicing
- stable height / bounded detail panel
5.5 固定验证场景
- 长 assistant 回答(至少 500 token)
- thought + content 混合流
- 冷启动(无 MCP)
- 冷启动(含 1 个慢 MCP server)
/settings上下切换- compact mode merge
- active view switch
- 终端宽高 resize
/clear
5.6 Done 定义
stdout.write频率显著下降clear_terminal_count明显下降- 结束 / cancel 不丢尾部内容
- 非致命布局变化不再默认清屏
6. PR-2:大输出与详情展开稳定性
6.1 关闭的问题类
这一条主 PR 负责解决“只要输出很大、或者一展开详情,界面就开始抖和变得不可读”的 UI 问题簇,包括:
- 大工具输出导致的全量 layout
- 工具 / 子 agent 展开导致的高度暴涨
- detail surface 缺乏边界,导致主流式区域和详情区域互相拖累
6.2 合并后的范围
- plain text / ANSI pre-render slicing
MaxSizedBox降级成 safety netuseStableHeight- bounded detail panel
- assistant / tool progress / subagent progress 的刷新节奏解耦
6.3 为什么这几块适合并在一起
从用户视角,它们其实是同一个问题类:
- “大结果一出来就闪”
- “展开详情就抖”
- “结果太长时既难读又浪费上下文”
这些问题共用同一组回归样例,也都围绕 tool/subagent detail surface。
6.4 明确不带
- synchronized output
- shell serializer 改造
refreshStatic()主链改造- 全量虚拟滚动
- scheduler 层统一 budgeting
llmContent/ 模型可见预算语义变更- 以 markdown 降级替代真正的 parser/block 降峰
Issue 边界:
#2818#1008#355不在这条 flicker 主 PR 的 closure 范围内,它们属于Follow-up-F1- markdown-heavy 大输出不应被这条 PR 宣称“已彻底解决”;它只需要保证不因本 PR 退化,并为后续 parser/block 降峰留下接口
6.5 固定验证场景
npm installgit log --oneline- 5000 行纯文本
- ANSI 彩色输出
ctrl+ectrl+f- subagent 执行中展开
- markdown-heavy 工具结果(仅验证“不退化”和“不会错误 claim 已解决”)
6.6 Done 定义
- 大工具输出不再每次 layout 全量内容
- 展开详情不再造成明显闪屏
- hidden lines 统计可解释
- 不引入
llmContent语义变化
7. PR-3:窄屏 / interactive shell 专项
7.1 关闭的问题类
窄终端、多 pane tmux、interactive shell 场景下的:
- 重复打印
- 顶部 / 底部来回跳
- 无限滚动
7.2 范围
shellExecutionService.tsterminalSerializer.ts- live viewport / transcript archival 语义拆分
- interactive / integration 回归
7.3 为什么必须单独保留
这不是普通的“主屏闪烁”问题,而是 shell viewport 序列化和滚动语义问题。继续把它混进主 UI PR,会同时拖慢验证和误伤其他路径。
7.4 明确不带
- synchronized output
- tool budgeting
- bounded detail panel
refreshStatic()语义改造
7.5 固定验证场景
- 40 列以下窄终端
- tmux 多 pane
- 宽度缩小后继续输出
git commit- interactive shell prompt / pager
showColor=true的彩色 shell 输出showColor=true+ tmux / 窄终端 组合路径
7.6 Done 定义
- 不再重复刷旧 viewport
- 顶 / 底往返滚动停止
- 文档与实现中不再把
#1778的 one-line fix 当作现状根因
8. PR-4:终端协议层残余闪烁收尾
8.1 关闭的问题类
在主 UI 路径已经收敛后,特定终端中仍残留的帧撕裂和可见中间帧问题。
Issue 边界:
#2903在这条 PR 中是必须显式验证的 JetBrains 环境样本,不是默认的 closure target- 只有当 JetBrains 被明确纳入 support matrix / allowlist / probe 成功路径时,才应把它提升为“修复目标”
8.2 范围
- synchronized output wrapper
- frame 内 write 合并
- runtime probe / allowlist
- fallback / rollback
8.3 为什么必须最后做
这条 PR 的收益很大,但风险也最高:
- 依赖终端家族差异
- 涉及 stdout monkeypatch 和 callback / Buffer 语义
- 如果应用层问题没先收敛,协议层 patch 的收益很难验证
8.4 明确不带
- 主屏语义改造
- shell serializer
- 大输出和 detail panel 重构
8.5 固定验证场景
- WezTerm
- kitty
- JetBrains 终端
- tmux / SSH
- Terminal.app 或未命中 allowlist 的终端
8.6 Done 定义
- 已支持终端的可见帧撕裂下降
- 未纳入 allowlist 的终端不出现退化
- Buffer / callback 语义不变
- JetBrains 若未进入 allowlist/probe 成功路径,则至少验证“不退化”,不宣称已修复
9. 为什么不再继续往下合
压到 4 条以后,我不建议再继续合并:
- 不要把
PR-3合进其他 PR 窄屏 / shell 问题的验证方法完全不同。 - 不要把
PR-4合进其他 PR 终端协议层需要单独灰度和单独回滚。 - 不要把
PR-1和PR-2合并 合起来就会重新变成一个“主屏语义 + 大输出表面 + detail panel + budgeting”的超大 PR,review 会再次失焦。
10. 统一 PR 模板
每条主 PR 都建议强制使用同一模板:
- Problem Class
- 本 PR 关闭哪一类用户问题
- Linked Issues
- 只链接与这一类问题直接相关的 issue
- Scope
- 明确文件和模块
- Non-goals
- 明确故意不带什么
- Reference
- 若借鉴
#3013/ Gemini CLI / Claude Code,写清楚借鉴点
- 若借鉴
- Validation
- 固定复现场景
- Rollback
- 开关或失败信号
11. 最终建议
如果现在就要进入真实实施,我建议按下面的 4 条主 PR 推进:
PR-1:主屏闪烁基础修复PR-2:大输出与详情展开稳定性PR-3:窄屏 / interactive shell 专项PR-4:终端协议层残余闪烁收尾
在这 4 条之后,再进入两个 follow-up:
Follow-up-F1:通用 tool budgetingFollow-up-F2:markdown-heavy 输出降峰与 parser/block 层优化
这样数量比 8 条明显更可执行,但还没有退回到“一个巨大 PR 试图一口吃掉所有闪屏问题”的老路。
如果下一步准备直接进入实现,请按顺序使用: