fix(ci): run /resolve without the container sandbox and pin its CLI version - #10428
Conversation
…ersion Every /resolve since 2026-08-15 has failed before the agent looked at the conflict: 0 of 81 agent runs pushed a resolution, against 84% of the 273 runs before that date. Three causes, none of them the model: - 14 runs: `@qwen-code/qwen-code@latest` pointed at an unresolvable 0.21.12 and the action's npm install died on `notarget`. - 24 runs: #9252 made the settings block take effect for the first time, `"sandbox": true` with it; the CLI hopped into the versioned ghcr image, which lags the npm release, and died on `manifest unknown` (#9898). - 40 runs: with the image present the sandboxed agent produced no output until the 120-minute job timeout; the merge had not even started. No sandboxed /resolve run has ever pushed a resolution and every pushed one ran outside the container, so pin tools.sandbox to false, pin the CLI to 0.21.10 (the last version with a measured record on this job), give the agent step a 75-minute timeout (p99 is 70), and when the step ends without outcome=success report it as an infrastructure failure that a re-run will repeat, instead of the generic "did not complete successfully" that read as the model giving up and drew 13 days of re-runs. The two contract tests that pinned sandbox: true now pin false; new tests pin the exact-semver version input, the step timeout mirrored into the comment, and the infra-failure wording.
|
Re-run at head
Moving on to code review — which at this head confirms the two Criticals above. 🔍 中文说明在 head
进入代码审查——在本头上确认了上述两个 Critical。🔍 — Qwen Code · qwen3.8-max Reviewed at |
|
Re-run at head The structural core verifies clean at this head:
Two Criticals remain open at this head. Both independently verified here, not taken on faith from the review lane:
Testing — the PR's own CI at this commit; I run none of this PR's code myself. The Final CI results for
One row per check name (latest run); skipped checks omitted; failures sort first. / 每个检查名一行(取最新一次运行),省略 skipped,失败项排在最前。 On sandboxed verification: the two open Criticals are static logic defects — a YAML wiring interaction and an action install-step branch — and both are settled by reading the code at this SHA plus the pinned action's source, as done above; no 中文说明在 head 结构性核心在本头上核实无误:
本头上仍有两个未关闭的 Critical,均已在此独立核实,不是照搬审查链路的结论:
测试——本提交上 PR 自己的 CI;我本人不运行这个 PR 的任何代码。 本头上的 关于沙箱化验证:两个未关闭的 Critical 都是静态逻辑缺陷——一处 YAML 接线交互、一处 action 安装步骤分支——靠阅读本 SHA 的代码加钉住版本的 action 源码即可定论(上文已做), — Qwen Code · qwen3.8-max Reviewed at |
|
Confidence: 2/5 — the structural split is right and verifies clean, but two independently confirmed Criticals stand on this head, and both come from the two newest increments. Stepping back: measured against the proposal I would have written from the incident data alone — pin the install, drop the container, report infra failures honestly, and move the credentialed push onto a runner the agent never touches — this PR's shape is the right one, and the two-job split closes the round-2-through-5 containment body of work by construction rather than by another denylist. That part I would defend. What I cannot sign is the tail: the The pattern deserves naming too: seven review rounds, a diff that grew ~4.6x, and findings that keep regenerating in the same cluster — the review lane's advisory that a human should re-weigh whether the shape is still right before more fix-on-fix is fair. My own read: the shape is right, the last two increments are not, and their fixes are small and bounded (partial-rerun-aware publish; pin-or-document for the follow-up bot). If the ECS-lane pin cannot be made real cheaply, cutting that hunk to an honest contract is the simpler move. Verdict: request changes. Practically: this bot's round-7 中文说明置信度:2/5——结构性拆分方向正确且核实无误,但本头上有两个经独立核实的 Critical,且都出自最新两次增量。 退一步看:如果只凭事故数据写方案——钉住安装版本、去掉容器、如实上报基础设施故障、把带凭据的推送挪到 agent 从未接触过的 runner——本 PR 的形态正是如此,双 job 拆分以构造性方式(而非又一张拒绝清单)关闭了第 2 至第 5 轮的隔离问题。这部分我为它背书。但我不能签字的是尾部:修复第 6 轮 R6-2 的 模式本身也值得点名:七轮评审、约 4.6 倍增长的 diff、不断在同一簇再生的发现——审查链路"在继续修复叠修复之前,应由人重新衡量整体形态是否仍然正确"的建议是合理的。我的判断:形态是对的,最后两次增量不对,且它们的修复小而有限(publish 侧感知部分重跑;follow-up bot 要么真钉住要么如实记录)。如果 ECS 链路上的钉无法低成本生效,把那段砍成诚实的契约是更简单的做法。 判定:要求修改。实际操作上:本 bot 第 7 轮 — Qwen Code · qwen3.8-max Reviewed at |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Requesting changes on one mechanical point — see the review notes above. 🙏 The workflow-size gate is red on this commit: the new comments grow qwen-code-pr-review.yml ~7.5 KB past its recorded baseline (4 KB allowance) and .size-baseline is untouched. Bump that line in this PR and say why (or move some prose to a sibling .md), ideally with the three stale sandbox-containment comments aligned too — then CI will finally run the tests that pin the change. Everything else checks out.
就一点机械性问题请求修改,详见上方审查意见。🙏 本提交上 workflow 体积门禁是红的:新注释让 qwen-code-pr-review.yml 超出记录基线约 7.5 KB(容差 4 KB),而 .size-baseline 未更新。请在本 PR 里更新该行并说明原因(或把部分注释移到 sibling .md),最好顺带对齐三处过时的沙箱隔离注释——之后 CI 才会真正运行钉住该改动的测试。其余均已核实无误。
|
@qwen-code /takeover |
|
🤝 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. Remove the 中文说明🤝 已接管:autofix 循环现在管理此 PR —— 将持续处理新的评审反馈与 base 冲突,直到移除标签或达到轮次上限。移除 |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
1 Suggestion-level finding(s) this review confirmed are already reported on this PR and are not repeated:
- R1-3 three stale sandbox-containment comments (~2794, ~2800-2801, ~2927) — already reported (triage review 5053859162, comment 5456134264, non-blocking item 3)
Not linted (tool limitation, not a blocker): the executable-script lint — .github/workflows/qwen-code-pr-review.yml: actionlint embedded-shell source mapping is not yet supported — not linted.
Test Plan (not a blocker): @qwen-code/qwen-code@0.21.12 — no such file or directory; @qwen-code/qwen-code@0.21.10 — no such file or directory.
中文说明
本轮确认的 1 条建议级发现已在 PR 上报告过,不再重复发布(列表见上方英文部分)。
未检查(工具限制,非阻断):the executable-script lint — .github/workflows/qwen-code-pr-review.yml: actionlint embedded-shell source mapping is not yet supported — not linted。
Test Plan(非阻断):@qwen-code/qwen-code@0.21.12 — no such file or directory; @qwen-code/qwen-code@0.21.10 — no such file or directory。
— qwen3.8-max via Qwen Code /review (v0.22.2)
|
🔀 Base updated: red check(s) [Test (ubuntu-latest, Node 22.x)] pass on current main — merged current main via update-branch; CI will re-run. 中文说明🔀 已更新 base:红色检查 [Test (ubuntu-latest, Node 22.x)] 在当前 main 上通过 —— 已通过 update-branch 合入当前 main,CI 将重新运行。 |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
5 Suggestion-level finding(s) this review confirmed are already reported on this PR and are not repeated:
- R1-3 three stale sandbox containment comments (~2794, ~2800-2801, ~2927) — already reported (triage review 5053859162, comment 5456134264, non-blocking item 3; recorded in round-1 review 5054818149)
- R1-4 follow-up bot still follows npm
latest— already reported (round-1 comment 3883806880) - R1-5 75-minute tail percentiles not reproducible from the PR — already reported (round-1 comment 3883806890)
- R1-6 unordered toContain pins none of the infra-branch structure (four surviving mutants measured) — already reported (round-1 comment 3883806896)
- R1-7 toBeLessThan(120) hardcodes the job ceiling — already reported (round-1 comment 3883806904)
Not linted (tool limitation, not a blocker): the executable-script lint — .github/workflows/qwen-code-pr-review.yml: actionlint embedded-shell source mapping is not yet supported — not linted.
Test Plan (not a blocker): @qwen-code/qwen-code@0.21.12 — no such file or directory; @qwen-code/qwen-code@0.21.10 — no such file or directory.
中文说明
本轮确认的 5 条建议级发现已在 PR 上报告过,不再重复发布(列表见上方英文部分)。
未检查(工具限制,非阻断):the executable-script lint — .github/workflows/qwen-code-pr-review.yml: actionlint embedded-shell source mapping is not yet supported — not linted。
Test Plan(非阻断):@qwen-code/qwen-code@0.21.12 — no such file or directory; @qwen-code/qwen-code@0.21.10 — no such file or directory。
— qwen3.8-max via Qwen Code /review (v0.22.2)
… sandbox mentions The settings-block rationale for tools.sandbox=false and the version pin grow qwen-code-pr-review.yml by ~7.5 KB, past the 4096-byte ratchet; the growth is the incident record itself, so bump the baseline. Two comments on the scope guard still named the sandbox as part of the containment story; align them with the settings block (write authorization + no agent token + ephemeral runner).
|
@qwen-code /triage |
|
Sandboxed verification: ✅ passed — merge-ready (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: 697 passed · 0 failed · 697 total Flakiness gate: ✅ 2 changed test file(s) x 5 identical rounds, no divergence 中文 — 判定:✅ 通过 · 可合入(agent 判定)沙箱验证在隔离、无凭证的容器中执行了该 PR 的代码(与 base 构建 A/B 对照、无 mock harness 断言、定向门禁)。仅作为评审证据,不构成评审、批准或 CI 检查。 脚本断言:697 通过 · 0 失败 · 697 总计 抖动门:✅ 2 changed test file(s) x 5 identical rounds, no divergence Verification reportPR #10428 deep verification —
|
| input | base HEAD^1 |
head HEAD^2 |
oracle |
|---|---|---|---|
settings.tools.sandbox |
true |
false |
parsed YAML → JSON |
with.qwen_cli_version |
(absent → action default latest) |
'0.21.10' |
parsed YAML |
step timeout-minutes |
(absent → job-level 120) | 75 |
parsed YAML |
settings top-level keys |
model, tools |
model, tools |
unchanged |
Reproduce:
node -e '…parse both revisions, print step.with…' (see logs/, harness/extract-blocks.mjs). The
version pin is a real, respected input of the pinned action SHA: the repo's own integration doc lists
qwen_cli_version (default latest, accepts an exact version), and sibling workflows pass it to the
same action SHA — qwen-ci-flaky-rerun.yml pins qwen_cli_version: '0.19.9', qwen-triage.yml passes
a computed version.
Table B — behavioral A/B of Report result (verbatim replay, gh/git stubbed)
| scenario (env) | base comment | head comment |
|---|---|---|
OUTCOME=failed FAILURE_KIND=infra RESOLVE_OUTCOME=failure |
"…attempted to resolve…but the run did not complete successfully." + "Check the [workflow run]…" | "…could not run conflict resolution…outcome=failure…the step's 75-minute timeout. This is not a verdict…Requesting /resolve again will fail the same way…" |
OUTCOME=failed FAILURE_KIND=infra RESOLVE_OUTCOME=cancelled |
generic | same infra wording with outcome=cancelled |
OUTCOME=failed + failure.md (no infra) |
generic + ### failure.md |
byte-identical |
OUTCOME=noop |
no-action body | byte-identical |
OUTCOME=fixed DRY_RUN=true |
dry-run message | byte-identical |
OUTCOME=fixed push (stubbed git) |
pushed message + --force-with-lease=…:<HEAD_SHA> |
byte-identical |
OUTCOME=failed, no files, no infra |
generic, no sections | byte-identical |
Witness: evidence/01-ab-report-replay-base-vs-head.png (84 scripted checks, all PASS). The infra
wording flips exactly on the FAILURE_KIND=infra axis and interpolates RESOLVE_OUTCOME and
AGENT_TIMEOUT_MINUTES; every non-infra path is byte-identical, proving the wording change is scoped and
introduces no comment regression. Resolution check cells likewise flip: head writes
outcome=failed and failure_kind=infra to $GITHUB_OUTPUT; base writes only outcome=failed.
Table C — scope-guard containment, behavioral A/A (real scratch git repo)
The PR's risk argument rests on the no-sandbox agent being contained by the Resolution check scope guard.
The diff only edits that step's comments, so this is an A/A — both arms must agree.
| scenario | base | head |
|---|---|---|
| accept (in-scope resolution) | exit 0, outcome=fixed |
exit 0, outcome=fixed (stdout byte-identical) |
| reject (edits a file base never touched) | exit 1, outcome=failed, names common.txt |
exit 1, outcome=failed, names common.txt (stdout byte-identical) |
Witness: evidence/03-scope-guard-accept-reject-aa.png (20 scripted checks). The guard fails closed on an
out-of-scope edit and accepts a clean resolution, identically on both arms — the containment the PR relies
on is present and unchanged.
Table D — mutation matrix (vacuity check on the new/changed tests)
Each mutant restores one pin in a scratch worktree; the expectation is a red test. All restorations were
verified byte-identical via SHA-256. Witness: evidence/02-mutation-matrix.png.
| mutant | expected | vitest (qwen-resolve-workflow.test.js) |
node --test (qwen-triage-workflow.test.mjs) |
|---|---|---|---|
M1 "sandbox": false→true |
red | RED — "pins the core security controls" | RED — "agent settings" |
M2 qwen_cli_version→'latest' |
red | RED — "pins the CLI version…" | RED — "agent settings" |
M3 step timeout-minutes 75→80 (pair broken) |
red | RED — "…bounds the agent step" | green (not pinned here) |
M4 delete failure_kind=infra output |
red | RED — "…infrastructure failure, not a verdict" | green (not pinned here) |
M5 0.21.10→0.21.11 (negative control) |
green | green | green |
The unmutated control went green on both suites first, so the kills are meaningful. M5 (still an exact
semver) staying green confirms the tests pin the shape of the pin, not a magic string.
Targeted gates
Witness: evidence/04-gates-vitest-262.png.
npx vitest run --config ./scripts/tests/vitest.config.ts scripts/tests/qwen-resolve-workflow.test.js scripts/tests/qwen-autofix-workflow.test.js scripts/tests/ci-flaky-rerun-workflow.test.js→ 3 files, 262 passed (resolve file: 31 tests, +2 new vs base's 29). Matches the author's claim.node --test --test-name-pattern='resolve-pr|agent settings|follow-up' .github/scripts/qwen-triage-workflow.test.mjs→ 6 passed, 0 fail. Matches the author's claim.node --test .github/scripts/qwen-triage-workflow.test.mjs(full file, Linux leg) → 119 passed, 0 fail. The "flakiness gate — behavioral" suites the author flagged as macOS-only passed here: GNUtimeoutis present at/usr/bin/timeouton this container.npx vitest run --config ./scripts/tests/vitest.config.ts scripts/tests/workflow-size.test.js→ 195 passed. The.size-baselinebump is correct:wc -c .github/workflows/qwen-code-pr-review.yml= 173427, exactly matching the recorded173427 qwen-code-pr-review.yml.- Lint on the extracted blocks:
bash -nclean andshellcheckclean on all fourrun:blocks (base+head × verify+report); repoactionlintgate passes (and was proven live — a planted invalid step id was caught, then reverted).
Corrections
None — no prior review round or bot comment needed correcting, and the author's description matches the
code (the settings: vs settings_json: history, the input names, and the timeout pairing all check out).
Findings
No blocking findings. Two non-blocking observations:
- (note) Timeout pair +
failure_kindare pinned in one file only. Thetimeout-minutes↔
AGENT_TIMEOUT_MINUTESpairing and thefailure_kind=infraoutput are asserted by
scripts/tests/qwen-resolve-workflow.test.jsbut not by.github/scripts/qwen-triage-workflow.test.mjs
(see Table D, M3/M4 green in the node column). This matches the author's own test plan, which assigns
those pins to vitest — it is a coverage-placement observation, not a defect. Both suites together catch
every mutant. - (note, self-inflicted, already corrected) Two transient harness failures were mine, not the PR's.
(i) The firstreport-replayrun reported 1 FAIL ("C6 git not called") because my harness asserted "git
not called" for the one scenario that legitimately invokes the stubbedgit push; fixed by excluding the
push scenario from that check, re-ran → 84/84. (ii) The firstscope-guardrun failed 8 checks because
my fixture committed the resolution on the branch used asHEAD_FETCH_REF, which in production is a
frozen fetched ref; fixed by detaching HEAD before the agent commit (the harness now asserts that
invariant), re-ran → 20/20. No PR code was involved in either; final counts reflect the corrected runs.
Not covered
- Real production
/resolverun. The end-to-end "agent resolves and pushes without the sandbox" outcome
is not reproducible offline (it needs a live@qwen-code /resolveon a conflicted PR after merge, which is
exactly the author's stated post-merge check). Every offline-verifiable precondition is proven above. - Author's production incident statistics (84%→0/81, 839 requests, p95/p99 latencies, the 14/24/40
failure split). These cannot be independently verified without the GitHub API; treated as motivation, not
verified fact. - Whether
0.21.10resolves on npm at merge time, and whether the third-party action runtime actually
consumesqwen_cli_version/settings. Input names/semantics are corroborated by the repo's integration
doc and sibling workflows using the same action SHA, but the action itself was not executed here. yamllint—pip3 install --userfailed (Permission denied) in this container; substituted with
actionlint + shellcheck +bash -n, all green.- Per-commit attribution. The checkout is depth 2:
git rev-list HEAD^1..HEAD^2yields 1 reachable
commit while the metadata snapshot lists 4 (two are merges ofmaininto the branch), and
git rev-parse --is-shallow-repositoryistrue— so the intermediate commits are unreachable and the
verification covers the aggregateHEAD^1..HEADdiff, not each commit separately. - Trial merge into current
main. Deeper history is unreachable at depth 2; the merge commit already
incorporates the base tip, and the diff touches only CI/test files, so the residual risk is noted rather
than measured.
Methodology
Environment: the sandboxed CI verify job (node:22-bookworm), working tree = refs/pull/10428/merge at
depth 2, with npm ci + npm run build already complete. The metadata snapshot's baseRefOid
(5ae363e2…) had drifted behind the checkout; per the merge-ref contract the effective base is HEAD^1
(a268b7d1…), which the merge commit message confirms. Base side came from git show HEAD^1:… and a
scratch git worktree (node_modules symlinked), so the control differs from head only by the PR diff.
How each harness drove the code: harness/extract-blocks.mjs parses both workflow revisions with yaml
and writes the Resolution check and Report result run: blocks verbatim (the single run-URL ${{ }}
expression substituted). harness/report-replay.mjs executes those blocks under bash --noprofile --norc
with stub gh/git on PATH across 7 report scenarios + 2 verify scenarios per arm, asserting exit codes,
$GITHUB_OUTPUT, and byte-equality of the generated report.md. harness/scope-guard-replay.mjs builds a
real scratch git repo (base advances with a conflicting edit + a base-only file, PR head diverges, agent
merge-resolves in-scope or out-of-scope) and runs the extracted guard end-to-end. Mutations ran in
tmp/…/mut-tree with byte-identical restore verified by SHA-256. Raw per-cell stdout/stderr and logs live in
logs/ (01-vitest-3files.log, 03-node-test-full.log, 05-mutation-matrix.log,
06-report-replay.log, 07-scope-guard.log, 09-actionlint.log, 10-shellcheck.log) and cells/.
Assertion accounting (assertions.json, all executed): report-replay 84, scope-guard 20, vitest 3-file gate
262, node --test full triage file 119, workflow-size gate 195, mutation matrix 17 (10 per-suite
killed/green-as-predicted outcomes + 5 byte-identical restores + 2 unmutated-control greens) =
697 pass, 0 fail. The 6-test pattern run is the author's command re-executed and passed; its assertions
are a subset of the 119 and are not double-counted.
Flakiness gate log
rounds=5 files=2 skipped=0
file .github/scripts/qwen-triage-workflow.test.mjs: (cd .) node --test ./.github/scripts/qwen-triage-workflow.test.mjs
file scripts/tests/qwen-resolve-workflow.test.js: (cd .) npx --no-install vitest run --config ./scripts/tests/vitest.config.ts ./scripts/tests/qwen-resolve-workflow.test.js
per-file results (P=pass F=fail I=infra-exit, one letter per run):
.github/scripts/qwen-triage-workflow.test.mjs: PPPPP
scripts/tests/qwen-resolve-workflow.test.js: PPPPP
verdict: pass
summary: 2 changed test file(s) x 5 identical rounds, no divergence
--- per-invocation detail (full copy in the artifact) ---
round 1 · .github/scripts/qwen-triage-workflow.test.mjs: P (exit 0)
round 1 · scripts/tests/qwen-resolve-workflow.test.js: P (exit 0)
round 2 · .github/scripts/qwen-triage-workflow.test.mjs: P (exit 0)
round 2 · scripts/tests/qwen-resolve-workflow.test.js: P (exit 0)
round 3 · .github/scripts/qwen-triage-workflow.test.mjs: P (exit 0)
round 3 · scripts/tests/qwen-resolve-workflow.test.js: P (exit 0)
round 4 · .github/scripts/qwen-triage-workflow.test.mjs: P (exit 0)
round 4 · scripts/tests/qwen-resolve-workflow.test.js: P (exit 0)
round 5 · .github/scripts/qwen-triage-workflow.test.mjs: P (exit 0)
round 5 · scripts/tests/qwen-resolve-workflow.test.js: P (exit 0)
Evidence images
Harness scripts and raw logs are in the workflow run artifacts (7-day retention).
— Qwen Code · sandboxed verification
|
Triage re-run completed without a new review.
The stage comments above were updated with the latest result. View workflow run. 上方各阶段评论已更新为最新结果。查看工作流运行。 |
Code Coverage Summary
CLI Package - Full Text ReportCore Package - Full Text ReportFor detailed HTML reports, please see the 'coverage-reports-22.x-ubuntu-latest' artifact from the main CI run. |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
1 Suggestion-level finding(s) this review confirmed are already reported on this PR and are not repeated:
- R3-1 remaining stale 'sandboxed' containment comment (qwen-code-pr-review.yml:2928) — already reported as the third instance of R1-3 (triage review 5053859162, comment 5456134264, non-blocking item 3; recorded in round-1 review 5054818149 a…
Not linted (tool limitation, not a blocker): the executable-script lint — .github/workflows/qwen-code-pr-review.yml: actionlint embedded-shell source mapping is not yet supported — not linted.
Test Plan (not a blocker): @qwen-code/qwen-code@0.21.12 — no such file or directory; @qwen-code/qwen-code@0.21.10 — no such file or directory.
中文说明
本轮确认的 1 条建议级发现已在 PR 上报告过,不再重复发布(列表见上方英文部分)。
未检查(工具限制,非阻断):the executable-script lint — .github/workflows/qwen-code-pr-review.yml: actionlint embedded-shell source mapping is not yet supported — not linted。
Test Plan(非阻断):@qwen-code/qwen-code@0.21.12 — no such file or directory; @qwen-code/qwen-code@0.21.10 — no such file or directory。
— qwen3.8-max via Qwen Code /review (v0.22.2)
…ntialed push The no-sandbox /resolve agent runs --yolo before 'Resolution check' and 'Report result' in the same job: anything it appends to $GITHUB_ENV reaches every later step, and it writes the workspace. A prompt-injected agent could therefore plant BASH_ENV/LD_* hijacks, PATH shims, git-config url rewrites or a pre-push hook that the workflow-scope push token then walks through (measured by review probes; the pre-fix shape fires all three channels). Harden both post-agent steps: pin BASH_ENV/LD_* empty at the step boundary (they execute before any body line runs), pin PATH, drop git's env channels, ignore host/global config, remove the agent-writable local .git/config, and run the push hookless (--no-verify, core.hooksPath=/dev/null). Pin the follow-up bot's CLI version too — it was the one sibling still following npm `latest` through the 2026-08-15 notarget incident class. Tests: witness assertions for every hardening layer (each one red when its layer is removed), arm-slice pins for the infra/generic report bodies and the failure_kind=infra write, and the step-vs-job timeout pair now reads the job ceiling instead of duplicating it. Bump the size baseline for the deliberate workflow growth.
|
🤖 Addressed the latest review feedback (round 1/100). What changed, and what I pushed back on: · 已处理最新评审反馈(第 1/100 轮)。改动内容与我反驳保留之处如下: Round 4 — Critical R2-1 fixed; four Suggestions closed; R1-5 answered with the extraction queryRoot cause (R2-1). The no-sandbox Fix (house playbook, sized to this job). Both post-agent steps now:
Same poisoned state against the hardened bodies: Other findings.
Mutation probes (every guard witnessed, all RED with the guard removed, green restored): drop Verification
中文说明第 4 轮 — Critical R2-1 已修复;四条 Suggestion 已关闭;R1-5 以提取查询作答根因(R2-1)。 无沙箱的 修复(仓库既有手册,按本 job 裁剪)。 两个 agent 之后的步骤现在都:
同一投毒状态下运行加固后的脚本: 其余发现。
变异探测(每个守卫都有见证,移除守卫全部变红,恢复后变绿): 移除 验证
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/模型 |
|
@qwen-code /triage |
|
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: 702 passed · 0 failed · 702 total Flakiness gate: ✅ 2 changed test file(s) x 5 identical rounds, no divergence 中文 — 判定:❌ 不通过 · 报告了发现(agent 判定)沙箱验证在隔离、无凭证的容器中执行了该 PR 的代码(与 base 构建 A/B 对照、无 mock harness 断言、定向门禁)。仅作为评审证据,不构成评审、批准或 CI 检查。 脚本断言:702 通过 · 0 失败 · 702 总计 抖动门:✅ 2 changed test file(s) x 5 identical rounds, no divergence Verification report<!-- qwen-resolve-result --> x Flakiness gate logEvidence imagesHarness scripts and raw logs are in the workflow run artifacts (7-day retention). — Qwen Code · sandboxed verification |
…, on top of the post-agent scrub Complements the scrub that 'Resolution check' and 'Report result' now open with (PATH pinned, git env channels dropped, every config scope ignored, .git/config removed, BASH_ENV/LD_* pinned empty): those close what the later steps can see, but a $GITHUB_ENV append made by the agent is applied by the runner to every later step's environment, where an unset list can only chase known names. So the agent is now invoked directly — the pinned CLI installed in its own step — with its invocation's $GITHUB_ENV, $GITHUB_PATH, $GITHUB_OUTPUT and $GITHUB_STEP_SUMMARY pointed at decoys that die with the step, inside a ::stop-commands:: window with a random resume token, the way the review lane runs its agent. Settings go to a per-run QWEN_HOME instead of the action input. On the scrubbed side: 'Show run artifacts' gets the same scrub, every post-agent git call goes through a wrapper that disables hooks, the fsmonitor command and credential helpers, and 'Report result' gives gh a fresh empty config dir instead of leaving ~/.config/gh in reach. The settings-block comment records the residual: a root-capable agent on a hosted runner can defeat any in-job measure; the structural answer is a downstream job for the credentialed push, tracked separately. Tests pin the direct invocation, the decoys, the stop-commands window, the scrub and wrapper on every post-agent step, the BASH_ENV/LD_* pins, the gh config dir and --no-verify; the size baseline moves with the growth.
|
@qwen-code /triage |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Partially reviewed — gaps disclosed.
13 Suggestion-level finding(s) this review confirmed are already reported on this PR and are not repeated:
- follow-up bot qwen_cli_version pin has no contract test — already reported as R4-7 (comment 3886465040 on qwen-issue-followup-bot.yml:306)
- producer half of the resolve-pr outputs contract unpinned (qwen-code-pr-review.yml:2478) — already reported as D6 in round-6 review 5059565836's deferred list
- post-agent credential scan misses job-level env: and inline secrets in run: (qwen-triage-workflow.test.mjs:444) — already reported in round-6 review 5059565836's deferred list
- resolve-lane decoy pins cover only two of the four assignments (qwen-resolve-workflow.test.js:960) — already reported as R5-2(d) (comment 3887462739)
- settings.json write / export QWEN_HOME ordering before the invocation unpinned (qwen-triage-workflow.test.mjs:266) — already reported in round-6 review 5059565836's deferred list
- skip-step exception gate pinned by substring only (qwen-triage-workflow.test.mjs:437) — already reported in round-6 review 5059565836's deferred list
- publish 'Resolution check'/'Report result' bare-always() gates unpinned (qwen-resolve-workflow.test.js:907) — already reported in round-6 review 5059565836's deferred list
- settings write source never bound to $QWEN_SETTINGS (qwen-triage-workflow.test.mjs:266) — already reported in round-6 review 5059565836's deferred list
- review-lane truncation asymmetry reframed (review-artifact-upload.test.js:306) — re-file of a candidate rejected by this round's verification: the review-lane agent step itself holds CI_BOT_PAT (qwen-code-pr-review.yml:931), so the channel …
- .size-baseline under-records the workflow size by 1209 bytes (.github/workflows/.size-baseline:40) — already reported in round-6 review 5059565836's deferred list
- decoy assignments unpinned as invocation-scoped prefixes (qwen-triage-workflow.test.mjs:356) — already reported in round-6 review 5059565836's deferred list
- moved-head update-ref re-pinning never exercised by the fixture (qwen-resolve-workflow.test.js:885) — already reported in round-6 review 5059565836's deferred list
- status reassignment between status=$? and exit "$status" unpinned (qwen-resolve-workflow.test.js:959) — already reported as R4-7 (comment 3886465060)
Not reviewed: reverse audit — stopped at the reverse-audit round cap of 5 without converging.
Not explored to full depth (tool budget reached): chunk 4: none — vitest could not be run (no node_modules in the review worktree), so I replicated the changed tests' extraction logic and executed the extracted shell he….
Not linted (tool limitation, not a blocker): the executable-script lint — .github/workflows/qwen-code-pr-review.yml: actionlint embedded-shell source mapping is not yet supported — not linted; the executable-script lint — .github/workflows/qwen-issue-followup-bot.yml: actionlint embedded-shell source mapping is not yet supported — not linted.
Test Plan (not a blocker): @qwen-code/qwen-code@0.21.12 — no such file or directory.
Deferred under the convergence posture (round 7, not a blocker) — recorded, not requested in this round:
.github/workflows/qwen-code-pr-review.yml:2934 — [review] 'Report skipped request' gate misses the empty decision — silent red run, no comment, on a pre-prepare failure.github/workflows/qwen-code-pr-review.yml:2845 — [review] model wiring (OPENAI_MODEL / MODEL_ARGS) of the direct qwen invocation pinned by no test — deletion mutants ship green.github/workflows/qwen-code-pr-review.yml:3027 — [review] the new unreachable-HEAD_SHA refusal guard has no test witness — guard-deletion mutant ships greenscripts/tests/qwen-resolve-workflow.test.js:1337 — [probe] 'Resolution check' crash before its first output write (transient fetch failure) posts the generic 'did not complete' wording, RESOLVE_OUTCOME=success unconsulted.github/scripts/qwen-triage-workflow.test.mjs:496 — [probe] GH_TOKEN on 'Report result'/'Report skipped request' unpinned — credential swaps ship green and the result comment silently 403s.github/workflows/qwen-code-pr-review.yml:3279 — [probe] artifact name quoted in the four push-failure comments is the only carrier pinned by no test — rename ships green, comments cite a nonexistent artifact.github/workflows/qwen-code-pr-review.yml:3181 — [probe] load-bearing mkdir -p "${WORKDIR}" on 'Report result' exercised by no test — deletion mutant ships green, infra comment dies on ENOENT.github/workflows/qwen-code-pr-review.yml:3028 — [probe] unreachable-HEAD refusal writes outcome=failed but no failure_kind — misleading 'did not complete' comment.github/workflows/qwen-code-pr-review.yml:3236 — [probe] 'Report result' push arm and exit-code contract executed by no test — deleting the push_failed exit-1 ships green and kills the retry path
Convergence: round 7 posted 1 inline comment(s), 1 of them reported for the first time; the previous round posted 2 (2 new). Findings keep coming back to the same files: .github/workflows/qwen-code-pr-review.yml (findings in round 6; 1 more now). A cluster that keeps producing siblings usually means the fixes are treating instances of a shared root cause — triaging that cause before the next round, or splitting an independent cluster into its own pull request, tends to end the loop faster than fixing them one at a time. (Observation only — nothing was withheld from this review because of this observation.)
Mechanism health: this round did not close cleanly, so it withholds the incremental anchor — and the round it recovered had no anchor this round could use either — none at all, one with no certifier, one certified by an identity other than the one this round runs under, or one this round's fetch refused or resolved to the head — so the next review re-reads the whole diff unless recovery grafts an earlier own anchor that the round running it can use onto the complete work list this round leaves behind, and keeps doing so until a round's marker carries an anchor again or a graft lands that the round running it can use. (Stated, not acted on — this changes nothing about what the round posts.)
[Critical] R7-1: [certifies-falsely] [new-surface] The follow-up bot's CLI version pin is a dead switch on its primary lane: the pinned action's 'Install Qwen Code' step unconditionally skips when qwen is already on PATH — always true on the ecs-qwen pool the job routes to (qwen-issue-followup-bot.yml:57) — so the bot there runs the fleet-installed version maintained at @latest by update-ecs-runner-qwen.yml, not '0.21.10'. The pin bites only on the ubuntu-latest fallback lane, and the new contract test only regex-matches the YAML shape, so the diff comment's claim "the pin wins" ships green and falsely certified on the primary lane. Failure scenario: a broken or unwanted npm publish propagates to the ECS fleet via the fleet-update workflow's @latest default, and the bot's next schedule run (cron 5 */6 * * *) executes the agent on that CLI despite the declared pin — the exact 2026-08-15 notarget outage class this hunk exists to prevent. Witness: probe executed the pinned action's real install-step script at SHA 05f8171 — qwen-on-PATH arm (ECS model): exit=0, "Qwen Code already installed, skipping install:", zero npm invocations (pin inert, fleet version runs); no-qwen arm (ubuntu-latest model): "Installing Qwen Code from npm: @qwen-code/qwen-code@0.21.10" — the probe flips. Suggested fix: make the pin actually install on the ECS lane (install the pinned version over/ahead of the fleet binary before invoking the action), or rewrite the comment and contract test to state the real contract — fleet version authoritative on ecs-qwen, pin effective only on the ubuntu-latest fallback. (Relocated to the body: its inline location qwen-issue-followup-bot.yml:306 overlaps the existing R4-7 thread, comment 3886465040.)
中文说明
仅完成部分审查,审查缺口已披露。
本轮确认的 13 条建议级发现已在 PR 上报告过,不再重复发布(列表见上方英文部分)。
未审查:reverse audit — stopped at the reverse-audit round cap of 5 without converging。
未探索到全部深度(达到工具调用预算):chunk 4:none — vitest could not be run (no node_modules in the review worktree), so I replicated the changed tests' extraction logic and executed the extracted shell he…。
未检查(工具限制,非阻断):the executable-script lint — .github/workflows/qwen-code-pr-review.yml: actionlint embedded-shell source mapping is not yet supported — not linted; the executable-script lint — .github/workflows/qwen-issue-followup-bot.yml: actionlint embedded-shell source mapping is not yet supported — not linted。
Test Plan(非阻断):@qwen-code/qwen-code@0.21.12 — no such file or directory。
收敛姿态下延后(第 7 轮,非阻断)——已记录,本轮不要求修改:共 9 条(原文未翻译,列表见上方英文部分)。
收敛情况:第 7 轮发布了 1 条行内评论,其中 1 条是首次提出;上一轮发布了 2 条(其中 2 条首次提出)。发现反复回到同一批文件:.github/workflows/qwen-code-pr-review.yml(第 6 轮已出过发现,本轮又有 1 条)。一个不断再生兄弟发现的簇,通常意味着逐条修复只在处理同一根因的实例——先定位并处理该根因,或把独立的簇拆成单独的 PR,通常比逐条修复更快结束循环。(仅为观察——本轮评审未因此扣留任何内容。)
机制健康:本轮未能干净收尾,因而扣留了增量锚点,而它恢复到的那一轮也没有留下本轮可用的锚点——要么完全没有、要么没有认证者、要么由本轮运行身份之外的身份认证、要么被本轮的获取拒绝或解析为头提交——因此下一次评审将重读整个 diff,除非恢复流程把本轮能使用的更早自有锚点嫁接到本轮留下的完整工作清单上;并会一直如此,直到某一轮的标记重新带上锚点,或落地的嫁接能被运行该轮的评审使用。(仅陈述,不据此行动——这不改变本轮发布的任何内容。)
[Critical] R7-1: [certifies-falsely] [new-surface] The follow-up bot's CLI version pin is a dead switch on its primary lane: the pinned action's 'Install Qwen Code' step unconditionally skips when qwen is already on PATH — always true on the ecs-qwen pool the job routes to (qwen-issue-followup-bot.yml:57) — so the bot there runs the fleet-installed version maintained at @latest by update-ecs-runner-qwen.yml, not '0.21.10'. The pin bites only on the ubuntu-latest fallback lane, and the new contract test only regex-matches the YAML shape, so the diff comment's claim "the pin wins" ships green and falsely certified on the primary lane. Failure scenario: a broken or unwanted npm publish propagates to the ECS fleet via the fleet-update workflow's @latest default, and the bot's next schedule run (cron 5 */6 * * *) executes the agent on that CLI despite the declared pin — the exact 2026-08-15 notarget outage class this hunk exists to prevent. Witness: probe executed the pinned action's real install-step script at SHA 05f8171 — qwen-on-PATH arm (ECS model): exit=0, "Qwen Code already installed, skipping install:", zero npm invocations (pin inert, fleet version runs); no-qwen arm (ubuntu-latest model): "Installing Qwen Code from npm: @qwen-code/qwen-code@0.21.10" — the probe flips. Suggested fix: make the pin actually install on the ECS lane (install the pinned version over/ahead of the fleet binary before invoking the action), or rewrite the comment and contract test to state the real contract — fleet version authoritative on ecs-qwen, pin effective only on the ubuntu-latest fallback. (Relocated to the body: its inline location qwen-issue-followup-bot.yml:306 overlaps the existing R4-7 thread, comment 3886465040.)
— qwen3.8-max via Qwen Code /review (v0.22.3)
|
@qwen-code /triage |
|
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: 946 passed · 1 failed · 947 total Flakiness gate: ✅ 3 changed test file(s) x 5 identical rounds, no divergence 中文 — 判定:❌ 不通过 · 报告了发现(agent 判定)沙箱验证在隔离、无凭证的容器中执行了该 PR 的代码(与 base 构建 A/B 对照、无 mock harness 断言、定向门禁)。仅作为评审证据,不构成评审、批准或 CI 检查。 脚本断言:946 通过 · 1 失败 · 947 总计 抖动门:✅ 3 changed test file(s) x 5 identical rounds, no divergence Verification reportSandboxed verification: ❌ findings reported — delta claims proven, one re-run regression found (agent verdict) Verified head: Scripted assertions: 946 passed · 1 failed · 947 total (the 1 fail is the size-baseline drift in Findings #2) 中文 — 判定:❌ 报告了发现 · 增量改动已证实,发现一处重跑回归(agent 判定)在隔离、无凭证的容器中执行了该 PR 的代码(与 base/上一轮 head 构建 A/B 对照、无 mock harness 断言、定向门禁)。仅作为评审证据,不构成评审、批准或 CI 检查。 脚本断言:946 通过 · 1 失败 · 947 总计(唯一失败项为 Findings #2 的尺寸基线漂移)
Follow-up round — status of the previous reportPrevious verdict: findings (840 passed · 1 failed), verified head
The previous round's Corrections section is also resolved: the current snapshot body describes the final (job-split) design accurately — the description no longer lags the code. Scope
Out of scope (see Not covered): post-merge live runs, the sandbox-hang root cause, per-commit attribution. A/B on the delta claim (R6-2)Harness Scenario A — "Re-run failed jobs" re-executes
Scenario B — only
14/14 scripted assertions. The R6-2 shape is fixed and the new pins are non-vacuous (mutations MD1/MD2 below). The scenario-B miss is the price paid — demonstrated end-to-end in the next section. R6-1 (yamllint): yamllint itself cannot be installed in this container (no Findings1. A publish-only re-run now misses its own artifact and misreports it — the sibling case of R6-2. Severity: worth fixing before merge if re-runs are expected; fails closed. The suffix keys the download to the publish job's What happens next was driven through the real
(Control cell: with Properties: fails closed (nothing is pushed, stale or otherwise — that is the point of R6-2 and it holds); no corruption. Cost: one wasted re-run, a misleading comment in the exact dimension this PR set out to fix, and the resolution is only recovered by a fresh Reproduce: Suggested fix (measured at the naming-semantics level)Have the uploader record the attempt it uploaded at, and have the downloader name by that output instead of by its own # resolve-pr outputs:
artifact_attempt: '${{ github.run_attempt }}'
# publish-resolution 'Download run artifacts':
name: 'qwen-resolve-pr-${{ needs.resolve-pr.outputs.pr_number }}-attempt-${{ needs.resolve-pr.outputs.artifact_attempt }}'Job outputs of a not-re-run job are preserved across failed-jobs re-runs, so this evaluates to 1 in the publish-only re-run and to 2 when 2. 3. Stale test comment (carried, stands). 4. Garbled comment (carried, stands). 5. Two guards remain unpinned (carried, stands). Deleting Mutation matrix at the new head
10/10 as expected. MD1/MD2 prove the delta's "extend the artifact-name agreement pins" claim non-vacuous on both sides. The previous round's lineage/bundle-verify/head-pinning/missing-artifact mutants were not re-run: the delta's hunks are confined to the two artifact names, the two Targeted gates (all executed at the new head)
Witness: Not covered
MethodologyRan in the lane's own Flakiness gate logEvidence imagesHarness scripts and raw logs are in the workflow run artifacts (7-day retention). — Qwen Code · sandboxed verification |
Round-7 review findings:
R6-2 (fix-induced) — the run_attempt artifact-name suffix that fixed the
stale-republish defect broke "Re-run failed jobs" the other way: the re-run
executes only publish-resolution, whose attempt number has no artifact, so
the by-name download missed the agent's upload, 'Resolution check' called a
SUCCESSFUL agent an infrastructure failure, and 'Report result' posted a
comment self-contradictory on every clause ("outcome=success ... CLI
install, model endpoint or infrastructure failure ... Requesting /resolve
again will fail the same way") while the resolution stayed unpublished —
every further partial re-run repeated it. Thread the attempt that ran the
agent across the job boundary (the runner provides GITHUB_RUN_ATTEMPT; the
agent never writes it) and download by it, so the partial re-run picks up
the artifact that actually exists and publishes it. For the residual case —
the agent succeeded but the artifact is genuinely gone, upload failure or
expiry — classify the lost artifact (failure_kind=artifact_missing) instead
of the agent run, and word the comment accordingly: the agent finished, the
result could not be published, and "Re-run all jobs" is the one recovery
that re-produces the artifact. Fixture scenario (d) now drives the real
'Resolution check' block across both arms and reds on the pre-fix shape.
R7-1 — the follow-up bot's qwen_cli_version pin is inert on its primary
lane: the pinned action's 'Install Qwen Code' step skips when qwen is
already on PATH (verified at the pinned SHA), always true on the ecs-qwen
pool whose fleet CLI update-ecs-runner-qwen.yml maintains, so the comment's
"the pin wins" falsely certified there. Rewrite it to state the real
contract — fleet version authoritative on ecs-qwen, the pin governing the
ubuntu-latest fallback lane — and say so in the contract test too.
Also lands the round's mechanical items: .size-baseline records both
workflows' exact new sizes (the ratchet passed only on its 4096-byte
allowance); the unpinned gitfile guard in 'Package resolution' and
GIT_TERMINAL_PROMPT=0 in 'Report result' get contract pins (mutation
survivors M9/M10 of the sandboxed verification); a stale test
cross-reference ('hardens the post-agent steps' no longer exists) and a
garbled scope-guard comment repaired in passing.
|
Triage re-run completed without a new review. The bot already has a review of its own on 机器人在 The stage comments above were updated with the latest result. View workflow run. 上方各阶段评论已更新为最新结果。查看工作流运行。 |
|
🤖 Addressed the latest review feedback (round 1/100). What changed, and what I pushed back on: · 已处理最新评审反馈(第 1/100 轮)。改动内容与我反驳保留之处如下: Autofix review round — PR #10428One commit this round: Findings and dispositionsR6-2 fix-induced — partial "Re-run failed jobs" wedged R7-1 — follow-up bot's CLI pin inert on its primary lane (Critical, review body) — FIXED (documentation option). Verified against the pinned action source (SHA 05f8171): Verify-report findings (ic:5464747770):
Re-verified as already resolved in earlier commits (threads listed for resolution): R1-8 size gate (both threads), R1-4, R1-6, R1-7, the R2-1 lineage, R4-1, R4-3, R4-5, R4-11, R4-12, R4-13, R4-17, R4-19, the R5-1/R5-5/R5-7/R5-8 findings, the R5-2 family (a/b/c/d/e/f/g/j), R6-1 yamllint quotes, R6-2 attempt suffix — all re-checked against the current tree: the structural split (af381c1), the wiring/ordering pins (1d7b829), and the attempt-suffix/yamllint fixes (4fb0c44) hold, and every pinned contract is green at this HEAD. Not resolved in code: rc:3883806890 (R1-5, percentile reproducibility) — the finding itself allows "no code change if the data confirms"; the author posted the full extraction method in-thread (comment rc:3887615289). Left for the maintainer to close; a reply records the disposition. The reviewer's round-6/7 deferred probe lists were explicitly "recorded, not requested" and were not touched. The "Failed checks" section (Test CANCELLED, web-shell E2E Smoke, Post Coverage Comment) reflects the previous push: the yamllint failure that red-walled the Test job was fixed at the current HEAD before this round; the other two touch no file this PR changes — CI re-runs on the next push are the check. VerificationCommands actually run this round (results):
中文说明Autofix 审查轮次 — PR #10428本轮一个提交: 发现与处置R6-2 修复诱发 — 部分 "Re-run failed jobs" 卡死 R7-1 — follow-up bot 的 CLI 版本钉在其主车道上无效(Critical,审查正文)— 已修复(采用文档化选项)。 对照钉住 SHA(05f8171)的 action 源码核实: 验证报告发现(ic:5464747770):
复核确认早先提交已解决(线程列表供解析): R1-8 体积门禁(两条)、R1-4、R1-6、R1-7、R2-1 谱系、R4-1、R4-3、R4-5、R4-11、R4-12、R4-13、R4-17、R4-19、R5-1/R5-5/R5-7/R5-8、R5-2 家族 (a/b/c/d/e/f/g/j)、R6-1 yamllint 引号、R6-2 attempt 后缀——全部在当前树上复核:结构性拆分(af381c10ac)、接线/顺序钉(1d7b829d7c)、attempt 后缀与 yamllint 修复(4fb0c4490a)均成立,所有被钉契约在本 HEAD 上为绿。 未以代码解决: rc:3883806890(R1-5,百分位可复现性)——该发现本身允许"数据证实则无需改代码";作者已在线程中贴出完整提取方法(评论 rc:3887615289)。留给维护者关闭;已回复记录处置。审查者第 6/7 轮的延后探针清单明确为"仅记录、本轮不要求",未触碰。"失败检查"部分(Test CANCELLED、web-shell E2E Smoke、Post Coverage Comment)反映的是上一次推送的状态:挡红 Test job 的 yamllint 失败在本轮之前已于当前 HEAD 修复;另外两项与本 PR 改动的文件无关——以下次推送的 CI 重跑为准。 验证本轮实际执行的命令(结果):
Base-conflict check · 基分支冲突检查: no conflict with main. · 与 main 无冲突。 🧵 Resolved all 37 selected review thread(s). · 已关闭全部选中的 37 条评审线程。 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/模型 |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Partially reviewed — gaps disclosed.
12 Suggestion-level finding(s) this review confirmed are already reported on this PR and are not repeated:
- resolve-pr outputs producer-half contract unpinned (qwen-code-pr-review.yml:2478) — already reported in round-6 review 5059565836's deferred list, re-confirmed in round 7
- resolve-pr job-level env credential scan (scripts/tests/qwen-resolve-workflow.test.js:842) — already reported in round-6 review 5059565836's deferred list
- bare always() gates on publish steps unpinned (scripts/tests/qwen-resolve-workflow.test.js:907) — already reported in round-6 review 5059565836's deferred list
- mkdir -p WORKDIR witness (qwen-code-pr-review.yml:3181) — already reported in round-6 review 5059565836's deferred list
- moved-head update-ref fixture case (scripts/tests/qwen-resolve-workflow.test.js:885) — already reported in round-6 review 5059565836's deferred list
- MODEL_ARGS/OPENAI_MODEL wiring pin (qwen-code-pr-review.yml:2845) — already reported in round-7 review 5060110516's deferred list
- unreachable-HEAD refusal failure_kind witness (qwen-code-pr-review.yml:3028) — already reported in round-7 review 5060110516's deferred list
- settings write source bound to $QWEN_SETTINGS (.github/scripts/qwen-triage-workflow.test.mjs:266) — already reported in round-6 review 5059565836's deferred list
- decoy pins substring/position (.github/scripts/qwen-triage-workflow.test.mjs:356) — already reported in round-6 review 5059565836's deferred list
- post-agent credential scan run:/if: coverage (.github/scripts/qwen-triage-workflow.test.mjs:444) — already reported in round-6 review 5059565836's deferred list
- resolve-side decoy pins cover 2 of 4 assignments (scripts/tests/qwen-resolve-workflow.test.js:989) — already reported as R5-2(d), comment 3887462739
- skip-step exception gate disjunction pin (.github/scripts/qwen-triage-workflow.test.mjs:437) — already reported in round-6 review 5059565836's deferred list
Not reviewed: reverse audit — stopped at the reverse-audit round cap of 5 without converging.
Not linted (tool limitation, not a blocker): the executable-script lint — .github/workflows/qwen-code-pr-review.yml: actionlint embedded-shell source mapping is not yet supported — not linted; the executable-script lint — .github/workflows/qwen-issue-followup-bot.yml: actionlint embedded-shell source mapping is not yet supported — not linted.
Test Plan (not a blocker): @qwen-code/qwen-code@0.21.12 — no such file or directory.
Deferred under the convergence posture (round 8, not a blocker) — recorded, not requested in this round:
.github/workflows/qwen-code-pr-review.yml:3047 — [review] infra early-exit discards a committed-and-packaged resolution bundle (probe-verified).github/scripts/qwen-triage-workflow.test.mjs:485 — [review] resolve-pr permissions block unpinned; delete/widen mutants ship green (probe-verified).github/workflows/qwen-code-pr-review.yml:3025 — [review] unguarded first git fetch aborts before outcome= is written (probe-verified).github/workflows/qwen-code-pr-review.yml:3072 — [review] bundle import lines unguarded under set -e bypass the classified refusal (probe-verified).github/workflows/qwen-code-pr-review.yml:2689 — [review] --registry flag on 'Install Qwen CLI' pinned by no test (probe-verified)scripts/tests/review-artifact-upload.test.js:306 — [review] GITHUB_STATE never decoyed; 'All four runner command files' claim inaccurate (probe-verified)
Mechanism health: this round did not close cleanly, so it withholds the incremental anchor — and the round it recovered had no anchor this round could use either — none at all, one with no certifier, one certified by an identity other than the one this round runs under, or one this round's fetch refused or resolved to the head — so the next review re-reads the whole diff unless recovery grafts an earlier own anchor that the round running it can use onto the complete work list this round leaves behind, and keeps doing so until a round's marker carries an anchor again or a graft lands that the round running it can use. (Stated, not acted on — this changes nothing about what the round posts.)
[Critical] R7-1: [certifies-falsely] [new-surface] The follow-up bot's CLI version pin is a dead switch on its primary lane: the pinned action's 'Install Qwen Code' step skips unconditionally when qwen is already on PATH — always true on the ecs-qwen pool the job routes to by default (qwen-issue-followup-bot.yml:57) — so the bot there runs the fleet-installed CLI maintained at @latest by update-ecs-runner-qwen.yml, not '0.21.10'; the pin binds only the ubuntu-latest fallback lane. A broken or unwanted npm publish propagates to the ECS fleet via the fleet-update workflow's @latest default, and the bot's next schedule run (cron 5 */6 * * *) executes the agent on that CLI despite the declared pin — the exact 2026-08-15 notarget outage class this hunk exists to prevent. Witness: verified at HEAD 06d174b — qwen-issue-followup-bot.yml routes to the ecs-qwen pool by default (line 57) and pins qwen_cli_version: '0.21.10' (line 312), and the diff's own added comment concedes: 'On the ecs-qwen pool — this job's default lane — the fleet-installed CLI maintained at @latest by update-ecs-runner-qwen.yml is therefore authoritative; the pin binds the ubuntu-latest fallback lane.' Fix: make the pin effective on the primary lane — route the follow-up job to ubuntu-latest (where the pin binds), pin the fleet-installed version in update-ecs-runner-qwen.yml, or force the pinned install on the ecs-qwen lane even when qwen is already on PATH. (This round's inline anchor at the pin line was dropped by position overlap with existing comment 3886465040 — R4-7, a different finding on the same line; R7-1's only prior copy is in round 7's review body, so the still-standing blocker is carried here.)
中文说明
仅完成部分审查,审查缺口已披露。
本轮确认的 12 条建议级发现已在 PR 上报告过,不再重复发布(列表见上方英文部分)。
未审查:reverse audit — stopped at the reverse-audit round cap of 5 without converging。
未检查(工具限制,非阻断):the executable-script lint — .github/workflows/qwen-code-pr-review.yml: actionlint embedded-shell source mapping is not yet supported — not linted; the executable-script lint — .github/workflows/qwen-issue-followup-bot.yml: actionlint embedded-shell source mapping is not yet supported — not linted。
Test Plan(非阻断):@qwen-code/qwen-code@0.21.12 — no such file or directory。
收敛姿态下延后(第 8 轮,非阻断)——已记录,本轮不要求修改:共 6 条(原文未翻译,列表见上方英文部分)。
机制健康:本轮未能干净收尾,因而扣留了增量锚点,而它恢复到的那一轮也没有留下本轮可用的锚点——要么完全没有、要么没有认证者、要么由本轮运行身份之外的身份认证、要么被本轮的获取拒绝或解析为头提交——因此下一次评审将重读整个 diff,除非恢复流程把本轮能使用的更早自有锚点嫁接到本轮留下的完整工作清单上;并会一直如此,直到某一轮的标记重新带上锚点,或落地的嫁接能被运行该轮的评审使用。(仅陈述,不据此行动——这不改变本轮发布的任何内容。)
[Critical] R7-1: [certifies-falsely] [new-surface] The follow-up bot's CLI version pin is a dead switch on its primary lane: the pinned action's 'Install Qwen Code' step skips unconditionally when qwen is already on PATH — always true on the ecs-qwen pool the job routes to by default (qwen-issue-followup-bot.yml:57) — so the bot there runs the fleet-installed CLI maintained at @latest by update-ecs-runner-qwen.yml, not '0.21.10'; the pin binds only the ubuntu-latest fallback lane. A broken or unwanted npm publish propagates to the ECS fleet via the fleet-update workflow's @latest default, and the bot's next schedule run (cron 5 */6 * * *) executes the agent on that CLI despite the declared pin — the exact 2026-08-15 notarget outage class this hunk exists to prevent. Witness: verified at HEAD 06d174b — qwen-issue-followup-bot.yml routes to the ecs-qwen pool by default (line 57) and pins qwen_cli_version: '0.21.10' (line 312), and the diff's own added comment concedes: 'On the ecs-qwen pool — this job's default lane — the fleet-installed CLI maintained at @latest by update-ecs-runner-qwen.yml is therefore authoritative; the pin binds the ubuntu-latest fallback lane.' Fix: make the pin effective on the primary lane — route the follow-up job to ubuntu-latest (where the pin binds), pin the fleet-installed version in update-ecs-runner-qwen.yml, or force the pinned install on the ecs-qwen lane even when qwen is already on PATH. (This round's inline anchor at the pin line was dropped by position overlap with existing comment 3886465040 — R4-7, a different finding on the same line; R7-1's only prior copy is in round 7's review body, so the still-standing blocker is carried here.)
— qwen3.8-max via Qwen Code /review (v0.22.3)
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Partially reviewed — gaps disclosed.
3 Suggestion-level finding(s) this review confirmed are already reported on this PR and are not repeated:
- R9-1 post-agent containment scan misses resolve-pr's job-level env block — already reported (round-6 review 5059565836's deferred list, 'job-level secret mutant ships green'; re-confirmed in rounds 7 and 8)
- R9-3 bare always() gates on publish's 'Resolution check'/'Report result' unpinned — already reported (round-6 review 5059565836's deferred list; re-confirmed in round 8)
- R9-4 six of nine resolve-pr.outputs producer mappings unpinned — already reported (round-6 review 5059565836's deferred list, 'producer half of the resolve-pr outputs contract unpinned'; re-confirmed in rounds 7 and 8)
Not reviewed: reverse audit — stopped at the reverse-audit round cap of 5 without converging.
Not linted (tool limitation, not a blocker): the executable-script lint — .github/workflows/qwen-code-pr-review.yml: actionlint embedded-shell source mapping is not yet supported — not linted; the executable-script lint — .github/workflows/qwen-issue-followup-bot.yml: actionlint embedded-shell source mapping is not yet supported — not linted.
Test Plan (not a blocker): @qwen-code/qwen-code@0.21.12 — no such file or directory.
6 Suggestion(s) were drafted inline past the resolved critical posting floor; the CLI moved them into the deferral list below (floor enforcement).
Deferred under the convergence posture (round 9, not a blocker) — recorded, not requested in this round:
scripts/tests/qwen-resolve-workflow.test.js:1397 — [review] D9-1: The never-ran block's terminating exit 1 has no test witness: with a packageable bundle present, deleting that exit 1 leaves every test in this suite green while the publ….github/workflows/qwen-code-pr-review.yml:2772 — [review] D9-2: The suites pin PROMPT's content and its consumption ( --prompt "$PROMPT" ), but nothing asserts the step env defines the key PROMPT by name — unlike QWEN_SETTINGS , which th…scripts/tests/qwen-resolve-workflow.test.js:800 — [review] D9-3: This pin moved Unresolved index conflicts remain to the publish job, but the git ls-files -u check it pins (qwen-code-pr-review.yml:3100) is structurally unreachable there….github/scripts/qwen-triage-workflow.test.mjs:406 — [review] D9-4: The agent step's credential scan matches env key names against /TOKEN|PAT/ only; no test scans the env values for ${{ secrets. refs, so a GitHub credential under an inno….github/workflows/qwen-code-pr-review.yml:2899 — [review] D9-5: The new artifact file resolution.sha is written and uploaded with the run artifact but has no reader anywhere — a dead write across the agent→publish trust boundary, implying….github/scripts/qwen-triage-workflow.test.mjs:384 — [review] D9-6: The new pin proves the four command-file truncations are textually present after status=$? , but not that they are reachable — an exit inserted between status=$? and th…
Mechanism health: this round did not close cleanly, so it withholds the incremental anchor — and the round it recovered had no anchor this round could use either — none at all, one with no certifier, one certified by an identity other than the one this round runs under, or one this round's fetch refused or resolved to the head — so the next review re-reads the whole diff unless recovery grafts an earlier own anchor that the round running it can use onto the complete work list this round leaves behind, and keeps doing so until a round's marker carries an anchor again or a graft lands that the round running it can use. (Stated, not acted on — this changes nothing about what the round posts.)
Residual risk: this loop is persistently critical — Criticals stood in the previous round's work-list and stand again this round (1 Critical(s)), the rate of first-time findings is not falling (this round 0, previous 0), and the standing Critical backlog is not shrinking. The severity floor will not converge it. Recommendation: land-with-residual-risk — the exit is a maintainer risk-acceptance decision (merge, carrying the residual risk), not another review round. Residual-risk inventory for that decision (maintainer to complete):
| standing Critical | attack surface | attacker-dependency | blast radius |
|---|---|---|---|
| (each standing Critical) | … | … | … |
Advisory only — it does not block this review.
[Critical] R7-1: [certifies-falsely] [new-surface] The follow-up bot's CLI version pin is a dead switch on its primary lane: the pinned action's 'Install Qwen Code' step skips unconditionally when qwen is already on PATH — always true on the ecs-qwen pool the job routes to by default (qwen-issue-followup-bot.yml:57) — so the bot there runs the fleet-installed CLI maintained at @latest by update-ecs-runner-qwen.yml, not '0.21.10'; the pin binds only the ubuntu-latest fallback lane. A broken or unwanted npm publish propagates to the ECS fleet via the fleet-update workflow's @latest default, and the bot's next schedule run (cron 5 */6 * * *) executes the agent on that CLI despite the declared pin — the exact 2026-08-15 notarget outage class this hunk exists to prevent. Witness: verified at HEAD d166a23 — fetched QwenLM/qwen-code-action at the exact pinned SHA 05f81718: its 'Install Qwen Code' step reads if command -v qwen >/dev/null 2>&1; then echo "Qwen Code already installed, skipping install:"; qwen --version; else … npm install … @qwen-code/qwen-code@"${VERSION_INPUT}" …; fi — skip-on-PATH, unconditionally; qwen-issue-followup-bot.yml routes to the ecs-qwen pool by default (line 57) and pins qwen_cli_version: '0.21.10' (line 312), and the diff's own added comment concedes: 'On the ecs-qwen pool — this job's default lane — the fleet-installed CLI maintained at @latest by update-ecs-runner-qwen.yml is therefore authoritative; the pin binds the ubuntu-latest fallback lane.' Fix: make the pin effective on the primary lane — route the follow-up job to ubuntu-latest (where the pin binds), pin the fleet-installed version in update-ecs-runner-qwen.yml, or force the pinned install on the ecs-qwen lane even when qwen is already on PATH. (Carried since round 7; the inline anchor at the pin line overlaps existing comment 3886465040 — R4-7, a different finding on the same line — so the still-standing blocker is carried here.)
中文说明
仅完成部分审查,审查缺口已披露。
本轮确认的 3 条建议级发现已在 PR 上报告过,不再重复发布(列表见上方英文部分)。
未审查:reverse audit — stopped at the reverse-audit round cap of 5 without converging。
未检查(工具限制,非阻断):the executable-script lint — .github/workflows/qwen-code-pr-review.yml: actionlint embedded-shell source mapping is not yet supported — not linted; the executable-script lint — .github/workflows/qwen-issue-followup-bot.yml: actionlint embedded-shell source mapping is not yet supported — not linted。
Test Plan(非阻断):@qwen-code/qwen-code@0.21.12 — no such file or directory。
6 条 Suggestion 在已解析的 critical 发布下限之外被起草为行内评论;CLI 已将其移入下方延后清单(下限强制执行)。
收敛姿态下延后(第 9 轮,非阻断)——已记录,本轮不要求修改:共 6 条(原文未翻译,列表见上方英文部分)。
机制健康:本轮未能干净收尾,因而扣留了增量锚点,而它恢复到的那一轮也没有留下本轮可用的锚点——要么完全没有、要么没有认证者、要么由本轮运行身份之外的身份认证、要么被本轮的获取拒绝或解析为头提交——因此下一次评审将重读整个 diff,除非恢复流程把本轮能使用的更早自有锚点嫁接到本轮留下的完整工作清单上;并会一直如此,直到某一轮的标记重新带上锚点,或落地的嫁接能被运行该轮的评审使用。(仅陈述,不据此行动——这不改变本轮发布的任何内容。)
残余风险:本循环处于 persistently-critical 形态——上一轮工作清单中的 Critical 本轮依然存在(本轮 1 条 Critical),首次发现的速率没有下降(本轮 0,上一轮 0),且未决 Critical 积压没有减少。severity floor 无法使其收敛。建议:land-with-residual-risk——出口是 maintainer 的风险接受决定(合入并承担残余风险),而非再开一轮评审。供该决定使用的残余风险清单(maintainer 填写):按每条未决 Critical 列出「攻击面 · 攻击者依赖性 · 影响范围」三栏。仅为建议——不阻断本次评审。
[Critical] R7-1: [certifies-falsely] [new-surface] The follow-up bot's CLI version pin is a dead switch on its primary lane: the pinned action's 'Install Qwen Code' step skips unconditionally when qwen is already on PATH — always true on the ecs-qwen pool the job routes to by default (qwen-issue-followup-bot.yml:57) — so the bot there runs the fleet-installed CLI maintained at @latest by update-ecs-runner-qwen.yml, not '0.21.10'; the pin binds only the ubuntu-latest fallback lane. A broken or unwanted npm publish propagates to the ECS fleet via the fleet-update workflow's @latest default, and the bot's next schedule run (cron 5 */6 * * *) executes the agent on that CLI despite the declared pin — the exact 2026-08-15 notarget outage class this hunk exists to prevent. Witness: verified at HEAD d166a23 — fetched QwenLM/qwen-code-action at the exact pinned SHA 05f81718: its 'Install Qwen Code' step reads if command -v qwen >/dev/null 2>&1; then echo "Qwen Code already installed, skipping install:"; qwen --version; else … npm install … @qwen-code/qwen-code@"${VERSION_INPUT}" …; fi — skip-on-PATH, unconditionally; qwen-issue-followup-bot.yml routes to the ecs-qwen pool by default (line 57) and pins qwen_cli_version: '0.21.10' (line 312), and the diff's own added comment concedes: 'On the ecs-qwen pool — this job's default lane — the fleet-installed CLI maintained at @latest by update-ecs-runner-qwen.yml is therefore authoritative; the pin binds the ubuntu-latest fallback lane.' Fix: make the pin effective on the primary lane — route the follow-up job to ubuntu-latest (where the pin binds), pin the fleet-installed version in update-ecs-runner-qwen.yml, or force the pinned install on the ecs-qwen lane even when qwen is already on PATH. (Carried since round 7; the inline anchor at the pin line overlaps existing comment 3886465040 — R4-7, a different finding on the same line — so the still-standing blocker is carried here.)
— qwen3.8-max via Qwen Code /review (v0.22.3)
|
🔀 Base updated: red check(s) [Post Coverage Comment (ubuntu-latest, 22.x)] pass on current main — merged current main via update-branch; CI will re-run. 中文说明🔀 已更新 base:红色检查 [Post Coverage Comment (ubuntu-latest, 22.x)] 在当前 main 上通过 —— 已通过 update-branch 合入当前 main,CI 将重新运行。 |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Partially reviewed — gaps disclosed.
16 Suggestion-level finding(s) this review confirmed are already reported on this PR and are not repeated:
- post-agent containment scan checks only env/with values, not run:/job-level env — already reported (round-6 review 5059565836's deferred list; re-confirmed rounds 7-9 as R9-1)
- BASE_REF/HEAD_FETCH_REF/HEAD_REF/HEAD_REPO consumer wires unpinned — already reported (round-6 review 5059565836's deferred list; re-confirmed rounds 7-9 as R9-4)
- moved-head update-ref fixture never exercises the head-move (qwen-resolve-workflow.test.js:885) — already reported (round-6 review 5059565836's deferred list; re-confirmed rounds 7-8)
- unreachable-HEAD_SHA refusal guard has no test witness (qwen-code-pr-review.yml:3027) — already reported (round-7 review 5060110516's deferred list; re-confirmed round 8)
- 'Report skipped request' writes report.md without mkdir -p (qwen-code-pr-review.yml:2942) — already reported (round-6 review 5059565836's deferred list)
- 'Resolution check' crash before its first output write posts the generic wording (qwen-resolve-workflow.test.js:1337) — already reported (round-7 review 5060110516's deferred list)
- resolve-pr permissions block unpinned (qwen-triage-workflow.test.mjs:485) — already reported (round-8 review 5060625911's deferred list)
- GITHUB_STATE never decoyed; 'All four runner command files' claim inaccurate (review-artifact-upload.test.js:306) — already reported (round-6 review 5059565836's deferred list)
- resolve-lane GITHUB_OUTPUT/GITHUB_STEP_SUMMARY decoys unpinned — already reported as R5-2(d) (comment 3887462739, qwen-resolve-workflow.test.js:989)
- settings.json write / export QWEN_HOME ordering before invocation unpinned (qwen-triage-workflow.test.mjs:266) — already reported (round-6 review 5059565836's deferred list)
- skip-step exception gate pinned by substring — appending || failure() (qwen-triage-workflow.test.mjs:437) — already reported (round-6 review 5059565836's deferred list)
- agent step decision=='run' gate unpinned (qwen-triage-workflow.test.mjs:435) — already reported (round-6 review 5059565836's deferred list)
- decoy pins substring-only; export-form rewrite passes (qwen-triage-workflow.test.mjs:356 +2 locations) — already reported (round-6 review 5059565836's deferred list)
- pr_number/head_sha producer wires unpinned — within R9-4's scope, 'six of nine resolve-pr.outputs producer mappings unpinned' (round-6 deferred list; re-confirmed rounds 7-9)
- agent env credential scan checks key names only, not values (qwen-triage-workflow.test.mjs:404-409) — already reported as D9-4 (round-9 review 5061437424's deferred list)
- 'Install Qwen CLI' before 'Resolve conflicts' unpinned (qwen-triage-workflow.test.mjs:334) — already reported (round-6 review 5059565836's deferred list)
Not reviewed: reverse audit — stopped at the reverse-audit round cap of 5 without converging.
Not reviewed: build-and-test — Test (macos-latest, Node 22.x) was skipped in CI and the changed suite ran locally on Linux only.
Not linted (tool limitation, not a blocker): the executable-script lint — .github/workflows/qwen-code-pr-review.yml: actionlint embedded-shell source mapping is not yet supported — not linted; the executable-script lint — .github/workflows/qwen-issue-followup-bot.yml: actionlint embedded-shell source mapping is not yet supported — not linted.
Test Plan (not a blocker): @qwen-code/qwen-code@0.21.12 — no such file or directory.
Deferred under the convergence posture (round 10, not a blocker) — recorded, not requested in this round:
.github/scripts/qwen-triage-workflow.test.mjs:401 — [probe] D10-1 decoy test pins presence only, not the resume-vs-diagnostics order (ordering mutant ships green).github/workflows/qwen-code-pr-review.yml:2817 — [probe] D10-2 rm -f .qwen/settings.json aborts the agent step when a fork tracks the path as a directory.github/workflows/qwen-code-pr-review.yml:2892 — [probe] D10-3 planted resolution.bundle survives 'Package resolution' early exits and is published with bot endorsementscripts/tests/qwen-resolve-workflow.test.js:1093 — [probe] D10-4 post-import rejection arms unpinned — flipping the failure.md arm's outcome ships a pushscripts/tests/qwen-resolve-workflow.test.js:1253 — [probe] D10-5 functional harness drops the runner's errexit; a broken git update-ref ships greenscripts/tests/qwen-resolve-workflow.test.js:845 — [probe] D10-6 'Report skipped request' gate's decision=='failed' arm — the documented crash path — is unpinnedscripts/tests/qwen-resolve-workflow.test.js:1436 — [probe] D10-7 push_fail_reason comment table pinned only by unordered substrings (arm transpose/delete ship green)scripts/tests/qwen-resolve-workflow.test.js:967 — [probe] D10-8 attempt-suffix pin under-covers three of the four push-failure arms
Mechanism health: this round did not close cleanly, so it withholds the incremental anchor — and the round it recovered had no anchor this round could use either — none at all, one with no certifier, one certified by an identity other than the one this round runs under, or one this round's fetch refused or resolved to the head — so the next review re-reads the whole diff unless recovery grafts an earlier own anchor that the round running it can use onto the complete work list this round leaves behind, and keeps doing so until a round's marker carries an anchor again or a graft lands that the round running it can use. (Stated, not acted on — this changes nothing about what the round posts.)
Residual risk: this loop is persistently critical — Criticals stood in the previous round's work-list and stand again this round (1 Critical(s)), the rate of first-time findings is not falling (this round 0, previous 0), and the standing Critical backlog is not shrinking. The severity floor will not converge it. Recommendation: land-with-residual-risk — the exit is a maintainer risk-acceptance decision (merge, carrying the residual risk), not another review round. Residual-risk inventory for that decision (maintainer to complete):
| standing Critical | attack surface | attacker-dependency | blast radius |
|---|---|---|---|
| (each standing Critical) | … | … | … |
Advisory only — it does not block this review.
[Critical] R7-1: [certifies-falsely] [new-surface] The follow-up bot's CLI version pin is a dead switch on its primary lane: the pinned action's 'Install Qwen Code' step skips unconditionally when qwen is already on PATH — always true on the ecs-qwen pool the job routes to by default (qwen-issue-followup-bot.yml:57) — so the bot there runs the fleet-installed CLI maintained at @latest by update-ecs-runner-qwen.yml, not '0.21.10'; the pin binds only the ubuntu-latest fallback lane. Re-checked at this HEAD: routing, pin, and the fleet-update workflow's @latest default (update-ecs-runner-qwen.yml:50) are unchanged, and no fix has landed since round 9. A broken or unwanted npm publish propagates to the ECS fleet via the fleet-update workflow's @latest default, and the bot's next schedule run (cron 5 */6 * * *) executes the agent on that CLI despite the declared pin — the exact 2026-08-15 notarget outage class this hunk exists to prevent. Witness: verified at HEAD 25c2935 — qwen-issue-followup-bot.yml routes to the ecs-qwen pool by default (line 57) and pins qwen_cli_version: '0.21.10' (line 312); update-ecs-runner-qwen.yml resolves specifier='@qwen-code/qwen-code@latest' by default (line 50); the diff's own added comment concedes: 'On the ecs-qwen pool — this job's default lane — the fleet-installed CLI maintained at @latest by update-ecs-runner-qwen.yml is therefore authoritative; the pin binds the ubuntu-latest fallback lane.' Fix: make the pin effective on the primary lane — route the follow-up job to ubuntu-latest (where the pin binds), pin the fleet-installed version in update-ecs-runner-qwen.yml, or force the pinned install on the ecs-qwen lane even when qwen is already on PATH. (Carried since round 7; the inline anchor at the pin line overlaps existing comment 3886465040 — R4-7, a different finding on the same line — so the still-standing blocker is carried here.)
中文说明
仅完成部分审查,审查缺口已披露。
本轮确认的 16 条建议级发现已在 PR 上报告过,不再重复发布(列表见上方英文部分)。
未审查:reverse audit — stopped at the reverse-audit round cap of 5 without converging。
未审查:build-and-test — Test (macos-latest, Node 22.x) was skipped in CI and the changed suite ran locally on Linux only。
未检查(工具限制,非阻断):the executable-script lint — .github/workflows/qwen-code-pr-review.yml: actionlint embedded-shell source mapping is not yet supported — not linted; the executable-script lint — .github/workflows/qwen-issue-followup-bot.yml: actionlint embedded-shell source mapping is not yet supported — not linted。
Test Plan(非阻断):@qwen-code/qwen-code@0.21.12 — no such file or directory。
收敛姿态下延后(第 10 轮,非阻断)——已记录,本轮不要求修改:共 8 条(原文未翻译,列表见上方英文部分)。
机制健康:本轮未能干净收尾,因而扣留了增量锚点,而它恢复到的那一轮也没有留下本轮可用的锚点——要么完全没有、要么没有认证者、要么由本轮运行身份之外的身份认证、要么被本轮的获取拒绝或解析为头提交——因此下一次评审将重读整个 diff,除非恢复流程把本轮能使用的更早自有锚点嫁接到本轮留下的完整工作清单上;并会一直如此,直到某一轮的标记重新带上锚点,或落地的嫁接能被运行该轮的评审使用。(仅陈述,不据此行动——这不改变本轮发布的任何内容。)
残余风险:本循环处于 persistently-critical 形态——上一轮工作清单中的 Critical 本轮依然存在(本轮 1 条 Critical),首次发现的速率没有下降(本轮 0,上一轮 0),且未决 Critical 积压没有减少。severity floor 无法使其收敛。建议:land-with-residual-risk——出口是 maintainer 的风险接受决定(合入并承担残余风险),而非再开一轮评审。供该决定使用的残余风险清单(maintainer 填写):按每条未决 Critical 列出「攻击面 · 攻击者依赖性 · 影响范围」三栏。仅为建议——不阻断本次评审。
[Critical] R7-1: [certifies-falsely] [new-surface] The follow-up bot's CLI version pin is a dead switch on its primary lane: the pinned action's 'Install Qwen Code' step skips unconditionally when qwen is already on PATH — always true on the ecs-qwen pool the job routes to by default (qwen-issue-followup-bot.yml:57) — so the bot there runs the fleet-installed CLI maintained at @latest by update-ecs-runner-qwen.yml, not '0.21.10'; the pin binds only the ubuntu-latest fallback lane. Re-checked at this HEAD: routing, pin, and the fleet-update workflow's @latest default (update-ecs-runner-qwen.yml:50) are unchanged, and no fix has landed since round 9. A broken or unwanted npm publish propagates to the ECS fleet via the fleet-update workflow's @latest default, and the bot's next schedule run (cron 5 */6 * * *) executes the agent on that CLI despite the declared pin — the exact 2026-08-15 notarget outage class this hunk exists to prevent. Witness: verified at HEAD 25c2935 — qwen-issue-followup-bot.yml routes to the ecs-qwen pool by default (line 57) and pins qwen_cli_version: '0.21.10' (line 312); update-ecs-runner-qwen.yml resolves specifier='@qwen-code/qwen-code@latest' by default (line 50); the diff's own added comment concedes: 'On the ecs-qwen pool — this job's default lane — the fleet-installed CLI maintained at @latest by update-ecs-runner-qwen.yml is therefore authoritative; the pin binds the ubuntu-latest fallback lane.' Fix: make the pin effective on the primary lane — route the follow-up job to ubuntu-latest (where the pin binds), pin the fleet-installed version in update-ecs-runner-qwen.yml, or force the pinned install on the ecs-qwen lane even when qwen is already on PATH. (Carried since round 7; the inline anchor at the pin line overlaps existing comment 3886465040 — R4-7, a different finding on the same line — so the still-standing blocker is carried here.)
— qwen3.8-max via Qwen Code /review (v0.22.3)
R7-1 (review rounds 7-10): the follow-up bot's qwen_cli_version pin was a dead switch on its primary lane. The job routes to the ecs-qwen pool by default, where `qwen` is always on PATH — the fleet-installed CLI that update-ecs-runner-qwen.yml maintains at `latest` — and the pinned action's 'Install Qwen Code' step skips unconditionally when `qwen` is already present (verified at the pinned action SHA). The bot therefore ran the fleet CLI, not 0.21.10: a broken npm publish propagating through the fleet update would run the agent despite the declared pin — the exact 2026-08-15 notarget outage class the pin exists to prevent. The previous round rewrote the comment to concede the dead pin; this makes the pin effective instead. Add a job-local install step before the action: `npm install --prefix` into $RUNNER_TEMP (not -g — a global install on the shared ECS machines would fight the fleet-maintenance contract) and prepend its bin dir to GITHUB_PATH. The pinned copy outranks the fleet CLI on ecs-qwen and is the only CLI on the ubuntu-latest fallback lane; the action's install then skips over the pinned copy on BOTH lanes. The action's qwen_cli_version input now carries the installed version from the step output, so the declared input can never drift from what runs (same ensure->wire shape as qwen-triage.yml). The contract test pins the whole mechanism: step presence, exact-version pin, job-local --prefix install, registry pin, PATH prepend, ordering before the action step, and the input wired from the step output. Mutation probes: step deletion, -g install, dropped PATH prepend, dist-tag version, reverted input literal, and reordered steps all red. Also records the exact sizes in .size-baseline: qwen-code-pr-review.yml 193208 (one byte merged in from #10537's runner rename) and the follow-up bot's grown file.
|
🤖 Addressed the latest review feedback (round 2/100). What changed, and what I pushed back on: · 已处理最新评审反馈(第 2/100 轮)。改动内容与我反驳保留之处如下: Review round summary — PR #10428Outcome: changed. One commit ( Feedback points and dispositionsCritical R7-1 — the follow-up bot's CLI version pin was a dead switch on its primary lane → FixedCarried in review rounds 8/9/10 (rv:5060625911, rv:5061437424, Fix (the reviewer's "force the pinned install even when qwen is on PATH"
Size baseline drift (verify-bot Finding #2) → FixedThe verification comment measured the baseline undershooting the file; after Verify-bot Finding #1 — publish-only re-run misses its own artifact → Already fixed at this HEADThe comment was produced against the previous head Verify-bot Findings #3 and #4 — stale test comment / garbled comment → Already fixed at this HEADBoth were repaired by commit Verify-bot Finding #5 — unpinned M9/M10 guards → Already pinned at this HEAD; pins probe-verified this roundCommit Review suggestions deferred under the convergence posture → No action (deferred by the reviews themselves)Rounds 8/9/10 each list Suggestion-level findings (D9-1…D9-6, D10-1…D10-8, Residual-risk recommendation → Maintainer decision; the standing Critical is now retiredRound 10 recommended Failed checks on the previous run → Nothing actionable in code
Changed files
Verification
中文说明评审轮次总结 — PR #10428结果:有改动。 一个提交( 反馈点与处置Critical R7-1 —— follow-up bot 的 CLI 版本钉在其主通道上是死开关 → 已修复由评审第 8/9/10 轮携带(rv:5060625911、rv:5061437424、 修复(采用评审给出的"即使 qwen 已在 PATH 上也强制钉版安装"选项 ——
尺寸基线漂移(验证机器人发现 #2)→ 已修复验证评论测得基线低于文件实际大小;在提交 验证机器人发现 #1 —— 仅重跑 publish 时找不到自己的 artifact → 本 HEAD 上已修复该评论产生于上一个 head 验证机器人发现 #3 与 #4 —— 过期测试注释 / 乱码注释 → 本 HEAD 上已修复两者均已由提交 验证机器人发现 #5 —— 未钉住的 M9/M10 守卫 → 本 HEAD 上已钉住;本轮以探针核实提交 收敛姿态下延后的评审建议 → 不处理(由评审自身延后)第 8/9/10 轮各自列出建议级发现(D9-1…D9-6、D10-1…D10-8,以及 残余风险建议 → 由 maintainer 决策;遗留 Critical 现已消除第 10 轮建议 上一次运行的失败检查 → 代码层面无可处理项
变更文件
验证
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/模型 |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Not linted (tool limitation, not a blocker): the executable-script lint — .github/workflows/qwen-code-pr-review.yml: actionlint embedded-shell source mapping is not yet supported — not linted; the executable-script lint — .github/workflows/qwen-issue-followup-bot.yml: actionlint embedded-shell source mapping is not yet supported — not linted.
Test Plan (not a blocker): @qwen-code/qwen-code@0.21.12 — no such file or directory.
Deferred under the convergence posture (round 11, not a blocker) — recorded, not requested in this round:
.github/scripts/qwen-triage-workflow.test.mjs:444 — [probe] D11-1 post-agent credential scan misses run:-inline and job-level env secrets.github/workflows/qwen-code-pr-review.yml:3006 — [probe] D11-2 always() gates on 'Resolution check'/'Report result' unpinned.github/workflows/qwen-code-pr-review.yml:2818 — [probe] D11-3 OPENAI_*/MODEL_ARGS model wiring unpinned.github/workflows/qwen-code-pr-review.yml:3200 — [probe] D11-4 'Report result' mkdir -p "${WORKDIR}" load-bearing but unpinned.github/workflows/qwen-code-pr-review.yml:1462 — [review] D11-5 review-side real command files never truncated; new comment contradicts the diff's own rationale.github/scripts/qwen-triage-workflow.test.mjs:405 — [probe] D11-6 agent-env credential pin checks key names only, never values.github/scripts/qwen-triage-workflow.test.mjs:438 — [probe] D11-7 'Report skipped request' gate admits || failure() widening.github/workflows/qwen-code-pr-review.yml:2474 — [probe] D11-8 six resolve-pr job-output mappings producer-unpinned.github/scripts/qwen-triage-workflow.test.mjs:641 — [probe] D11-9 followup-bot version= output producer unpinned.github/workflows/qwen-code-pr-review.yml:2686 — [probe] D11-10 cross-workflow QWEN_CLI_VERSION equality unpinned.github/workflows/qwen-code-pr-review.yml:2945 — [probe] D11-11 'Report skipped request' lacks the mkdir -p its mirror got in this diffscripts/tests/qwen-resolve-workflow.test.js:874 — [probe] D11-12 publish checkout fetch-depth: 0 unpinned (shallow aborts 'Resolution check').github/workflows/qwen-code-pr-review.yml:3079 — [probe] D11-13 mid-stream artifact download defeats the artifact_missing classifier.github/workflows/qwen-code-pr-review.yml:2825 — [review] D11-14 containment comment contradicts itself about the credentialed push.github/workflows/qwen-code-pr-review.yml:2835 — [probe] D11-15 resolve-lane stop_token entropy assignment unpinned.github/scripts/qwen-triage-workflow.test.mjs:512 — [probe] D11-16 BASE_REF/HEAD_FETCH_REF/PR_NUMBER consumer env wiring unpinned.github/scripts/qwen-triage-workflow.test.mjs:493 — [probe] D11-17 no-agent pin on publish-resolution blind to uses: stepsscripts/tests/qwen-resolve-workflow.test.js:967 — [probe] D11-18 push-failure arms' attempt-suffix pinned once for the whole step.github/workflows/qwen-code-pr-review.yml:3186 — [probe] D11-19 OUTCOME binding (the push gate) unpinned.github/workflows/qwen-code-pr-review.yml:3183 — [probe] D11-20 HEAD_REF/HEAD_SHA/HEAD_REPO push-destination wiring unpinned- …and 9 more (see the run report)
中文说明
未检查(工具限制,非阻断):the executable-script lint — .github/workflows/qwen-code-pr-review.yml: actionlint embedded-shell source mapping is not yet supported — not linted; the executable-script lint — .github/workflows/qwen-issue-followup-bot.yml: actionlint embedded-shell source mapping is not yet supported — not linted。
Test Plan(非阻断):@qwen-code/qwen-code@0.21.12 — no such file or directory。
收敛姿态下延后(第 11 轮,非阻断)——已记录,本轮不要求修改:共 29 条(原文未翻译,列表见上方英文部分)。
— qwen3.8-max via Qwen Code /review (v0.22.3)
doudouOUC
left a comment
There was a problem hiding this comment.
Requesting changes for one blocking concurrency regression in the new two-job handoff. The credential boundary, exact-head bundle lineage checks, partial-rerun artifact selection, and focused fixtures otherwise check out; the inline comment describes the race. This finding is independent of the still-running CI job.
…esolution is never dropped The two-job split put publish-resolution in the shared qwen-pr-head-write-<pr> concurrency group. GitHub keeps only one PENDING job per group and replaces it when another same-group job is queued — cancel-in-progress:false protects only a RUNNING job. So after resolve-pr finished and uploaded a completed resolution, its publisher sat pending in that group, where a second /resolve, a later publisher, or an autofix writer could silently cancel it before the push or the result comment: a resolution that already succeeded, dropped with no response to the user — the exact silent-failure class this PR set exists to remove. (Reported by a reviewer on #10428.) Move the publisher to a per-run group (qwen-pr-publish-<run_id>): one member for its whole life, so it can never be the replaced pending job. Two publishers that do run at once stay correct through the push, not the group — it is already force-with-lease pinned to the head the agent resolved from, so exactly one wins and the other reports "moved" cleanly. The expensive, wasteful-to-race work — the agent — stays in the shared head-write group on resolve-pr, so #7355 (resolve vs autofix agent runs) stays covered. Tests: a new 'cannot drop a completed resolution while it waits to publish' pins the per-run publisher group, the head-write agent group and the lease; the triage and publish-job contracts are updated to the new split. Reverting the publisher to the shared group reds all three.
|
🤖 Reviewed the latest feedback — no changes needed. Why, point by point: · 已审阅最新反馈——无需改动。逐点说明原因如下: Autofix round: no new changes were needed. The single blocking finding in this round's feedback is already fixed at the branch head; this round independently re-verified the fix instead of re-implementing it. Triage of the feedback:
VerificationAll commands actually run this round, on HEAD
中文说明Autofix 本轮:无需任何新改动。 本轮反馈中唯一的阻断性问题已经在分支最新提交上修复;本轮只是独立复核了该修复,而不是重复实现。 反馈分诊:
验证本轮在 HEAD
Base-conflict check · 基分支冲突检查: no conflict with main. · 与 main 无冲突。 🧵 Resolved all 1 selected review thread(s). · 已关闭全部选中的 1 条评审线程。 🧠 Handled by Qwen Code · model/模型 |
|
@qwen-code /triage |
|
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: 753 passed · 1 failed · 754 total Flakiness gate: ✅ 3 changed test file(s) x 5 identical rounds, no divergence 中文 — 判定:❌ 不通过 · 报告了发现(agent 判定)沙箱验证在隔离、无凭证的容器中执行了该 PR 的代码(与 base 构建 A/B 对照、无 mock harness 断言、定向门禁)。仅作为评审证据,不构成评审、批准或 CI 检查。 脚本断言:753 通过 · 1 失败 · 754 总计 抖动门:✅ 3 changed test file(s) x 5 identical rounds, no divergence Verification report<!-- qwen-resolve-result --> Flakiness gate logEvidence imagesHarness scripts and raw logs are in the workflow run artifacts (7-day retention). — Qwen Code · sandboxed verification |
yiliang114
left a comment
There was a problem hiding this comment.
Approving. Reviewed the security split carefully: the push token (CI_DEV_BOT_PAT) lives only in publish-resolution, which runs on a fresh runner that never executed the agent, checks out fresh with persist-credentials:false, fetches base/head refs from GitHub itself, and admits the agent's output only as a git-bundle-verified set of objects pinned to the head the agent resolved from. resolve-pr carries no push token on the agent path (Report skipped only fires on skip/failed; Report result moved to the publish job). Tests pin each security-critical behavior with real git fixtures. Tradeoff is sound and evidenced: the container sandbox was already inert (config block silently dropped; 0/81 sandboxed runs ever pushed), so removing it and structurally isolating the push token is a net improvement. Note for merge ordering: this and #10429 both edit qwen-code-pr-review.yml, so whichever merges second needs a rebase.
doudouOUC
left a comment
There was a problem hiding this comment.
Re-reviewed the current head 15399f428a after the concurrency fix. The previous blocking race is fixed: resolve-pr keeps the shared per-PR head-write group, publish-resolution uses a per-run group so a completed resolution cannot be replaced while pending, and the exact-head force-with-lease prevents concurrent publishers or autofix from clobbering a moved branch. I also rechecked the fresh-runner credential boundary, run-attempt artifact handoff, bundle verification and lineage/scope guards, head-moved and missing-artifact failure paths, the follow-up CLI pin, and all updated comments/threads. Focused verification passed: resolve fixture 34/34, workflow agent-settings/follow-up contracts 10/10, routing/sandbox tests 53/53, YAML parse, and diff check. No new blocking findings.
…503s and drafts (QwenLM#10429) * fix(ci): recover /resolve requests lost to moved heads, fork pushes, 503s and drafts Of the 294 resolutions the /resolve agent has ever produced, 17 were thrown away because the PR head moved while it worked, ten more agent runs were spent on fork PRs whose push was then rejected for lack of maintainer edits, a 503 from the permission API denied writers silently, and 182 explicit requests on drafts were answered with a skip comment. - Report result: when the lease push is declined for a moved head, fetch the new head, redo the base merge on it, take the agent's resolution for every still-conflicting file the new commits did not touch, re-run the marker / clean-merge / base-changed-files guards, and push with a lease on the new head. Anything else gives up and reports "moved" as before. The agent is never re-run. - Prepare: refuse fork PRs with "Allow edits by maintainers" off before the agent runs, via maintainerCanModify. - authorize: retry a 5xx from the permission API up to three times before treating it as a denial; still fails closed. - Prepare: drop the draft gate for the command (the review lane keeps its own). The replay functions are exercised against a real git fixture in three cases; contract pins cover the push wiring, the fork preflight, the draft gate and the retry loop. * ci: record the resolve-pr replay growth in .size-baseline The moved-head replay, the fork preflight and the authorize retry grow qwen-code-pr-review.yml past the 4096-byte ratchet; the growth is the recovery logic itself, so bump the baseline. * fix(ci): pass BASE_REF into the /resolve Report result step The moved-head replay merges the base branch again, but the step's env never defined BASE_REF, so under `set -u` the first replay would have aborted the step. Add the env entry and a test that walks every run block of resolve-pr and fails on any expanded uppercase variable that neither the job env, the step env, a shell assignment nor a runner builtin defines — the fixture test injected BASE_REF itself, which is why nothing caught it. * fix(ci): fetch the moved head by URL in the /resolve replay The post-agent scrub landing in QwenLM#10428 removes the workspace .git/config — and with it the `origin` remote — before any credentialed step runs, so the replay's `git fetch origin` would fail there. Fetch by URL, with an env hook (empty in production) that the fixture test points at its bare repository; a pin forbids `git fetch origin` in the replay. * fix(ci): classify moved heads before permission patterns in /resolve replay Address the second-round review findings on the /resolve recovery paths: - classify_push_failure now tests the lease-decline signature (stale info, force-with-lease, non-fast-forward, fetch first) BEFORE the permission patterns: git echoes the destination branch into the push log, so a moved head on a branch named e.g. fix/permission-prompt or fix-403-error matched the permission arm first and the replay never ran — the exact loss class this PR recovers. - The replay handles resolutions that deleted the conflicted file (delete/modify conflicts): checkout fails on a path absent from the resolved commit, so take the deletion via git rm instead. - The replay-success comment no longer claims files were taken from the agent's merge when the replay merged clean, and says that the run artifact describes the original resolution, not the pushed tree. - Tests: behavioural coverage for the authorize retry loop against a scripted gh (3 attempts, 5s/10s backoff, fail-closed denial arms) and for the classifier against real push logs (branch-name echoes, re-classification after a failed replay push); fixture coverage for the empty-merge give-up and the deletion resolution; the draft-gate pin now names the real delay-automatic-review job without the silent whole-file fallback. * fix(ci): give the /resolve replay its own committer identity, match git's parenthesised lease reasons The replay commits with `git commit -C`, which reuses the agent's author and message but takes the committer from the environment or config. The only identity in production was the workspace .git/config written by 'Prepare pull request branch' — the file the post-agent scrub (QwenLM#10428) removes before this step. Export the bot identity inside the replay; the fixture now runs it with no identity in the environment and no .git/config, and asserts the committer. Also match the moved-head push failure on git's parenthesised reason — `(stale info)`, `(fetch first)`, `(non-fast-forward)` — rather than the bare words, which are legal branch-name substrings echoed into the same rejection line. * fix(ci): keep the /resolve replay's pathspecs literal and lease reasons anchored The replay's per-file git commands treated the literal conflicted filename as a pathspec pattern, so a file named `spec[1].md` widened diff/checkout/rm to its sibling `spec1.md` — give-ups on untouched files, and `git rm` staging a sibling's deletion. Export GIT_LITERAL_PATHSPECS inside replay_on_moved_head and pin both shapes with glob-named fixtures. Anchor the classifier's moved arm to end-of-line: git prints the reason last, so a branch merely containing `(non-fast-forward)` no longer matches before the permission arm; pinned by branch-echo classifyLog cases that also guard the parenthesised patterns against a bare-word regression. Fold the four copies of the workflow-YAML block extractor into one extractBlock helper and the duplicated classify scaffolding into one shared spawn runner. Reword the replay comment so a clean-merge replay no longer implies files were taken from the agent's merge. Re-record the size baseline at the merged workflow size. * fix(ci): preserve the pushed tree when the /resolve replay lands The `qwen-resolve-pr-N` artifact is uploaded before 'Report result' runs, so after a successful moved-head replay it describes the original resolution, not the tree that was pushed. Record the replay's diff against the base and its SHA under WORKDIR/pushed/ and upload them as `qwen-resolve-pr-N-pushed` in a step after 'Report result' (same always()/decision gate as the first upload, so it still runs when the report exits 1); the replay-success comment now points at that artifact. A contract test pins the regeneration, the second upload's name/path/gate and its position after 'Report result'. * fix(ci): re-record the review-workflow size after the pushed-tree step (QwenLM#10429) * test(ci): point the /resolve replay tests at the publish job after the split re-home The conflict resolution in cde150c re-homed the replay into publish-resolution in the workflow but left the 'recovering requests that used to be lost' test block sourcing `reportStep` (and the pushed-tree upload, and the env-coverage walk) from resolve-pr, where 'Report result' no longer exists — so reportStep was '' and every replay assertion and fixture in that block failed. Source the replay/push/upload assertions from publish-resolution, keep prepare/authorize on resolve-pr, walk both jobs in the uppercase-variable env-coverage test (the replay's BASE_REF lives in publish now, from needs.resolve-pr.outputs), and match the pushed-tree artifact name (needs.resolve-pr.outputs.pr_number) and its always() gate regardless of quote style. 53 pass; mutation-checked (breaking the lease move or the untouched-file guard in replay_on_moved_head reds the fixtures). --------- Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com> Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com>
Main split the /resolve lane into resolve-pr and publish-resolution (#10428, #10429). Resolve the HELPER_TESTS conflict as the union of both new entries, extend the health watch's producer pins to both jobs, and classify the lane's new lost-artifact report sentence as infra_failed instead of letting it fall through to unknown. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
|
Released in v0.23.0. |















What this PR does
Restores
@qwen-code /resolveto a working state, stops it from mis-reporting infrastructure failures as agent verdicts, and — because the fix turns the container sandbox off — moves the credentialed half of the lane onto a runner the agent never touches. Changes, plus the tests that pin them:tools.sandbox: false, explicit rather than absent).QWEN_CLI_VERSION: '0.21.10') instead of the npmlatestdist-tag; the follow-up bot's action input is pinned to the same version.timeout-minutes: 75, and when it ends withoutoutcome=successthe run recordsfailure_kind=infraand posts a comment that says the agent never ran — naming the outcome and the timeout — instead of the generic "attempted to resolve … did not complete successfully" and the "check the logs" nudge that reads as an invitation to re-run.qwen --auth-type openai --approval-mode yolo …, settings written to a per-runQWEN_HOME, workspace.qwen/settings.jsonremoved first) rather than throughqwen-code-action, inside a::stop-commands::window with decoyed runner command files that are truncated on every exit path — this keeps the agent job's own later steps (packaging, upload) honest.resolve-prnow ends without credentials once the agent has run: aPackage resolutionstep bundles the resolution (git bundle, prerequisite = the head the agent resolved from) into the run artifact next to the report files, and the job exposesdecision,pr_number, the agent step's outcome and the prepare-time refs as outputs.publish-resolution(needs: resolve-pr,always() && decision == 'run') checks out fresh, downloads the artifact, fetches the base and PR-head refs from GitHub, pins the comparison ref to the head the agent resolved from (fetched by SHA if the PR head moved),git bundle verifys and imports the bundle, requires the resolution to descend from that head, and only then runs the unchangedResolution checkguards and theReport resultpush/comment. A missing artifact (agent job cancelled or crashed) is reported as an infrastructure failure. Both jobs hold theqwen-pr-head-write-<pr>lock. The in-job scrubs,.gitguards andBASH_ENV/LD_*pins from earlier rounds are removed rather than extended.Tests: contract pins moved to the publish job and now assert the structure itself (no
secrets.inresolve-prafter the agent step; no agent and no CLI install inpublish-resolution; the token-bearing step only there; matching artifact names; the verify → import → lineage order; both jobs on ephemeral hosted runners); a fixture test drives the realPackage resolutionandResolution checkrun blocks across a bare origin, an agent-side clone and a fresh publish-side clone — happy path, a bundle that does not descend from the head, a bundle with an unknown prerequisite, a no-op against a clean base and its refusal against a still-conflicting one, a missing artifact, and an out-of-scope file smuggled in the bundle. The invocation wiring (--prompt,--auth-type), theset +e/set -ebracketing, the four decoys and the settings-file removal order are pinned too.Why it's needed
A survey of every
/resolverequest since the command shipped (839 requests on 323 PRs, 2026-06-25 → 08-27, paired with the bot's<!-- qwen-resolve-result -->comments and the failed runs' job logs) shows the command has had zero successes since 2026-08-15: 0 of 81 agent runs, after pushing a resolution in 84% of the 273 agent runs before that date. None of the 81 failures involved the model looking at the conflict:@qwen-code/qwen-code@latestresolved to 0.21.12 while npm reportedNo matching version found— the action's install step died. Pinning the version removes the dependency on the release pipeline of the moment; 0.21.10 is the last version with a measured record on this job (16 of 19 requests pushed on 08-12).settings_json→settingsinput name, so the block that had been silently dropped since the command shipped took effect for the first time — including"sandbox": true. The CLI then hopped intoghcr.io/qwenlm/qwen-code:<cli version>and died onmanifest unknownbecause the versioned image lags the npm release (Sandbox image for 0.22.0 never published: v0.22.0 image build failed (ETXTBSY) and was not retried, breaking sandbox-based CI lanes #9898 covers this half for 0.22.0; the same happened for 0.21.13 and 0.22.1).git statusin the artifact step afterwards showed the merge had not even started.Every
/resolverun that ever pushed a resolution ran without the container; every run that entered the container failed. Turning the sandbox off, however, removed the one thing that kept the agent's writes away from the runner the credentialed push ran on. Review rounds 2–5 on this PR demonstrated, with probes, that an in-job scrub cannot close that surface: git config scopes and hooks, a gitfile-replaced.git, a moved ref, PATH shims, appends to the real$GITHUB_ENV(discoverable on disk despite the decoys), lowercase proxy variables,GIT_TRACE, a FIFO in place of a command file,BASH_FUNC_*exports, and a detached process reading the push step's/proc/<pid>/environ— the last one reachable by no scrub at all. Item 5 is the structural answer: nothing credentialed runs on the agent's runner once the agent has started.The 120-minute job timeout has never ended a healthy run (p95 41 min, p99 70 min, request to result comment over the 294 resolutions ever produced; only two exceeded 75), so a step-level 75 keeps the tail while cutting the cost of a hang by a third. And because the failure comment did not distinguish "the agent never started" from "the agent gave up", the outage produced 13 days of re-runs against the same broken install (13 requests each on 08-21 and 08-24); the new wording says the request will fail the same way until the cause in the run is fixed.
Reviewer Test Plan
How to verify
npx vitest run --config ./scripts/tests/vitest.config.ts scripts/tests/qwen-resolve-workflow.test.js scripts/tests/qwen-autofix-workflow.test.js scripts/tests/ci-flaky-rerun-workflow.test.js scripts/tests/qwen-pr-review-workflow.test.js scripts/tests/workflow-size.test.js→ all pass (33 in the resolve file, including the two-runner fixture test).node --test --test-name-pattern='resolve-pr|agent settings|follow-up|resolve jobs|publishes' .github/scripts/qwen-triage-workflow.test.mjs→ 10 pass;node --test .github/scripts/ci-runner-routing.test.mjs .github/scripts/resolve-sandbox-image.test.mjs→ 53 pass. (The triage file's "flakiness gate — behavioral" suites need GNUtimeout, absent on macOS, and fail identically onorigin/main; they run on the Linux CI leg.)run:block of both jobs passesbash -nafter stripping${{ }}expressions; the workflow parses with js-yaml;bash .github/scripts/check-workflow-size.shpasses.git merge-base --is-ancestor), thegit bundle verify, the head pinning (git update-ref "$HEAD_FETCH_REF" "$HEAD_SHA") or the missing-artifact branch fails the fixture test (2, 2, 2 and 1 tests respectively); moving the push back intoresolve-pror adding asecrets.reference after the agent step fails the structure tests;"sandbox": false→true,QWEN_CLI_VERSION→'latest', or a steptimeout-minuteschange withoutAGENT_TIMEOUT_MINUTESeach fail their pins.@qwen-code /resolveon a conflicted PR is the real check: expectresolve-prto end atUpload run artifactswith aresolution.bundlein the artifact,publish-resolutionto loggit bundle verifythen the guards, and the result comment to be a pushed resolution or the agent's ownfailure.md.Evidence (Before & After)
Before — every
/resolvesince 08-15, three failure shapes from the run logs (105 failed runs classified; the 81 since 08-15 are 14 / 24 / 40 + 3 in a lost-log/other bucket):Monthly, agent runs → pushed: June 34 → 28 (82%), July 208 → 175 (84%), 08-01..14 31 → 27 (87%), 08-15..27 81 → 0.
After — the two-runner fixture, from the new test (real git, the workflow's own run blocks):
Live: N/A until merged (the workflow runs from the default branch).
Tested on
Environment (optional)
Unit tests only (
node --test, vitest with real git fixtures); GitHub data viagh api.Risk & Scope
preparefinishes,publish-resolutionis skipped and no comment is posted (the old single-job shape posted one); the artifact-missing path covers cancellation after that point. Pinning the CLI means /resolve does not pick up new CLI releases until someone bumps the pin, the same trade theuses:SHA already makes.maintainerCanModifydetection, and a consecutive-failure alarm are fix(ci): recover /resolve requests lost to moved heads, fork pushes, 503s and drafts #10429 and ci: watch /resolve for consecutive failures and file a tracking issue #10439; fix(ci): recover /resolve requests lost to moved heads, fork pushes, 503s and drafts #10429's replay will re-home intopublish-resolutiononce this lands./resolve.Linked Issues
Related: #9898, #9961 (the missing-image half), #9252 (the change that activated the block).
中文说明
本 PR 做了什么
让
@qwen-code /resolve恢复可用,不再把基础设施故障报成 agent 的判断,并且——由于修复会关闭容器沙箱——把链路中带凭据的一半挪到 agent 从未接触过的 runner 上。改动及相应测试:tools.sandbox: false,显式写 false 而不是删掉键)。QWEN_CLI_VERSION: '0.21.10'),不再跟随 npm 的latestdist-tag;follow-up bot 的 action 输入也钉到同一版本。timeout-minutes: 75;当该步骤不是outcome=success结束时,run 记录failure_kind=infra,评论改为说明 agent 根本没跑起来(写明 outcome 与超时值),而不是原来那句笼统的 "attempted to resolve … did not complete successfully" 加 "check the logs"——后者读起来像是在邀请重试。qwen --auth-type openai --approval-mode yolo …,settings 写入每次运行独立的QWEN_HOME,先删除工作区.qwen/settings.json),不再经过qwen-code-action,运行在::stop-commands::窗口内,runner 命令文件诱饵化并在每条退出路径上清空——这只是为了让 agent 自己 job 里后续步骤(打包、上传)保持诚实。resolve-pr在 agent 运行之后不再持有任何凭据:新增Package resolution步骤把解决结果打成git bundle(前置提交 = agent 当时基于的 head),与报告文件一起上传为 run artifact,并以 job 输出暴露decision、pr_number、agent 步骤结果和 prepare 阶段的 ref。publish-resolution(needs: resolve-pr,always() && decision == 'run')全新检出、下载 artifact、从 GitHub 重新拉取 base 与 PR head ref、把比较用的 ref 钉到 agent 当时基于的 head(PR head 已移动时按 SHA 拉取)、git bundle verify并导入 bundle、要求解决提交必须派生自该 head,之后才运行原封不动的Resolution check守卫与Report result推送/评论。artifact 缺失(agent job 被取消或崩溃)作为基础设施故障上报。两个 job 都持有qwen-pr-head-write-<pr>锁。早前轮次加的 job 内清洗、.git守卫和BASH_ENV/LD_*钉子一并移除而非继续扩充。测试:契约钉子移到 publish job,并改为断言结构本身(agent 步骤之后的
resolve-pr不含secrets.;publish-resolution没有 agent、没有 CLI 安装;带 token 的步骤只在那里;artifact 名字两边一致;verify → 导入 → 谱系的顺序;两个 job 都在一次性托管 runner 上);一个 fixture 测试把真实的Package resolution与Resolution checkrun 块跑在裸 origin、agent 侧克隆和全新 publish 侧克隆上——正常路径、不派生自 head 的 bundle、前置提交未知的 bundle、干净 base 上的 no-op 及其在仍冲突 base 上的拒绝、artifact 缺失、bundle 里夹带越界文件。调用接线(--prompt、--auth-type)、set +e/set -e包裹、四个诱饵与 settings 文件删除顺序也都钉住。为什么需要
统计了该命令上线以来的全部
/resolve请求(2026-06-25 → 08-27,323 个 PR 上 839 条,与 bot 的<!-- qwen-resolve-result -->结果评论配对,失败 run 再拉 job 日志归因):自 2026-08-15 起 81 次 agent 运行 0 次成功,而此前 273 次 agent 运行的推送成功率是 84%。这 81 次失败没有一次是模型看过冲突之后失败的:@qwen-code/qwen-code@latest解析到 0.21.12,而 npm 报No matching version found,action 的安装步骤直接挂掉。钉版本后不再依赖当时的发布流水线状态;0.21.10 是在这条 job 上有实测记录的最后一个版本(08-12 当天 19 次请求 16 次推送成功)。settings_json改成settings,让上线以来一直被静默丢弃的这段配置第一次生效——包括"sandbox": true。CLI 于是尝试进入ghcr.io/qwenlm/qwen-code:<cli 版本>镜像,因为版本镜像滞后于 npm 发布而在manifest unknown上挂掉(Sandbox image for 0.22.0 never published: v0.22.0 image build failed (ETXTBSY) and was not retried, breaking sandbox-based CI lanes #9898 记录了 0.22.0 的情况;0.21.13、0.22.1 也一样)。git status显示 merge 根本没开始。历史上所有推送成功的
/resolve都是在容器外跑的;所有进过容器的都失败了。但关掉沙箱也就去掉了唯一把 agent 写入与带凭据推送所在 runner 隔开的东西。本 PR 第 2–5 轮评审用探针证明 job 内清洗关不住这个面:git config 各层与 hooks、被 gitfile 替换的.git、被移动的 ref、PATH 垫片、写入真实$GITHUB_ENV(诱饵挡不住磁盘上的发现)、小写代理变量、GIT_TRACE、用 FIFO 冒充命令文件、BASH_FUNC_*导出,以及读取推送步骤/proc/<pid>/environ的脱离进程——最后这一个任何清洗都碰不到。第 5 项就是结构性答案:agent 一旦启动,其 runner 上就不再运行任何带凭据的步骤。120 分钟的 job 超时从未终止过一次健康的 run(历史上 294 次产出解决结果的请求,从请求到结果评论 p95 41 分钟、p99 70 分钟,只有两次超过 75),因此步骤级 75 分钟既保住长尾又把挂死的代价砍掉三分之一。另外,失败评论此前不区分"agent 根本没启动"和"agent 放弃了",导致这次故障期间对同一个坏掉的安装重试了 13 天(08-21、08-24 各 13 次);新措辞明确说明在 run 里的原因修复之前,再次请求会以同样方式失败。
评审验证方案
如何验证
npx vitest run --config ./scripts/tests/vitest.config.ts scripts/tests/qwen-resolve-workflow.test.js scripts/tests/qwen-autofix-workflow.test.js scripts/tests/ci-flaky-rerun-workflow.test.js scripts/tests/qwen-pr-review-workflow.test.js scripts/tests/workflow-size.test.js→ 全部通过(resolve 文件 33 个,含双 runner fixture 测试)。node --test --test-name-pattern='resolve-pr|agent settings|follow-up|resolve jobs|publishes' .github/scripts/qwen-triage-workflow.test.mjs→ 10 通过;node --test .github/scripts/ci-runner-routing.test.mjs .github/scripts/resolve-sandbox-image.test.mjs→ 53 通过。(triage 文件里 "flakiness gate — behavioral" 套件依赖 GNUtimeout,macOS 没有,在origin/main上同样失败;CI 的 Linux 腿会跑。)run:块去掉${{ }}表达式后通过bash -n;workflow 能被 js-yaml 解析;bash .github/scripts/check-workflow-size.sh通过。git merge-base --is-ancestor)、git bundle verify、head 钉定(git update-ref "$HEAD_FETCH_REF" "$HEAD_SHA")或 artifact 缺失分支,fixture 测试分别有 2、2、2、1 个用例变红;把推送挪回resolve-pr或在 agent 步骤之后加secrets.引用使结构测试变红;"sandbox": false→true、QWEN_CLI_VERSION→'latest'、只改步骤timeout-minutes不改AGENT_TIMEOUT_MINUTES各使其钉子变红。@qwen-code /resolve才是真正的检验:预期resolve-pr在Upload run artifacts结束且 artifact 里有resolution.bundle,publish-resolution日志先出现git bundle verify再是各项守卫,结果评论要么是推送成功,要么是 agent 自己写的failure.md。证据(改动前后)
改动前——08-15 之后每一次
/resolve都是 run 日志里的三种形态之一(105 个失败 run 已分类;08-15 以来的 81 个为 14 / 24 / 40,另有 3 个日志丢失/其他):按月,agent 运行数 → 推送成功数:6 月 34 → 28(82%),7 月 208 → 175(84%),08-01..14 31 → 27(87%),08-15..27 81 → 0。
改动后——新测试里的双 runner fixture(真实 git,跑的是 workflow 自己的 run 块):
真实运行:合入前无法实测(workflow 从默认分支运行)。
测试平台
环境(可选)
仅单元测试(
node --test、带真实 git fixture 的 vitest);GitHub 数据通过gh api获取。风险与范围
prepare完成前被取消,publish-resolution会被跳过且不发评论(旧的单 job 形态会发);此后的取消由 artifact 缺失路径覆盖。钉 CLI 版本意味着 /resolve 在有人主动升级前不会拿到新 CLI 版本,这与uses:钉 SHA 是同一种取舍。maintainerCanModify、连续失败告警分别在 fix(ci): recover /resolve requests lost to moved heads, fork pushes, 503s and drafts #10429 与 ci: watch /resolve for consecutive failures and file a tracking issue #10439;本 PR 合入后 fix(ci): recover /resolve requests lost to moved heads, fork pushes, 503s and drafts #10429 的重放会挪进publish-resolution。/resolve的 run 现在显示两个 job。关联 Issue
相关:#9898、#9961(镜像缺失那一半)、#9252(激活该配置块的改动)。