Skip to content

perf(autofix): build the review CLI bundle once per scan and fan it out to legs - #8548

Merged
wenshao merged 3 commits into
QwenLM:mainfrom
wenshao:perf/autofix-shared-cli-bundle
Aug 5, 2026
Merged

perf(autofix): build the review CLI bundle once per scan and fan it out to legs#8548
wenshao merged 3 commits into
QwenLM:mainfrom
wenshao:perf/autofix-shared-cli-bundle

Conversation

@wenshao

@wenshao wenshao commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

What this PR does

The autofix review phase fans out one review-address leg per PR, and every leg used to repeat the exact same trusted-base build — npm ci + npm run build + npm run bundle — before the agent could start. This PR adds a build-cli job that compiles the CLI bundle once per scan, uploads the repo-root dist/ as a run-scoped artifact, and has each leg download and restore it instead of building. Each leg's checkout is pinned to the SHA the bundle was compiled from, so a mid-run base push can never pair a leg's bundle with different sources than its checkout.

The legs still run npm ci (the agent and the verification gate need node_modules against the PR branch), and the verification gate still rebuilds from branch sources — nothing about what gets verified changes. The issue phase is untouched: it runs only when no review targets exist, so gating the build on do_issue as well would have rebuilt on every quiet scheduled tick.

Why it's needed

Measured on run 30912069124 (6 legs): "Install dependencies and build" took 204-307 seconds per leg, ~25 runner-minutes per scan of identical repeated work, and on some legs more wall time than the agent round itself. After this change each leg pays only npm ci (warm-cached) plus a ~23 MB artifact download (~10-20s), so the agent starts minutes earlier per leg and feedback latency drops correspondingly.

Reviewer Test Plan

How to verify

  • Confirm the job graph: review-scan (unchanged) → build-cli (gated on has_targets == 'true', so an idle tick builds nothing) → review-address (needs now includes build-cli, checkout ref pinned to needs.build-cli.outputs.base_sha).
  • Confirm the leg no longer builds: its install step is npm ci only, followed by artifact download/restore with a test -f dist/cli.js guard and the existing qwen --version smoke.
  • Confirm the security model is unchanged: the bundle is still compiled from the trusted base checkout (persist-credentials: false), the artifact is run-scoped (same-run download only, 1-day retention), and PR-branch code never participates in the build.
  • Workflow contract tests cover the needs chain, the gating, the SHA pin, and the absence of build/bundle in the leg: npx vitest run --config ./scripts/tests/vitest.config.ts scripts/tests/qwen-autofix-workflow.test.js scripts/tests/package-scripts.test.js scripts/tests/qwen-resolve-workflow.test.js — 155 tests pass.
  • The one thing local verification cannot cover is GitHub runtime behavior (matrix fan-out, cross-job artifact transfer); the first scheduled run with review targets after merge is the real smoke test.

Evidence (Before & After)

N/A (CI-only change; no user-visible behavior). Before: each leg's "Install dependencies and build" step 204-307s (run 30912069124). After: legs download the shared artifact; the build happens once in build-cli.

Tested on

OS Status
🍏 macOS
🪟 Windows N/A
🐧 Linux N/A

Environment (optional)

Unit-level verification only: workflow contract tests, yamllint, actionlint (no new findings vs base), prettier, eslint, and a local tar round-trip of dist/ (node dist/cli.js --version runs from the restored archive).

Risk & Scope

  • Main risk or tradeoff: if build-cli fails, all legs skip without writing markers — fail-closed, and the next scan re-emits the targets, so a transient build failure costs one tick (~10 min) of latency, never a stranded PR.
  • Not validated / out of scope: live GitHub Actions fan-out (needs a real run post-merge); the issue phase deliberately keeps its own build.
  • Breaking changes / migration notes: none.

Linked Issues

N/A — follow-up to the resource-usage discussion of run 30912069124.

中文说明

本 PR 的内容

autofix 的 review 阶段会为每个 PR 扇出一条 review-address leg,而此前每条 leg 在 agent 启动前都要重复一次完全相同的 trusted-base 构建——npm ci + npm run build + npm run bundle。本 PR 新增一个 build-cli job:每次 scan 只编译一次 CLI bundle,将仓库根目录的 dist/ 作为 run 级 artifact 上传,各 leg 改为下载并还原它,不再各自构建。每条 leg 的 checkout 固定为 bundle 编译时对应的 SHA,因此即使运行期间 base 分支有新的推送,也不可能出现 leg 的 bundle 与其 checkout 源码不一致的情况。

各 leg 仍会运行 npm ci(agent 和验证门禁需要针对 PR 分支的 node_modules),验证门禁也仍然从分支源码重新构建——被验证的内容没有任何变化。issue 阶段不受影响:它只在没有 review 目标时运行,如果构建 job 同时挂在 do_issue 上,反而会在每个空闲的定时 tick 都白跑一次构建。

为什么需要

以 run 30912069124(6 条 leg)实测:每条 leg 的 "Install dependencies and build" 耗时 204-307 秒,一次 scan 约 25 分钟的 runner 时间在做完全相同的重复构建,有些 leg 的构建耗时甚至超过 agent 轮次本身。改动后每条 leg 只需 npm ci(有缓存)加上约 23 MB 的 artifact 下载(约 10-20 秒),agent 启动提前数分钟,反馈延迟相应降低。

审查者测试计划

如何验证

  • 确认 job 依赖图:review-scan(不变)→ build-cli(由 has_targets == 'true' 门控,空闲 tick 不构建)→ review-addressneeds 新增 build-cli,checkout ref 固定为 needs.build-cli.outputs.base_sha)。
  • 确认 leg 不再构建:其 install 步骤仅含 npm ci,随后是 artifact 下载/还原,带 test -f dist/cli.js 兜底和原有的 qwen --version 冒烟。
  • 确认安全模型不变:bundle 仍由 trusted base checkout(persist-credentials: false)编译,artifact 仅限同一次 run 内下载(保留 1 天),PR 分支代码不参与构建过程。
  • workflow 契约测试覆盖 needs 链、门控、SHA 固定以及 leg 中不再出现 build/bundle:npx vitest run --config ./scripts/tests/vitest.config.ts scripts/tests/qwen-autofix-workflow.test.js scripts/tests/package-scripts.test.js scripts/tests/qwen-resolve-workflow.test.js —— 155 个测试通过。
  • 本地无法覆盖的是 GitHub 运行时行为(matrix 扇出、跨 job artifact 传递);合并后第一次有 review 目标的定时 run 是真正的冒烟测试。

证据(前后对比)

N/A(仅 CI 改动,无用户可见行为)。改动前:每条 leg 的 "Install dependencies and build" 204-307 秒(run 30912069124)。改动后:leg 下载共享 artifact,构建只在 build-cli 发生一次。

测试平台

macOS ✅;Windows / Linux N/A。

环境(可选)

仅单元级验证:workflow 契约测试、yamllint、actionlint(与基线相比无新增 finding)、prettier、eslint,以及本地对 dist/ 的 tar 往返验证(从还原的归档中 node dist/cli.js --version 可正常运行)。

风险与范围

  • 主要风险或权衡:如果 build-cli 失败,所有 leg 会跳过且不写 marker——失败关闭(fail-closed),下一轮 scan 会重新选出这些目标,因此一次瞬时构建失败只损失一个 tick(约 10 分钟)的延迟,绝不会让 PR 搁浅。
  • 未验证 / 超出范围:GitHub Actions 的真实扇出(需要合并后的真实 run);issue 阶段有意保留自己的构建。
  • 破坏性变更 / 迁移说明:无。

关联 Issue

N/A —— 源自 run 30912069124 的资源消耗讨论。

…ut to legs

Each review-address leg repeated the same trusted-base build: measured
3.5-5 minutes of npm ci + build + bundle per leg (~25 runner-minutes on
one 6-leg scan) before the agent could start. A build-cli job now
compiles the bundle once per scan, uploads the repo-root dist/ as an
artifact, and the legs download it; their checkout is pinned to the
compiled SHA so a mid-run base push can never pair a leg's bundle with
different sources. The legs keep npm ci (the agent and the verify gate
still need node_modules against the PR branch), and the issue phase is
untouched — it runs only when no review targets exist, so gating the
build on do_issue too would rebuild on every quiet scheduled tick.
@qwen-code-ci-bot

qwen-code-ci-bot commented Aug 4, 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

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

Copy link
Copy Markdown
Collaborator

Thanks for the PR! Re-run on the new head (8c4b5250 — merge of main plus the fix round for the four inline review findings).

Template looks good ✓

Problem: observed, not theoretical — run 30912069124 shows each of the 6 review-address legs spending 204–307s rebuilding the same trusted-base bundle before the agent can start (~25 runner-minutes of identical work per scan). For a perf/CI change the timing measurement is the reproduction, and it's provided.

Direction: aligned. This is internal CI efficiency with a clear latency win for autofix feedback, and it explicitly does not change what gets verified. No CHANGELOG signal applies (CI-only change).

Size: no core paths touched — the autofix workflow is 148 production lines (+139/−9) and the two contract-test files are 138 test lines (+131/−7). Below every threshold; no advisory.

Approach: the scope still feels right. Build once, fan out a run-scoped artifact — that's the minimal mechanism here, and the obvious alternative (an actions/cache entry keyed on the base SHA) is cross-run shared and would add a cache-poisoning/staleness surface for no gain. Keeping the issue phase on its own build remains the right call. The new commit stays within scope: it adds the fail-closed SHA validation and the cross-site contract pins the review asked for, and nothing else.

Risk: no Stage 1e high-risk path matches. The workflow is security-adjacent (the address legs carry secrets), so the shared artifact's trust chain got the detailed look at the code-review stage — build-cli checks out the base with contents: read only, the artifact is run-scoped, and the leg's checkout is now validated loud before it happens.

Moving on to code review. 🔍

中文说明

感谢贡献!这是针对新 head(8c4b5250——合入 main 加上处理四条 inline 评审意见的修复轮次)的 re-run。

模板完整 ✓

问题:已观测而非理论——run 30912069124 显示 6 条 review-address leg 每条都要花 204–307 秒重复构建同一个 trusted-base bundle(每次 scan 约 25 runner-分钟的相同工作)。对 perf/CI 类改动,耗时测量本身就是复现,且已提供。

方向:对齐。这是纯内部 CI 效率优化,能明显降低 autofix 反馈延迟,且明确不改变被验证的内容。CHANGELOG 无对应信号(CI-only 改动)。

规模:未触及核心路径——autofix 工作流 148 行生产逻辑(+139/−9),两个契约测试文件 138 行测试(+131/−7)。低于所有阈值,无需大 PR 提示。

方案:范围依然合理。构建一次并通过 run 级 artifact 分发是这里的最小机制;显而易见的替代方案(按 base SHA 键控的 actions/cache)是跨 run 共享的,会引入缓存投毒/过期表面,没有收益。issue 阶段保留自己的构建仍是正确选择。新 commit 未超出范围:只加入了评审要求的 fail-closed SHA 校验和跨站点契约固定。

风险:Stage 1e 高风险路径无命中。该工作流与安全相关(address leg 携带 secrets),因此共享 artifact 的信任链在代码审查阶段做了重点核查——build-cli 仅以 contents: read checkout base,artifact 为 run 级作用域,leg 的 checkout 现在会先大声校验再执行。

进入代码审查 🔍

Qwen Code · qwen3.8-max

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

@qwen-code-ci-bot

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

Copy link
Copy Markdown
Collaborator

Code review

Re-run after two new commits since the last pass: a merge of current main, and the fix round addressing the four inline suggestions from the automated review. The core design I reviewed before stands, and the delta holds up:

  • Trust chain unchanged. build-cli still compiles from the default branch with contents: read and persist-credentials: false; the artifact stays run-scoped with 1-day retention and if-no-files-found: error. Nothing PR-controlled touches the bundle.
  • Validate bundle SHA closes the last hole. The new first step of review-address rejects any base_sha that isn't a 40-char hex SHA — loud, with an ::error::, before checkout. That matters because actions/checkout resolves an empty ref to the event default, which on pull_request_review triggers is the PR merge ref; a broken build-cli output now fails the leg instead of silently unpinning it. The value travels through env:, not inline expression expansion.
  • The new contract pins are real. I checked each assertion against the YAML: the id: 'meta' link between the producer output and the leg's SHA-pinned checkout, the download-directory / extract-path pair, the lockstep Node-setup recipe across all three jobs, and the validate-before-checkout ordering all fail the suite if either side is renamed. The decision not to extract a composite action is defensible — after this PR the three install recipes are deliberately different (no husky arming in build-cli, no build in the legs), and a shared action would need configuration inputs just to express that variance.
  • Staleness still can't leak into verification. Re-checked against current main after the base merge: scripts/build_package.js rms dist/ and esbuild.config.js rms the root bundle before rebuilding, so the verify gate consumes only branch-built output.

No blockers, no convention violations.

Testing evidence

Unattended CI run — PR code is never executed here; the evidence is the PR's own CI on the reviewed commit, fetched via the API. Test (ubuntu-latest, Node 22.x) is the check that matters: npm run test:ci ends with npm run test:scripts, the suite carrying both updated contract-test files, and it's green on this commit. The macOS/Windows matrix legs and integration tests are skipped by PR classification for workflow-only changes, as expected.

Two checks need explaining. review-pr failed because the automated Qwen review used its full 180-minute budget and timed out — the second time on this PR, on two different heads. That's the review bot's infra limit, not something this PR caused (it doesn't touch that workflow). The skipped build-cli check is quietly useful evidence: it comes from the review-event autofix run, whose workflow file carries this PR's changes — the new job graph ran live and build-cli correctly skipped on a scan with no review targets.

Check Conclusion
review-pr ❌ failure (bot review timeout — see note)
Classify PR ✅ success
Desktop Shell (ubuntu-22.04) ✅ success
precheck-pr / precheck ✅ success
route ✅ success
Test (ubuntu-latest, Node 22.x) ✅ success
web-shell E2E Smoke (ubuntu-latest, Node 22.x) ✅ success

One row per check name (latest run); skipped checks omitted; failures sort first. / 每个检查名一行(取最新一次运行),省略 skipped,失败项排在最前。

Not verified (and can't be, pre-merge): a real multi-leg fan-out with review targets — cross-job artifact transfer and matrix fan-out only happen on a live scheduled scan. The design bounds that risk: a failed build-cli skips every leg without writing markers, and the next scan re-emits the targets, so the worst case is one slow tick. A sandboxed verify run kicked off alongside this triage is still in progress and will post its own report, but there is no CLI behavior in this diff for it to A/B.

中文说明

代码审查

自上次审查后新增两个 commit:合入当前 main,以及处理自动评审四条 inline 建议的修复轮次。此前审查的核心设计保持不变,增量部分也经得起检验:

  • 信任链未变。 build-cli 仍以 contents: readpersist-credentials: false 从默认分支编译;artifact 仍为 run 级作用域、保留 1 天、if-no-files-found: error。PR 控制的代码接触不到 bundle。
  • Validate bundle SHA 堵上了最后一个缺口。 review-address 的新首步会拒绝任何不是 40 位十六进制 SHA 的 base_sha——大声失败、带 ::error::、发生在 checkout 之前。这一点很关键:actions/checkout 遇到空 ref 会回退到事件默认 ref,而 pull_request_review 触发下那是 PR merge ref;现在 build-cli 输出损坏会让 leg 失败,而不是悄悄解除固定。该值通过 env: 传递,不做内联表达式展开。
  • 新的契约固定是真实有效的。 逐条对照 YAML 核实:id: 'meta' 连接(生产者输出与 leg 的 SHA 固定 checkout 之间)、下载目录/解压路径配对、三个 job 间 Node 安装配方的同步锁定、校验先于 checkout 的顺序——任一侧被改名都会让套件失败。不抽复合 action 的决定可以接受:本 PR 之后三处安装配方本就不相同(build-cli 不挂 husky、leg 不再构建),共享 action 反而需要配置输入才能表达这种差异。
  • 陈旧产物仍不可能渗入验证。 合入 main 后重新核对:scripts/build_package.js 会先 rm dist/esbuild.config.js 会先 rm 根级 bundle,验证门禁只消费从分支源码构建的产物。

无阻塞问题,无约定违规。

测试证据

无人值守 CI 运行——此处不执行 PR 代码;证据为被审 commit 上 PR 自身的 CI(经 API 获取)。Test (ubuntu-latest, Node 22.x) 是关键检查:npm run test:ci 最后执行 npm run test:scripts,即承载两个契约测试文件的套件,该 commit 上为绿。macOS/Windows 矩阵腿与集成测试按 PR 分类对 workflow-only 改动跳过,符合预期。

两个检查需要解释。review-pr 失败是因为自动 Qwen 评审用满 180 分钟预算后超时——本 PR 上已是第二次(两个不同的 head 各一次)。这是评审机器人的基础设施限制,并非本 PR 所致(本 PR 未触及该 workflow)。被跳过的 build-cli 检查反而是个安静的佐证:它来自 review 事件触发的 autofix 运行,该运行使用的 workflow 文件已包含本 PR 的改动——新的 job 图实际跑了一次,且在没有 review 目标的 scan 中 build-cli 正确地跳过了。

CI 结果摘要:除 review-pr(机器人评审超时,属既有基础设施问题,见上文说明)外,该 commit 上所有实际运行的检查均为绿。表格区域会在 CI 稳定后由 finalize job 就地更新。

未验证(merge 前也无法验证):带真实 review 目标的多 leg 扇出——跨 job artifact 传输与矩阵扇出只有实际定时 scan 才会发生。设计上已限制该风险:build-cli 失败时所有 leg 跳过且不写标记,下次 scan 会重新发出目标,最坏情况只是慢一个 tick。随本次 triage 启动的沙箱验证仍在进行中,会单独发布报告,但本 diff 没有可供 A/B 的 CLI 行为。

Qwen Code · qwen3.8-max

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

@qwen-code-ci-bot

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

Copy link
Copy Markdown
Collaborator

Confidence: 4/5 — clean across every stage; the one reservation remains the piece nobody can check pre-merge (a live multi-leg fan-out with real targets), and the design bounds it fail-closed.

The fix round since the last review did the work properly — all four inline findings answered with real pins rather than minimal text edits: the loud fail-closed SHA guard now runs before the leg's checkout, the id: 'meta' producer/consumer link and the download/restore path pair are pinned so a one-sided rename fails the suite, and the Node-setup recipe is lockstep across all three jobs. The maintainer approval on this commit landed while this re-run was in flight, and reading the delta I agree with it. Going back to my independent proposal — one gated build job, a run-scoped artifact, SHA-pinned leg checkouts, loud SHA validation — the PR matches it point for point, and I still don't see a simpler shape that preserves the trust model. In six months, the lockstep contract tests are exactly what will catch drift here, and the comment blocks say why each piece exists.

Verdict: approve. CI has settled on this commit — the one red check is the automated review timing out on its 180-minute budget (pre-existing bot infra, twice now on this PR), and the unit suite carrying the contract tests is green — so no deferred approval this round.

中文说明

置信度:4/5 —— 各阶段审查均干净;唯一的保留仍是 merge 前谁都无法验证的部分(带真实目标的多 leg 实际扇出),而设计上已用 fail-closed 收口。

上次审查之后的修复轮次做得扎实——四条 inline 意见全部用真正的契约固定来回应,而不是最小化的文字修改:大声的 fail-closed SHA 校验现在先于 leg 的 checkout 执行;id: 'meta' 生产者/消费者连接和下载/还原路径配对都被固定,任一侧改名都会让套件失败;Node 安装配方在三个 job 间同步锁定。本次 re-run 进行期间维护者已在此 commit 上批准,读完增量后我认同该判断。回到我的独立方案——一个受门控的构建 job、run 级 artifact、固定 SHA 的 leg checkout、大声的 SHA 校验——PR 逐点吻合,且没有更简单又保留信任模型的形式。六个月后,正是这些同步锁定的契约测试能在此处捕获漂移,注释块也说明了每个部分存在的原因。

结论:批准。CI 已在该 commit 上稳定——唯一的红色检查是自动评审用满 180 分钟预算超时(既有机器人基础设施问题,本 PR 上已出现两次),承载契约测试的单元测试套件为绿——因此本轮无需延迟批准。

Qwen Code · qwen3.8-max

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

@wenshao

wenshao commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator Author

@qwen-code /takeover

@qwen-code-dev-bot qwen-code-dev-bot added the autofix/takeover Summon the autofix loop to manage this PR (remove to release; needs triage+) label Aug 4, 2026
@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

🤝 Takeover engaged: the autofix loop now manages this PR — it will address new review feedback and resolve base conflicts until the label is removed or the round cap is reached. This is a fork PR, so the first round comes from the next scheduled scan (usually within minutes). Remove the autofix/takeover label (or comment @qwen-code /takeover stop) to release.

中文说明

🤝 已接管:autofix 循环现在管理此 PR —— 将持续处理新的评审反馈与 base 冲突,直到移除标签或达到轮次上限。本 PR 来自 fork,首轮处理将由下一次定时扫描执行(通常几分钟内)。移除 autofix/takeover 标签(或评论 @qwen-code /takeover stop)即可释放。

@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 — CI landed green after the review. ✅

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Qwen Code review timed out. Qwen review timed out after 10800 seconds (of the 180-minute budget). For large PRs, retry with a longer timeout by commenting: @qwen-code /review --timeout=360. See workflow logs.

@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

🔀 Base updated: red check(s) [review-pr] pass on current main — merged current main via update-branch; CI will re-run.

中文说明

🔀 已更新 base:红色检查 [review-pr] 在当前 main 上通过 —— 已通过 update-branch 合入当前 main,CI 将重新运行。

@qwen-code-dev-bot

qwen-code-dev-bot commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

AutoFix round 1 finishedview run. See this round's report below.

中文说明

AutoFix 第 1 轮已完成 —— 查看运行。本轮报告见下方。

@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

🤖 Reviewed the latest feedback — no changes needed. Why, point by point: · 已审阅最新反馈——无需改动。逐点说明原因如下:

Autofix review round: no action needed

No actionable feedback arrived in this round for PR #8548 (branch perf/autofix-shared-cli-bundle at HEAD dd4ab703, against main).

Feedback triage (items newer than the last evaluation at 2026-08-04T18:53:11Z):

  • Review timeout notice (@qwen-code-ci-bot, issue-level comment): the automated Qwen review used its full 180-minute budget without producing findings. This is an informational status notice, not a defect report — it names nothing in the code to fix. Its suggested remedy (re-running the review with a longer timeout via a PR comment) is a GitHub write action that this headless job is not permitted to perform, and a fresh review-pr run is already in progress on the current head, so the retry path is already underway. No code change made.
  • Reviews: the only review in the window is an APPROVED on the current HEAD ("LGTM, looks ready to ship — CI landed green after the review") — no findings to address.
  • Inline comments: none.
  • Checks: no failed checks and no persisting red checks (all completed checks are SUCCESS or SKIPPED; the new review-pr run is IN_PROGRESS).
  • Conflicts: none (--conflict false); no base merge performed.

Outcome: no code changes and no commit this round. The working tree remains at dd4ab703 with a clean status. No verification commands were executed because nothing was modified.

中文说明

Autofix 评审轮次:无需处理

本轮 PR #8548(分支 perf/autofix-shared-cli-bundle,HEAD dd4ab703,基于 main)没有收到任何需要处理的反馈。

反馈分类(上次评估 2026-08-04T18:53:11Z 之后的新条目):

  • 评审超时通知@qwen-code-ci-bot,issue 级评论):自动 Qwen 评审耗尽了 180 分钟的完整预算,未产出任何评审意见。这是一条信息性状态通知,而非缺陷报告——它没有指出任何需要修复的代码问题。其中建议的补救方式(通过 PR 评论以更长的超时时间重新运行评审)属于 GitHub 写入操作,本无头任务不被允许执行;而且当前 head 上已经有一个新的 review-pr 运行正在进行中,说明重试路径已在进行中。未做任何代码变更。
  • 评审(Reviews):该时间窗口内唯一的评审是对当前 HEAD 的 APPROVED("LGTM, looks ready to ship — CI landed green after the review")——没有需要处理的意见。
  • 行内评论(Inline comments):无。
  • 检查(Checks):没有失败的检查,也没有持续红色的检查(所有已完成的检查均为 SUCCESS 或 SKIPPED;新的 review-pr 运行处于 IN_PROGRESS 状态)。
  • 冲突:无(--conflict false);未执行 base 合并。

结果: 本轮未做代码变更,未创建提交。工作树保持在 dd4ab703,状态干净。由于没有任何修改,未执行验证命令。

Base-conflict check · 基分支冲突检查: no conflict with main. · 与 main 无冲突。


🧠 Handled by Qwen Code · model/模型 qwen3.8-max

@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.

Reviewed — no blockers. Suggestions are inline. Not linted (tool limitation, not a blocker): the executable-script lint — .github/workflows/qwen-autofix.yml: actionlint embedded-shell source mapping is not yet supported — not linted.

中文说明

已审查——无阻断问题。 建议见行内评论。 未检查(工具限制,非阻断):the executable-script lint — .github/workflows/qwen-autofix.yml: actionlint embedded-shell source mapping is not yet supported — not linted。

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

Comment on lines +5333 to +5335
expect(buildCliJob).toContain(
"base_sha: '${{ steps.meta.outputs.base_sha }}'",
);

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.

[Suggestion] The new fan-out test pins each side of the SHA chain but not the link itself: nothing asserts the step writing base_sha actually carries id: 'meta' — which is what makes steps.meta.outputs.base_sha resolve. Probed empirically: renaming id: 'meta'id: 'meta2' survives the full suite (126/126). At runtime a nonexistent step id resolves to '', so needs.build-cli.outputs.base_sha becomes empty and every leg checks out ref: '' — falling back to the event-default ref, which on pull_request_review triggers is the PR merge ref, the exact checkout the SECURITY comment in the workflow exists to prevent.

Failure scenario: a future rename of the step id ships with a green suite → every leg silently checks out the PR merge ref in a secret-bearing run.

Suggested change
expect(buildCliJob).toContain(
"base_sha: '${{ steps.meta.outputs.base_sha }}'",
);
expect(buildCliJob).toContain(
"base_sha: '${{ steps.meta.outputs.base_sha }}'",
);
expect(stepOf(buildCliJob, 'Upload CLI bundle')).toContain("id: 'meta'");
中文说明

新增的 fan-out 测试固定了 SHA 链的两端,但没有固定链本身:没有任何断言保证写出 base_sha 的步骤携带 id: 'meta'——而正是它让 steps.meta.outputs.base_sha 能够解析。经实测:将 id: 'meta' 改名为 id: 'meta2' 后整套测试仍然通过(126/126)。运行时,不存在的 step id 会静默解析为 '',于是 needs.build-cli.outputs.base_sha 变为空,每条 leg 以 ref: '' checkout——回退到事件默认 ref,在 pull_request_review 触发器下即 PR 合并 ref,正是 workflow 中 SECURITY 注释要防止的 checkout。

失败场景:未来某次 step id 改名会在测试全绿的情况下上线 → 每条 leg 在携带密钥的运行中静默 checkout PR 合并 ref。

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

Comment on lines +5358 to +5360
expect(stepOf(addressJob, 'Download CLI bundle')).toContain(
"name: 'qwen-autofix-cli-dist'",
);

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.

[Suggestion] Same pattern on the artifact side of this contract: the restore step's ${RUNNER_TEMP}/cli-dist/qwen-cli-dist.tar.gz is asserted, but the Download step's path: '${{ runner.temp }}/cli-dist' input is never asserted — so the download-directory/extract-path agreement is untested. Probed empirically: changing the download path: to cli-dist2 survives the full suite (126/126), after which every leg dies at tar -xzf ("No such file or directory").

Failure scenario: a future rename of the download directory ships green → every review-address leg fails at the restore step, re-adding as a hard failure exactly the per-leg latency this PR removes.

Suggested change
expect(stepOf(addressJob, 'Download CLI bundle')).toContain(
"name: 'qwen-autofix-cli-dist'",
);
expect(stepOf(addressJob, 'Download CLI bundle')).toContain(
"name: 'qwen-autofix-cli-dist'",
);
expect(stepOf(addressJob, 'Download CLI bundle')).toContain(
"path: '${{ runner.temp }}/cli-dist'",
);
中文说明

同一契约的 artifact 端存在相同问题:restore 步骤的 ${RUNNER_TEMP}/cli-dist/qwen-cli-dist.tar.gz 有断言,但 Download 步骤的 path: '${{ runner.temp }}/cli-dist' 输入从未被断言——下载目录与解压路径的一致性未被测试。经实测:将下载 path: 改为 cli-dist2 后整套测试仍然通过(126/126),随后每条 leg 都会在 tar -xzf 处失败("No such file or directory")。

失败场景:未来某次下载目录改名会在测试全绿的情况下上线 → 每条 review-address leg 在还原步骤失败,把本 PR 消除的逐 leg 延迟以硬失败的形式重新引入。

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

Comment on lines +2627 to +2629
- name: 'Install dependencies and build'
env:
QWEN_SKIP_PREPARE: '1'

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.

[Suggestion] This diff adds a third copy of the npm-ci retry + build + bundle recipe and a third byte-identical qwen shim heredoc, leaving the build procedure triplicated across issue-autofix, build-cli, and review-address — all three sites are touched by this change. Cross-site agreement on node-version: '22.x', the GITHUB_PATH wiring, chmod, and the --version smoke is pinned by no test.

Failure scenario: a partial recipe edit (Node version bump applied to two of the three jobs, npm flag, backoff tweak) passes the whole suite and silently diverges build-cli's bundle from what the legs' shims point at — the exact class of mismatch this PR's SHA-pinning was written to prevent, reintroduced at the recipe level.

Suggested fix: extract the shared install/build/bundle + shim-staging block into a composite action (e.g. .github/actions/prepare-qwen-cli) invoked by all three jobs; at minimum add cross-reference comments at each copy and extend the contract tests to pin node-version and the GITHUB_PATH wiring across all three sites.

中文说明

本 diff 新增了第三份 npm-ci 重试 + build + bundle 配方和第三份逐字节相同的 qwen shim heredoc,使构建配方在 issue-autofixbuild-clireview-address 三处重复——三处都在本次改动中被触及。三处之间 node-version: '22.x'GITHUB_PATH 接线、chmod--version 冒烟的一致性没有任何测试固定。

失败场景:一次局部的配方修改(对三个 job 中的两个升级 Node 版本、npm 参数、退避调整)会通过整套测试,并悄悄让 build-cli 的 bundle 与各 leg 的 shim 指向产生偏差——正是本 PR 的 SHA 固定要防止的那类不匹配,在配方层面被重新引入。

建议修复:把共享的安装/构建/打包 + shim 部署代码块提取为复合 action(如 .github/actions/prepare-qwen-cli)供三个 job 调用;至少在每个副本处加交叉引用注释,并扩展契约测试以固定三处的 node-versionGITHUB_PATH 接线。

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

Comment on lines 2764 to +2765
with:
ref: '${{ github.event.repository.default_branch }}'
ref: '${{ needs.build-cli.outputs.base_sha }}'

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.

[Suggestion] This ref pin degrades fail-open: if needs.build-cli.outputs.base_sha ever resolves empty, actions/checkout silently falls back to the event-default ref — on pull_request_review triggers that is refs/pull/N/merge, the contributor-controlled tree the adjacent SECURITY comment says this pin exists to prevent. Verified against the pinned action's source (input-helper.ts: empty ref falls through to github.context.ref), and no guard validates the SHA before checkout. The current wiring is sound (hence Suggestion, not Critical), but this diff converted an always-present context pin into a cross-job output reference that can resolve empty, and the test suite is proven not a backstop — renaming id: 'meta' survives 126/126.

Failure scenario: a future rename of the step id or a typo in the output name → ref: '' → every leg silently checks out the PR merge ref → PR-influenced .github/scripts/* and run-agent.mjs are staged as the "trusted" copies and PR-influenced npm lifecycle scripts run at npm ci time, in the job carrying CI_DEV_BOT_PAT and the model key.

Suggested fix — a fail-loud validation step before this checkout (a job-level if: is inadequate: it only skips, silently stalling the fleet; skip ≠ fail-closed):

      - name: 'Validate bundle SHA'
        env:
          BASE_SHA: '${{ needs.build-cli.outputs.base_sha }}'
        run: |-
          if [[ ! "${BASE_SHA}" =~ ^[0-9a-f]{40}$ ]]; then
            echo "::error::build-cli published no usable base_sha ('${BASE_SHA:-<empty>}') — refusing to fall back to the event-default ref."
            exit 1
          fi
中文说明

此 ref 固定以 fail-open 方式退化:一旦 needs.build-cli.outputs.base_sha 解析为空,actions/checkout 会静默回退到事件默认 ref——在 pull_request_review 触发器下即 refs/pull/N/merge,也就是相邻 SECURITY 注释声明本固定要防止的贡献者可控代码树。已对照固定版本的 action 源码核实(input-helper.ts:空 ref 会落到 github.context.ref),且 checkout 前没有任何校验该 SHA 的保护。当前接线是健全的(因此是 Suggestion 而非 Critical),但本 diff 把一个永远存在的上下文固定转换成了可能解析为空的跨 job 输出引用,而测试套件已被证明不是兜底——将 id: 'meta' 改名后 126/126 测试仍全绿。

失败场景:未来某次 step id 改名或输出名拼写错误 → ref: '' → 每条 leg 静默 checkout PR 合并 ref → PR 可控的 .github/scripts/*run-agent.mjs 被当作 "trusted" 副本部署,PR 可控的 npm 生命周期脚本在 npm ci 时运行,而该 job 携带 CI_DEV_BOT_PAT 与模型密钥。

建议修复——在此 checkout 之前增加一个 fail-loud 校验步骤(job 级 if: 不够:它只会 skip,使整个队列静默停摆;skip ≠ fail-closed),代码见上。

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

…ontracts (QwenLM#8548)

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

🤖 Addressed the latest review feedback (round 1/100). What changed, and what I pushed back on: · 已处理最新评审反馈(第 1/100 轮)。改动内容与我反驳保留之处如下:

Autofix review round summary — PR #8548

Commit: 8c4b52508fix(autofix): validate fan-out bundle SHA and pin shared CLI recipe contracts (#8548)

All four inline findings addressed and resolved in code. No base conflict (--conflict false; no merge performed).

Findings

  1. [Suggestion] id: 'meta' link unpinned (fan-out test pins each side of the SHA chain but not the step id that makes steps.meta.outputs.base_sha resolve) → Resolved. Added expect(stepOf(buildCliJob, 'Upload CLI bundle')).toContain("id: 'meta'") to the fan-out contract test. Verified by mutation: renaming the id now fails the suite (previously it shipped green).

  2. [Suggestion] artifact download-directory / extract-path agreement untestedResolved. The fan-out test now pins the Download step's path: '${{ runner.temp }}/cli-dist' alongside the restore step's ${RUNNER_TEMP}/cli-dist/... extract path, so renaming either side fails the suite (verified by mutation).

  3. [Suggestion] recipe triplicated across issue-autofix / build-cli / review-address with no cross-site pinResolved via the suggestion's minimal option.

    • Contract tests now pin node-version: '22.x', the setup-node action SHA, and the npm cache wiring across all three Set up Node.js (hosted) steps (new lockstep test), plus the shim's chmod, GITHUB_PATH wiring, and --version smoke across all three Prepare Qwen Code CLI steps (extended the existing loop).
    • Added cross-reference comments at each duplicated recipe copy ("edit them together").
    • Did NOT extract a composite action: after this PR the three install steps are deliberately not identical (build-cli does not arm husky hooks; review-address no longer builds), so a shared action would need configuration inputs for per-job variance — more machinery than the problem calls for (AGENTS.md Simplicity First). The lockstep pins catch the described failure scenario (a partial recipe edit shipping green); verified by mutation.
  4. [Suggestion] leg checkout degrades fail-open on an empty base_shaResolved. Added a Validate bundle SHA step as the FIRST step of review-address, before Checkout trusted base: it exits 1 with an ::error:: annotation unless the value is exactly 40 lowercase hex chars, so a broken build-cli output fails the leg loudly instead of silently falling back to the event-default ref (the PR merge ref on pull_request_review triggers). The guard logic was verified locally against empty / ref-name / uppercase / valid-SHA inputs, and the contract test pins the guard's wiring, shape, and position-before-checkout (verified by mutation: deleting the guard fails the suite).

Verification

  • npm run build — passed
  • npm run typecheck — passed
  • npm run lint — passed (0 errors / 0 warnings)
  • npx vitest run --config ./scripts/tests/vitest.config.ts scripts/tests/qwen-autofix-workflow.test.js scripts/tests/package-scripts.test.js (focused run, touched files) — 2 files passed, 127/127 tests passed (baseline was 126; +1 new Node-setup lockstep test)
  • Mutation checks against the new pins (workflow mutated temporarily, then restored): id: 'meta'meta2; download path:cli-dist2; build-cli node-version'24.x'; GITHUB_PATH line dropped from the first shim; Validate bundle SHA step removed — all 5 mutations fail the suite, tree restored to green afterward
  • npm run test:scripts (full scripts suite) — 900 passed, 9 skipped, 5 failed; all 5 failures are spawnSync zip ENOENT in install-script.test.js: the zip binary is not installed on this runner (which zip → not found) and that test file references nothing changed by this PR — a pre-existing environmental failure, not caused by this change
  • No package sources touched → no packages/* Vitest or integration runs required; no settings source touched → generate:settings-schema not required
中文说明

Autofix 审查轮次总结 — PR #8548

提交:8c4b52508fix(autofix): validate fan-out bundle SHA and pin shared CLI recipe contracts (#8548)

四条行内发现全部处理并在代码中解决。无 base 冲突(--conflict false;未执行任何 merge)。

发现

  1. [建议] id: 'meta' 链路未被固定(fan-out 测试固定了 SHA 链的两端,但没有固定让 steps.meta.outputs.base_sha 能够解析的 step id)→ 已解决。 在 fan-out 契约测试中新增 expect(stepOf(buildCliJob, 'Upload CLI bundle')).toContain("id: 'meta'")。已通过变异验证:改名该 id 现在会使测试套件失败(此前该改动可以在全绿的情况下上线)。

  2. [建议] artifact 下载目录 / 解压路径的一致性未被测试已解决。 fan-out 测试现在同时固定 Download 步骤的 path: '${{ runner.temp }}/cli-dist' 与 restore 步骤的 ${RUNNER_TEMP}/cli-dist/... 解压路径,任何一侧改名都会使套件失败(已通过变异验证)。

  3. [建议] 构建配方在 issue-autofix / build-cli / review-address 三处重复且无跨处固定按该建议给出的最小方案解决。

    • 契约测试现在固定三个 Set up Node.js (hosted) 步骤的 node-version: '22.x'、setup-node action SHA 与 npm 缓存接线(新增 lockstep 测试),并固定三个 Prepare Qwen Code CLI 步骤中 shim 的 chmodGITHUB_PATH 接线与 --version 冒烟(扩展已有循环)。
    • 在每份重复配方处添加了交叉引用注释("edit them together")。
    • 未提取复合 action:本 PR 之后三个 install 步骤刻意不再完全相同(build-cli 不启用 husky hooks;review-address 不再构建),共享 action 因此需要为各 job 差异引入配置输入——相对问题本身属于过度设计(AGENTS.md Simplicity First)。lockstep 固定已能捕获所述失败场景(局部配方修改在全绿下上线);已通过变异验证。
  4. [建议] base_sha 为空时 leg 的 checkout 会 fail-open 退化已解决。 在 review-address 中新增 Validate bundle SHA 步骤并置于 Checkout trusted base 之前作为第一步:除非该值恰好是 40 位小写十六进制字符,否则以 ::error:: 注解退出 1,使 build-cli 输出损坏时该 leg 大声失败,而不是静默回退到事件默认 ref(在 pull_request_review 触发器下即 PR 合并 ref)。guard 逻辑已在本地针对空值 / ref 名 / 大写 / 合法 SHA 输入验证,契约测试固定了 guard 的接线、形态与"位于 checkout 之前"的顺序(已通过变异验证:删除该 guard 会使套件失败)。

验证

  • npm run build — 通过
  • npm run typecheck — 通过
  • npm run lint — 通过(0 错误 / 0 警告)
  • npx vitest run --config ./scripts/tests/vitest.config.ts scripts/tests/qwen-autofix-workflow.test.js scripts/tests/package-scripts.test.js(针对改动文件的聚焦运行)— 2 个文件通过,127/127 测试通过(基线为 126;新增 1 个 Node-setup lockstep 测试)
  • 针对新固定项的变异检查(临时变异 workflow 后还原):id: 'meta'meta2;下载 path:cli-dist2;build-cli node-version'24.x';删除第一处 shim 的 GITHUB_PATH 行;删除 Validate bundle SHA 步骤——5 种变异全部使套件失败,随后工作树恢复全绿
  • npm run test:scripts(scripts 全量套件)— 900 通过、9 跳过、5 失败;5 个失败全部是 install-script.test.js 中的 spawnSync zip ENOENT:本 runner 未安装 zip 二进制(which zip → 未找到),且该测试文件不引用本 PR 改动的任何文件——属于预先存在的环境问题,非本次改动引入
  • 未触及任何 package 源码 → 无需 packages/* Vitest 或集成测试;未触及 settings 源 → 无需 generate:settings-schema

Base-conflict check · 基分支冲突检查: no conflict with main. · 与 main 无冲突。

Re-review when you have a moment. After round 100 this bot stops and leaves the PR for a human. · 有空请复审;第 100 轮后本 bot 停止并将 PR 交给人工。


🧠 Handled by Qwen Code · model/模型 qwen3.8-max

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Qwen Code review timed out. Qwen review timed out after 10800 seconds (of the 180-minute budget). For large PRs, retry with a longer timeout by commenting: @qwen-code /review --timeout=360. See workflow logs.

@yiliang114 yiliang114 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, no blockers — the security pinning is genuinely careful (empty/malformed base_sha fails loud BEFORE checkout, closing the empty-ref->PR-merge-ref hole; default_branch is repo metadata not PR-controlled; the artifact can only be uploaded by the PR-free build-cli job). Contract tests pin the SHA link, validate-before-checkout order, both artifact paths, and the 3-way recipe lockstep. Non-blocking follow-ups: (1) P2 — build-cli is now a single point of failure: npm ci has 3-attempt retry but 'npm run build'/'bundle' don't, so one transient build failure kills the whole scan (previously legs built independently). Add retry or a leg self-build fallback. (2) P3 — this optimizes runner-minutes/cost, not wall-clock: legs serialize behind build-cli and still run their own npm ci, so per-PR latency may rise ~2-3min. Fine if the goal is cost/queue, just be clear which perf it buys.

@wenshao

wenshao commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator Author

@qwen-code /triage

@qwen-code-ci-bot

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

Copy link
Copy Markdown
Collaborator

Sandboxed verification: ❌ not passed — findings reported (agent verdict) - workflow run

Ran the PR in an isolated, token-free container: A/B against the base build, mock-free harness assertions, targeted gates. Advisory evidence for human reviewers — not a review, an approval, or a CI check.

Scripted assertions: 114 passed · 3 failed · 117 total

中文 — 判定:❌ 不通过 · 报告了发现(agent 判定)

沙箱验证在隔离、无凭证的容器中执行了该 PR 的代码(与 base 构建 A/B 对照、无 mock harness 断言、定向门禁)。仅作为评审证据,不构成评审、批准或 CI 检查

脚本断言:114 通过 · 3 失败 · 117 总计

Verification report

Verification report — PR #8548

Verdict: findings — 114/117 scripted assertions passed, 3 unexpected (two test-coverage survivors, one informational shellcheck note; none blocking). Verified head: 8c4b5250809ebc4185b781e459d0457bb92f693f (merge base 32e27415779226b23174a3b0aa6c04e094f1aca2).

中文摘要
  • 结论:findings(可合并倾向,附两条测试覆盖建议)。核心主张已用 A/B 证明承重:build-cli 每轮 scan 只构建一次可信基线 bundle 并经 run 级 artifact 扇出,各 leg 恢复该 bundle 且 checkout 固定为 bundle 编译时的 SHA;head 32/32 结构断言、44/44 行为重放断言全绿,base 侧按预期呈现旧形态(leg 内构建、无 SHA 固定、无守卫)。
  • A/B 结论:base 的 leg 安装步骤含 npm run build+bundle(重放确认会调用),head 的 leg 零构建调用;空 SHA 守卫对 9 种畸形/注入输入全部 exit 1 并报 ::error::,2 个真实 SHA 放行;真实 dist/ tar 往返 24.5 MB,恢复后 node dist/cli.js --version = 0.21.5 且字节一致;守卫缺失/顺序颠倒/id: 'meta' 移除等 8 个突变体均被契约测试杀死,vacuity 检查(head 测试对 base workflow)4 个测试变红,测试不空。
  • Findings:①(建议级,安全相关覆盖缺口)build-cli checkout 的 persist-credentials: false 无任何测试钉住——删掉它 156 个测试仍全绿(突变体 M9 存活);head 的 YAML 本身正确,附已度量的修复 fixture。②(nit)fetch-depth: 1 未钉住(M8 存活,良性)。③(nit,信息性)新 base_sha=$(git rev-parse HEAD) 行在 shellcheck --enable=all 下有 SC2312 提示,仓库门禁不检查 workflow 内嵌块,且该假想失败已被 SHA 守卫兜底。
  • 未覆盖:GitHub 运行时行为(矩阵扇出、跨 job artifact 传输、upload v7 ↔ download v8 互通、204–307s/leg 基线数字);bash 重放未用真实 run 日志校准(无 token);浅克隆(depth 2)下逐 commit 归因不可达,验证的是聚合 diff。

Central claim and A/B

Central claim: the review phase compiles the trusted-base CLI bundle once per scan in a new build-cli job (gated on has_targets), ships repo-root dist/ as a run-scoped artifact, and each review-address leg restores it instead of building — with its checkout pinned to the SHA the bundle was compiled from, guarded fail-loud by a Validate bundle SHA step ordered before the checkout.

cell oracle base 32e2741 head 8c4b525
build-cli job, gating, outputs YAML-parsed structure (H1) absent (B4) present; needs:[route,review-scan]; if: has_targets=='true' only; outputs.base_sha via id:'meta' (H1–H4, H9)
leg build behaviour stub-npm replay of the verbatim install blocks (H2-C) leg runs npm run build+bundle in-leg (C07) leg makes zero build/bundle calls (C06, H18)
checkout pin parsed with.ref ${{ github.event.repository.default_branch }} (B6) ${{ needs.build-cli.outputs.base_sha }} (H15)
empty-SHA guard verbatim replay ladder (H2-A) no guard exists (B5) 2 real SHAs exit 0; empty/39/41/non-hex/UPPER/space/newline/$(…) all exit 1 with ::error:: … refusing to fall back (A01–A13)
empty-ref danger chain vendored action source at pinned SHAs (H3) n/a checkout v6.0.3 input-helper.ts:60-63: empty refgithub.context.ref; @actions/github 6.0.1 context.js:24: = process.env.GITHUB_REF; pull_request_reviewrefs/pull/N/merge (documented; corroborated by this run's own merge-ref checkout)
producer→consumer round trip real dist/ tar → restore → --version (H2-B) n/a (legs built locally) 24.5 MB tarball; restored dist/cli.js byte-identical; node dist/cli.js --version = 0.21.5; absent/without-cli tarballs fail the leg loud (B01–B12)
contract suite lockstep vacuity + mutation matrix (H4) head tests vs base wf: 4 failed (not vacuous) 156/156 green; 8/10 mutants killed; survivors M8, M9 (Findings)

Captures: 01-ab-structure-head-vs-base.png (the H1 table as printed), 02-guard-ladder-and-tar-roundtrip.png (H2 as printed), 03-mutation-matrix.png (per-cell vitest logs), 04-vendor-sha-pins-and-fallback.png (H3).

The security model holds as described: bundle compiled from the default branch with persist-credentials: false (line 2619, same as the two pre-existing checkouts at 680/2786), artifact run-scoped with 1-day retention and if-no-files-found: error, and the leg's npm run build in the verify gate wipes only packages/*/dist (build_package.js rmSync runs per-package), never the trusted root dist/cli.js — identical to base. dist is git-ignored (.gitignore:71), so the restored bundle survives the later PR-branch checkout. issue-autofix is byte-equivalent after YAML parse (H24), confirming the issue phase is untouched; its schedule-tick gate already requires has_targets != 'true', which is the premise for not gating build-cli on do_issue.

Corrections

  • The PR body states "155 tests pass" for the three contract suites. Measured at the verified head: 156 (3 files). Harmless drift — the fix commit added a test; the claim is corroborated modulo the count.

Findings

F1 — Suggestion (security-adjacent coverage gap): persist-credentials: false on the new build-cli checkout is pinned by no test. Mutant M9 (delete that line from build-cli's checkout only, YAML validated) leaves all 156 contract tests green. Reproduce: node harness/mutate.mjs M9-persist-creds-build-cli .github/workflows/qwen-autofix.yml && npx vitest run --config ./scripts/tests/vitest.config.ts scripts/tests/qwen-autofix-workflow.test.js scripts/tests/package-scripts.test.js scripts/tests/qwen-resolve-workflow.test.jsTests 156 passed (156). The PR built a lockstep-test discipline for every duplicated recipe (npm-ci retry, node setup, qwen shim) and its description cites persist-credentials: false as part of the unchanged security model — but the suite never asserts it, for any of the three autofix checkouts, while sibling workflows do pin it (ci-flaky-rerun-workflow.test.js:138, main-ci-failure-issue-workflow.test.js:108, qwen-resolve-workflow.test.js:667, auto-minimize-spam.test.mjs:58). The YAML at head is correct; this is a gap a future edit could silently widen.

Measured fix (scratch fixture, not committed): a 3-case lockstep test asserting with['persist-credentials'] === false on the issue-autofix/build-cli/review-address checkouts runs 3/3 green at head and, under M9, fails exactly the build-cli arm (2 passed | 1 failed) with the other two arms still green — zero collateral.

Minimal suggested fix (for the author)
for (const jobName of ['issue-autofix', 'build-cli', 'review-address']) {
  it(`${jobName} checkout disables persist-credentials`, () => {
    const job = workflow.jobs[jobName];
    const checkout = (job?.steps ?? []).find((s) =>
      String(s?.uses ?? '').startsWith('actions/checkout@'),
    );
    expect(checkout).toBeTruthy();
    expect(checkout.with['persist-credentials']).toBe(false);
  });
}

Measured: head 3/3 green; M9 → build-cli arm red only; the shipped suite's 156 counts unaffected.

F2 — Nit (benign coverage gap): fetch-depth: 1 on build-cli's checkout is unpinned. Mutant M8 (1→2) survives 156/156 (node harness/mutate.mjs M8-fetch-depth-drift … + same suite command). The value is a documented perf choice; drift is harmless. Completeness reporting only.

F3 — Nit (informational): SC2312 on the new base_sha=$(git rev-parse HEAD) line. Reproduce: shellcheck --shell=bash --enable=all --exclude=SC2002,SC2129,SC2310,SC2148,SC2154 --severity=style scratch/blocks/upload.sh → one note at line 1 (masked return value of the substitution). Under shellcheck --enable=all --severity=style (the repo's own flag set, applied to the verbatim extracted block) the substitution's return value is masked by echo. The hypothetical empty output is exactly what the leg's Validate bundle SHA guard exists to catch, and the repo's shellcheck gate only lints tracked .sh files, so there is no CI impact. Counted as a failed assertion for strictness; see 02-guard-ladder-and-tar-roundtrip.png for the guard's ladder.

Not covered

  • GitHub runtime behavior: matrix fan-out, cross-job artifact transfer, upload-artifact v7 ↔ download-artifact v8 service interop, the claimed 10–20 s download, and the 204–307 s/leg baseline (run 30912069124 — needs run history, no token here). The PR itself names the first post-merge scheduled run with targets as the real smoke test.
  • Replay calibration: the bash replays verify block semantics (shape), not the trigger environment; no real emitted artifact was retrievable to calibrate against (no previous-report.md, no token). Per-leg job logs of run 30912069124 would have calibrated it.
  • Per-commit attribution: checkout is depth 2; the snapshot lists 3 commits but git rev-list HEAD^1..HEAD^2 returns 1 (shallow-boundary truncation). The aggregate HEAD^1..HEAD diff is what was verified.
  • Repo-wide gates: full lint:ci, repo-wide yamllint/shellcheck sweeps, and the full vitest suite were left to the PR's own CI; this round ran file-scoped/targeted gates, each proven live with a planted violation (actionlint, yamllint, shellcheck, eslint all caught their probes).
  • Discarded harness cells: the first M3 run (mutant corrupted the YAML via an unscoped indexOf that hit build-cli's identically named checkout step) and the first H3-9 attempt (harness wrongly demanded an annotated-tag ^{} entry for setup-node's lightweight tag). Both re-ran corrected and are counted once; see TALLY.md.
  • GITHUB_REF for pull_request_review = refs/pull/N/merge is GitHub-documented event semantics, not derivable from library source; corroborated locally by this verify run itself being a merge-ref checkout.

Methodology

Environment: node:22-bookworm container on the CI merge-ref checkout (HEAD=760134ed8, base HEAD^1=32e274157, head HEAD^2=8c4b52508); base arm from a scratch worktree tmp/base-tree (no node_modules there; module resolution walks up to the root install — the contract tests import only node builtins + vitest, so no workspace-link confound). All run: blocks were extracted verbatim from the parsed YAML (yaml package) and replayed under GitHub's shell contract bash --noprofile --norc -e with stubbed npm/sleep/git for the retry recipes and the real built dist/ for the round trip. Third-party claims verified from vendored sources: actions/checkout cloned at the pinned SHA df4cb1c0 (= v6.0.3), @actions/github 6.0.1 tarball, and tag dereference of all four pinned action SHAs via git ls-remote. Linters installed at the repo-pinned versions (actionlint 1.7.12, shellcheck 0.11.0, yamllint 1.35.1) and run with the repo's exact flags. Harnesses (h1-structure.mjs, h2-replay.mjs, h3-vendor.mjs, mutate.mjs, print-matrix.mjs) live in this artifact dir; raw per-cell logs in logs/; evidence in evidence/.

Evidence images

01-ab-structure-head-vs-base

02-guard-ladder-and-tar-roundtrip

03-mutation-matrix

04-vendor-sha-pins-and-fallback

Harness scripts and raw logs are in the workflow run artifacts (7-day retention).

Qwen Code · sandboxed verification

@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. ✅

@wenshao
wenshao added this pull request to the merge queue Aug 5, 2026
Merged via the queue into QwenLM:main with commit da37110 Aug 5, 2026
49 of 50 checks passed
@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Released in v0.21.6.

wenshao added a commit to he-yufeng/qwen-code that referenced this pull request Aug 6, 2026
* fix(autofix): ship core dist in the review CLI bundle

The review fan-out (QwenLM#8548) dropped the per-leg build: legs restore the
shared bundle's repo-root dist/ and rely on the verify gate's full
build for branch verification. But the gate's settings-schema and i18n
checks run BEFORE any build, on every path including no-action, and
their tsx-transpiled cli sources import '@qwen-code/qwen-code-core',
which resolves through the workspace symlink to
packages/core/dist/index.js. With no build on the leg, the generator
crashes with ERR_MODULE_NOT_FOUND and the gate misreports a
deterministic "settings schema is stale" rejection (run 31031063525 on
PR 8600), then burns an 18-minute repair agent round on an environment
problem no agent can fix.

Ship packages/core/dist (+~8.5MB gzipped) alongside the root dist/ in
the fan-out artifact and assert its entry point on restore. This
restores exactly the pre-fan-out state: legs used to build the trusted
base themselves before the branch checkout, so the gate always ran
against base-built core dist. The workflow contract tests pin the new
tar command and the restore-side assertion.

* fix(autofix): rebuild branch-touched core dist before the schema gate

Review feedback on the core-dist bundle fix:

- Rebuild packages/core from branch sources in the review verify gate
  when the branch diff touches core's sources, so the pre-build
  settings-schema check never compares the branch's committed schema
  against a base-built dist (changed runtime constants) or crashes the
  generator (changed exports). Lives in the shared gate script so both
  the initial and the repair gate are covered.
- Narrow the bundle/restore comments and their test mirror to the
  settings-schema generator: the i18n check resolves core to sources
  via the packages/cli tsconfig paths map and needs no dist (verified
  empirically).
- Anchor the tar contract pin at end-of-line so additive path drift
  fails the suite instead of passing on a substring match.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

autofix/takeover Summon the autofix loop to manage this PR (remove to release; needs triage+)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants