fix(ci): tell a visuals coverage gap apart from "no visual change" - #7375
Conversation
An empty visual preview means one of two opposite things: the change genuinely moves no pixel, or no scenario renders the UI it touches. The bot printed the same green check for both, so the second — a coverage gap, where the preview literally cannot see the feature — read as a clean bill of health. That has now happened three times (QwenLM#7035 primary label, QwenLM#7221 worktree badge, QwenLM#7365 empty-state toggle), each caught only because a maintainer noticed the missing image and asked. The signal to tell them apart was already there and unused: the render workflow only runs when the web-shell client or webui source changed, so an empty preview is by construction "UI code changed, nothing rendered differently". When no view changed, look at which files the PR touched. If any are render-shaping (.tsx / .css / .svg under the rendered surface, excluding test and scenario code), list them and say the result is ambiguous, with a pointer to where a scenario goes. Otherwise keep the green check — a logic-only PR with no visual delta is expected, and prompting there would train everyone to ignore the prompt when it matters. The path list comes from the PR files API in the privileged publish job, which never checks out PR code; if that call fails the comment falls back to the current wording.
|
Thanks for the PR! Template looks good ✓ Problem: observed and recurring. Three real PRs (#7035, #7221, #7365) shipped new UI that the preview bot couldn't see, and each time the green check masked a coverage gap that only a human happened to notice. This is not theoretical — it has a track record. Direction: aligned. This is a CI quality-of-life fix for the visual preview bot, squarely within the project's infrastructure. CHANGELOG has no direct reference, but the area is clearly relevant — the preview bot is an active part of the review workflow. Size: not applicable — no core module paths touched (all changes are in Approach: the scope feels right. The fix is proportionate — it only changes what the comment says when the preview is empty, and only for the narrow set of files that actually shape rendering ( Moving on to code review. 🔍 中文说明感谢贡献! 模板完整 ✓ 问题:已观测到且反复出现。三个真实 PR(#7035、#7221、#7365)的新 UI 预览 bot 看不见,每次绿勾都掩盖了只有人碰巧注意到的覆盖缺口。这不是理论问题——有实际记录。 方向:对齐。这是视觉预览 bot 的 CI 质量改进,完全在项目基础设施范围内。CHANGELOG 无直接引用,但该领域明显相关——预览 bot 是审查流程的活跃部分。 规模:不适用——未触及核心模块路径(所有改动在 方案:范围合理。修复是成比例的——只改预览为空时评论的措辞,且只针对真正影响渲染的文件(渲染面下的 进入代码审查 🔍 — Qwen Code · qwen3.7-max Reviewed at |
Code ReviewIndependent proposal: given the ambiguity of an empty preview, I would filter the PR's changed files for render-shaping extensions ( Comparison with the diff: the PR's approach matches this almost exactly. The implementation is clean and well-structured:
No critical blockers. No AGENTS.md violations. The two path prefixes are duplicated from the render workflow's Real-Scenario TestingRan the script the way the workflow does — empty stage dir + changed-paths file, comparing base code vs PR code. Before (base code — green check regardless)After (this PR — coverage gap flagged)Control (logic-only PR — green check preserved)The 中文说明代码审查独立方案: 鉴于空预览的歧义性,我会过滤 PR 改动文件中渲染面下的渲染相关扩展名( 与 diff 的对比: PR 的方案几乎完全匹配。实现干净、结构良好:
无关键阻塞项。无 AGENTS.md 违规。两个路径前缀从渲染工作流的 真实场景测试按工作流的方式运行脚本——空的 stage 目录 + 改动路径文件,对比基础代码和 PR 代码。
单元测试:19/19 通过。 — Qwen Code · qwen3.7-max Reviewed at |
|
Confidence: 5/5 — clean across every stage; the problem is real, the fix is proportionate, and the implementation is exactly what I would have written. This PR does one thing and does it well. Three real incidents (#7035, #7221, #7365) showed that the preview bot's green check can mask a coverage gap — new UI that no scenario renders reads as "nothing broke." The fix disambiguates by checking which files the PR touched before declaring victory, and only for the narrow set of extensions that actually shape rendering. The fallback is safe (API failure → existing green check), the injection handling is correct (backtick stripping + HTML escaping on fork-controlled paths), and the tests cover every branch including the adversarial case. The before/after confirms the behavior change is exactly what was promised: the If I had to maintain this in six months, I'd thank the author — the code is well-commented, the design decisions are documented, and the scope is tight. No drive-by refactors, no unrelated changes, no over-abstraction. 中文说明置信度:5/5 —— 每个阶段都干净;问题真实存在,修复成比例,实现正是我会写的样子。 这个 PR 只做一件事,而且做得好。三次真实事件(#7035、#7221、#7365)表明预览 bot 的绿勾可能掩盖覆盖缺口——没有场景渲染的新 UI 被读成"没坏"。修复通过在宣布胜利前检查 PR 改了哪些文件来消除歧义,且只针对真正影响渲染的扩展名。回退是安全的(API 失败 → 现有绿勾),注入处理正确(对 fork 控制的路径去除反引号 + HTML 转义),测试覆盖了每个分支包括对抗性场景。 Before/after 确认行为变化正是所承诺的: 如果六个月后我要维护这个,我会感谢作者——代码注释良好,设计决策有文档,范围紧凑。没有顺手重构,没有无关改动,没有过度抽象。 — Qwen Code · qwen3.7-max Reviewed at |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
LGTM, looks ready to ship. ✅
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
No issues found. LGTM! ✅
— qwen3.7-max via Qwen Code /review
yiliang114
left a comment
There was a problem hiding this comment.
changes for web shell, LGTM
yiliang114
left a comment
There was a problem hiding this comment.
LGTM — the logic is correct and the tests replay the real extracted blocks convincingly.
Non-blocking, for a follow-up: the amount of branching logic living inside run: | blocks (GraphQL queries, jq pipelines, while-read loops, multi-arm conditionals) is getting hard to maintain — tests have to regex-extract YAML substrings and replay them under bash, which couples test correctness to YAML indentation. Worth extracting these into .github/scripts/autofix-*.mjs standalone scripts (same pattern as web-shell-visuals-publish.mjs) so they get proper linting, direct unit-test imports, and reuse across workflows. Not blocking this PR — the current approach works and is well-tested; just flagging the direction for when the next chunk of logic lands here.
|
Released in v0.20.1. |
|
PR #7375 ( ✅ completed |
What this PR does
When the web-shell visual preview finds that no view changed, it now checks which files the PR actually touched before declaring victory. If any of them shape what a view looks like, the comment says the result is ambiguous and lists them, instead of printing a green check. If nothing render-shaping changed, the green check stays exactly as it is today.
Why it's needed
An empty preview means one of two opposite things: the change genuinely moves no pixel, or no scenario renders the UI it touches. The bot printed the same
✅ _No screenshot changes against the PR base._for both — so the second case, where the preview literally cannot see the feature, read as a clean bill of health.That is not hypothetical. It has happened three times, each caught only because a maintainer noticed the missing image and asked about it:
session.worktreemetadata; no scenario set it/; every scenario goes to/session/:idThe signal needed to tell the two cases apart already existed and was simply unused: the render workflow only triggers on
packages/web-shell/client/**andpackages/webui/src/**, so an empty preview is by construction "UI code changed, yet nothing rendered differently". That is worth a second look, and nobody was taking it.The prompt is deliberately narrow, because a warning everyone learns to skip is worse than no warning. Only
.tsx/.css/.svgunder the rendered surface count —.tshooks, utils and types routinely land with no visual delta, and test/scenario code drives the preview rather than being previewed. Every.svgunder that surface is a bundled UI icon (client/assets/icons/), so a changed icon that moves no pixel is the same signal.Reviewer Test Plan
How to verify
Unit tests cover the classifier and both comment branches:
The whole CI helper set still passes (
88/88), andactionlintis clean on the modified workflow.To see the real output, drive the CLI the way the workflow does — an empty stage dir plus a changed-paths file:
Confirm it names only the
.tsxand the.css— the.tshook, the spec file and thepackages/corechange are all correctly ignored. Then re-run with onlyuseThing.tsin the paths file and confirm the green check comes back unchanged.Evidence (Before & After)
Before — what the bot posted on #7365, a PR whose entire point was new UI (real comment):
After — same run, same zero images:
Control — a logic-only PR is left alone. With only
packages/web-shell/client/hooks/useThing.tschanged, the output is byte-identical to today's:Control — the API call failing does not break the comment. With no changed-paths file at all (what happens if the
pulls/:n/filesrequest fails), the output is again today's green check. The workflow logs a::warning::and carries on.Tested on
Environment (optional)
node --testfor the helper tests,actionlintfor the workflow, plus the CLI invocation above. No daemon or browser needed — the changed code is a pure comment builder plus onegh apicall.Risk & Scope
.tsx/.css/.svgwith no visual delta — a prop rename, a comment, dead-code removal. The wording leads with "either the change has no visual effect", and the file list makes it a one-glance dismissal, but the rate is not knowable until it runs on real traffic. If it turns out noisy, narrowing to added/renamed files or gating on an additions threshold is a small follow-up.paths:trigger and must stay in sync by hand; a comment on each side flags this.Linked Issues
Follow-up to #6880 (the preview bot), #6963 (before/after compositor), #7041 (animation freeze) and #7210 (cluster denoise). Motivated by the coverage gaps in #7035, #7221 and #7365.
中文说明
这个 PR 做了什么
当 web-shell 视觉预览发现没有任何视图变化时,先看这个 PR 到底改了哪些文件,再决定怎么说。如果改动里有影响渲染的文件,评论会说明这个结果是有歧义的并列出这些文件,而不是打一个绿勾。如果没有改到渲染相关的文件,绿勾保持原样。
为什么需要
空预览意味着两件完全相反的事:改动确实不产生任何像素差异,或者根本没有场景渲染到它所改的 UI。而 bot 对两种情况打印的是同一句
✅ _No screenshot changes against the PR base._——于是第二种情况(预览压根看不见这个功能)被读成了"一切正常"。这不是假设。已经发生三次了,每次都只是因为维护者碰巧注意到少了配图才问出来:
session.worktree元数据,没有场景设置过/,而所有场景都进/session/:id区分这两种情况所需的信号其实早就存在、只是没被用起来:渲染工作流只在
packages/web-shell/client/**和packages/webui/src/**改动时才触发,所以一个空预览按构造必然是"改了 UI 代码,却没有任何东西渲染得不一样"。这值得看第二眼,而此前没有人去看。提示的触发面刻意收窄,因为一个所有人都学会忽略的警告比没有警告更糟。只有渲染面下的
.tsx/.css/.svg算数——.ts的 hook、工具函数和类型经常正常地不带视觉差异,而测试与场景代码是驱动预览的,本身不是被预览的对象。该目录下的.svg全部是打包进 bundle 的 UI 图标(client/assets/icons/),所以图标改了却零像素差,是同一个信号。如何验证
单元测试覆盖了分类器和评论的两个分支:
node --test .github/scripts/web-shell-visuals-publish.test.mjs→ 19/19 通过。整套 CI helper 测试仍然全绿(88/88),actionlint对改动的工作流无告警。想看真实输出,就按工作流的方式跑 CLI(空的 stage 目录 + 一个改动路径文件),见上文英文部分的命令。确认它只列出
.tsx和.css——.tshook、spec 文件、packages/core的改动都被正确忽略。然后把路径文件换成只有useThing.ts,确认绿勾原样返回。风险与范围
.tsx/.css/.svg但确实没有视觉差异的 PR(改个 prop 名、改注释、删死代码)会误报。文案以"要么这个改动没有视觉影响"开头,加上文件列表,一眼就能排除,但真实误报率要跑过真实流量才知道。如果确实噪音大,收窄到"新增/重命名的文件"或加一个新增行数阈值,都是很小的后续改动。paths:触发器手工复制过来的,需要人工保持同步,两边都加了注释标注这一点。