feat(review): check cache identity when reviewing workflow PRs - #8205
Conversation
A cache whose producer and consumer never agree on identity is invisible
to every lens the review currently has. `actions/cache` matches an entry
on `(key | restore-key)` AND a `version` that hashes the literal `path`
strings plus the compression method, not the key alone. So two jobs can
share a key, share the `path:` line exactly as written, and never hit
once: `${{ runner.temp }}` expands to a different string on a hosted
runner than in a container job or on a self-hosted runner, and an image
without the `zstd` binary picks gzip where a hosted runner picks zstd.
The workflow path-rule already covered a cache a fork can poison; it said
nothing about one that can never fire. Add that class as a blocker, so
every dimension agent whose territory holds a workflow is asked whether
the side that writes agrees with the side that reads — a question no
assertion about the YAML's shape can answer, because the two sides'
strings match in exactly the case that fails.
Give verify-pr the method to settle it: read the matching key from the
implementation rather than the documentation, compare the two sides'
environment tuples rather than their YAML strings, and treat a hit that
leaves no observable signal as the finding rather than a nit.
Also record that the verify container is a live sample of the lane's own
runtime. When the diff changes what those lanes execute, `command -v
zstd` or `echo "$RUNNER_TEMP"` settles in one shell command what no
amount of YAML reading settles, and needs no GitHub token — which that
environment does not have.
|
✅ Qwen Triage finished — CI landed green on ✅ Qwen Triage 已完成 —— |
|
Thanks for the PR! Template looks good ✓ — all required sections present, bilingual body included. Problem: this is a coverage gap, not a runtime bug, and it's a real one. The workflow checklist already covers a cache a fork can poison, but nothing asks whether a cache can ever fire. The mechanism cited is correct: Direction: aligned. This is squarely the review tooling's job — the Size: not applicable — Approach: the scope feels right and minimal — one checklist bullet, one test pinning it, and a matching method note in the Risk: no elevated risk signals — none of the changed files match the high-revert paths. Moving on to code review. 🔍 中文说明感谢贡献! 模板完整 ✓ —— 必填章节齐全,含中文正文。 问题:这是一个覆盖盲区,而非运行时 bug,而且是真实存在的。workflow checklist 已经覆盖了 fork 可以毒化的缓存,但没有任何一条问"缓存到底能不能生效"。所引用的机制是正确的: 方向:对齐。这正是 review 工具的职责—— 规模:不适用 —— 方案:范围合理且最小化——一条 checklist 条目、一条钉住它的测试、以及 风险:无升级风险信号 —— 改动文件均未命中高 revert 路径。 进入代码审查 🔍 — Qwen Code · qwen3.8-max-preview Reviewed at |
Code reviewRead the title and "Why it's needed" first and proposed my own version before looking at the diff: I'd add a single blocker bullet to the The production change is one markdown bullet appended to the The Testing evidence (this PR's own CI — I do not run PR code on the triage path)The Linux unit suite is still running on the reviewed commit; nothing has failed. Final CI results for
One row per check name (latest run); skipped checks omitted; failures sort first. / 每个检查名一行(取最新一次运行),省略 skipped,失败项排在最前。 Not verified: the runtime behaviour of any product surface — there is none here. The change is review-agent prompt text plus skill documentation, and the unit test above pins the text; there is no behavioural claim for a sandboxed 中文说明代码审查先只看标题与"Why it's needed",在看 diff 之前提出我自己的方案:我会在 生产改动是追加到
测试证据(本 PR 自己的 CI —— triage 路径不运行 PR 代码)Linux 单元测试套件在被审 commit 上仍在运行;目前无任何失败。 未验证:任何产品面的运行时行为——本次没有。改动是 review-agent 提示词加 skill 文档,上面的单元测试已钉住文本;没有需要 — Qwen Code · qwen3.8-max-preview Reviewed at |
|
Confidence: 5/5 — a real, demonstrated gap closed by one idiomatic bullet and a test that pins it; nothing to second-guess. Stepping back: the problem is genuine and the PR proves it rather than asserting it — a cache whose two sides share a key and a I'm approving on the merits. CI is still running on the reviewed commit (the Linux unit suite is in flight, nothing red), so the approval is deferred rather than posted now — approval lands green on 中文说明置信度:5/5 —— 一个真实且有实证支撑的盲区,由一条符合惯例的条目加一条钉住它的测试关闭;无可质疑之处。 退一步看:问题是真实的,且 PR 证明了它而非仅仅断言——一个两端共用 key 与 我基于实质内容认可本 PR。CI 仍在被审 commit 上运行(Linux 单元测试套件进行中,无红),因此认可推迟发出而非现在提交——待套件通过后,针对 — Qwen Code · qwen3.8-max-preview Reviewed at |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
LGTM, looks ready to ship — CI landed green after the review. ✅
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
— qwen3.8-max-preview via Qwen Code /review
yiliang114
left a comment
There was a problem hiding this comment.
LGTM — well-motivated addition based on a real dogfood finding. The cache identity check fills a real gap: YAML-shape parity tests pass when neither side can reach the other's cache. The verify-pr environment contract note is practical (one shell command settles what YAML reading can't). Test follows existing content-presence pattern with vacuity check documented.
|
Released in v0.21.3. |
What this PR does
Adds one review class and one verification method for cache and reuse mechanisms whose two sides never agree on identity. The
/reviewworkflow path-rule gains a blocker entry, so every dimension agent whose territory holds a.github/workflows/*.ymlfile is asked whether the side that writes a cache and the side that reads it can actually match. Theverify-prskill gains a matching artifact-type entry describing how to settle that question, plus an environment-contract note that the verify container is itself a live sample of the lane's runtime.Why it's needed
actions/cachematches an entry on(key | restore-key)and aversionthat hashes the literalpathstrings plus the compression method — not the key alone. Two jobs can therefore share a key, share thepath:line exactly as written, and never hit once:${{ runner.temp }}expands to a different string onubuntu-latestthan in a container job or on a self-hosted runner, and an image without thezstdbinary picks gzip where a hosted runner picks zstd.Nothing in the review asks that question today. The workflow checklist covered a cache a fork can poison, but not one that can never fire, and no assertion about the YAML's shape can answer it — the two sides'
key:andpath:strings match in exactly the case that fails, so shape-parity tests stay green forever. A mechanism that can never fire is not a slow optimisation; it is a no-op carrying maintenance cost.The verify lane also has an advantage that was never written down: its agent runs inside the same
node:22-bookwormcontainer the lane itself uses. When the diff changes what those lanes execute,command -v zstdorecho "$RUNNER_TEMP"settles in one shell command what no amount of YAML reading settles — and it needs no GitHub token, which that environment does not have. Two facts that recur are recorded with it:$RUNNER_TEMPis/__w/_tempinside the container while the${{ runner.temp }}expression evaluates to the runner's host path, and that image ships nozstdbinary.Reviewer Test Plan
How to verify
packages/cli:npx vitest run src/commands/review/lib/path-rules.test.ts— 14 passed, including the newasks whether a cache mechanism can fire at allcase.sed -i '/never agree on identity/d' packages/cli/src/commands/review/lib/path-rules.ts, re-run step 1, and confirm exactly one test fails (Tests 1 failed | 13 passed (14)). Restore the file withgit checkoutafterwards.npx vitest run scripts/tests/qwen-triage-workflow.test.js— 107 passed. This suite reads.qwen/skills/verify-pr/SKILL.md, so it is the gate on the skill edit.node --test .github/scripts/qwen-triage-workflow.test.mjs— 31 passed.npx prettier --checkandnpx eslinton the three touched files — clean.Observed: all of the above pass, and step 2 fails exactly one test as described.
Evidence (Before & After)
N/A — no user-visible or TUI change. The change alters the text handed to review agents and the verify skill's method, not any rendered output.
Tested on
Environment (optional)
Node 22, unit tests only.
Risk & Scope
pathRulesForattaches the checklist only to agents whose territory actually contains a workflow file — and the new entry names a concrete comparison to run rather than a thing to be alert to.agent-briefs.ts. Also out of scope: the builtdist/copy of the review skill, which picks this up on the next normal build.Linked Issues
N/A
中文说明
这个 PR 做了什么
为"写入端与读取端永远对不上身份"的缓存/复用机制补上一条审查类别和一套验证方法。
/review的 workflow path-rule 新增一条 blocker,凡是 territory 中含.github/workflows/*.yml的 dimension agent 都会被要求追问:写缓存的一端和读缓存的一端到底能不能匹配上。verify-prskill 新增一条对应的 artifact-type 条目说明如何判定这个问题,并在 environment contract 中记录:verify 容器本身就是该 lane 运行时的活样本。为什么需要
actions/cache的匹配条件是(key | restore-key)加上 一个version,而这个version是对 path 字面量加压缩方式求的哈希,不只是 key。因此两个 job 可以共用同一个 key、共用逐字相同的path:行,却一次都命中不了:${{ runner.temp }}在ubuntu-latest上、在容器 job 中、在 self-hosted runner 上会展开成不同的字符串;没有zstd二进制的镜像会选 gzip,而 hosted runner 选 zstd。目前的审查完全不会问这个问题。workflow checklist 覆盖了 fork 可以毒化的缓存,但没覆盖永远无法生效的缓存;而任何关于 YAML 形状的断言都答不了它——恰恰在会失败的那种情形下,两端的
key:和path:字符串完全一致,于是形状断言永远是绿的。一个永远不会触发的机制不是"慢一点的优化",而是带着维护成本的 no-op。verify lane 还有一个从未被写下来的优势:它的 agent 就跑在该 lane 自己使用的同一个
node:22-bookworm容器里。当 diff 改的正是这些 lane 所执行的东西时,command -v zstd或echo "$RUNNER_TEMP"一条 shell 命令就能定案,而这是再多的 YAML 阅读都定不了的;并且它不需要 GitHub token——而那个环境恰恰没有 token。随之记录了两条反复用到的事实:容器内$RUNNER_TEMP是/__w/_temp,而${{ runner.temp }}表达式求值得到的是 runner 的宿主机路径;以及该镜像不带zstd二进制。审查者测试计划
如何验证
packages/cli目录下:npx vitest run src/commands/review/lib/path-rules.test.ts—— 14 条通过,含新增的asks whether a cache mechanism can fire at all。sed -i '/never agree on identity/d' packages/cli/src/commands/review/lib/path-rules.ts,重跑第 1 步,确认恰好有一条失败(Tests 1 failed | 13 passed (14))。之后用git checkout还原文件。npx vitest run scripts/tests/qwen-triage-workflow.test.js—— 107 条通过。该套件会读取.qwen/skills/verify-pr/SKILL.md,是 skill 改动的把关测试。node --test .github/scripts/qwen-triage-workflow.test.mjs—— 31 条通过。npx prettier --check与npx eslint—— 均干净。实测:以上全部通过,且第 2 步如描述那样恰好失败一条。
证据(Before & After)
N/A —— 无用户可见或 TUI 变化。本次改动改变的是交给 review agent 的文本与 verify skill 的方法,不涉及任何渲染输出。
测试平台
环境(可选)
Node 22,仅单元测试。
风险与范围
pathRulesFor只会把 checklist 附加给 territory 中确实含有 workflow 文件的 agent——并且新条目给出的是一个可执行的具体比对动作,而不是一句"要留意"。agent-briefs.ts的另一处改动。同样范围外的还有 review skill 的dist/构建产物,它会在下一次正常构建时取得本次改动。关联 Issue
N/A