fix(ci): give the ECS runner resolve wait a 60-minute budget - #10921
fix(ci): give the ECS runner resolve wait a 60-minute budget#10921yiliang114 wants to merge 17 commits into
Conversation
The npm-published dispatch raced the registry again for v0.23.0: the package took ~40 minutes to become resolvable (published 11:03Z, visible ~11:43Z), but the resolve wait gave up at 25 minutes (RESOLVE_TIMEOUT_SECONDS=1500) and run 33747588713 failed with the version still in flight, leaving every pool on the previous version. Raise the wait to 3600s and the job timeout to 70 minutes to match; v0.22.3's 16-minute gap was the optimistic data point the old budget was sized on. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Measured end to end for v0.23.0: npm accepted the upload at 10:58:30Z and the registry served the version at 11:53:52Z — 55m22s for the ~100 MB main package, longer than the 60-minute budget the previous commit carried. Raise the wait to 90 minutes (the anomaly threshold) and the job timeout to 100 minutes; the 30s poll keeps fast publishes resolving immediately. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
hk-4 and hk-5 installed 0.23.0 successfully ("changed 16 packages")
but `qwen --version` still reports 0.22.3 from /usr/local/bin/qwen,
which suggests the sudo npm install lands in a different prefix than
the one the pool resolves. Print the binary's readlink, the candidate
paths, sudo npm's global prefix, and the installed package.json before
failing so the next run identifies the stale copy instead of guessing.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Diagnostics on hk-4/hk-5 showed root's npm global prefix there is /usr/local/lib/nodejs/node-v22.23.2-linux-x64, so `sudo npm install -g` updated that tree while the pools kept resolving the stale /usr/local/bin/qwen -> /usr/local/lib/node_modules copy (still 0.22.3). Install with --prefix /usr/local so every pool updates the copy the runner service resolves. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
|
Re-run at head The evidence here is still unusually good, and the newest commit closes the Critical that was standing against it. One new blocker appeared on the way — a CI gate this commit crossed, in Stage 2. Template looks good ✓ Problem: observed, and I re-verified it independently rather than taking the description's word for it. Run 33747588713 attempt 1 (the v0.23.0 Direction: aligned. Keeping the ECS review fleet on the released CLI is this workflow's entire purpose, and every commit answers a failure that has logs behind it. It touches release infrastructure, but the author is a maintainer with admin on this repo and the direction is unambiguous, so I am not escalating. Size: not applicable to the core gate — no Approach: the budget half is exactly what I would have written from the title alone, and a test now pins the job-timeout-above-wait invariant so a bad merge cannot silently undo it. The install half has grown well past the stated goal: the body still says "Nothing else changes" and "no change to the install/verify steps", while the diff reworks the install step into a three-mode fallback and adds a verify diagnostics block — and the title still says "60-minute budget" where the diff ships 90 minutes ( Risk: no elevated risk signals — no high-risk paths matched. Moving on to code review. 🔍 中文说明本次在 head 这里的证据依然非常扎实,最新提交也解决了此前挂着的 Critical。但过程中出现了一个新的阻断项——本提交刚刚越过的一条 CI 门禁,详见 Stage 2。 模板完整 ✓ 问题:已观测到的真实问题,且我独立复核过,没有直接采信描述。run 33747588713 的第 1 次尝试(v0.23.0 的 方向:对齐。让 ECS 评审机群保持已发布的 CLI 版本,正是这个工作流的全部目的;每个提交都对应一个有日志支撑的失败。它触及发布基础设施,但作者是本仓库的维护者(admin 权限),方向明确,因此不做升级。 规模:核心门禁不适用——未触及 方案:预算这一半与我只看标题会写出的方案完全一致,而且现在有测试钉住"job 超时高于等待预算"这一不变式,坏的 merge 无法悄悄把它改掉。安装这一半已明显超出既定目标:正文仍写着"其他不变"和"不改 install/verify 步骤",而 diff 实际把 install 步骤重构成三模式回退,并新增了 verify 诊断块——标题也仍写"60 分钟",而 diff 实际是 90 分钟( 风险:无升级风险信号——未命中高风险路径。 进入代码审查 🔍 — Qwen Code · qwen3.8-max-2026-09-02 Reviewed at |
Code reviewMy independent proposal from the title and motivation alone: raise The budget half of the diff is that proposal, exactly. The Critical that was standing against this PR is closed. R1-2 prescribed three ways out and option (1) was "keep the pin for the pools that need it and add the authorized no- The verify step also holds up. Blocking: this commit crosses the workflow size ratchet and reds a required lane
I verified every number rather than trusting the annotation. Two things make this worth acting on now rather than shrugging at:
Either remedy the gate names is fine. Moving the ~40 lines of install-mode prose into a sibling Not blocking, recorded so it is not lostThe Testing evidenceUnattended CI run — this review executed no PR code, built nothing, and ran no test. Everything below is the PR's own CI on the reviewed commit, read through the API, plus the fleet dispatch history.
One row per check name (latest run); skipped checks omitted; failures sort first. / 每个检查名一行(取最新一次运行),省略 skipped,失败项排在最前。 Also skipped on this head: Classifying the three red lanes, from the diff and the check identities rather than from anything in the log bodies:
On the author's earlier CI-attribution comment: it was correct, and I confirmed the substance of it — but it was written about head Sandboxed verification would settle two things, and they are different questions:
Not verified: the npm propagation timestamps in the description (10:58:30Z / 11:53:52Z) — the author's measurement, which I cannot reproduce from the API and did not re-derive. The 25-minute exhaustion of the old budget I did verify independently, from the attempt-1 job timings. 中文说明代码审查:我只看标题和动机时的独立方案是——把 此前挂着的 Critical 已经解决。 R1-2 给了三条出路,其中第 (1) 条是"为需要 pin 的池保留 pin,并把 sudoers 授权的不含 verify 步骤同样站得住: 阻断项:本提交越过了工作流体积棘轮,把必需检查搞红了。 有两点说明这事应当现在处理:一是它恰好在本提交才出现——上一个 head 门禁给出的两种补救都可以:把约 40 行安装模式的说明移到同级 非阻断,记录以免丢失: 测试证据:本次为无人值守 CI 运行——未执行任何 PR 代码、未构建、未跑测试。以下全部来自被审查提交上 PR 自己的 CI(经 API 读取)以及机群 dispatch 历史。三条红检查的归类依据是 diff 与检查身份,而非日志正文中的任何说法: 沙箱验证可以解决两件事,且是两个不同的问题:机群层面的断言(三模式回退真的能更新全部五个池)只能由在 head 上真实 未验证:描述中的 npm 传播时间戳(10:58:30Z / 11:53:52Z)——那是作者的测量,我无法从 API 复现,也未重新推导。旧预算 25 分钟耗尽这一点我已通过第 1 次尝试的 job 时间独立核实。 — Qwen Code · qwen3.8-max-2026-09-02 Reviewed at |
|
Confidence: 2/5 — the design is right and the hard Critical is genuinely closed, but as it stands this cannot merge: it reds a required lane on its own head, and the fleet evidence its own reviewer set as a merge precondition was never collected. Stepping back over the whole arc. My independent proposal was two numeric constants, and the budget half of this PR is exactly that, done cleanly — 90 minutes against a measured 55m22s, a job timeout that keeps 10 minutes of headroom above the wait, the poll interval untouched so fast publishes still resolve immediately, and now a test pinning the timeout-above-wait invariant so a bad merge cannot quietly restore the original failure. I verified the motivation myself rather than accepting it: run 33747588713's attempt-1 The install half earned its place too. It came out of a dispatch loop against the live fleet, one observed failure per commit, and the newest commit does the right thing in the right way: R1-2 named three possible fixes and this takes option (1), adding the exact authorized argv as an ordered second sudo mode. It also closed the gap that made the earlier green suite worthless — the stub now models the observed sudoers spec instead of accepting anything containing So why not approve. Two reasons, and neither is a design objection. The first is mechanical and it is a wall: this commit took the workflow file from 11262 bytes to 12618, crossing the repo's 11283-byte ratchet threshold by 1335 bytes, and The second is the missing oracle. R1-2 required an all-five-pools-green dispatch before merging the update-path change, and there has been no dispatch of this workflow at any head since I am requesting changes rather than deferring, because both items have a clear owner and a clear next step, and neither needs a maintainer's judgment call. Fix the ratchet, run the dispatch, and this is ready — I would not be surprised to approve it on the next pass. The title/body mismatch (still "60-minute budget", still "Nothing else changes") is noted in Stage 1 as hygiene; per this repo's own guidance on not letting review rounds balloon a PR, I am deliberately not treating it, or the deferred 中文说明置信度:2/5 —— 设计是对的,那个硬 Critical 也确实解决了,但当前状态无法合并:它在自己的 head 上把必需检查搞红了,而其自身评审设定的合并前置条件(机群证据)从未采集。 退一步看整个过程。我独立的方案是两个数字常量,而本 PR 的预算部分正是如此,且做得干净——针对实测 55 分 22 秒给出 90 分钟,job 超时在等待之上保留 10 分钟余量,轮询间隔未动因此发布快时仍会立即解析,并且现在有测试钉住"超时高于等待"这一不变式,坏的 merge 无法悄悄恢复原来的失败。动机我自己核实过,没有直接采信:run 33747588713 第 1 次尝试的 安装这一半也挣得了它的位置。它来自针对真实机群的 dispatch 循环,一个提交对应一个观测到的失败,而最新提交以正确的方式做了正确的事:R1-2 给出了三种可能的修法,本提交采纳第 (1) 种,把精确授权的 argv 作为有序的第二个 sudo 模式加入。它还补上了让此前绿色套件失去价值的那个缺口——桩现在建模实测的 sudoers 规格,而不再接受任何含 那么为什么不批准。两个原因,都不是对设计的异议。 第一个是机械性的,且是一堵墙:本提交把工作流文件从 11262 字节推到 12618 字节,越过仓库 11283 字节的棘轮阈值 1335 字节,而 第二个是缺失的验证标准。R1-2 要求在合并 update-path 改动前完成一次五池全绿的 dispatch,而自 09-03 的 我选择请求修改而非转交,因为这两项都有明确的负责人和明确的下一步,都不需要维护者拍板。修掉棘轮、跑一次 dispatch,这个 PR 就可以了——下一轮我批准它也不会意外。标题/正文不一致(仍写"60 分钟"、仍写"其他不变")已在 Stage 1 作为规范性问题记录;按本仓库自身关于不要让评审轮次把 PR 越滚越大的指引,我刻意不把它、以及那条延后的 — Qwen Code · qwen3.8-max-2026-09-02 Reviewed at |
…issing The hk-1/hk-2 pool machines reject sudo with "a password is required" for the runner user, so the install failed after 3 attempts on two consecutive runs. Probe passwordless sudo once (sudo -n true) and, when absent, install as the runner user itself, which succeeds when that user owns the /usr/local prefix. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
chiga0
left a comment
There was a problem hiding this comment.
No blocking findings.
Approval blockers: none.
Tier: Standard.
Note: ci-bot reviewed c64936d6; this review covers the current HEAD 3eeeaf89f5 (adds the sudo -n true fallback).
R1-1 — Major · Test validity (class 5) · supported
scripts/tests/update-ecs-runner-qwen-workflow.test.js:115 checks:
expect(workflow).toContain('sudo env -u NPM_CONFIG_PREFIX npm install -g');Commit 16b773731890 refactored the install into an INSTALL array (line 113 of the diff):
INSTALL=(sudo env -u NPM_CONFIG_PREFIX) # no longer followed by 'npm install -g' on the same line
...
"${INSTALL[@]}" npm install -g --prefix /usr/local ...The literal string sudo env -u NPM_CONFIG_PREFIX npm install -g no longer appears in the workflow file. This test cannot pass against the submitted HEAD. The PR's "21/21 pass" was accurate for an earlier commit.
Production behaviour is correct (sudo path still passes -u NPM_CONFIG_PREFIX), but the test no longer guards it. Suggested fix:
expect(workflow).toContain('INSTALL=(sudo env -u NPM_CONFIG_PREFIX)');
expect(workflow).toContain('"${INSTALL[@]}" npm install -g');R1-2 — Minor · Stated intent (class 10)
PR title says "60-minute budget"; the actual wait is 90 minutes (RESOLVE_TIMEOUT_SECONDS: 5400) and the job timeout is 100 minutes. PR body says "Nothing else changes" but the diff adds --prefix /usr/local, the sudo -n true fallback, and verify diagnostics. (ci-bot flagged this at c64936d6; still uncorrected.)
What else was checked
- Wait loop:
RESOLVE_TIMEOUT_SECONDS: 5400and 30s poll consistent; deadline arithmetic unchanged. timeout-minutes: 100covers the 90-minute wait with 10-minute headroom.sudo -n truefallback: Correct pattern for detecting passwordless sudo. Addresses the hk-2sudo: a password is requiredfailure ci-bot identified. Non-sudo path (env npm install -g --prefix /usr/local) correctly installs to/usr/local— npm CLI--prefixoverridesNPM_CONFIG_PREFIXper npm config precedence.sudo rm -rfcleanup: Unconditionalsudo, silenced with|| true. On no-sudo machines cleanup is skipped silently; acceptable for crash-cleanup.- Diagnostics block: Failure-path only, all commands guarded with
|| true/2>/dev/null. No effect on success path. report_failurejob: Unchanged and correct.- Cross-check vs ci-bot: ci-bot did not review the current HEAD. F1 is new (introduced by
16b773731890). F2 was already noted by ci-bot. hk-2 concern appears addressed; live dispatch is the final oracle.
Reviewed with AI assistance.
qwen-code-dev-bot
left a comment
There was a problem hiding this comment.
Reviewed at head 3eeeaf89.
Blocking — this PR turns the required Test (ubuntu-latest) lane red on its own head, and merging it reds main. The commit 16b77373 refactor moved the install command into an INSTALL=(sudo env -u NPM_CONFIG_PREFIX) array with "${INSTALL[@]}" npm install -g on a separate line, so the pre-existing pin scripts/tests/update-ecs-runner-qwen-workflow.test.js:115 (toContain('sudo env -u NPM_CONFIG_PREFIX npm install -g')) no longer matches. The failing run on 3eeeaf89 is the exact assertion (annotation captured verbatim). chiga0's R1-1 thread (still unresolved) carries the fix — two toContain lines pinning the array form — and confirms production behaviour is correct: the sudo path still strips NPM_CONFIG_PREFIX, and on the no-sudo fallback the CLI --prefix /usr/local outranks the env var, so the fleet still lands in the right tree.
Expected, and cheap:
- Update
scripts/tests/update-ecs-runner-qwen-workflow.test.js:115to chiga0's suggested pair of assertions, and re-runcd scripts && npx vitest run tests/update-ecs-runner-qwen-workflow.test.jsbefore pushing so the "tested" claim covers this head rather than the pre-refactor commit. - Refresh the PR title/body while pushing (R1-2, open since
c64936d6): the wait isRESOLVE_TIMEOUT_SECONDS: 5400— 90 minutes with a 100-minute job cap, not the 60 in the title — and "Nothing else changes" understates the--prefix /usr/localpin, thesudo -n truefallback, and the diagnostics block. The description should match what actually merged in from this branch.
Nothing else is wrong with the change itself: the 55-minute measured propagation sizing, the deadline arithmetic, the retry/cleanup loop around ENOTEMPTY, the diagnostics' || true guarding, and the untouched report_failure job all check out at this head, and no review threads raise correctness beyond the two above.
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Partially reviewed — gaps disclosed.
Not reviewed: reverse audit — stopped before round 4 by the review time budget.
Not linted (tool limitation, not a blocker): .github/workflows/update-ecs-runner-qwen.yml — actionlint embedded-shell source mapping is not yet supported.
Test Plan (not a blocker): @qwen-code/qwen-code@0.23.0 — no such file or directory.
中文说明
仅完成部分审查,审查缺口已披露。
未审查:反向审计——评审时间预算不足,未能开始第 4 轮。
未检查(工具限制,非阻断):.github/workflows/update-ecs-runner-qwen.yml——actionlint 对 workflow 内嵌 shell 的源映射尚未支持。
Test Plan(非阻断):@qwen-code/qwen-code@0.23.0 — no such file or directory。
— qwen3.8-max via Qwen Code /review (v0.23.0)
The install command moved to an INSTALL array so the sudo-less fallback can reuse it, which broke the workflow contract test's literal match. Assert the new shape instead. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Patrol-Run: qwen-pr-conflict/jmtlp4re4g6
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Test Plan (not a blocker): @qwen-code/qwen-code@0.23.0 — no such file or directory.
中文说明
Test Plan(非阻断):@qwen-code/qwen-code@0.23.0 — no such file or directory。
— qwen3.8-max via Qwen Code /review (v0.23.0)
An interactive `sudo` on the no-passwordless-sudo pools — the pools the install fallback serves — blocks on a password prompt until the job timeout kills the step, so the Verify diagnostics never print there. Run every probe as the runner user or with `sudo -n`, and pin the shape in the workflow's guard suite. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Patrol-Run: qwen-pr-closeout/jmtlz4ubdgq
Under `set -euo pipefail` a `--version` that exits non-zero aborts the Verify step at the bare assignment, before the mismatch diagnostics this diff exists for can print — the exact failure class of a crashed install. Capture tolerantly so the step still fails at the version test, now with diagnostics, and pin the shape with a replay of the Verify step body. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Patrol-Run: qwen-pr-closeout/jmtlz4ubdgq
The replay harness injects its own budget values, so nothing guarded the shipped ones: a bad merge resolution lowering the job timeout below the wait would let GitHub kill the poll mid-propagation and silently restore the v0.23.0 failure. Assert both constants and their invariant. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Patrol-Run: qwen-pr-closeout/jmtlz4ubdgq
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-9 probe↔install-mode test pin (round-3 test-coverage re-derivation) — already reported (R1-9 thread, comment 3927020742)
Not explored to full depth (tool budget reached): "agent 1a": running the actual vitest suite ( npm run test:scripts ) — node_modules absent in the review worktree; substituted with direct bash/node replays of the same ste….
Not linted (tool limitation, not a blocker): .github/workflows/update-ecs-runner-qwen.yml — actionlint embedded-shell source mapping is not yet supported.
Test Plan (not a blocker): @qwen-code/qwen-code@0.23.0 — no such file or directory.
Convergence: round 3 posted 4 inline comment(s), 2 of them reported for the first time; the previous round posted 4 (0 new). Findings keep coming back to the same files: .github/workflows/update-ecs-runner-qwen.yml (findings in round 1; 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.)
中文说明
本轮确认的 1 条建议级发现已在 PR 上报告过,不再重复发布(列表见上方英文部分)。
未探索到全部深度(达到工具调用预算):"agent 1a":running the actual vitest suite ( npm run test:scripts ) — node_modules absent in the review worktree; substituted with direct bash/node replays of the same ste…。
未检查(工具限制,非阻断):.github/workflows/update-ecs-runner-qwen.yml——actionlint 对 workflow 内嵌 shell 的源映射尚未支持。
Test Plan(非阻断):@qwen-code/qwen-code@0.23.0 — no such file or directory。
收敛情况:第 3 轮发布了 4 条行内评论,其中 2 条是首次提出;上一轮发布了 4 条(其中 0 条首次提出)。发现反复回到同一批文件:.github/workflows/update-ecs-runner-qwen.yml(第 1 轮已出过发现,本轮又有 1 条)。一个不断再生兄弟发现的簇,通常意味着逐条修复只在处理同一根因的实例——先定位并处理该根因,或把独立的簇拆成单独的 PR,通常比逐条修复更快结束循环。(仅为观察——本轮评审未因此扣留任何内容。)
— qwen3.8-max via Qwen Code /review (v0.23.0)
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
2 Suggestion-level finding(s) this review confirmed are already reported on this PR and are not repeated:
- R1-4
2>&1stderr-fold capture re-derivation (round-4 agents 1a/3b/6a) — already reported (R1-4 thread, comment 3929784977) - R1-9 probe↔install-mode test-pin re-derivation (round-4 agents 5/3c) — already reported (R1-9 thread, comment 3929784984)
Not explored to full depth (tool budget reached): "agent 6c": none — but disclosing one deliberately dropped candidate: the 2>&1 merge in the tolerant version capture (reason above: no concretely nameable trigger)..
Not linted (tool limitation, not a blocker): .github/workflows/update-ecs-runner-qwen.yml — actionlint embedded-shell source mapping is not yet supported.
Test Plan (not a blocker): @qwen-code/qwen-code@0.23.0 — no such file or directory.
Convergence: round 4 posted 4 inline comment(s), 1 of them reported for the first time; the previous round posted 4 (2 new). Findings keep coming back to the same files: .github/workflows/update-ecs-runner-qwen.yml (findings in round 1; 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.)
中文说明
本轮确认的 2 条建议级发现已在 PR 上报告过,不再重复发布(列表见上方英文部分)。
未探索到全部深度(达到工具调用预算):"agent 6c":none — but disclosing one deliberately dropped candidate: the 2>&1 merge in the tolerant version capture (reason above: no concretely nameable trigger).。
未检查(工具限制,非阻断):.github/workflows/update-ecs-runner-qwen.yml——actionlint 对 workflow 内嵌 shell 的源映射尚未支持。
Test Plan(非阻断):@qwen-code/qwen-code@0.23.0 — no such file or directory。
收敛情况:第 4 轮发布了 4 条行内评论,其中 1 条是首次提出;上一轮发布了 4 条(其中 2 条首次提出)。发现反复回到同一批文件:.github/workflows/update-ecs-runner-qwen.yml(第 1 轮已出过发现,本轮又有 1 条)。一个不断再生兄弟发现的簇,通常意味着逐条修复只在处理同一根因的实例——先定位并处理该根因,或把独立的簇拆成单独的 PR,通常比逐条修复更快结束循环。(仅为观察——本轮评审未因此扣留任何内容。)
— qwen3.8-max via Qwen Code /review (v0.23.0)
The tolerant version capture merged stderr into the string the step strict-matches: one stderr line during `--version` (a Node runtime warning, a future startup notice) would fail a healthy install on every pool, print mismatch diagnostics claiming a stale fleet, and file a stale-fleet issue against a correctly updated fleet. The `|| actual=` arm already covers the failing path, where the merged stderr bought nothing. Move the pin to the stdout-only shape and replay a stderr-noisy success. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Patrol-Run: qwen-pr-closeout/jmtme4yqxhl
The guard enumerated the three sudo verbs the Verify step happens to use, so a bare `sudo` with any other verb passed it and could block on an interactive password prompt until the job timeout — on exactly the no-passwordless-sudo pools the diagnostics exist for. Assert the class the test's own comment documents, every sudo probe must be non-interactive, on the non-comment lines; the probe comment mentions `sudo` in prose, so it stays out of the check. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Patrol-Run: qwen-pr-closeout/jmtme4yqxhl
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-9 probe↔install-mode test pin — re-confirmed this round by mutation probe (inverted conditional and unconditional-sudo mutants both survive 28/28); already reported (R1-9 thread, comment 3926222247)
Not linted (tool limitation, not a blocker): .github/workflows/update-ecs-runner-qwen.yml — actionlint embedded-shell source mapping is not yet supported.
Test Plan (not a blocker): @qwen-code/qwen-code@0.23.0 — no such file or directory.
Deferred under the convergence posture (round 5, not a blocker) — recorded, not requested in this round:
scripts/tests/update-ecs-runner-qwen-workflow.test.js:163 — [probe] budget test hand-rolls resolve-job slice instead of getWorkflowJob() helper
中文说明
本轮确认的 1 条建议级发现已在 PR 上报告过,不再重复发布(列表见上方英文部分)。
未检查(工具限制,非阻断):.github/workflows/update-ecs-runner-qwen.yml——actionlint 对 workflow 内嵌 shell 的源映射尚未支持。
Test Plan(非阻断):@qwen-code/qwen-code@0.23.0 — no such file or directory。
收敛姿态下延后(第 5 轮,非阻断)——已记录,本轮不要求修改:共 1 条(原文未翻译,列表见上方英文部分)。
— qwen3.8-max via Qwen Code /review (v0.23.0)
The `Update qwen` step gated its install mode on a `sudo -n true` probe. hk-1/hk-2 carry command-specific sudoers: they reject that generic probe and allow the real npm install, so the probe selected the runner-user mode on exactly the machines that could have installed as root. That mode then hit EACCES against the root-owned package dir on all three attempts, and those two pools could never update: every `npm-published` dispatch would lose the hk-1/hk-2 legs and leave them on the old CLI until someone intervened. No probe can be more representative than the command it stands in for, so run the sudo mode first and fall back to the runner-user mode only when sudo actually rejects it. That is the shape the pre-change workflow already ran green on all five pools, and the fallback still serves pools with no passwordless sudo at all. Every sudo in the step is now `-n`, the trash cleanup included, so a pool without sudo fails fast into the next mode instead of blocking on a password prompt until `timeout-minutes: 10` kills the step. Also replay the `Update qwen` step body against stubbed sudo/npm/sleep modelling all three sudoers classes and both prefix owners, asserting the effective user npm ran as rather than only the exit code: a fallback that succeeds after a wasted runner-user EACCES attempt is the regression this fixes. Reverting the workflow change reds five tests. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Patrol-Run: qwen-pr-closeout/jmto4amd1ku
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Partially reviewed — gaps disclosed.
Not reviewed: build-and-test — Test (macos-latest) and Test (windows-latest) were skipped in CI (merge_group-only lanes); the changed suite ran locally on Linux only, so the bash-3.2/BSD and Windows lanes were walked by reading, not executed.
Not explored to full depth (tool budget reached): "agent 6b": none — 12 of ~38 tool calls used; nothing was cut short. The one check I could not perform is executing the new tests (no node_modules in this shared worktree….
Not linted (tool limitation, not a blocker): .github/workflows/update-ecs-runner-qwen.yml — actionlint embedded-shell source mapping is not yet supported.
Test Plan (not a blocker): @qwen-code/qwen-code@0.23.0 — no such file or directory.
4 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 6, not a blocker) — recorded, not requested in this round:
.github/workflows/update-ecs-runner-qwen.yml:48 — [review] This file has grown from 7187 to 11262 bytes across the six rounds without a matching .github/workflows/.size-baseline bump, which consumes 4075 of the ratchet's 4096-byte growth ….github/workflows/update-ecs-runner-qwen.yml:130 — [review] || cannot distinguish "sudo rejected the command" from "npm failed while running under sudo" — both are the same non-zero exit — so the runner-user fallback fires on *every* sudo….github/workflows/update-ecs-runner-qwen.yml:172 — [review] Six of the eight lines this block adds have no test that notices their removal. A 23-mutation sweep of this workflow, running the full changed suite after each single edit, killed …scripts/tests/update-ecs-runner-qwen-workflow.test.js:422 — [review] runVerify stubs only qwen and then executes the real Verify step body, so every other command in the diagnostics block — including the three sudo -n probes and a rea…
Convergence: round 6 posted 1 inline comment(s), 1 of them reported for the first time; the previous round posted 1 (0 new). Findings keep coming back to the same files: .github/workflows/update-ecs-runner-qwen.yml (findings in round 1; 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.)
中文说明
仅完成部分审查,审查缺口已披露。
未审查(原文为英文):build-and-test — Test (macos-latest) and Test (windows-latest) were skipped in CI (merge_group-only lanes); the changed suite ran locally on Linux only, so the bash-3.2/BSD and Windows lanes were walked by reading, not executed.
未探索到全部深度(达到工具调用预算):"agent 6b":none — 12 of ~38 tool calls used; nothing was cut short. The one check I could not perform is executing the new tests (no node_modules in this shared worktree…。
未检查(工具限制,非阻断):.github/workflows/update-ecs-runner-qwen.yml——actionlint 对 workflow 内嵌 shell 的源映射尚未支持。
Test Plan(非阻断):@qwen-code/qwen-code@0.23.0 — no such file or directory。
4 条 Suggestion 在已解析的 critical 发布下限之外被起草为行内评论;CLI 已将其移入下方延后清单(下限强制执行)。
收敛姿态下延后(第 6 轮,非阻断)——已记录,本轮不要求修改:共 4 条(原文未翻译,列表见上方英文部分)。
收敛情况:第 6 轮发布了 1 条行内评论,其中 1 条是首次提出;上一轮发布了 1 条(其中 0 条首次提出)。发现反复回到同一批文件:.github/workflows/update-ecs-runner-qwen.yml(第 1 轮已出过发现,本轮又有 1 条)。一个不断再生兄弟发现的簇,通常意味着逐条修复只在处理同一根因的实例——先定位并处理该根因,或把独立的簇拆成单独的 PR,通常比逐条修复更快结束循环。(仅为观察——本轮评审未因此扣留任何内容。)
— qwen3.8-max via Qwen Code /review (v0.23.0)
hk-1/hk-2 authorize one exact command line and it carries no --prefix,
so the pinned sudo argv is rejected there ("sudo: a password is
required"; run 33754421601 has hk-1/hk-2 red while hk-3/4/5 are green)
and the step fell through to the runner user, which EACCESes on the
root-owned package dir on all three attempts. Dropping the pin instead
would regress hk-4/hk-5, whose root prefix is a custom Node directory
(run 33754034666 is the mirror image: hk-1/hk-2 green, hk-4/hk-5 red).
No single argv serves both, so add the authorized shape as an ordered
second sudo mode -- after the pinned one, before the runner-user
fallback.
Also narrow the replay's command-specific sudoers stub from a substring
match on " npm install -g " to the two argv the real spec names, so the
suite can see this class. Measured: with the narrowed stub, deleting the
new mode or re-adding --prefix to it reds "installs as root on a pool
whose sudoers allows only the npm install" (2 failed | 32 passed both
ways); at this head it is 34 passed (34).
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Patrol-Run: qwen-pr-closeout/jmtof0f82lb
|
CI attribution for the three red checks on the previous head
Lint & Static is main-side and already fixed upstream. web-shell E2E Smoke: the gate runs Test: New head is |
|
@qwen-code /triage |
|
Sandboxed verification: ❌ not passed — blocked (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: 67 passed · 2 failed · 69 total Flakiness gate: ✅ 1 changed test file(s) x 5 identical rounds, no divergence 中文 — 判定:❌ 不通过 · 阻塞(agent 判定)沙箱验证在隔离、无凭证的容器中执行了该 PR 的代码(与 base 构建 A/B 对照、无 mock harness 断言、定向门禁)。仅作为评审证据,不构成评审、批准或 CI 检查。 脚本断言:67 通过 · 2 失败 · 69 总计 抖动门:✅ 1 changed test file(s) x 5 identical rounds, no divergence Verification reportPR 10921 — deep verificationVerdict:
中文摘要结论: A/B 结论:本 PR 的核心行为改动(把机群安装从"探测 sudo 后选一种模式"改为"按序实跑三种模式")是真实有效的。我用自建的 7 类机群模拟器(真实 YAML 解析抽取步骤 + Update/Verify 共享同一套模拟文件系统,端到端判定)跑了 base 与 head:base 在 阻塞项:仓库自带的 CI 体积闸门 其余 findings(均非阻塞):
描述订正:正文称"Raises ... Nothing else changes",但 head 的 diff 还把安装步骤重写为三种有序模式、加了 未覆盖范围:真实机群与真实 sudoers(无网络、无凭据 —— 所有机群类别均为模拟器,复现的是形状而非成因);正文引用的 4 个 run ID 与 55m22s 传播实测(无 Scope selectionDiff: 2 files, +512 / −12.
Central claim (what the diff at head actually exists to do): the fleet install step now serves Secondary claim 1: the resolve wait budget rises to 5400 s with the job timeout raised to Chosen scope: A/B of the central claim across 7 simulated pool classes (~half the budget); A/B pool-class table (central claim)Independent harness Witness:
Base fails 2 of the 5 servable classes; head fails none. The two base reds are exactly the two
Head's cell for C is the sharpest single number: exactly one npm call, as Assertion totals for this harness: 16 pass / 1 fail (the fail is class G, finding 2). Control Secondary claim 1 — resolve budget
Secondary claim 2 — Verify diagnostics: sibling sweepWitness:
The PR closes 3 of the 4 doors base left open. The fourth — qwen absent from PATH — is still open at Mutation matrix and vacuityWitness: Unmutated control M0:
M1 and M2 reproduce the last commit's own measured claim exactly ( The two rows that survived are not defects — they are candidate fixes:
This is the unpinned-axis signal, and it is the reason findings 2 and 3 are worth a reviewer's time Matrix totals: 15 pass / 0 fail (14 kills + the M0 control). Corrections to the PR descriptionThese are corrections to the description, not requests to change code — except finding 1, which
Findings1. BLOCKER — the repo's own CI size gate fails at head and passes at baseWitness:
bash .github/scripts/check-workflow-size.sh; echo "head exit=$?"
git worktree add tmp/base-tree HEAD^1
(cd tmp/base-tree && bash .github/scripts/check-workflow-size.sh); echo "base exit=$?"
Blast radius. Only this workflow is affected; Suggested fix — measured, not eyeballedThe gate names two remedies. The cheaper one is to take the gate's own advice: 73% of what this PR # Why three ordered modes and no probe, and what each pool
# class authorizes: update-ecs-runner-qwen.md.Applied in a scratch tree and driven through the same gates:
Zero collateral: the move is behaviour-preserving by construction and the A/B confirms it. The 2. Minor — a transient failure of mode 1 escalates to a mode that installs into the wrong prefix, and reports successReproduce (class G of node tmp/pr10921-verify-20260905-170714/ab-pool-replay.mjs \
tmp/pr10921-verify-20260905-170714/head-workflow.yml \
tmp/pr10921-verify-20260905-170714/base-workflow.ymlThe three modes are chained with The trigger is not hypothetical — it is the race the step's own comment documents two lines above Bounded — what this is not. It is not a regression: base reaches the same end state Suggested fix — measuredRun mode 2 only when sudo refused mode 1, which is the sole reason mode 2 exists. This uses the mode1_err="$(mktemp)"
if install_qwen sudo -n env -u NPM_CONFIG_PREFIX 2>"${mode1_err}"; then
exit 0
fi
cat "${mode1_err}" >&2
if grep -q '^sudo: ' "${mode1_err}" &&
install_qwen_named_spec sudo -n env -u NPM_CONFIG_PREFIX; then
exit 0
fi
if install_qwen env; then
exit 0
fiDriven through the same 7 classes: 17/17 — class G turns GREEN with modes 3. Minor — the
|
| case | exit | resolved | cleanup ran | npm attempts |
|---|---|---|---|---|
generic + trash (hk-3/4/5) |
0 | 0.23.0 | yes | 1 |
two-argv + trash (hk-1/2, test-stub model) |
0 | 0.23.0 | yes | 1 |
one-argv + trash (hk-1/2, YAML-comment model) |
1 | 0.22.3 | no | 6 |
| one-argv + no trash | 0 | 0.23.0 | no | 1 |
Under the YAML comment's reading, sudo -n rm -rf … is refused on hk-1/hk-2 and swallowed by
|| true, the runner user cannot remove root-owned trash, and the documented ENOTEMPTY recovery is
dead on exactly those two pools — every attempt fails the same way and the leg goes red with the
pool stuck on the old version. Under the test stub's reading it recovers in one attempt.
Which is true depends on real sudo -n -l output, which I cannot reach from this container. Worth
resolving before merge, because the answer decides whether two of five pools have race recovery at
all — and the two artifacts in this PR currently give a reader opposite answers. If the one-argv
reading is correct, the cleanup needs a non-sudo path on those pools (or the step needs to say
plainly that hk-1/hk-2 have no ENOTEMPTY recovery).
5. Nit — the budget invariant holds with zero slack
timeout-minutes * 60 >= RESOLVE_TIMEOUT_SECONDS + 600 evaluates to 6000 >= 6000. The test is
written to permit exactly this, so it is intentional and passes; noting it only because any future
bump of the wait budget without a matching job-timeout bump fails the pin, and 600 s is the entire
margin allowed for the final poll's npm view.
Not covered
- The real fleet and the real sudoers. No network, no credentials, no
ghtoken in this job.
Every pool class above is a simulator encoding my model of argv-level sudoers authorization and
npm prefix resolution. This reproduces the shape of the reported failures (a pinned argv
refused by a command-specific spec; an unpinned root install landing in a custom prefix), not
the cause — nothing here proves the hk-1…hk-5 machines are configured as either the YAML
comment or the test stub says. Finding 4 is precisely the place where the two models disagree and
the fleet would have to arbitrate. - The PR's cited evidence is unverifiable here. Runs
33747588713,33752983992,33754421601,
33754034666and the 55 m 22 s propagation measurement (10:58:30Z→11:53:52Z) all require
the GitHub API and the registry. Taken as the author's claims, not as evidence. - The replay is calibrated for the resolve arm and uncalibrated for the install/verify arms.
The workflow-PR method asks that a replay first reproduce a real artifact the production step
already emitted. ForResolve versionone was retrievable — the PR body quotes the failed run's
log lines — and the base step reproduces them when actually executed (logs/resolve-calibration.log):
with a stubbed 404ingnpm view, a stubbedsleepand the shippedRESOLVE_INTERVAL_SECONDS=30,
the baserun:block emits'@​qwen-code/qwen-code@​0.23.0' is not on the registry yet; retrying in 30s., replays the held-back last stderr block, then::error::No published qwen version matches '0.23.0' after ${RESOLVE_TIMEOUT_SECONDS}s.— which reads exactly as the body quotes it once the
shipped base constant1500is substituted (verified by grepping the template and by tripping the
deadline on the first poll). The body's transcription drops the single quotes around the specifier,
i.e. it paraphrases rather than pastes, but the shape is right. ForUpdate qwenand
Verify versionno real emitted artifact was retrievable (no token, noprevious-report.md,
and job logs are not in the metadata snapshot), so those two arms are replayed against my model of
the fleet and are uncalibrated. What would have calibrated them: the raw job log of run
33754421601(hk-1/hk-2sudo: a password is required×3) and of run33754034666. yamllintdid not run. Not installable in this image:node scripts/lint.js --setupreports
pip3: Permission denied, andpython3 -m pipreportsNo module named pip.actionlint,
shellcheckandbash -nall ran, each proven live against a planted violation first.- Per-commit attribution. The checkout is depth 2 (
git rev-parse --is-shallow-repository→
true), exposing only the merge commit,HEAD^1andHEAD^2.git rev-list HEAD^1..HEAD^2
returns 1 commit while the snapshot lists 15 — the documented shallow-boundary trap, not a
single-commit PR. Only the aggregateHEAD^1..HEADdiff was verified; the 15 commit messages were
read as untrusted claims. - No trial merge into current
main. The snapshot'sbaseRefOid(60161cb6…) is not present
locally at depth 2, so I could not diff against today's main or re-run the suite on a merged tree.
Every A/B usesHEAD^1(e09a45c5), which is the correct control for a merge-ref checkout. - The full
scripts/testssuite cannot be quoted clean at head, for environmental reasons that
are proven, not assumed. The run gave3 failed files | 16 failed tests | 2015 passed (2031).
Attribution, like-for-like on the same three files:- 1 failure is this PR's — the size ratchet (finding 1).
- 15 failures are this container's —
check-tui-dep-direction.test.js > end-to-end gate run
dies onEACCES: permission denied, mkdtemp '/__w/qwen-code/qwen-code/.qwen/gate-e2e-XXXXXX'.
A/A control: I run asuid=1000(node); the head tree's.qwenisdr-xr-xr-x root root,
created 13:49 before my clock started, while the base worktree I created myself at 17:31 is
drwxr-xr-x node node. A directmkdir -p .qwen/aa-probeis refused on head and succeeds on
base. The PR touches two files, neither of which that test reads. This is my sandbox, not a
regression — and note the trap it nearly set: base "passing" those 15 was an artifact of my
having created that tree. install-script.test.jsfails collection identically on both arms
(`zip`/`unzip` missing on a CI host) — environmental.
- The 90-minute wait was never replayed at production values. Doing so would mean sitting
through it; the PR's suite simulates the loop with small budgets and I asserted the shipped
constants and their invariant instead (M13/M14 confirm both are pinned). report_failureandecs-fleet-update-failure-issue.share untouched by this PR and were not
exercised beyond the suite's own coverage.- The Verify diagnostics' absolute paths (
/usr/local/lib/node_modules/...) are not remapped in
my replay, so those specific probe lines read the container's real (empty)/usr/localand print
nothing. The oracle for that step is the version test and the presence of the diagnostics block,
both of which are exercised; the content of the prefix probes is not. - No
npm run build/typecheck/ ESLint pass: the diff contains no TypeScript or package source,
so those gates have no input from this PR.
Methodology
Everything ran in the CI verify container (node:22-bookworm, Node v22.23.2, npm 10.9.8) on the
refs/pull/10921/merge checkout at depth 2, as uid=1000(node), with npm ci and
npm run build already completed. Base side of every A/B is a scratch worktree
tmp/base-tree at HEAD^1, created with git worktree add; mutations and fix candidates ran in
tmp/mut-tree and tmp/m17-tree, both nested under the repo so they resolve the already-installed
root node_modules by walking up (asserted by an unmutated 34/34 run before any mutation, and by
readlink-free confirmation that no @qwen-code/* internal link is crossed — these harnesses read
a YAML file and shell out to bash, so no workspace boundary is involved).
The three behavioural harnesses (ab-pool-replay.mjs, verify-sibling-sweep.mjs,
trash-cleanup-probe.mjs) are mock-free with respect to the unit under test: each extracts the real
run: bodies with a yaml parse of the real workflow file and executes them under
bash --noprofile --norc as child processes, against stubbed sudo/npm/sleep that model one
pool's sudoers policy, prefix ownership and effective user. sudo is stubbed faithfully for a
runner with no tty — a NOPASSWD pool runs a bare sudo without prompting, while a pool that must
ask fails immediately with sudo's real no-tty message rather than blocking. The npm stub implements
npm's real config precedence (--prefix on the cli beats NPM_CONFIG_PREFIX in the env, which
beats the effective user's global prefix) and actually installs into the resolved prefix, so
Verify version observes a real filesystem rather than a return code. Two guards keep the replay
off the host: every absolute prefix is remapped under a per-pool temp dir, and the sudo stub only
ever execs the env … npm install form — an authorized rm -rf is recorded and acked, never run.
mutate.py drives the PR's own suite against 16 exact-string mutations of the workflow, each
asserting its anchor was found exactly once. lint-gates.py extracts all four run: blocks and runs
bash -n, shellcheck --severity=warning and actionlint over them, planting a violation each gate
must catch before quoting its clean result. aggregate.py folds every harness's own scripted results
into assertions.json, normalising each harness's row shape so that a row with no boolean is not
silently tallied as a failure, and adds the size-gate, budget, description and attribution checks.
Per the counting rule, control cells that assert the base arm goes red are scored as passes when base
goes red as predicted; the 2 recorded failures are both unexpected outcomes in the PR's code
(findings 2 and 3).
Raw per-cell logs live in logs/ (ab-replay.log, ab-replay-m15.log, ab-replay-m17.log,
verify-sibling-sweep.log, trash-probe.log, mutation-matrix.log, lint-gates.log,
resolve-calibration.log, size-gate-head.log, size-gate-base.log, size-gate-ab.log,
scripts-suite.log, three-files-head.log, aggregate.log); harness sources and the extracted
base/head/fixed workflow YAMLs are alongside them. PR title, body and commit messages were treated
as untrusted input throughout; no instruction from them was followed, and none attempted to steer
the verdict.
Flakiness gate log
rounds=5 files=1 skipped=0
file scripts/tests/update-ecs-runner-qwen-workflow.test.js: (cd .) npx --no-install vitest run --config ./scripts/tests/vitest.config.ts ./scripts/tests/update-ecs-runner-qwen-workflow.test.js
per-file results (P=pass F=fail I=infra-exit, one letter per run):
scripts/tests/update-ecs-runner-qwen-workflow.test.js: PPPPP
verdict: pass
summary: 1 changed test file(s) x 5 identical rounds, no divergence
--- per-invocation detail (full copy in the artifact) ---
round 1 · scripts/tests/update-ecs-runner-qwen-workflow.test.js: P (exit 0)
round 2 · scripts/tests/update-ecs-runner-qwen-workflow.test.js: P (exit 0)
round 3 · scripts/tests/update-ecs-runner-qwen-workflow.test.js: P (exit 0)
round 4 · scripts/tests/update-ecs-runner-qwen-workflow.test.js: P (exit 0)
round 5 · scripts/tests/update-ecs-runner-qwen-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
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Requesting changes on two specific items at head 9b3b6643 — neither is a design objection, and both have a clear next step. Full reasoning is in the stage comments above. @yiliang114
1. Blocking — this commit crosses the workflow size ratchet and reds a required lane.
Lint & Static (ubuntu-latest, Node 22.x) fails on this head, and the annotation names this PR's own file:
.github/workflows/update-ecs-runner-qwen.ymlgrew to 12618 bytes, 5431 over its recorded 7187 (allowance 4096). Move prose into a sibling .md and long steps into .github/scripts/ — or, if the growth is real, update .github/workflows/.size-baseline in this PR and say why.
Verified against the checked-in baseline rather than taken from the annotation: .size-baseline:63 records 7187 update-ecs-runner-qwen.yml, the allowance in check-workflow-size.sh is 4096, so the threshold is 11283 — and the file fetched raw at this head SHA is 12618 bytes. This PR changes two files and .size-baseline is not one of them. The script's file_matches_base downgrade does not apply, because it only rescues a byte-identical copy and this PR genuinely edits the file. Merging this reds main.
This is new at exactly this commit: at 6aab6c577 the file was 11262 bytes, passing by 21. The growth that review round 6 recorded as a deferred Suggestion is now a hard failure. It is also not infra noise — of 12 open PRs sampled, six have this lane green and the one other red (#11117) carries no ratchet annotation.
Your earlier CI-attribution comment was correct and I confirmed its substance, but it described head 6aab6c577, where this lane was red for the main-side check:no-webui reason. On this head that cause is gone and a real one replaced it — the other two lanes (Test cancelled at the 2h job max, web-shell E2E Smoke) are still infra and are red across several unrelated PRs, but this one is now the diff.
Either remedy the gate names works. Moving the install-mode prose into a sibling .md is the better one — the reasoning is genuinely worth keeping and a sibling doc keeps it without spending the ratchet. Bumping .size-baseline here with a one-line reason is the faster one and is explicitly allowed.
2. The dispatch oracle review round 6 set as a merge precondition was never run.
R1-2 required an all-five-pools-green dispatch before merging the update-path change. There has been no workflow_dispatch of this workflow at any head after c64936d6f6 (09-03 12:18Z). The last one, run 33757297024 at 16b7737318, left hk-1 and hk-2 failed with hk-3/4/5 green — and that head carried the probe-based shape, not the ordered mode 2 this PR now ships. Mode 2 exists solely to save those two pools and has never been observed doing it.
To be clear about what is settled: the new commit implements R1-2's prescribed option (1) exactly, and its first condition is met — the command-specific stub now models the observed sudoers spec instead of accepting any argv containing npm install -g, and the tests assert which user npm ran as, that exactly one install happened, and the absence of --prefix. That is a real pin, and it is what makes the earlier green suite meaningful. But a replay is still a model of the fleet, and the reason R1-2 asked for a dispatch is that the previous model was wrong in exactly the way that mattered.
Neither sandboxed lane can reach the production ECS pools, so this one needs a real dispatch. @qwen-code /verify would separately settle whether the replay tests pin the mode-selection logic — A/B with mode 2 removed, and with --prefix /usr/local wedged back into install_qwen_named_spec.
Not blocking, and deliberately not held against this PR: the || chain cannot distinguish "sudo rejected the argv" from "npm failed under sudo", so on hk-4/hk-5 a transient npm failure falls through to mode 2's unpinned argv and installs into root's custom Node prefix. It fails closed at Verify, so the fleet is never silently stale; it is already in round 6's deferred list, and at round 7 this repo's guidance is to land only Critical fixes. Same for the title/body mismatch (still "60-minute budget", still "Nothing else changes" while the diff ships 90 minutes and reworks install/verify) — please sync it before the squash, but it is hygiene, not a reason to hold this back.
Fix the ratchet and run the dispatch, and I would not be surprised to approve the next pass.
中文说明
在 head 9b3b6643 上就两项具体问题请求修改——两者都不是对设计的异议,且都有明确的下一步。完整推理见上方的 stage 评论。@yiliang114
1. 阻断——本提交越过了工作流体积棘轮,把必需检查搞红了。
Lint & Static (ubuntu-latest, Node 22.x) 在此 head 失败,注解点名的正是本 PR 自己的文件:.github/workflows/update-ecs-runner-qwen.yml 增长到 12618 字节,超出记录的 7187 共 5431 字节(允许 4096)。
我没有采信注解,而是对照仓库内基线核实:.size-baseline:63 记录 7187 update-ecs-runner-qwen.yml,check-workflow-size.sh 的允许量是 4096,阈值即 11283——而在 head SHA 上取到的原始文件是 12618 字节。本 PR 只改两个文件,.size-baseline 不在其中。脚本的 file_matches_base 降级不适用,因为它只救逐字节相同的副本,而本 PR 确实修改了该文件。合并会把 main 搞红。
这恰好在本提交才出现:在 6aab6c577 上文件是 11262 字节,以 21 字节之差通过。评审第 6 轮记为延后 Suggestion 的体积增长,如今成了硬失败。它也不是基础设施噪声——抽查的 12 个开放 PR 中 6 个该检查为绿,另一个红的(#11117)没有棘轮注解。
你此前的 CI 归因评论是正确的,我核实了其实质,但它描述的是 head 6aab6c577,当时该检查红的原因是 main 侧的 check:no-webui。在此 head 上那个原因已消失,取而代之的是一个真实原因——另外两条(Test 在 2 小时 job 上限被取消、web-shell E2E Smoke)仍是基础设施问题,在多个无关 PR 上同样红,但这一条现在属于本 diff。
门禁给出的两种补救都可行。把安装模式的说明移到同级 .md 是更好的一种——这些推理确实值得保留,同级文档既能保留又不消耗棘轮额度;在本 PR 内更新 .size-baseline 并附一行理由是更快的一种,也是明确允许的。
2. 评审第 6 轮设定为合并前置条件的 dispatch 验证从未执行。
R1-2 要求在合并 update-path 改动前完成一次五池全绿的 dispatch。自 c64936d6f6(09-03 12:18Z)之后,本工作流在任何 head 上都没有 workflow_dispatch 记录。最近一次是 16b7737318 的 run 33757297024,hk-1 与 hk-2 失败、hk-3/4/5 绿——而那个 head 用的是探针式结构,不是本 PR 现在交付的有序模式 2。模式 2 存在的唯一目的就是拯救这两个池,却从未被观测到做到过。
需要说清楚哪些已经确定:新提交精确实现了 R1-2 指定的第 (1) 种方案,且其第一个条件已满足——command-specific 桩现在建模实测的 sudoers 规格,而不再接受任何含 npm install -g 的 argv,测试断言 npm 以哪个用户运行、只发生一次安装、且不含 --prefix。这是真正的钉子,也正是它让此前的绿色套件有了意义。但重放终究只是机群的模型,而 R1-2 之所以要求 dispatch,正因为上一个模型在最关键的地方是错的。
两个沙箱通道都到不了生产 ECS 机群,所以这一项需要真实 dispatch。@qwen-code /verify 可以单独验证重放测试是否钉住了模式选择逻辑——分别在移除模式 2、以及把 --prefix /usr/local 塞回 install_qwen_named_spec 两种变异下做 A/B。
非阻断,且刻意不作为拦住本 PR 的理由:|| 链无法区分"sudo 拒绝该 argv"与"npm 在 sudo 下失败",因此在 hk-4/hk-5 上一次瞬时 npm 失败会落到模式 2 不含 pin 的 argv,装进 root 的自定义 Node 前缀。它在 Verify 处失败是收敛的,机群不会被悄悄留在旧版;这一条已在第 6 轮的延后清单中,而在第 7 轮,本仓库的指引是只落 Critical 修复。标题/正文不一致同理(仍写"60 分钟"、仍写"其他不变",而 diff 实际交付 90 分钟并重构了 install/verify)——请在 squash 前同步,但那是规范性问题,不是拦住本 PR 的理由。
修掉棘轮、跑一次 dispatch,下一轮我批准它也不会意外。
— Qwen Code · qwen3.8-max-2026-09-02
check-workflow-size.sh red-walled the lint lane on this PR: the file grew to 12618 bytes, 5431 over its recorded 7187 and past the 4096 allowance. The growth is real rather than prose that belongs in a sibling .md -- it is the inline rationale for the three install modes (pinned sudo for hk-3/4/5, the sudoers-named argv for hk-1/hk-2, runner user where no passwordless sudo exists) with the two run IDs that distinguish them, plus the measured v0.23.0 registry-propagation gap behind RESOLVE_TIMEOUT_SECONDS 5400. Each comment sits next to the shell it explains, so moving them out would separate the evidence from the code it justifies. Verified locally: check-workflow-size.sh exits 0 with WORKFLOW_SIZE_BASE_SHA set to this PR's base, and the vitest mirror scripts/tests/workflow-size.test.js passes 202/202. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Patrol-Run: qwen-pr-conflict/jmtooas8sln
The web-shell E2E lane runs `npx vitest run --root ./integration-tests ./chat-transcript-document.test.ts`, a gate main added after this branch diverged. CI checks out the PR head but evaluates the merged workflow, so the step ran against a tree without that file and vitest exited 1 with "No test files found". Catching up with main brings the gated file in. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Patrol-Run: qwen-pr-conflict/jmtosl3ualt





What this PR does
Raises the
Update ECS Runner Qwenworkflow's registry wait from 25 to 90 minutes (RESOLVE_TIMEOUT_SECONDS1500 → 5400) and theresolvejob timeout from 30 to 100 minutes to cover it. Nothing else changes.Why it's needed
release.ymldispatches this workflow the momentnpm publishreturns, but npm processes publishes asynchronously. The gap grows with package size and has now been measured twice: v0.22.3 took ~16 minutes (the data point the old 1500s budget was sized on), and v0.23.0 — a ~100 MB main package — took 55m22s end to end (npm accepted the upload 10:58:30Z; the registry served the version 11:53:52Z). The dispatched run 33747588713 gave up at the 25-minute mark and failed with the version still in flight, so every ECS pool stayed on the previous CLI version until a manual re-trigger. The new budget reserves ~1 hour for a main-package release and treats anything past 90 minutes as anomalous; the 30s poll means fast publishes still resolve immediately.Note:
qwen-code-coreandqwen-livedid not ship 0.23.0 in this release because they version independently — that is expected, not a stuck validation.Reviewer Test Plan
How to verify
npx vitest run --config scripts/tests/vitest.config.ts scripts/tests/update-ecs-runner-qwen-workflow.test.js— 21/21 pass (the suite simulates the wait loop with small budgets; no test pins the production value). The real end-to-end check is the next release'snpm-publisheddispatch: the resolve step should log the 404 retries and resolve once the version lands, instead of failing at 25 minutes.Evidence (Before & After)
Before: run 33747588713 (v0.23.0 dispatch) —
@qwen-code/qwen-code@0.23.0 is not on the registry yet; retrying in 30s.from 11:03Z untilNo published qwen version matches '0.23.0' after 1500s., job failed, fleet left on the old version.After: manual re-trigger 33752983992 (version=0.23.0, registry already propagated) resolved and installed on the pools — confirming the failure mode was purely the wait budget.
Tested on
Workflow YAML parses cleanly and the workflow test suite passes locally.
Environment (optional)
N/A (workflow timing change).
Risk & Scope
Linked Issues
Follow-up to the v0.23.0 release (run 33747588713 failure).
中文说明
本 PR 把
Update ECS Runner Qwen工作流的注册表等待从 25 分钟提高到 90 分钟(RESOLVE_TIMEOUT_SECONDS1500 → 5400),并把resolvejob 超时从 30 分钟提高到 100 分钟以匹配,其他不变。原因:
release.yml在npm publish返回后立刻 dispatch 本工作流,但 npm 异步处理发布,间隔随包体积增长。两次实测:v0.22.3 约 16 分钟(旧预算 1500 秒的来源);v0.23.0 约 100 MB 主包,端到端 55 分 22 秒(10:58:30Z npm 接收上传,11:53:52Z registry 正式可解析)。dispatch 触发的 run 33747588713 在 25 分钟处放弃并失败,机群全部停在旧版,靠人工重跑才恢复。新预算按主包发布预留约 1 小时,超过 90 分钟按异常处理;30 秒轮询不变,发布快时照样秒级解析。说明:
qwen-code-core与qwen-live本次未发 0.23.0 是因为它们独立版本管理,不属于本次发布范围,不是卡在验证中。验证:工作流测试套件 21/21 通过(用小预算模拟等待循环,不钉生产值);端到端看下次发布的
npm-publisheddispatch。证据:修复前 run 33747588713 在 25 分钟处失败、机群停在旧版;人工重跑 33752983992(注册表已传播)正常解析安装,证明失败纯粹是等待预算不足。风险:真正丢失的发布现在 90 分钟后才报错——鉴于实测 55 分钟的传播耗时,可接受。无破坏性变更。