Skip to content

fix(ci): retry transient sandbox:none E2E shard failures once (#10566) - #10572

Open
qwen-code-dev-bot wants to merge 3 commits into
mainfrom
autofix/issue-10566
Open

fix(ci): retry transient sandbox:none E2E shard failures once (#10566)#10572
qwen-code-dev-bot wants to merge 3 commits into
mainfrom
autofix/issue-10566

Conversation

@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

What this PR does

Gives the sandbox:none leg of the post-merge Linux E2E lane one bounded retry at its test step. The shard command is wrapped in a small shell function and, if the first attempt exits non-zero, the step emits a ::warning:: naming the runner and re-runs the same shard once; the second attempt's exit status is the step's, so a deterministic test failure fails both attempts and keeps the job red. The retry is deliberately scoped to the sandbox:none leg — two ~30-minute sandbox:docker attempts would outrun the job's timeout-minutes — and is pinned by new witness tests next to the existing sandbox-image-build-retry block, including one asserting the docker leg is not retried.

Why it's needed

Run 33317457036 failed E2E Test (Linux) - sandbox:none - shard 2/3 at the Run E2E tests step with exit code 1 and zero vitest FAIL lines in the log, which is the class this repo documents in the integration test config: under shared self-hosted pool pressure, vitest's fixed 60-second worker→main RPC budget turns a stalled-but-otherwise-green run red via unhandled-error paths, and the deaths that survive the lane's existing self-hosted exemption sit inside vitest itself, unreachable from repository code. The two previous main E2E runs failed the identical leg and step on two different hosts and commits, while the run between them was fully green and a local reproduction of the exact shard passes — the same transient, pressure-correlated shape the sandbox-image build retry in this same workflow was added for (#10355). Without a retry, one host under pressure exits a whole shard red and files a per-commit "Main CI failed" issue (this one is the twentieth such open issue in ~36 hours); with it, a transient death costs one extra shard attempt and a deterministic failure still reports red.

Reviewer Test Plan

How to verify

This is CI-harness behavior plus its witness tests; the retry logic is fully inspectable from the workflow file and was verified three ways. First, the witness block in the scripts suite: npx vitest run --config ./scripts/tests/vitest.config.ts e2e-workflow passes 12/12, and removing the retry block from the workflow (or duplicating the docker leg's command) makes the corresponding witness test fail. Second, the bash semantics were simulated under bash -eo pipefail (the Actions default shell) with a stubbed shard command: one failure retries and exits 0, two failures exit 1, and a passing first attempt never retries. Third, the real gate is the lane itself: the next main push should either pass both attempts or, if a genuine test failure exists, report its FAIL lines on the second attempt instead of dying silently.

Evidence (Before & After)

N/A — CI-harness change, not user-visible. Before: run 33317457036 (and runs 33302550436, 33293739505) red at Run E2E tests with zero FAIL lines. After: one bounded retry absorbs a single transient death; deterministic failures stay red on both attempts.

Tested on

OS Status
🍏 macOS ⚠️ not tested
🪟 Windows ⚠️ not tested
🐧 Linux ✅ tested

Environment (optional)

Self-hosted pool machine (the same pool the failing lane runs on): witness suite with mutation probes, functional retry simulation under bash -eo pipefail, bash -n on the generated step script for both matrix legs, npm run build, npm run typecheck, npm run lint, npm run test:scripts, and a full surrogate of the failing shard (test:integration:sandbox:none, shard 2/3, real model credentials) which passes at the failing commit.

Risk & Scope

  • Main risk or tradeoff: a genuinely red sandbox:none shard now takes up to one extra shard attempt (~15–20 minutes, still inside the job's 60-minute timeout) before reporting red; transient pressure deaths report green with a ::warning:: in the log instead of filing another per-commit issue. No test assertion, timeout, or exclusion is weakened anywhere.
  • Not validated / out of scope: the exact vitest-internal path of each CI death cannot be named without authenticated log access; the sandbox:docker leg keeps no retry because two attempts would not fit the job timeout; the poisoned-host and disk-pressure classes tracked by other issues/PRs (ci: gate heavy jobs on a disk floor and persist pressure samples #10394's disk floor, fix(cli): don't crash startup when the output-language file is unwritable (#10453) #10455's unwritable-home crash sites) are separate and untouched.
  • Breaking changes / migration notes: none — workflow step behavior and test witnesses only.

Linked Issues

Fixes #10566

中文说明

本 PR 做了什么

为合并后 Linux E2E 通道的 sandbox:none 支路在其测试步骤上增加一次有界重试。分片命令被包进一个小的 shell 函数,首次尝试非零退出时,步骤输出一条点名运行器的 ::warning:: 并将同一分片重跑一次;第二次尝试的退出状态即步骤的状态,因此确定性测试失败会在两次尝试中都失败、保持任务红色。重试刻意限定在 sandbox:none 支路——两次约 30 分钟的 sandbox:docker 尝试会超出任务的 timeout-minutes——并由紧邻既有"沙箱镜像构建重试"块的新见证测试固定,其中包括一条断言 docker 支路不被重试。

为什么需要

运行 33317457036 在 Run E2E tests 步骤以退出码 1 失败,且日志中没有任何一条 vitest FAIL 行,这正是本仓库在集成测试配置中记录过的类别:在共享自托管池压力下,vitest 固定的 60 秒 worker→main RPC 预算会把一个停滞但本来全绿的运行经由未处理错误路径变红;而在该通道已有的自托管豁免之后仍会发生的那些死亡,位于 vitest 内部,仓库代码无法触及。此前两次 main E2E 运行在不同的主机、不同的提交上以完全相同的支路与步骤失败,夹在中间的一次运行完全绿色,且对同一分片的本地复现通过——与同一 workflow 中沙箱镜像构建重试(#10355)所针对的瞬时、压力相关形态完全一致。没有重试时,一台承压主机就会让整个分片变红并归档一个按提交的 "Main CI failed" issue(本 issue 是约 36 小时内的第 20 个同类开放 issue);有了重试,一次瞬时死亡只多花一次分片尝试,而确定性失败依旧报红。

审阅者测试计划

如何验证

这是 CI 基础设施行为及其见证测试;重试逻辑可以直接从 workflow 文件中审阅,并已用三种方式验证。第一,scripts 套件中的见证块:npx vitest run --config ./scripts/tests/vitest.config.ts e2e-workflow 12/12 通过;从 workflow 中移除重试块(或复制 docker 支路的命令)会使对应的见证测试失败。第二,在 bash -eo pipefail(Actions 默认 shell)下用桩分片命令模拟了 bash 语义:失败一次会重试并以 0 退出,失败两次以 1 退出,首次通过则绝不重试。第三,真正的门槛是通道本身:下一次 main 推送要么两次尝试都通过,要么在存在真实测试失败时于第二次尝试上报出 FAIL 行,而不是无声死亡。

证据(前后对比)

N/A — CI 基础设施变更,无用户可见行为。之前:运行 33317457036(以及 33302550436、33293739505)在 Run E2E tests 变红且零条 FAIL 行。之后:一次有界重试吸收单次瞬时死亡;确定性失败在两次尝试中保持红色。

测试环境

OS 状态
🍏 macOS ⚠️ 未测试
🪟 Windows ⚠️ 未测试
🐧 Linux ✅ 已测试

环境(可选)

自托管池机器(与失败通道相同的池):带变异探测的见证套件、bash -eo pipefail 下的重试功能模拟、对两个矩阵支路生成步骤脚本的 bash -nnpm run buildnpm run typechecknpm run lintnpm run test:scripts,以及对失败分片的完整替代复现(test:integration:sandbox:none,shard 2/3,真实模型凭证),在失败提交上通过。

风险与范围

  • 主要风险或权衡:真正变红的 sandbox:none 分片现在最多多花一次分片尝试(约 15–20 分钟,仍在任务 60 分钟超时之内)才报红;瞬时压力死亡会在日志中留下一条 ::warning:: 并报绿,而不是再归档一个按提交的 issue。任何测试断言、超时或排除都未被削弱。
  • 未验证 / 超出范围:没有认证日志访问时无法点名每次 CI 死亡所处的 vitest 内部具体路径;sandbox:docker 支路不加重试,因为两次尝试放不下任务超时;由其他 issue/PR 跟踪的宿主机污染与磁盘压力类别(ci: gate heavy jobs on a disk floor and persist pressure samples #10394 的磁盘下限、fix(cli): don't crash startup when the output-language file is unwritable (#10453) #10455 的不可写主目录崩溃点)是独立问题,未被触及。
  • 破坏性变更 / 迁移说明:无——仅 workflow 步骤行为与测试见证。

关联 Issue

Fixes #10566

@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator Author

E2E report — issue #10566 (Main CI failed: E2E Tests on 3aa1b14)

Background

Workflow run 33317457036 failed on main at commit 3aa1b14624 "before any test result was reported" — the per-commit fallback the issue-filing analyzer uses when the failed jobs' logs contain no identifiable vitest FAIL lines. This environment has no GitHub credentials for log downloads (the job-log endpoint answers 403 without admin rights), so the investigation below combines the run's public job metadata with a local reproduction of the exact failing lane on the same self-hosted pool infrastructure.

Investigation

Which job failed. The public jobs API shows exactly one failing job: E2E Test (Linux) - sandbox:none - shard 2/3 on pool runner ecs-qwen-runner-hk-j6c03lyei7s809zq1s6t-22, failing step Run E2E tests, conclusion failure, annotation Process completed with exit code 1. All five sibling legs of the same run passed — including sandbox:docker - shard 2/3 (identical shard and file set) and sandbox:none - shard 1/3 on a neighbouring HK pool host.

The failure is a recurring class, not this commit. The two previous main E2E runs failed the exact same leg at the exact same step on two other hosts and two other commits (33302550436 on e40040cd26, 33293739505 on bf054479b1fc), while the run between them (33309279860, commit c7c47deb94) was fully green. Every open "Main CI failed: E2E Tests" issue of the last ~36 hours (20 issues) carries the same per-commit "no test results reported" shape, and the post-merge unit lane shows the same shape in the same window — consistent with shared-pool pressure, not a code regression in any one merge. The failing commit itself (ci: stabilize tests under shared ECS host contention (#10552)) only touches unit-test timeout budgets plus one optional-timeout plumb; nothing in it reaches the integration lane.

Local reproduction. A full surrogate of the failing leg at 3aa1b14624 — the exact Run E2E tests command (npm run test:integration:sandbox:none -- --exclude '**/interactive/cron-interactive.test.ts' --exclude '**/channel-plugin.test.ts' --shard=2/3 with RUNNER_ENVIRONMENT=self-hosted, KEEP_OUTPUT=true, VERBOSE=true, a fresh /var/tmp TMPDIR, and real model credentials) — passes on this pool host: 21 files (20 passed, 1 statically skipped), 91 tests passed, 3 skipped, exit 0. The shard's test files are therefore not deterministically broken; the CI deaths are transient and environment-correlated.

Root-cause analysis of the signature. "Exit 1 with zero vitest FAIL lines" is the class this repo already documents in integration-tests/vitest.config.ts: vitest's worker→main onTaskUpdate RPC runs on a fixed 60-second budget, and under shared-host resource pressure a stall longer than that surfaces as an unhandled error that exits an all-green run red with no failing test. The E2E lane already carries the self-hosted half of that exemption (dangerouslyIgnoreUnhandledErrors when RUNNER_ENVIRONMENT=self-hosted, from #10325/#10329), and the sibling autofix PR #10498 extends the same flag to the unit lanes — yet the E2E deaths continue, which places them on paths the flag does not cover (vitest-internal process-level unhandled-rejection handling and worker-death plumbing inside node_modules, which repository code cannot patch). The failure is thus a transient pool-pressure death of the test process itself, with every test green.

Change made

One bounded retry for the sandbox:none leg inside the Run E2E tests step, mirroring the existing sandbox-image build retry in the same file (#10355) and its "transient environment failure on a pool runner, genuine failures still fail the job" rationale. The shard command is wrapped in a run_shard function and retried exactly once with a ::warning:: marker between attempts; a deterministic test failure fails both attempts and keeps the shard red, so no real signal is weakened. The retry is scoped to the sandbox:none leg because two ~30-minute sandbox:docker attempts would outrun the job's timeout-minutes: 60, and the three consecutive failures tracked here are all sandbox:none. A witness block in scripts/tests/e2e-workflow.test.js pins the retry shape, and both guards were mutation-probed (removing the retry, or giving the docker leg a second invocation, fails the witness; restoring goes green). The retry's bash semantics were functionally verified under bash -eo pipefail with a stubbed shard command: fail-once → retry → exit 0; fail-twice → exit 1; pass-first → no retry.

Honest limits: without authenticated log access the exact internal vitest path of each death cannot be named, and the retry is a mitigation for a transient environment class, not a patch of vitest internals — the same shape of mitigation this file already carries for the image build. The final gate is the E2E lane itself turning green on main.

Verification

  • npm run build — passed (exit 0)
  • npm run typecheck — passed
  • npm run lint — passed
  • npx prettier --check .github/workflows/e2e.yml scripts/tests/e2e-workflow.test.js — passed
  • npx vitest run --config ./scripts/tests/vitest.config.ts e2e-workflow — 12/12 passed (9 pre-existing + 3 new witness tests)
  • npm run test:scripts — 1966 passed, 16 skipped, 1 failed: verify-capture.test.js > keeps black-foreground text readable instead of black-on-black; re-run on the pristine base tree (both changed files restored to HEAD) where it fails identically — pre-existing environmental failure (PNG font rendering in this container), unrelated to this change
  • Mutation probe 1: retry block removed from e2e.ymlretries the sandbox:none shard exactly once FAILED; restored → green
  • Mutation probe 2: docker leg command duplicated → does not retry the docker leg FAILED; restored → green
  • Functional retry simulation under bash -eo pipefail with a stubbed failing shard command: fail-once → exit 0 after retry; fail-twice → exit 1; pass-first → exit 0 with no retry
  • bash -n on the step's generated script for both matrix legs (sandbox:none and sandbox:docker) — no syntax errors
  • Failing-lane surrogate at 3aa1b14624 (test:integration:sandbox:none, shard 2/3, real model credentials, RUNNER_ENVIRONMENT=self-hosted) — 20 files / 91 tests passed, exit 0
  • Workflow size ratchet: e2e.yml grew 2468 bytes, within its .size-baseline + 4096-byte allowance (no baseline bump needed; workflow-size.test.js green in the scripts suite)
中文说明

E2E 报告 — issue #10566(Main CI failed: E2E Tests on 3aa1b14

背景

Workflow 运行 33317457036main 的提交 3aa1b14624 上"在任何测试结果上报之前"失败——这是 issue 归档分析器在失败任务的日志中找不到可识别的 vitest FAIL 行时使用的按提交回退格式。本环境没有 GitHub 凭证用于下载日志(job 日志接口在无 admin 权限时返回 403),因此下面的调查结合了该运行的公开 job 元数据,以及在同一自托管池基础设施上对失败通道的本地复现。

调查

哪个任务失败。 公开的 jobs API 显示恰好一个失败任务:池运行器 ecs-qwen-runner-hk-j6c03lyei7s809zq1s6t-22 上的 E2E Test (Linux) - sandbox:none - shard 2/3,失败步骤为 Run E2E tests,结论 failure,注解为 Process completed with exit code 1。同一运行的其余五条通道全部通过——包括 sandbox:docker - shard 2/3(相同的分片与文件集)以及相邻 HK 池主机上的 sandbox:none - shard 1/3

失败是复发的失败类别,而非本提交引入。 此前两次 main E2E 运行在另外两台主机、另外两个提交上以完全相同的通道、完全相同的步骤失败(e40040cd2633302550436bf054479b1fc33293739505),而夹在中间的运行(c7c47deb9433309279860)完全绿色。最近约 36 小时内所有开放的 "Main CI failed: E2E Tests" issue(20 个)都带有同样的按提交"无测试结果上报"形态,同一窗口内合并后单元通道也呈现同样形态——与共享池资源压力一致,而非任何一次合并引入的代码回归。失败提交本身(ci: stabilize tests under shared ECS host contention (#10552))只改动了单元测试超时预算和一个可选超时透传,完全不触及集成通道。

本地复现。3aa1b14624 上对失败通道做完整替代复现——与 Run E2E tests 完全相同的命令(npm run test:integration:sandbox:none -- --exclude '**/interactive/cron-interactive.test.ts' --exclude '**/channel-plugin.test.ts' --shard=2/3,配 RUNNER_ENVIRONMENT=self-hostedKEEP_OUTPUT=trueVERBOSE=true、全新的 /var/tmp TMPDIR 和真实模型凭证)——在本池主机上通过:21 个文件(20 通过,1 个静态跳过),91 个测试通过,3 个跳过,退出码 0。该分片的测试文件并非确定性损坏;CI 上的死亡是瞬时的、与环境相关的。

签名层面的根因分析。 "退出码 1 且零条 vitest FAIL 行"正是本仓库已在 integration-tests/vitest.config.ts 中记录过的类别:vitest 的 worker→main onTaskUpdate RPC 有固定的 60 秒预算,在共享主机资源压力下,超过该预算的停滞会以未处理错误的形式出现,使一个全绿的运行以红色退出且没有任何失败测试。E2E 通道已带有该豁免的自托管部分(#10325/#10329 引入的 RUNNER_ENVIRONMENT=self-hosted 时的 dangerouslyIgnoreUnhandledErrors),姊妹 autofix PR #10498 又把同样的标志扩展到单元通道——但 E2E 的死亡仍在继续,说明它们走的是该标志覆盖不到的路径(vitest 内部的进程级未处理 rejection 处理与 worker 死亡管道,位于 node_modules 内,仓库代码无法修补)。因此该失败是测试进程本身在池资源压力下的瞬时死亡,所有测试都是绿的。

所做变更

Run E2E tests 步骤内为 sandbox:none 通道加一次有界重试,与同一文件中已有的沙箱镜像构建重试(#10355)及其"池运行器上的瞬时环境失败、真实失败仍会让任务失败"的理由保持一致。分片命令被包在 run_shard 函数中,恰好重试一次,两次尝试之间输出 ::warning:: 标记;确定性测试失败会在两次尝试中都失败、保持分片红色,因此不会削弱任何真实信号。重试限定在 sandbox:none 通道,因为两次约 30 分钟的 sandbox:docker 尝试会超出任务的 timeout-minutes: 60,且本次跟踪到的三连败全部是 sandbox:nonescripts/tests/e2e-workflow.test.js 中新增见证块固定该重试形态,两个守卫都做了变异探测(移除重试、或给 docker 通道加第二次调用,都会令见证失败;恢复后变绿)。重试的 bash 语义已在 bash -eo pipefail 下用桩命令功能验证:失败一次 → 重试 → 退出码 0;失败两次 → 退出码 1;首次即通过 → 不触发重试。

诚实的边界:在没有认证日志访问的情况下,无法点名每次死亡所处的 vitest 内部具体路径;该重试是对瞬时环境类别的缓解,而非对 vitest 内部的修补——这与该文件中镜像构建重试的缓解形态相同。最终门槛是 main 上的 E2E 通道本身变绿。

验证

  • npm run build — 通过(退出码 0)
  • npm run typecheck — 通过
  • npm run lint — 通过
  • npx prettier --check .github/workflows/e2e.yml scripts/tests/e2e-workflow.test.js — 通过
  • npx vitest run --config ./scripts/tests/vitest.config.ts e2e-workflow — 12/12 通过(9 个既有 + 3 个新见证测试)
  • npm run test:scripts — 1966 通过,16 跳过,1 失败:verify-capture.test.js > keeps black-foreground text readable instead of black-on-black;在干净的基线树(两个改动文件恢复到 HEAD)上重跑同样失败——属于预先存在的环境性失败(本容器中的 PNG 字体渲染),与本变更无关
  • 变异探测 1:从 e2e.yml 移除重试块 → retries the sandbox:none shard exactly once 失败;恢复后变绿
  • 变异探测 2:复制 docker 通道命令 → does not retry the docker leg 失败;恢复后变绿
  • bash -eo pipefail 下用桩失败命令做重试功能模拟:失败一次 → 重试后退出码 0;失败两次 → 退出码 1;首次通过 → 退出码 0 且不触发重试
  • 对两个矩阵通道(sandbox:nonesandbox:docker)生成的步骤脚本做 bash -n — 无语法错误
  • 3aa1b14624 上复现失败通道(test:integration:sandbox:none,shard 2/3,真实模型凭证,RUNNER_ENVIRONMENT=self-hosted)— 20 个文件 / 91 个测试通过,退出码 0
  • Workflow 尺寸棘轮:e2e.yml 增长 2468 字节,在其 .size-baseline + 4096 字节余量之内(无需上调基线;scripts 套件中 workflow-size.test.js 为绿)

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

@qwen-code-ci-bot

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

Copy link
Copy Markdown
Collaborator

Qwen Triage finishedview run. See the stage comments in this thread for the result.

Qwen Triage 已完成 —— 查看运行。结果见本线程中的各阶段评论。

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Thanks for the PR!

Template looks good ✓

Problem: observed, with evidence — workflow run 33317457036 died at the Run E2E tests step with exit 1 and zero vitest FAIL lines; the two previous main E2E runs failed the same sandbox:none leg and step on different hosts and commits, with a fully green run between them, and a local reproduction of the exact failing shard passes. That is the shared-pool-pressure class already documented in integration-tests/vitest.config.ts.

Direction: aligned — this is the same shape of mitigation the workflow already carries for the sandbox image build (#10355), and consistent with the just-merged #10552. CI stability is squarely in-repo remit; no product-surface concerns.

Size: not applicable — no core paths touched. The whole diff is 17 production lines in the workflow file plus 32 lines of witness tests.

Approach: the scope feels right. One bounded retry, scoped to the leg that has actually been failing; a deterministic failure still fails both attempts and keeps the job red; the docker leg is left alone because two ~30-minute attempts would outrun the job timeout. Witness tests pin both the retry and the docker-no-retry invariants.

Risk: no elevated risk signals (no high-risk paths matched).

Moving on to code review. 🔍

中文说明

感谢贡献!

模板完整 ✓

问题:已观测到,且有证据——运行 33317457036 在 Run E2E tests 步骤以退出码 1 失败且日志中零条 vitest FAIL 行;此前两次 main E2E 运行在不同主机、不同提交上以相同的 sandbox:none 支路与步骤失败,中间夹着一次完全绿色的运行,而对失败分片的本地复现通过。这正是 integration-tests/vitest.config.ts 中已记录的共享池压力类别。

方向:对齐——与本 workflow 中沙箱镜像构建已有的重试(#10355)是同一形态的缓解,也与刚合并的 #10552 一致。CI 稳定性完全属于仓库职责范围,无产品面顾虑。

规模:不适用——未触及核心路径。整个 diff 是 workflow 文件 17 行生产改动加 32 行见证测试。

方案:范围合理。一次有界重试,且限定在实际失败的支路;确定性失败在两次尝试中都会失败、任务保持红色;docker 支路不动,因为两次约 30 分钟的尝试会超出任务超时。见证测试同时固定了"重试"与"docker 不重试"两个不变量。

风险:无升级风险信号(未命中高风险路径)。

进入代码审查 🔍

Qwen Code · qwen3.8-max

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

@github-actions github-actions Bot added the review/self-reported The linked issue was opened by the PR author (self-reported) label Aug 30, 2026
@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Code review

Reviewed the diff at the commit below. No blockers.

  • Retry semantics are correct under the Actions default shell (bash -eo pipefail). The first run_shard sits on the left of ||, so a first-attempt failure does not trip set -e; the block's last command is the second attempt, whose exit status becomes the step's — a deterministic failure fails both attempts and keeps the job red, exactly as intended. Both attempts share the step's TMPDIR setup and cleanup trap.
  • Scope is right. The retry wraps the exact shard command that has been dying (runs 33293739505, 33302550436, 33317457036 — same leg, same step, three different hosts); the docker leg is untouched, and the witness test counts QWEN_SANDBOX=docker vitest run occurrences to pin that. One retry, not an open loop: worst case adds one ~15–20 min attempt inside the job's 60-minute budget.
  • House style followed. The inline function retry is a slimmer variant of the sandbox-image build retry precedent (Main CI failed: E2E Tests on bc6f1a015cfb #10355) already in this same file; reusing that pattern via a duplicated second step would have copied the whole env block and TMPDIR setup for more YAML, and a third-party retry action would add an external dependency to privileged CI. The witness block matches the existing e2e-workflow.test.js conventions (same step lookup, same run-ID comment style, mutation-resistant assertions), and I verified the pinned counts against the diff: run_shard appears exactly 3 times.
  • No unrelated changes in the diff; the added comment documents the rationale with the run IDs, which is what will save the next maintainer.

CI test evidence

From the PR's own CI on the reviewed commit (fetched via API; nothing PR-derived was executed here):

Check Conclusion
Test (ubuntu-latest, Node 22.x) ⏳ in progress
Integration Tests (no-AK, No Sandbox) ✅ success
Desktop Shell (ubuntu-22.04) ✅ success
Desktop Shell (windows-2022) ✅ success
Security Checks ✅ success
Dependency CVE audit ✅ success
Secret scan (TruffleHog) ✅ success
Test (macos-latest, Node 22.x) ⏭ skipped
Test (windows-latest, Node 22.x) ⏭ skipped
Integration Tests (CLI, No Sandbox) ⏭ skipped

The three skips are by design, not caused by this PR: ci.yml runs the macOS/Windows legs only on merge_group/schedule/workflow_dispatch, and the Integration Tests (CLI, No Sandbox) check is merge-group-only (it shows as skipped on every PR). The in-progress Test (ubuntu-latest) job is the load-bearing one here — the root test:ci it runs includes test:scripts, which executes the new e2e-workflow.test.js witness block.

One honest limit: the retry shape is pinned by the witness tests and the exit-status semantics are statically verifiable, but the live absorption effect only manifests the next time a real transient shard death hits the post-merge lane. That is environmental by definition for any retry mitigation — same profile as the already-merged #10355 build retry — and no sandboxed lane (/verify, /tmux) can exercise an Actions workflow, so there is nothing further to trigger here.

Real-scenario testing: N/A — CI-harness change with nothing user-visible, and this is an unattended CI run.

中文说明

代码审查

在上文记录的提交上审阅了 diff,无阻塞问题。

  • 重试语义在 Actions 默认 shell(bash -eo pipefail)下正确。 首个 run_shard 位于 || 左侧,首次失败不会触发 set -e;代码块的最后一条命令是第二次尝试,其退出状态即步骤的状态——确定性失败会在两次尝试中都失败、任务保持红色,与意图完全一致。两次尝试共享步骤的 TMPDIR 设置与清理 trap。
  • 范围正确。 重试管住的是实际一直在死亡的命令(运行 33293739505、33302550436、33317457036——三台不同主机上相同的支路与步骤);docker 支路未动,见证测试通过计数 QWEN_SANDBOX=docker vitest run 出现次数来固定这一点。只重试一次、不是开放循环:最坏情况是在任务 60 分钟预算内多一次约 15–20 分钟的尝试。
  • 遵循仓库惯例。 内联函数重试是同一文件中沙箱镜像构建重试先例(Main CI failed: E2E Tests on bc6f1a015cfb #10355)的更精简变体;用复制第二个步骤的方式复用该模式会连带复制整个 env 块与 TMPDIR 设置、产生更多 YAML,而第三方重试 action 会给特权 CI 引入外部依赖。见证块符合 e2e-workflow.test.js 的既有惯例(相同的步骤查找方式、相同的运行 ID 注释风格、抗变异断言),并已对照 diff 核实固定计数:run_shard 恰好出现 3 次。
  • diff 无无关改动;新增注释用运行 ID 记录了理由,这正是能为后续维护者省力的部分。

CI 测试证据

来自该提交上 PR 自身的 CI(经 API 获取;此处未执行任何 PR 派生代码)。表中三个跳过项均为设计使然、与本 PR 无关:ci.yml 仅在 merge_group/schedule/workflow_dispatch 上运行 macOS/Windows 支路,Integration Tests (CLI, No Sandbox) 仅限合并队列(在每个 PR 上都显示跳过)。进行中的 Test (ubuntu-latest) 任务是此处的关键——它运行的根级 test:ci 包含 test:scripts,会执行新增的 e2e-workflow.test.js 见证块。

诚实的边界:重试形态由见证测试固定、退出状态语义可静态验证,但实际吸收效果要等下一次真实的瞬时死亡发生在合并后通道上才会显现。对任何重试缓解而言这都是环境使然——与已合并的 #10355 构建重试同一形态——且没有任何沙箱通道(/verify/tmux)能执行 Actions workflow,因此此处无可触发的进一步验证。

真实场景测试:N/A——CI 基础设施变更、无用户可见行为,且本次为无人值守 CI 运行。

Qwen Code · qwen3.8-max

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

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Confidence: 5/5 — minimal, well-evidenced mitigation that matches the repo's existing retry precedent; the only open item is the CI run still in flight.

My independent proposal for this problem — wrap the failing shard command and retry it once, warn between attempts, let the final attempt's exit status decide, pin the shape with witness tests, leave the docker leg alone — is essentially what this PR does, and I did not find a simpler path it missed. A step-level retry in the #10355 style would have duplicated the whole env/TMPDIR block for more YAML, and a marketplace retry action would trade an in-file pattern for an external dependency in privileged CI.

On the bigger picture: this is the twentieth "Main CI failed" open issue of this shape in ~36 hours, three consecutive main runs died at the same leg and step on three different hosts, and the shard passes on re-run — the problem is real and bleeding maintainer attention, and a bounded retry is the same mitigation already accepted for the sandbox build in this exact file. Nothing in the diff weakens a real failure signal: two attempts both fail → the job stays red. Every line in the diff serves the stated goal; the comments carry the run IDs a future maintainer will want.

Approval is deferred only because Test (ubuntu-latest, Node 22.x) is still running on the reviewed commit — that job executes the new witness tests, so it is the remaining evidence. Approval deferred until CI lands green on d8e833b1aa45281dae14193cb1f0304f9ecec15a.

中文说明

我针对此问题的独立方案——包住失败的分片命令并重试一次、两次尝试之间告警、以最后一次尝试的退出状态为准、用见证测试固定形态、不动 docker 支路——与本 PR 基本一致,且我没有找到它遗漏的更简路径。采用 #10355 的步骤级重试会把整个 env/TMPDIR 块复制一遍、产生更多 YAML;用市场重试 action 则是以特权 CI 中的外部依赖换掉文件内模式。

全局来看:约 36 小时内已有第 20 个同形态的 "Main CI failed" 开放 issue,连续三次 main 运行在三台不同主机上死于同一支路与步骤,而该分片重跑即通过——问题真实存在且持续消耗维护者精力,有界重试正是同一文件中沙箱构建已被接受的缓解形态。diff 没有削弱任何真实失败信号:两次尝试都失败时任务依旧红色。每一行改动都服务于既定目标;注释中保留了未来维护者需要的运行 ID。

暂缓批准仅因 Test (ubuntu-latest, Node 22.x) 仍在该提交上运行——该任务会执行新增的见证测试,是剩余的最后一项证据。待 CI 在该提交上全绿后批准。

Qwen Code · qwen3.8-max

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

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not explored to full depth (tool budget reached): "agent 3c": executing scripts/tests/e2e-workflow.test.js — the review worktree has no node_modules (no vitest, no yaml package) and a full install exceeds the tool bu….

Not linted (tool limitation, not a blocker): the executable-script lint — .github/workflows/e2e.yml: actionlint embedded-shell source mapping is not yet supported — not linted.

中文说明

未探索到全部深度(达到工具调用预算):"agent 3c"executing scripts/tests/e2e-workflow.test.js — the review worktree has no node_modules (no vitest, no yaml package) and a full install exceeds the tool bu…

未检查(工具限制,非阻断):the executable-script lint — .github/workflows/e2e.yml: actionlint embedded-shell source mapping is not yet supported — not linted。

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

Comment thread .github/workflows/e2e.yml
Comment thread scripts/tests/e2e-workflow.test.js
Comment thread scripts/tests/e2e-workflow.test.js
Comment thread scripts/tests/e2e-workflow.test.js Outdated
Comment thread scripts/tests/e2e-workflow.test.js
Comment thread scripts/tests/e2e-workflow.test.js
@qwen-code-dev-bot

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

Copy link
Copy Markdown
Collaborator Author

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

中文说明

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

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

Copy link
Copy Markdown
Collaborator Author

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

Review round summary — PR #10572 (issue #10566)

All 6 findings from the automated review (1 Critical, 5 Suggestions) are resolved in code in commit 4140912b34. No conflict was reported (--conflict false), and no merge was performed.

The review body [rv:5061654349] carries no actionable finding of its own — it reports the reviewer's own tool-budget exhaustion (it could not execute scripts/tests/e2e-workflow.test.js in a worktree without node_modules) and an actionlint embedded-shell lint it could not run. Its actionable content is the six inline findings below, all addressed.

Findings and dispositions

R1-1 [Critical] — unconditional retry can outrun timeout-minutes: 60 — RESOLVED (rc:3890261023)

The claim checks out against the workflow: timeout-minutes: 60 is job-wide, and the retry re-ran the whole shard unconditionally — in two of the three motivating runs (33293739505, 33302550436) the shard died with ~10–12 job minutes left, less than one shard-time (17–21 min measured), so the retry could only be cancelled mid-flight by the job timeout.

Fix, following the finding's job-elapsed option (the step-level variant cannot work: setup spend varies ~10–29 min, so no static step-level threshold separates the three runs):

  • A new first step Record job start epoch exports E2E_JOB_START_EPOCH=$(date +%s) to GITHUB_ENV, so the gate sees the whole job's spend, not just the test step's.
  • Inside the || { … } group, the retry is now behind elapsed=$(( $(date +%s) - ${E2E_JOB_START_EPOCH:-0} )) / if (( elapsed > 2100 )): past 2100s of job time (3600s budget minus a 25-minute reserve — worst measured shard ~21min plus slack), the step fails fast with an ::error:: annotation instead of starting a retry that cannot finish. With the epoch missing the arithmetic fails closed (elapsed ≈ now > 2100 → no retry).
  • timeout-minutes: 60 was NOT raised, per the finding's constraint.

Checked against all three motivating runs: 33302550436 (retry would start at job minute ~49.7) → denied; 33293739505 (~47.8) → denied; 33317457036 (~19.5) → allowed, and 19.5 + 25 reserve ≤ 60 fits.

R1-2 [Suggestion] — retry not pinned as conditional (end-anchor) — RESOLVED (rc:3890261025)

Added the end-anchored assertion expect(runStep.run).toMatch(/run_shard\s*\n\s*\}\s*\n\s*fi\s*$/) in retries the sandbox:none shard exactly once, pinning the retried run_shard as the group's last command and the group as the script's last statement. The pattern tolerates the literal } inside ${RUNNER_NAME:-this runner} because it is anchored to the trailing run_shard, not scope-matched. Probe P3 (retry moved outside the group) turns it red.

R1-3 [Suggestion] — nothing pins that a failed second attempt fails the step — RESOLVED (rc:3890261026)

Added a bash-execution witness in a new file scripts/tests/e2e-shard-retry.test.js: it extracts runStep.run, substitutes ${{ matrix.sandbox }}sandbox:none and ${{ matrix.shard }}1/3, stubs npm with a controllable per-call exit code, and runs the script under bash -e -o pipefail exactly as the runner does. It asserts (a) a fail-once-then-succeed stub exits 0 with two npm calls, and (b) an always-failing stub exits non-zero with two npm calls. Probe P4 (appending || true to the brace group) turns (b) red. The new file joins the win32 exclude list in scripts/tests/vitest.config.ts alongside the other bash-driven suites, so test_windows and windows-runner-smoke.yml stay green; the pure YAML-parse e2e-workflow.test.js keeps running on Windows.

R1-4 [Suggestion] — retried command's argument list not pinned — RESOLVED (rc:3890261031)

Added retries the full shard command, shard and excludes included, pinning the verbatim invocation including both --exclude arguments and --shard='${{ matrix.shard }}'. Probe P5 (reducing the run_shard() body to the bare script name) turns it red.

R1-5 [Suggestion] — does not retry the docker leg pins count, not structure — RESOLVED (rc:3890261034)

Added the structural assertion expect(runStep.run).not.toMatch(/[A-Za-z_]+\(\)\s*\{[^}]*QWEN_SANDBOX=docker/s) to the existing test, pinning the docker command as outside any function body. It passes against the current unwrapped command; probe P6 (function-wrapped docker command called twice) turns it red.

R1-6 [Suggestion] — nothing blocks continue-on-error: true on Run E2E tests — RESOLVED (rc:3890261039)

Added keeps the run step red when the shard stays red asserting expect(runStep['continue-on-error']).toBeUndefined(), mirroring the sibling block's pin. It targets the run step only — the sandbox-image build step's deliberate continue-on-error: true is untouched. Probe P7 (adding the key) turns it red.

Mutation probes (each applied, suite run red, then restored)

Probe Mutation Tests that went red
P1 Budget gate removed (pre-round unconditional retry restored) fails fast when the remaining job budget cannot fit a retried shard (bash witness), gates the retry on the remaining job budget
P2 Record job start epoch step removed records the job start epoch before the expensive setup steps
P3 Retry run_shard moved after the `
P4 || true appended to the brace group keeps the step red when the shard fails both attempts (bash witness), retries the sandbox:none shard exactly once
P5 Shard/exclude arguments stripped from run_shard() body retries the full shard command, shard and excludes included
P6 Docker command wrapped in a function called twice does not retry the docker leg
P7 continue-on-error: true added to Run E2E tests keeps the run step red when the shard stays red

All seven mutants were caught, and the suite returned to 19/19 green after restoring the fix. P1 also doubles as proof that the Critical's witness tests fail against the pre-round code.

Failed checks noted in the feedback

  • Test (ubuntu-latest Node 22.x): CANCELLED — a cancellation, not a test failure; consistent with concurrency cancelling the previous push's run on this branch. Re-runs with the new push; nothing to fix in code.
  • Post Coverage Comment: FAILURE — downstream of the cancelled Test lane; same treatment.

Verification

  • npm run build — passed (exit 0)
  • npm run typecheck — passed (exit 0). A first attempt exited 2 with TS6305 "output file has not been built" errors while a concurrent npm run build was still writing dist/; re-run sequentially after the build completed and passed cleanly — that failure was a concurrency artifact, not a code defect.
  • npm run lint — passed (exit 0, zero errors/warnings from both eslint invocations)
  • Focused Vitest (touched files): npx vitest run --config ./scripts/tests/vitest.config.ts scripts/tests/e2e-workflow.test.js scripts/tests/e2e-shard-retry.test.js — 19/19 passed (re-run green after the probes and after the stash round-trip)
  • npm run test:scripts (full scripts suite) — 1971/1974 runnable tests pass in the final sequential run; the 3 remaining failures are all outside this PR's footprint and each is evidenced as not caused by this change:
    • no-config-object-create-config.test.js and qwen-autofix-workflow.test.js fail only under full-suite load (30s timeouts on shared-host contention) and pass when run in isolation (isolated re-run: 252/253, the one failure below).
    • verify-capture.test.js > renders 256-colour and truecolor via the default-grey fallback fails deterministically in isolation, and reproduces identically with this change stashed (base tree d8e833b1aa) — a pre-existing librsvg/rendering environment issue on this pool runner, not introduced by this commit.
  • yamllint — the yamllint binary is not installed on this runner and installing it is out of scope for allowed commands; the YAML edits follow the file's existing quoting/indentation conventions and CI's node scripts/lint.js --yamllint step remains the authority.
  • Integration tests after npm run bundle — not applicable: the changed behavior is CI workflow YAML and its script-test witnesses, which are not exercised through the bundled CLI or the integration harness.
中文说明

评审轮次总结 — PR #10572(issue #10566

自动评审的全部 6 条发现(1 条 Critical、5 条 Suggestion)已在提交 4140912b34 中以代码解决。本轮未报告冲突(--conflict false),也未执行任何合并。

评审主体 [rv:5061654349] 本身不含可执行的发现——它报告的是评审者自身的工具预算耗尽(在没有 node_modules 的 worktree 中无法执行 scripts/tests/e2e-workflow.test.js),以及一项无法运行的 actionlint 内嵌 shell 检查。其可执行内容就是下面六条内联发现,均已处理。

发现与处置

R1-1 [Critical] — 无条件重试可能超出 timeout-minutes: 60 — 已解决(rc:3890261023)

该论断与 workflow 相符:timeout-minutes: 60 是整个任务共享的预算,而重试会无条件从零重跑整个分片——在三次动机运行中的两次(33293739505、33302550436)里,分片死亡时任务只剩约 10–12 分钟,不足一个分片时长(实测 17–21 分钟),重试只会在进行中被任务超时取消。

修复采用该发现建议的任务级耗时方案(步骤级方案不可行:安装耗时在约 10–29 分钟之间浮动,没有任何静态的步骤级阈值能区分这三次运行):

  • 新增首个步骤 Record job start epoch,向 GITHUB_ENV 导出 E2E_JOB_START_EPOCH=$(date +%s),使门控能看到整个任务的消耗,而不仅是测试步骤的消耗。
  • || { … } 组内,重试现在位于 elapsed=$(( $(date +%s) - ${E2E_JOB_START_EPOCH:-0} )) / if (( elapsed > 2100 )) 之后:任务耗时超过 2100 秒(3600 秒预算减去 25 分钟预留——实测最差分片约 21 分钟加上余量)时,步骤以 ::error:: 注解快速失败,而不是启动一次不可能完成的重试。若 epoch 缺失,算术上失败即关闭(elapsed ≈ 当前时间戳 > 2100 → 不重试)。
  • 按该发现的约束,未上调 timeout-minutes: 60

对三次动机运行逐一核对:33302550436(重试将在任务第 ~49.7 分钟开始)→ 拒绝;33293739505(~47.8)→ 拒绝;33317457036(~19.5)→ 允许,且 19.5 + 25 分钟预留 ≤ 60,放得下。

R1-2 [Suggestion] — 未固定"重试是有条件的"(末尾锚定)— 已解决(rc:3890261025)

retries the sandbox:none shard exactly once 中加入末尾锚定断言 expect(runStep.run).toMatch(/run_shard\s*\n\s*\}\s*\n\s*fi\s*$/),把被重试的 run_shard 固定为组的最后一条命令、且组是脚本的最后一条语句。该模式能容忍 ${RUNNER_NAME:-this runner} 内的字面 },因为它锚定在末尾的 run_shard 上,而非按作用域匹配。探针 P3(把重试移到组外)使其变红。

R1-3 [Suggestion] — 没有断言固定"第二次尝试失败会让步骤失败" — 已解决(rc:3890261026)

在新文件 scripts/tests/e2e-shard-retry.test.js 中加入 bash 执行见证:提取 runStep.run,把 ${{ matrix.sandbox }} 替换为 sandbox:none${{ matrix.shard }} 替换为 1/3,用可按调用次序控制退出码的桩替换 npm,并在与运行器完全相同的 bash -e -o pipefail 下执行脚本。断言:(a) 失败一次后成功的桩以退出码 0 结束、npm 被调用两次;(b) 总是失败的桩以非零退出、npm 被调用两次。探针 P4(给大括号组追加 || true)使 (b) 变红。新文件已加入 scripts/tests/vitest.config.ts 的 win32 排除列表(与其他 bash 驱动套件并列),test_windowswindows-runner-smoke.yml 通道保持绿色;纯 YAML 解析的 e2e-workflow.test.js 仍继续在 Windows 上运行。

R1-4 [Suggestion] — 未固定被重试命令的参数列表 — 已解决(rc:3890261031)

新增 retries the full shard command, shard and excludes included,逐字固定完整调用,包括两个 --exclude 参数与 --shard='${{ matrix.shard }}'。探针 P5(把 run_shard() 函数体简化为裸脚本名)使其变红。

R1-5 [Suggestion] — does not retry the docker leg 固定的是计数而非结构 — 已解决(rc:3890261034)

在既有测试中补充结构断言 expect(runStep.run).not.toMatch(/[A-Za-z_]+\(\)\s*\{[^}]*QWEN_SANDBOX=docker/s),把 docker 命令固定为不位于任何函数体内。它对当前未包裹的单条命令仍然通过;探针 P6(把 docker 命令包进函数并调用两次)使其变红。

R1-6 [Suggestion] — 没有任何断言阻止给 Run E2E testscontinue-on-error: true — 已解决(rc:3890261039)

新增 keeps the run step red when the shard stays red,断言 expect(runStep['continue-on-error']).toBeUndefined(),与姊妹块的固定方式一致。它只针对 run 步骤——sandbox 镜像构建步骤上有意保留的 continue-on-error: true 不受影响。探针 P7(加上该键)使其变红。

变异探针(逐一应用、跑红后还原)

探针 变异 变红的测试
P1 移除预算门控(恢复轮次前的无条件重试) fails fast when the remaining job budget cannot fit a retried shard(bash 见证)、gates the retry on the remaining job budget
P2 移除 Record job start epoch 步骤 records the job start epoch before the expensive setup steps
P3 把重试的 run_shard 移到 || { … } 组之后 retries the sandbox:none shard exactly once(末尾锚定)、gates the retry on the remaining job budget
P4 给大括号组追加 || true keeps the step red when the shard fails both attempts(bash 见证)、retries the sandbox:none shard exactly once
P5 run_shard() 函数体中移除分片/排除参数 retries the full shard command, shard and excludes included
P6 把 docker 命令包进函数并调用两次 does not retry the docker leg
P7 Run E2E testscontinue-on-error: true keeps the run step red when the shard stays red

七个变异体全部被捕获;还原修复后套件恢复 19/19 全绿。P1 同时证明 Critical 的见证测试在轮次前代码上会失败。

反馈中提到的失败检查

  • Test (ubuntu-latest Node 22.x): CANCELLED — 这是取消而非测试失败;与本分支上新一次推送触发的并发取消一致。新推送后会重新运行,代码层面无需修复。
  • Post Coverage Comment: FAILURE — 是被取消的 Test 通道的下游结果,同样处理。

验证

  • npm run build — 通过(退出码 0)
  • npm run typecheck — 通过(退出码 0)。首次尝试以退出码 2 失败,报 TS6305 "output file has not been built" 错误,原因是并发的 npm run build 当时仍在写 dist/;在构建完成后按顺序重跑即干净通过——该失败是并发伪影,不是代码缺陷。
  • npm run lint — 通过(退出码 0,两次 eslint 调用均零错误零警告)
  • 聚焦 Vitest(触及文件):npx vitest run --config ./scripts/tests/vitest.config.ts scripts/tests/e2e-workflow.test.js scripts/tests/e2e-shard-retry.test.js — 19/19 通过(探针之后与 stash 往返之后均复跑为绿)
  • npm run test:scripts(完整脚本套件)— 最终按序运行 1971/1974 个可运行测试通过;其余 3 个失败都在本 PR 足迹之外,且各有证据证明与本变更无关:
    • no-config-object-create-config.test.jsqwen-autofix-workflow.test.js 只在完整套件负载下失败(共享主机争用下的 30 秒超时),单独运行时通过(隔离复跑:252/253,唯一失败见下条)。
    • verify-capture.test.js > renders 256-colour and truecolor via the default-grey fallback 在隔离运行时确定性失败,且在 stash 掉本变更后(基线树 d8e833b1aa)同样复现——是该池运行器上既有的 librsvg/渲染环境问题,非本提交引入。
  • yamllint — 本运行器未安装 yamllint 二进制,安装不属于允许的命令范围;YAML 改动遵循该文件既有的引号/缩进惯例,以 CI 的 node scripts/lint.js --yamllint 步骤为准。
  • npm run bundle 后的集成测试 — 不适用:本变更的行为是 CI workflow YAML 及其脚本测试见证,不经过打包后的 CLI 或集成测试框架执行。

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

🧵 Resolved all 6 selected review thread(s). · 已关闭全部选中的 6 条评审线程。

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


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

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed. Suggestions are inline.

Not explored to full depth (tool budget reached): "agent 3b": could not execute scripts/tests/e2e-shard-retry.test.js / scripts/tests/e2e-workflow.test.js — no node_modules exists in the review worktree or the main c….

Not linted (tool limitation, not a blocker): the executable-script lint — .github/workflows/e2e.yml: actionlint embedded-shell source mapping is not yet supported — not linted.

中文说明

已审查。 建议见行内评论。

未探索到全部深度(达到工具调用预算):"agent 3b"could not execute scripts/tests/e2e-shard-retry.test.js / scripts/tests/e2e-workflow.test.js — no node_modules exists in the review worktree or the main c…

未检查(工具限制,非阻断):the executable-script lint — .github/workflows/e2e.yml: actionlint embedded-shell source mapping is not yet supported — not linted。

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

Comment thread .github/workflows/e2e.yml
Comment thread .github/workflows/e2e.yml
Comment thread scripts/tests/e2e-shard-retry.test.js Outdated
Comment thread scripts/tests/e2e-workflow.test.js
…10566)

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

Copy link
Copy Markdown
Collaborator Author

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

Autofix review round — PR #10572 (issue #10566)

Round outcome: all 4 inline findings addressed in code (commit fb7c9c9afc). No conflict (--conflict false; no merge performed).

Feedback points and decisions

  1. [rc:3890694365] R2-1 — budget-gate arithmetic coupling unwitnessedResolved.

    • Added expect(yml.jobs['e2e-test-linux']['timeout-minutes']).toBe(60) as a new pin in the sandbox:none shard retry YAML-parse suite — the 2100s threshold is derived from this 60-minute timeout (3600s − 25min reserve), so the coupling is now pinned.
    • Added the two exact boundary execution cases to e2e-shard-retry.test.js: elapsed 2100s → retry admitted (npmCalls 2, ::warning::, exit 0), and elapsed 2101s → retry refused (npmCalls 1, ::error::, non-zero exit).
    • One deliberate deviation from the suggested patch: the boundary cases pin date in the harness (same mechanism as the existing npm stub). With a real clock, elapsed only ever grows between the test process's Date.now() and the script's date +%s, which would race the 2100s threshold by up to a second on a loaded runner — a flake source in a PR whose purpose is removing flakes. With the clock pinned, elapsed is exact in every case.
  2. [rc:3890694367] R2-2 — epoch-step unconditionality unwitnessedResolved. Added expect(epochStep.if).toBeUndefined() to the existing 'records the job start epoch...' test: any if: added to the step now turns this red, so the gate's ${E2E_JOB_START_EPOCH:-0} fallback cannot silently disable the retry on one leg.

  3. [rc:3890694368] R1-3 — harness shell flags stricter than the runnerResolved. Verified the claim first: e2e.yml has no defaults: block and the 'Run E2E tests' step has no shell: override, so the runner executes it under GitHub's default Linux step shell bash -e {0} — no pipefail (only explicit shell: bash composite steps get -o pipefail). Took the fidelity fix over the comment-only fix: the harness now runs bash -e scriptFile, exactly matching the runner, so a future pipeline added to the step cannot diverge harness semantics from production. The header comment now states the actual default shell.

  4. [rc:3890694371] R2-3 — job-level continue-on-error unwitnessedResolved. Added expect(yml.jobs['e2e-test-linux']['continue-on-error']).toBeUndefined() alongside the existing step-level pin in 'keeps the run step red when the shard stays red'. Scoped to e2e-test-linux only — isolated-nightly's deliberate job-level key stays untouched.

Mutation probes (every new witness verified against its own mutant)

Mutant Witness that went red
timeout-minutes: 6045 pins the job timeout the budget-gate arithmetic is built on
timeout-minutes: 6090 pins the job timeout the budget-gate arithmetic is built on
elapsed > 2100elapsed > 2900 refuses a retry one second past the 2100s budget-gate threshold
elapsed > 2100elapsed > 2099 retries at exactly the 2100s budget-gate threshold
if: (sandbox:docker) added to the epoch step records the job start epoch before the expensive setup steps
job-level continue-on-error: true on e2e-test-linux keeps the run step red when the shard stays red

Each probe failed exactly one test — the intended witness — with the rest of the suite green; .github/workflows/e2e.yml was restored to pristine after every probe (final git status clean, no workflow diff committed).

Failed checks triage

  • Integration Tests (no-AK No Sandbox): FAILURE — not reproducible locally. Reproduced the gate's exact steps (npm run typecheck:integration + npm run test:integration:no-ak:sandbox:none) under the CI empty-credential environment after npm run build: 13 files / 152 tests all green, exit 0. (A first local attempt failed only because this checkout lacked packages/sdk-typescript/dist; npm run build fixed that — CI's npm ci builds via prepare, so this mode cannot explain the CI failure.) This PR touches no file the gate exercises, and the job runs on the same contended ECS pool this PR exists to stabilize; the evidence points to a transient/environment failure of the same class this change absorbs in e2e.yml. The workflow's independent CI remains the final verification gate.
  • Test / web-shell E2E Smoke: CANCELLED; Post Coverage Comment: FAILURE — cancelled/downstream statuses with no failure content to act on; no code path connects this round's diff (two test files) to those jobs.

Verification

  • npm run build — passed
  • npm run typecheck — passed
  • npm run lint — passed
  • npx vitest run --config ./scripts/tests/vitest.config.ts scripts/tests/e2e-shard-retry.test.js scripts/tests/e2e-workflow.test.js — 2 files / 22 tests passed (re-run post-commit)
  • npm run typecheck:integration — passed (failed-check triage)
  • npm run test:integration:no-ak:sandbox:none under a CI-equivalent empty-credential env — 13 files / 152 tests passed, exit 0 (failed-check triage)
  • Mutation probes — 6 mutants applied to .github/workflows/e2e.yml; each turned exactly its witness red; file restored after each probe
中文说明

Autofix 审查轮次 — PR #10572(issue #10566

本轮结果:4 条行内发现全部在代码中处理完毕(提交 fb7c9c9afc)。无冲突(--conflict false,未执行任何合并)。

反馈点与处理决定

  1. [rc:3890694365] R2-1 — 预算门控的算术耦合无见证已解决。

    • sandbox:none shard retry 的 YAML 解析套件中新增固定断言 expect(yml.jobs['e2e-test-linux']['timeout-minutes']).toBe(60)——2100s 阈值正是由这个 60 分钟超时推导而来(3600s − 25 分钟预留),二者的耦合从此被固定。
    • e2e-shard-retry.test.js 中加入两个精确边界执行用例:elapsed 2100s → 允许重试(npmCalls 2、::warning::、退出码 0);elapsed 2101s → 拒绝重试(npmCalls 1、::error::、非零退出)。
    • 与建议补丁有一处有意偏差:边界用例在 harness 中固定了 date(与现有 npm 桩相同的机制)。若使用真实时钟,从测试进程的 Date.now() 到脚本内 date +%s 之间 elapsed 只会增大,在高负载运行器上可能与 2100s 阈值相差最多一秒——在一个以消除抖动为目的的 PR 里引入新的抖动源。固定时钟后,所有用例的 elapsed 都是精确值。
  2. [rc:3890694367] R2-2 — epoch 步骤的无条件性无见证已解决。 在现有的 'records the job start epoch...' 测试中加入 expect(epochStep.if).toBeUndefined():今后给该步骤添加任何 if: 都会让此断言变红,门控的 ${E2E_JOB_START_EPOCH:-0} 回退就不可能在某一支路上静默禁用重试。

  3. [rc:3890694368] R1-3 — harness 的 shell 旗标比运行器更严格已解决。 先核实了该主张:e2e.yml 没有 defaults: 块,'Run E2E tests' 步骤也没有 shell: 覆盖,因此运行器实际以 GitHub 的 Linux 默认步骤 shell bash -e {0} 执行——没有 pipefail(只有显式 shell: bash 的复合步骤才有 -o pipefail)。选择了保真修复而非仅改注释:harness 现在以 bash -e scriptFile 执行,与运行器完全一致,未来给步骤加入管道时,harness 语义不会与生产环境背离。文件头注释已改为陈述真实的默认 shell。

  4. [rc:3890694371] R2-3 — 任务级 continue-on-error 无见证已解决。 在 'keeps the run step red when the shard stays red' 测试中,于现有步骤级固定之外补加 expect(yml.jobs['e2e-test-linux']['continue-on-error']).toBeUndefined()。只断言 e2e-test-linux——isolated-nightly 有意携带的任务级键不受影响。

变异探测(每个新见证都针对其自身变异体验证)

变异体 变红的见证
timeout-minutes: 6045 pins the job timeout the budget-gate arithmetic is built on
timeout-minutes: 6090 pins the job timeout the budget-gate arithmetic is built on
elapsed > 2100elapsed > 2900 refuses a retry one second past the 2100s budget-gate threshold
elapsed > 2100elapsed > 2099 retries at exactly the 2100s budget-gate threshold
给 epoch 步骤加 if:(sandbox:docker) records the job start epoch before the expensive setup steps
给 e2e-test-linux 加任务级 continue-on-error: true keeps the run step red when the shard stays red

每个探测恰好只有一个测试失败——正是预期的见证——套件其余部分保持绿色;每次探测后 .github/workflows/e2e.yml 均恢复原状(最终 git status 干净,未提交任何 workflow 差异)。

失败检查的分诊

  • Integration Tests (no-AK No Sandbox): FAILURE — 本地无法复现。在 npm run build 之后,按 CI 的空凭据环境精确复现了该门控的两个步骤(npm run typecheck:integration + npm run test:integration:no-ak:sandbox:none):13 个文件 / 152 个测试全部通过,退出码 0。(第一次本地尝试失败仅因本检出缺少 packages/sdk-typescript/distnpm run build 后即解决——CI 的 npm ci 会通过 prepare 构建,因此该模式不可能解释 CI 的失败。)本 PR 未触碰该门控执行的任何文件,而该任务恰好运行在本 PR 所要稳定的同一套存在竞争的 ECS 池上;证据指向与本变更在 e2e.yml 中所吸收的同类瞬时/环境失败。工作流的独立 CI 仍是最终验证门。
  • Test / web-shell E2E Smoke: CANCELLED;Post Coverage Comment: FAILURE — 属于被取消/下游状态,没有可据以行动的失败内容;本轮差异(两个测试文件)与这些任务之间不存在任何代码路径上的关联。

验证(Verification)

  • npm run build — 通过
  • npm run typecheck — 通过
  • npm run lint — 通过
  • npx vitest run --config ./scripts/tests/vitest.config.ts scripts/tests/e2e-shard-retry.test.js scripts/tests/e2e-workflow.test.js — 2 个文件 / 22 个测试通过(提交后复跑)
  • npm run typecheck:integration — 通过(失败检查分诊)
  • npm run test:integration:no-ak:sandbox:none,CI 等价空凭据环境 — 13 个文件 / 152 个测试通过,退出码 0(失败检查分诊)
  • 变异探测 — 对 .github/workflows/e2e.yml 施加 6 个变异体,每个恰好使其对应见证变红;每次探测后恢复原文件

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

🧵 Resolved all 4 selected review thread(s). · 已关闭全部选中的 4 条评审线程。

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


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

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Partially reviewed — gaps disclosed. Suggestions are inline.

Not reviewed: reverse audit — stopped before round 1 by the review time budget.

Deferred under the convergence posture (round 3, not a blocker) — recorded, not requested in this round:

  • scripts/tests/e2e-shard-retry.test.js:94 — [probe] No execution witness for the green first-attempt path — an unconditional pre-gate side effect (e.g. a spurious ::warning:: before run_shard || { ) ships with every witness green
  • scripts/tests/e2e-workflow.test.js:180 — [probe] Docker-leg anti-retry pin [^}]* cannot see through an inner } — a function-wrapped docker double invocation (folding the e2e.yml:85 preflight into it) ships with every witness green

Convergence: round 3 posted 1 inline comment(s), 1 of them reported for the first time; the previous round posted 4 (4 new). Findings keep coming back to the same files: scripts/tests/e2e-shard-retry.test.js (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 轮。

收敛姿态下延后(第 3 轮,非阻断)——已记录,本轮不要求修改:共 2 条(原文未翻译,列表见上方英文部分)。

收敛情况:第 3 轮发布了 1 条行内评论,其中 1 条是首次提出;上一轮发布了 4 条(其中 4 条首次提出)。发现反复回到同一批文件:scripts/tests/e2e-shard-retry.test.js(第 1 轮已出过发现,本轮又有 1 条)。一个不断再生兄弟发现的簇,通常意味着逐条修复只在处理同一根因的实例——先定位并处理该根因,或把独立的簇拆成单独的 PR,通常比逐条修复更快结束循环。(仅为观察——本轮评审未因此扣留任何内容。)

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

import { parse } from 'yaml';

// Executes the e2e workflow's 'Run E2E tests' script under GitHub Actions'
// default Linux step shell — `bash -e {0}`: the step has no `shell:` override

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] R1-3: (fix-induced) The round-2 fix for R1-3 moved this harness to bash -e and documented the shell-fidelity premise in the header comment — the step has no shell: override and e2e.yml no defaults: block — but that premise is load-bearing and pinned by no assertion, while the sibling shape suite pins strictly weaker premises (the epoch step's if:, continue-on-error at both levels, timeout-minutes: 60). If a later change adds defaults: run: shell: bash — explicit bash expands to bash --noprofile --norc -e -o pipefail {0}; .github/workflows/qwen-autofix.yml:71-73 does exactly this — or a step-level shell: override to e2e.yml, the production 'Run E2E tests' block switches shell semantics while this harness keeps executing bare bash -e, so every execution witness stays green while silently blessing a shell contract the lane no longer runs. Since e2e.yml is post-merge-only, this unit suite is the only pre-merge signal for the step. The divergence is latent today — the step script contains no pipelines, so both shell modes agree — but once a pipeline exists, or the shell becomes sh (where [[ ]] is a syntax error), the lane changes semantics while the suite reports green.

Witness:

MUTATED (shell: bash added to the step), no pin: Tests 22 passed (22)
MUTATED + pin: × keeps the default step shell the execution harness assumes → expected 'bash' to be undefined
CLEAN + pin: Tests 23 passed (23)

Fix — pin the premise in the sandbox:none shard retry describe of scripts/tests/e2e-workflow.test.js:

expect(runStep.shell).toBeUndefined();
expect(yml.defaults).toBeUndefined();

The pin must assert absence only and never sweep jobs that deliberately set a shell — shell: bash expands to bash --noprofile --norc -eo pipefail, as pinned at scripts/tests/qwen-autofix-workflow.test.js:5139-5143 and exercised by .github/workflows/qwen-autofix.yml:71-73. Please also add the mutation proof: introducing shell: bash or defaults: run: shell: bash into e2e.yml must turn the new assertion red while the rest of the suite stays green.

中文说明

[Suggestion] R1-3:(fix-induced,由上一轮 R1-3 的修复引入)上一轮对 R1-3 的修复把本 harness 改为以 bash -e 执行,并在文件头注释中记录了 shell 保真前提——该步骤没有 shell: 覆盖、e2e.yml 也没有 defaults: 块——但这个前提是承重的,却没有任何断言固定它;而姊妹形态测试套件连更弱的前提都固定了(epoch 步骤的 if:、步骤级与任务级的 continue-on-errortimeout-minutes: 60)。若未来有人给 e2e.yml 加上 defaults: run: shell: bash(显式 bash 会展开为 bash --noprofile --norc -e -o pipefail {0}.github/workflows/qwen-autofix.yml:71-73 正是这样做的)或步骤级 shell: 覆盖,生产环境的 'Run E2E tests' 脚本就切换了 shell 语义,而本 harness 仍以裸 bash -e 执行——所有执行见证依旧全绿,却悄悄为一个通道已不再运行的 shell 契约作证。由于 e2e.yml 只在合并后运行,这套单元测试是该步骤唯一的合并前信号。目前该分歧是潜在的——步骤脚本里没有任何管道,两种 shell 模式行为一致——但一旦出现管道,或 shell 变成 sh[[ ]] 在 sh 下是语法错误),通道语义已变而套件依旧报绿。

见证:变异(给步骤加 shell: bash)且无固定时 22/22 全绿;加上固定后该断言变红(expected 'bash' to be undefined);干净代码加固定 23/23 全绿。

修复——在 scripts/tests/e2e-workflow.test.jssandbox:none shard retry 块中加入 expect(runStep.shell).toBeUndefined(); expect(yml.defaults).toBeUndefined();。该固定只应断言缺席,绝不能横扫有意设置 shell 的任务(见 scripts/tests/qwen-autofix-workflow.test.js:5139-5143 与 .github/workflows/qwen-autofix.yml:71-73)。请同时补上变异证明:给 e2e.yml 引入 shell: bashdefaults: run: shell: bash 时新断言必须变红,其余套件保持绿色。

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

review/self-reported The linked issue was opened by the PR author (self-reported)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Main CI failed: E2E Tests on 3aa1b1462478

2 participants