perf(ci): shard release quality checks - #10619
Conversation
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
|
Gate re-run (explicit
The PR was merged by yiliang114 at 13:12 UTC (merge commit 中文说明门禁复查(显式
PR 已于 13:12 UTC 由 yiliang114 合入(合并提交 — Qwen Code · qwen3.8-max Reviewed at |
Code Coverage Summary
CLI Package - Full Text ReportCore Package - Full Text ReportFor detailed HTML reports, please see the 'coverage-reports-22.x-ubuntu-latest' artifact from the main CI run. |
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
🖼️ web-shell visual previewRendered against a mock daemon (no real backend): the PR base vs this PR head Screenshots · before / after✅ No screenshot changes against the PR base. Full-resolution recordings (.webm) are attached to the workflow run. — Qwen Code · web-shell visuals |
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
|
One merge-blocking gap remains in the sharding semantics: We should keep empty individual shards valid while adding a separate assertion that every workspace with a The rest of the former quality lane is preserved: format, lint, serve fast-path, build, typecheck, workspace tests, and script tests all ran successfully, and the aggregate still fails closed on failure/cancellation before publish. The final dry run executed 2,044 test files / 62,358 tests. It validates the complete Quality Checks path; it does not prove the full Integration tail because that run was cancelled after the quality aggregate passed. Dry-run timing evidenceRun: 33383828492 on commit
The quality critical path was 20m 21s, down from 41m 44s in the Aug 29 monolithic release run (about 51% faster). The overall workflow is marked Workflow DAG — beforeBaseline run: 33222204757 — the monolithic Workflow DAG — afterDry run: 33383828492 — build once, three parallel workspace shards, independent static/typecheck/script lanes, and one fail-closed aggregate. The quality critical path took 20m 21s. |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Not explored to full depth (tool budget reached): "agent 1a": empirical round-trip (full npm run build → pack → unpack → run a workspace test:ci under --shard ) was not executed — a full build plus test suite exceeds ….
Not linted (tool limitation, not a blocker): the executable-script lint — .github/workflows/release.yml: actionlint embedded-shell source mapping is not yet supported — not linted.
中文说明
未探索到全部深度(达到工具调用预算):"agent 1a":empirical round-trip (full npm run build → pack → unpack → run a workspace test:ci under --shard ) was not executed — a full build plus test suite exceeds …。
未检查(工具限制,非阻断):the executable-script lint — .github/workflows/release.yml: actionlint embedded-shell source mapping is not yet supported — not linted。
— qwen3.8-max via Qwen Code /review (v0.22.3)
--passWithNoTests is required so a shard that receives no files can exit 0, but it also lets a workspace that lost every test file pass in all three shards while zero tests execute; the monolithic test:release exited 1 in that case and blocked the release. Add a contract test in the test:scripts lane (quality_scripts) that fails when any test:ci workspace discovers no test file, so the release is blocked again. Discovery mirrors vitest's default include, which every workspace's test:ci config uses or narrows, so zero matches here means zero discoverable tests under vitest. Red probe: moving packages/channels/telegram's single test file out of the tree fails the new test; restoring it goes green. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Patrol-Run: qwen-pr-closeout/jmthe3hev7k
The quality aggregate used if: always(), so a cancelled release run re-ran the result loop, read the component 'cancelled' results as not-success, and exited 1 — turning quality.result into 'failure'. That opens notify_failure's pre-existing needs.quality.result == 'failure' gate, filing a "Release Failed" issue labeled autofix/approved and dispatching autofix for a run an operator stopped on purpose. Before this PR the monolithic quality job carried no always(), so cancelled runs left quality.result='cancelled' and stayed silent. Use !cancelled() instead: cancelled runs skip the aggregate (result 'skipped', still non-success, so publish stays fail-closed), while any failed component still runs the loop and fails the gate. Pin the new gate and the notify_failure failure clause in the contract test. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Patrol-Run: qwen-pr-closeout/jmthe3hev7k
This PR introduced &release_test_env for the five quality jobs but left integration_none and integration_docker with literal copies of the same three OPENAI_* variables. Reuse the anchor so the seven release-test jobs share one env definition; parsed output is byte-identical for every job. Also record the PR's final release.yml size (59855 bytes) in .size-baseline: the sharding rewrite grew the file 1977 bytes past the recorded 57878, inside the 4096 allowance but past the ratchet, which exists to be bumped in the same PR as real growth. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Patrol-Run: qwen-pr-closeout/jmthe3hev7k
|
@qwen-code /triage |
|
Sandboxed verification: The verification run did not complete, so the phases below may be partial or missing entirely. Advisory evidence for human reviewers — not a review, an approval, or a CI check. Scripted assertions: 1309 passed · 0 failed · 1309 total Flakiness gate: ✅ 4 changed test file(s) x 5 identical rounds, no divergence 中文 — 判定:
|
| arm | what ran | oracle | result |
|---|---|---|---|
| head aggregate gate script (extracted verbatim from parsed YAML) | all 4^5=1024 component-result states, each executed under bash --noprofile --norc -eo pipefail |
exit 0 iff all five results are success |
1024/1024: only the all-success cell exits 0; a skipped-component cell prints ::error::A release quality check did not succeed. and exits 1 — 01-aggregate-gate-1024-cells.png |
| base graph (monolith) | 27 scenarios under a documented GitHub result-semantics model | publish gate invariant; notify fires exactly on watched failures | all hold |
| head graph (DAG) | same 27 scenarios incl. shard-only states (matrix leg failure, build failure → downstream skip, prepare failure, run cancellation) | same invariants + aggregate derives fail-closed from component results | 107/107 assertions — 02-dag-failclosed-base-vs-head.png |
| coverage parity | base quality steps vs head lanes | every base check appears exactly once; script halves byte-equal; integration env/if unchanged via anchor | 21/21 — 03-coverage-parity.png |
| artifact handoff (mock-free) | pack step verbatim at head → isolated consumer tree (git worktree OUTSIDE the repo, own npm ci lane-verbatim, realpath asserted) → download/unpack verbatim → lane consumers |
lane exit codes | install OK, unpack OK, typecheck lane EXIT=0; script lane + shard lane: see F3/Not covered — 07-artifact-handoff-panel.png |
| contract suite | npx vitest run --config ./scripts/tests/vitest.config.ts scripts/tests/release-workflow.test.js scripts/tests/package-scripts.test.js |
suite green at head | 55/55 passed — 06-contract-suite-green.png |
| mutation matrix | 7 central-hunk mutations + 1 fixture mutation | each killed by its expected test with the intended assertion | 7/7 — 05-mutation-matrix-7-of-7.png |
| M4 behavioral probe | mutated gate (== 'failure') re-executed over 1024 cells |
fail-closed cells flip to silent pass | 243/1024 cells silently pass under the mutation — the shipped != 'success' form is load-bearing and the contract test pins it — 04-m4-mutation-silent-pass.png |
Key scenario rows (simulator, abridged; s/f/c/k = success/failure/cancelled/skipped):
S3 head: quality_build fails -> agg=failure[sfsss] publish not-run notify fires (downstream skips do NOT open the gate)
S5 head: one matrix leg fails -> agg=failure[sssfs] publish not-run notify fires
S10 head: prepare fails -> agg=failure[kkkkk] publish not-run notify fires (aggregate still runs via !cancelled() override)
S2 force_skip_tests=true -> all quality skipped, publish runs (emergency override preserved, base-equal)
S6 run cancelled -> publish cancelled, notify quiet (no spurious "Release Failed" issue)
S8/S9, S7a/S7b -> schedule vs dry-run and version_refusal notify semantics base/head equivalent
The Quality Checks display name and job id (quality) are unchanged,
so required-status-check references and the publish/notify dependency names
survive the split (publish/notify needs and if expressions are
byte-identical base→head, asserted in the parity harness).
Additional verified mechanics: all 22 test:ci workspaces' scripts end in
vitest run, so npm argument forwarding delivers --shard=N/3 --passWithNoTests to vitest in every workspace (22/22 enumerated); 2,049
test files across the 22 workspaces, none empty, 5 workspaces have <3 files —
exactly the shape where --passWithNoTests is required; the size ratchet
passes with the committed baseline byte-exact (59,855 = actual file size);
bash -n over all 55 extracted run blocks is clean; actionlint (repo-pinned,
repo ignore-list) is clean on head and live-proven with a planted invalid
needs; the .size-baseline bump matches the ratchet semantics.
Corrections
None applicable (first round; no prior bot claims to correct).
Findings
F1 (low, non-blocking): design doc describes cancellation semantics the code does not implement
docs/design/2026-08-31-release-quality-sharding.md (added by this PR),
Failure semantics:
The aggregate quality job runs after success, failure, or cancellation, and
fails unless every component succeeded.
The shipped gate is
${{ !cancelled() && github.event.inputs.force_skip_tests != 'true' }}, so
on a cancelled run the aggregate is skipped, not run — its result never
becomes failure (simulator scenario S6; the expression's own semantics are
self-checked in harness 02). The inline code comment documents exactly this
choice (commit f453f629, "keep cancelled runs out of the quality failure
gate"): an operator-cancelled run must not open notify_failure's
"Release Failed" issue. The substantive invariant is unaffected and proven —
publish requires needs.quality.result == 'success', so a cancelled or
skipped aggregate can never publish — but the doc (new in this PR) states a
mechanism the code deliberately avoids.
suggested fix
Reword to: "The aggregate quality job runs after success or failure and fails
unless every component succeeded; on a cancelled run it stays skipped, and
publishing stays closed because publish requires the aggregate result to be
success."
F2 (low, non-blocking): pack step ships 72 node_modules dependency dist dirs in the build artifact (+61 MB)
Pack Build Outputs collects paths with
find packages integrations -type d -name dist -prune -print0, which
descends into per-workspace node_modules. Measured at head:
findas shipped returns 99 dist dirs; 72 of them live inside
node_modules(e.g.packages/sdk-typescript/node_modules/vite-node/dist).- Tarball: 20,483 entries, of which 6,587 (32%) are node_modules content;
tar -tvfsizes them at 61.0 MB of the 417 MB uncompressed payload (14.6%). - Functionally harmless: the consumer runs the identical lockfile install
before unpacking, and a spot-check (cmp) showed the tarball's
vite-node/dist/utils.mjsis byte-identical to the consumer'snpm ci
copy — unpacking just overwrites node_modules with itself. But it bloats
every upload/download ofrelease-quality-build, on each re-run too.
suggested fix (measured)
done < <(find packages integrations -name node_modules -prune -o -type d -name dist -prune -print0)Measured consequences: the pruned find returns exactly the 27 genuine
build-output dirs (every build-producing workspace incl.
packages/web-templates/src/*/dist, packages/channels/plugin-example/dist
and the integration packages), and the excluded 72 paths are byte-identical
to the consumer's own install, so nothing consumed changes. The contract test
shares generated web templates with build consumers still passes under this
change (it pins the packages/web-templates/src/generated entry, untouched).
No test pins the node_modules bloat either way — the fixture that would pin
it: assert tar -tzf of the packed artifact contains no node_modules/ path.
F3 (informational): timing-sensitive tests flake under this box's concurrent load, on head AND base trees alike
Two observations, both adjudicated away from the PR's code:
- web-shell files touched by the PR (15 s boot timeout;
waitForLoadCount
pagination waits). At idle, base passes all 165/165 tests in the two
files (A/A control). The modified boot test's natural duration is ~1.9 s
idle vs the new 15 s cap — web-shell's config sets no global timeout
escalation, so the change strictly relaxes the former 5 s default (8×
margin measured). Under saturation (four overlapping heavy jobs) head
showed the boot test exceeding even 15 s and twoMessageListpagination
timing assertions missing — one of them (restores the scroll position…,
line 2432) is an unmodified adjacent test in the same describe. No
equivalently-saturated base capture exists, so the saturation regime is
reported as environmental; the controlled idle comparison is green. - Workspace-test shard lanes in the consumer trees. Full shard-2/3 runs
timed out 15–23 files (cli/core/acp-bridgei18n,json-string-bytes,
UI component tests, all at 5 s/20 s caps) in every attempt on this box,
including a nominally idle rerun. Adjudication: the failing files pass in
isolated single-file runs in BOTH trees (i18n and json-string-bytes
verified exit 0 in the consumer tree and the main tree), and the same
timeout shapes appear on the main tree under the same concurrency — so
the failures are worker-contention timeouts of a loaded shared runner,
not the artifact handoff or the shard wiring. CI shard lanes run one per
dedicated runner, a lower load per runner than this box carried.
The suite's own config comments already document ECS hosts pausing healthy
tests past default timeouts. The full-lane exit codes are therefore not
counted as assertions about the PR (see Not covered).
Not covered
- Per-commit verification. Checkout is depth 2:
git rev-list HEAD^1..HEAD^2returns 1 commit while the metadata lists 8; the shallow
boundary makes deeper history unreachable (treated as such per the
shallow-boundary rule). Verified the aggregateHEAD^1..HEADdiff only. - Reviewer Test Plan steps 1–4 require dispatching a real release run on
GitHub; this environment has no token. Local contract-level equivalents
were verified instead: one build producer + three shards + fail-closed
aggregate (harnesses 02/03), artifact overwrite + pack/unpack round-trip
(handoff simulation), publish blocked for dry-run-equivalent states
(simulator S2/S9). Step 2's exact shard-vs-monolithic test totals need a
real run pair. - Exact shard file-partition parity:
vitest listhangs silently in
this environment on the jsdom workspaces (probed repeatedly incl.
--json=<file>and closed stdin), so per-shard file sets could not be
enumerated. Vitest's--shardpartition is a property of the tool; the
shard invocation itself was exercised end-to-end through the consumer path. - Full workspace-test shard lane green exit on this box: every full
shard run here carried concurrent heavy workloads (including this
verification's own parallel jobs) and produced contention timeouts that
isolated runs in both trees prove environmental. A clean single-shard
timing belongs to a dedicated runner. - Wall-clock improvement (27:17 → ~20–25 min claim): needs real CI runs;
shared-runner timings are not representative. - Integration lanes (
integration_none,integration_docker): verified
structurally only (anchor/env parity byte-identical to base,bash -n
clean,ifunchanged); not executed (bundle + docker out of scope for
this PR's claims — their test commands are unchanged). audio_capture_prebuilds, publish steps, notify_failure steps:
unchanged hunks (byte-identicalif/needsproven); behavior out of scope.- yamllint: the container refuses
pip3 install --user(permission
denied), so yamllint could not run; actionlint (repo-pinned 1.7.12) +
bash -n+ pinned shellcheck 0.11.0 covered the workflow instead.
Shellcheck over the 55 extracted run blocks reports one error-level
SC2296 on${{ matrix.shard }}and one SC2154 onRUNNER_TEMP— both the
expected GitHub-expression/runner-env false positives (the repo runs
actionlint with-shellcheck=for exactly this reason); remaining hits are
style warnings on the pre-existing canonical wipe script. - DAG replay calibration: first round, no token, no previous report and
no retrievable real run artifact, so the gate simulation is NOT calibrated
against an actual GitHub run log; calibrating would require one real run's
job-result set. - Script-suite natural duration: the full
test:scriptssuite takes5 min; every capture here overlapped other heavy jobs. The lane's
30-minute timeout bounds it in CI. Separately,install-script.test.js
throws in THIS container becausezipis absent whileCI=true— an
environment guard, file untouched by the PR, identical at base.
Methodology
Environment: the CI verify container (64-core shared loaded runner, Node
v22.23.2, RUNNER_TEMP=/__w/_temp), repo pre-installed and built at HEAD.
Harnesses live in tmp/pr10619-verify-20260831-161500/ (01–08 scripts,
*.log raw outputs, extracted run blocks under run-blocks/; consumer trees
under /__w/_temp/qc-verify-10619/).
The aggregate gate script was extracted from the parsed YAML (never retyped)
and executed per state under GitHub's Linux shell contract
(bash --noprofile --norc -eo pipefail). The DAG simulator encodes
documented GitHub semantics — implicit success() gate on needs unless the
if contains a status function; cancelled() = run-level cancellation;
matrix needs.*.result = worst leg (failure > cancelled > skipped >
success) — with self-check controls quoted in its output; it is a model of
GitHub, cross-checked against the shipped gate script's real execution. The
artifact handoff used git worktrees deliberately placed OUTSIDE the repo
(/__w/_temp/qc-verify-10619/{handoff,shard,base}) so no node_modules
walk-up could leak head-tree build outputs into a consumer; each consumer ran
the lane install verbatim (npm ci --ignore-scripts && npm run postinstall && npm run generate), the download/unpack steps verbatim, and the consumer's
node_modules/@​qwen-code/* realpath was asserted to stay inside the consumer
tree. Mutations were applied to the real file with restore-after-run
verified by git diff (empty at every restore); the fake-workspace fixture
(M7) was removed. Gates: bash -n over all 55 extracted run blocks;
repo-pinned actionlint with the repo's own ignore list, live-proven with a
planted invalid needs; pinned shellcheck 0.11.0 over the extracted blocks;
the workflow-size ratchet re-run (59,855 bytes, exact match with the
committed baseline). The two PR-touched web-shell files were A/A'd against
base at idle (base 165/165). Assertion counts include expected-failure cells
as passes (the base arm failing as predicted is the assertion). Full-lane
shard exits under load are excluded from the counts per F3's adjudication and
reported here instead.
Flakiness gate log
rounds=5 files=4 skipped=0
file packages/web-shell/client/components/MessageList.dom.test.tsx: (cd packages/web-shell) npx --no-install vitest run ./client/components/MessageList.dom.test.tsx
file packages/web-shell/client/main-boot.test.tsx: (cd packages/web-shell) npx --no-install vitest run ./client/main-boot.test.tsx
file scripts/tests/package-scripts.test.js: (cd .) npx --no-install vitest run --config ./scripts/tests/vitest.config.ts ./scripts/tests/package-scripts.test.js
file scripts/tests/release-workflow.test.js: (cd .) npx --no-install vitest run --config ./scripts/tests/vitest.config.ts ./scripts/tests/release-workflow.test.js
per-file results (P=pass F=fail I=infra-exit, one letter per run):
packages/web-shell/client/components/MessageList.dom.test.tsx: PPPPP
packages/web-shell/client/main-boot.test.tsx: PPPPP
scripts/tests/package-scripts.test.js: PPPPP
scripts/tests/release-workflow.test.js: PPPPP
verdict: pass
summary: 4 changed test file(s) x 5 identical rounds, no divergence
--- per-invocation detail (full copy in the artifact) ---
round 1 · packages/web-shell/client/components/MessageList.dom.test.tsx: P (exit 0)
round 1 · packages/web-shell/client/main-boot.test.tsx: P (exit 0)
round 1 · scripts/tests/package-scripts.test.js: P (exit 0)
round 1 · scripts/tests/release-workflow.test.js: P (exit 0)
round 2 · packages/web-shell/client/components/MessageList.dom.test.tsx: P (exit 0)
round 2 · packages/web-shell/client/main-boot.test.tsx: P (exit 0)
round 2 · scripts/tests/package-scripts.test.js: P (exit 0)
round 2 · scripts/tests/release-workflow.test.js: P (exit 0)
round 3 · packages/web-shell/client/components/MessageList.dom.test.tsx: P (exit 0)
round 3 · packages/web-shell/client/main-boot.test.tsx: P (exit 0)
round 3 · scripts/tests/package-scripts.test.js: P (exit 0)
round 3 · scripts/tests/release-workflow.test.js: P (exit 0)
round 4 · packages/web-shell/client/components/MessageList.dom.test.tsx: P (exit 0)
round 4 · packages/web-shell/client/main-boot.test.tsx: P (exit 0)
round 4 · scripts/tests/package-scripts.test.js: P (exit 0)
round 4 · scripts/tests/release-workflow.test.js: P (exit 0)
round 5 · packages/web-shell/client/components/MessageList.dom.test.tsx: P (exit 0)
round 5 · packages/web-shell/client/main-boot.test.tsx: P (exit 0)
round 5 · scripts/tests/package-scripts.test.js: P (exit 0)
round 5 · scripts/tests/release-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.
Reviewed. Suggestions are inline.
Not linted (tool limitation, not a blocker): the executable-script lint — .github/workflows/release.yml: actionlint embedded-shell source mapping is not yet supported — not linted.
Deferred under the convergence posture (round 2, not a blocker) — recorded, not requested in this round:
.github/workflows/release.yml:550 — [review] D2-1 aggregate failure annotation does not name the failed component
中文说明
已审查。 建议见行内评论。
未检查(工具限制,非阻断):the executable-script lint — .github/workflows/release.yml: actionlint embedded-shell source mapping is not yet supported — not linted。
收敛姿态下延后(第 2 轮,非阻断)——已记录,本轮不要求修改:共 1 条(原文未翻译,列表见上方英文部分)。
— qwen3.8-max via Qwen Code /review (v0.22.3)
The Pack Build Outputs find matched every directory named dist under packages/integrations, so npm ci's nested dependency dist dirs (72 in the current tree, e.g. packages/cli/node_modules/markdown-it/dist) were tarred into release-quality-build and re-downloaded/extracted by all five consumer jobs. Prune node_modules before matching dist, and pin the prune in the release-workflow contract test. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Patrol-Run: qwen-pr-closeout/jmthmo4kw80
The 'fans workspace tests into three complete Vitest shards' test hand-rolled workspace-glob expansion (plus its own !-exclusion set), duplicating the tested shared helper in scripts/workspaces.js that the zero-test ratchet already uses. Swap in getWorkspacePackageJsonPaths so both contract tests validate one implementation; behavior is unchanged (both implementations enumerate the same 26 workspaces at this ref). Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Patrol-Run: qwen-pr-closeout/jmthmo4kw80
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Reviewed. Suggestions are inline.
2 Suggestion-level finding(s) this review confirmed are already reported on this PR and are not repeated:
- R1-3 aggregate double-enumeration of dependencies — still standing, author declined this round (YAGNI gate, no live divergence at head); already reported (comment 3895786914)
- R2-1 zero-test ratchet default include glob vs narrower workspace includes — still standing, author declined this round (YAGNI; independent 22/22 sweep confirmed no workspace in the gap state); already reported (comment 3896708378)
Not linted (tool limitation, not a blocker): the executable-script lint — .github/workflows/release.yml: actionlint embedded-shell source mapping is not yet supported — not linted.
Deferred under the convergence posture (round 3, not a blocker) — recorded, not requested in this round:
.github/workflows/release.yml:513 — [probe] D3-1 lane substance steps unpinned (static/typecheck/scripts)scripts/tests/release-workflow.test.js:372 — [probe] D3-2 release_sha producer arm unpinned
Convergence: round 3 posted 8 inline comment(s), 2 of them reported for the first time; the previous round posted 10 (1 new). Findings keep coming back to the same files: scripts/tests/release-workflow.test.js (findings in rounds 1, 2; 1 more now); .github/workflows/release.yml (findings in round 1; 1 more now). The rate of new findings is not falling. 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. Batching the remaining fixes and verifying them before the next push, or dropping this PR's reviews to --severity-floor critical, keeps the loop from re-deriving the same set. (Observation only — nothing was withheld from this review because of this observation.)
中文说明
已审查。 建议见行内评论。
本轮确认的 2 条建议级发现已在 PR 上报告过,不再重复发布(列表见上方英文部分)。
未检查(工具限制,非阻断):the executable-script lint — .github/workflows/release.yml: actionlint embedded-shell source mapping is not yet supported — not linted。
收敛姿态下延后(第 3 轮,非阻断)——已记录,本轮不要求修改:共 2 条(原文未翻译,列表见上方英文部分)。
收敛情况:第 3 轮发布了 8 条行内评论,其中 2 条是首次提出;上一轮发布了 10 条(其中 1 条首次提出)。发现反复回到同一批文件:scripts/tests/release-workflow.test.js(第 1、2 轮已出过发现,本轮又有 1 条);.github/workflows/release.yml(第 1 轮已出过发现,本轮又有 1 条)。新发现的产出速度没有下降。一个不断再生兄弟发现的簇,通常意味着逐条修复只在处理同一根因的实例——先定位并处理该根因,或把独立的簇拆成单独的 PR,通常比逐条修复更快结束循环。把剩余修复攒成一批、验证后再推送,或将本 PR 的评审降到 --severity-floor critical,可以避免循环反复推导同一组发现。(仅为观察——本轮评审未因此扣留任何内容。)
— qwen3.8-max via Qwen Code /review (v0.22.3)
…lper The shard-completeness pin and the zero-test ratchet both selected their workspace sets through byte-identical blocks (read root package.json, getWorkspacePackageJsonPaths, filter on test:ci). Extract the selection once at describe scope so both release-gating ratchets keep gating on the same workspace list. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Patrol-Run: qwen-pr-closeout/jmtht3lw98c
test:release:workspaces appends --shard/--passWithNoTests to each workspace's test:ci body, so the flags only reach vitest when the vitest invocation is the last command in the chain; a trailing command would receive them while vitest ran the full suite in all three shards. Assert the last command starts with `vitest run`; commands before it (e.g. sdk-typescript's typecheck:public-surface) stay accepted. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Patrol-Run: qwen-pr-closeout/jmtht3lw98c
…gate R1-17: assert every checkout consumer declares prepare in needs, so the pinned ref expression cannot silently un-resolve to the event ref. R1-4: assert the aggregate's env is the exact five-result mapping and the verify loop references all five *_RESULT vars, so dropping or remapping an entry fails here instead of publishing over a failed component. R1-6: assert each of the five component quality jobs keeps its own force_skip_tests gate, not just the aggregate. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Patrol-Run: qwen-pr-closeout/jmthxdxgy8k
Pack Build Outputs now prints every path it packs and refuses to tar when find contributes nothing beyond the two hardcoded paths. A dropped `-o` would turn the two -prune clauses into one conjunction matching nothing; before this guard that packed an incomplete artifact with an empty log, moving the symptom to downstream consumers as missing-dist errors. Pin the guard and print in the pack-step contract test. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Patrol-Run: qwen-pr-closeout/jmthxdxgy8k
R1-5: the build-once contract was pinned only on the producer side. Assert every consumer job (typecheck, workspace shards, scripts) keeps the quality_build needs edge, downloads release-quality-build via actions/download-artifact to the runner.temp path, and unpacks it with the pinned tar invocation after the download. R1-10: the repo-root dist that Pack Build Outputs ships only exists as a side effect of check:serve-fast-path-bundle (the check runs the esbuild bundle with outdir dist; scripts/build.js never writes it). Document the dependency with a step-level comment and pin the ordering plus the documented contract in the workflow test. R1-11: retention-days: 1 broke "Re-run failed jobs" more than a day later, where the succeeded producer is not re-run and consumers must still download its artifact. Raise retention to three days, update the design doc re-run story, and pin the value next to the other upload contract pins. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Patrol-Run: qwen-pr-closeout/jmthzj3978p
…e-quality-10422 # Conflicts: # .github/workflows/release.yml
|
CI attribution for
Classified NOT PR-caused (runner/infra failure), left to maintainers. A rerun of the failed jobs is already in flight (run 33482709617, attempt 2, started ~08:36Z). |
chiga0
left a comment
There was a problem hiding this comment.
Review — perf(ci): shard release quality checks
Scope: All 8 changed files reviewed. Contract tests read in full. Execution: read-only — no local toolchain.
CI status (must-disclose)
| Check | Status |
|---|---|
| Test (ubuntu-latest, Node 22.x) | in_progress (not yet confirmed) |
| Desktop Shell (ubuntu / windows) | success |
| Integration Tests (no-AK, No Sandbox) | success |
| Dependency CVE audit / Secret scan | success |
| Integration Tests (CLI) / macos / windows tests | SKIPPED |
The ubuntu unit-test job (which runs the contract tests for this workflow change) was still in_progress when this review ran. The CI bot's CHANGES_REQUESTED was filed on the earlier SHA 1ca54b23 before the fix commits; the current head 0a474963 incorporates 11 additional commits addressing those findings.
What I reviewed
DAG structure and fail-closed aggregation
The new DAG: quality_static and quality_build run in parallel after prepare; quality_typecheck, workspace_tests (3 shards), and quality_scripts consume the build artifact; quality (aggregate) gates publish and notify_failure. The aggregate uses if: ${{ !cancelled() && ... }} — correct: cancelled runs leave it skipped (not failed), preventing spurious "Release Failed" issues for operator-stopped runs.
Build artifact handoff (quality_build → consumers)
Pack step correctly prunes node_modules (-type d -name node_modules -prune -o -type d -name dist -prune -print0) and fails closed with [[ ${#build_paths[@]} -gt 2 ]] if the find expression silently matches nothing. retention-days: 3 covers "Re-run failed jobs" more than 24 hours after the original build. overwrite: true makes "Re-run all jobs" safe. ✓
Immutable source pinning (release_sha)
prepare resolves git rev-parse HEAD to one commit SHA. All validation and publish jobs check out via ${{ needs.prepare.outputs.release_sha }}, not the moving branch tip. The test pins validation and publishing to the commit resolved by prepare also verifies the needs edge for every consumer — without the edge, ${{ needs.prepare.outputs.release_sha }} evaluates to '' and the checkout falls back to the event ref. ✓
Shallow history for validation jobs
fetch-depth: 1 for all validation jobs (quality_static, quality_build, quality_typecheck, workspace_tests, quality_scripts, integration_none, integration_docker). Neither the workspace tests nor the integration build needs git history (they build and test one selected ref). prepare and publish keep fetch-depth: 0. The test uses shallow history only for validation jobs pins this split. ✓
--passWithNoTests and the zero-test ratchet
--passWithNoTests is necessary for small shards that receive no files; without it, a shard of an empty workspace would fail. The ratchet test (discovers at least one test file in every test:ci workspace) runs in quality_scripts, gates the release, and fires discovery using vitest's default include glob. R2-1 from the CI bot notes that some workspaces declare narrower configs (e.g. include: ['test/**/*.test.ts']) — the ratchet finds those files via the broader default pattern, so it correctly prevents the "zero tests, silent green" regression in practice even though the glob is not an exact match of each workspace's config. Acceptable residual risk.
force_skip_tests gate integrity
Every component job carries ${{ github.event.inputs.force_skip_tests != 'true' }}. The test keeps every component quality job behind the force_skip_tests gate pins this for all five, not just the aggregate — correct: if only the aggregate were gated, a red lane would still run and block the emergency release override.
Contract tests coverage
New tests pin: workspace cleanup order; shallow vs full history split; immutable SHA + needs edge; artifact overwrite flag; retention-days; download + unpack in every consumer; dist ordering (fast-path check before pack); shard strategy; vitest-last-in-chain per workspace; zero-test ratchet; needs/env/loop mapping for the aggregate; force_skip_tests gate per component; runner routing for the aggregate (hosted, not ECS). Comprehensive.
Cross-check against CI bot CHANGES_REQUESTED (review 5068132647, SHA 1ca54b23)
| Finding | Current-head status |
|---|---|
| R1-1 Critical: --passWithNoTests zero-test regression | Fixed — zero-test ratchet added in test(release): ratchet test-file discovery |
| R1-2: node_modules not pruned in pack find | Fixed — fix(release): prune node_modules when packing build outputs |
| R1-3/R1-4: aggregate double-enum unpinned | Fixed — full env map + loop variable names pinned in test(release): pin needs edge, five-result mapping |
| R1-5: consumer artifact contract unpinned | Fixed — fix(release): pin consumer artifact contract adds download/unpack assertions for all three consumers |
| R1-6: force_skip_tests gate unpinned | Fixed — test(release): pin needs edge, five-result mapping, and per-job skip gate |
| R1-7: vitest not enforced as last command | Fixed — test(release): pin vitest as the last test:ci command in shard lanes |
| R1-8: expandWorkspace re-implements existing helper | Fixed — refactored to use getWorkspacePackageJsonPaths |
| R1-9: integration jobs not using release_test_env anchor | Fixed — refactor(release): reuse release_test_env anchor |
| R1-10: dist coupling undocumented | Fixed — ordering comment + keeps the dist producer ahead of the pack step test |
| R1-11: retention-days: 1 too short | Fixed — retention-days: 3 |
| R1-12: size baseline under-records | Fixed — baseline updated to 59855 |
R1-16: if: always() opens notify_failure on cancel |
Fixed — fix(release): keep cancelled runs out of the quality failure gate |
| R1-17: needs edge not asserted for checkout ref | Fixed — pins validation and publishing test verifies the needs edge per consumer |
| R2-1: ratchet uses default glob vs narrower workspace configs | Remains — see note above; acceptable as residual risk |
All Critical and 13 of 14 Suggestion findings resolved. R2-1 is theoretical.
Verdict
No blockers at current head. All CI bot Critical findings addressed; comprehensive contract test suite. Approving with CI-pending disclosure.
Reviewed with AI assistance.
qwen-code-dev-bot
left a comment
There was a problem hiding this comment.
Reviewed at head 0a474963.
- The only formal review was the round-1 CHANGES_REQUESTED on
1ca54b23, 48 commits behind this head. Walked every finding (1 Critical, 15 Suggestions) through the follow-up commits and the workflow at this head: the empty-shard hole left by--passWithNoTestsis closed by the fail-closedtest:scriptsratchet asserting everytest:ciworkspace discovers at least one test file; the shard-flag pass-through is pinned via the vitest-is-last-command assertion; the aggregate now uses!cancelled()so operator-cancelled runs no longer mint "Release Failed" issues; artifact producer/consumer contracts, retention for re-run recovery, theforce_skip_testsgate parity, and the size-baseline ratchet are each pinned inrelease-workflow.test.js. 0 threads remain unresolved. - The sharding structure itself reads sound: one build artifact consumed by static/typecheck/shard lanes, five results feeding a single fail-closed aggregate before publish.
- No new Critical issues found. CI on this head has no failures (Test and the review pipeline still running); per the channel convention the call is on the review itself.
yiliang114
left a comment
There was a problem hiding this comment.
Review findings (self-PR, cannot self-approve):
- The split is safe for the release contract: publishing still hangs off a single fail-closed
qualityaggregate (checks every component result explicitly,!cancelled()keeps operator-cancelled runs from opening 'Release Failed' issues),force_skip_testsgates every component and the aggregate, andpublish.needsdoes not leak the matrix job. - Pinning validation + publish to
prepare's resolvedrelease_sha(with theneedsedge asserted in tests) removes the moving-branch hazard, and shallow checkouts on validation jobs are fine because prepare/publish keep full history. - Build-once/consume-thrice artifact flow is correctly guarded: retention-days 3 + overwrite:true cover both re-run paths; pack fails closed on silent under-pack; dist-producer ordering is pinned; every consumer is tested to download+unpack after the needs edge.
- Sharding uses vitest-native
--shardthroughtest:release:workspaces, and the ratchet tests close the two new holes sharding opens: flags only reach vitest because every test:ci chain ends with it, and per-workspace test-file discovery blocks a zero-test workspace from passing all shards green. - The web-shell test tweaks (waitForLoadCount, main-boot 15s timeout) are shard-runner timing stabilizations, not behavior changes.
Verified locally on 0a47496: release-workflow.test.js + package-scripts.test.js 60 passed / 1 skipped. No unresolved threads. No blocking issues found.
|
E2E / verification report (recurring review round, independent of ci-bot) — head No merge-blocking (Critical) issue found in review. Verification run locally against the exact head (
Review notes (non-blocking):
Not approving: ci-bot's latest head review is COMMENTED with deferred suggestions, and no maintainer approval is on record yet. E2E / 验证报告(例行巡检,独立于 ci-bot) — head 评审未发现阻塞合并的 Critical 问题。基于精确 head(
评审备注(非阻塞): 暂不 Approve:ci-bot 最新 head 评审为 COMMENTED(仅有延后建议),且尚无 maintainer 批准记录。 |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
3 Suggestion-level finding(s) this review confirmed are already reported on this PR and are not repeated:
- R2-1 zero-test ratchet default-include glob vs narrower workspace includes — confirmed again this round by five lenses; still standing, author declined round 2 (YAGNI; independent 22/22 sweep confirmed no workspace in the gap state); alread…
- D3-1 lane substance steps unpinned (static/typecheck/scripts) — confirmed again this round; already disclosed in the round-3 review body deferral list
- D2-1 aggregate failure annotation does not name the failed component — confirmed again this round; already disclosed in the round-2 review body deferral list
Not linted (tool limitation, not a blocker): the executable-script lint — .github/workflows/release.yml: actionlint embedded-shell source mapping is not yet supported — not linted.
2 Suggestion(s) were drafted inline past the resolved critical posting floor — the floor engaged early: the first-time-finding rate has not fallen for 2 consecutive round(s); the CLI moved them into the deferral list below (floor enforcement).
Deferred under the convergence posture (round 4, not a blocker) — the floor engaged early: the first-time-finding rate has not fallen for 2 consecutive round(s) — recorded, not requested in this round:
scripts/tests/release-workflow.test.js:506 — [review] R4-2: The Pack Build Outputs fail-closed guard this PR adds is pinned only by its condition substrings ( toContain('printf') , toContain('${#build_paths[@]} -gt 2') ); the branch that a….github/workflows/release.yml:419 — [review] R4-3: Pack Build Outputs hardcodes the workspace roots ( find packages integrations ... ), duplicating layout knowledge whose source of truth is the root package.json workspaces , and its fail….github/workflows/.size-baseline:50 — [review] baseline records 59855 release.yml but the shipped file is 61280 bytes — 1425 bytes of growth unrecorded (code-age rule: anchor line unchanged since round 3)docs/design/2026-08-31-release-quality-sharding.md:25 — [review] design doc says the aggregate runs 'after success, failure, or cancellation' — !cancelled() leaves it skipped on cancellation (code-age rule: anchor line unchanged since round…scripts/tests/release-workflow.test.js:439 — [review] artifact identity pinned consumer-side only — producer tar target and upload path unpinned (code-age rule: pin lines unchanged since round 3)
Convergence: round 4 posted 1 inline comment(s), 1 of them reported for the first time; the previous round posted 8 (2 new). Findings keep coming back to the same files: .github/workflows/release.yml (findings in rounds 1, 3; 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.)
中文说明
本轮确认的 3 条建议级发现已在 PR 上报告过,不再重复发布(列表见上方英文部分)。
未检查(工具限制,非阻断):the executable-script lint — .github/workflows/release.yml: actionlint embedded-shell source mapping is not yet supported — not linted。
2 条 Suggestion 在已解析的 critical 发布下限之外被起草为行内评论——发布下限因首次发现速率连续 2 轮未下降而提前生效;CLI 已将其移入下方延后清单(下限强制执行)。
收敛姿态下延后(第 4 轮,非阻断)——发布下限因首次发现速率连续 2 轮未下降而提前生效——已记录,本轮不要求修改:共 5 条(原文未翻译,列表见上方英文部分)。
收敛情况:第 4 轮发布了 1 条行内评论,其中 1 条是首次提出;上一轮发布了 8 条(其中 2 条首次提出)。发现反复回到同一批文件:.github/workflows/release.yml(第 1、3 轮已出过发现,本轮又有 1 条)。一个不断再生兄弟发现的簇,通常意味着逐条修复只在处理同一根因的实例——先定位并处理该根因,或把独立的簇拆成单独的 PR,通常比逐条修复更快结束循环。(仅为观察——本轮评审未因此扣留任何内容。)
— qwen3.8-max via Qwen Code /review (v0.22.3)
The aggregate's !cancelled() gate overrides the implicit needs success() check, so on forks — where prepare is repository-gated and the five component lanes follow it into skipped — the aggregate would still run, see five non-success results, and exit 1. That turns a benign all-skipped fork dispatch into quality=failure and can open notify_failure's "Release Failed" issue. Gate the aggregate on needs.prepare.result == 'success' (prepare is now declared in its needs list). Same-repo releases keep the existing fail-closed aggregation; fork runs stay fully skipped as on main. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Patrol-Run: qwen-pr-closeout/jmtiej7n89k
c42ee9a
qqqys
left a comment
There was a problem hiding this comment.
Review — perf(ci): shard release quality checks (Critical-only)
Reviewed at head c42ee9a6f8f2c54023441ed8d6b663eadfffa5c6.
Historical blocking issues — both confirmed fixed at this head
- R1-1 (round-1 Critical,
--passWithNoTestszero-test hole): closed by thediscovers at least one test file in every test:ci workspaceratchet inscripts/tests/release-workflow.test.js, running in thequality_scriptslane that gates publish; workspace selection reusesgetWorkspacePackageJsonPaths, the same resolverscripts/clean.jsconsumes. - R4-1 (round-4 Critical, fork dispatch turns into a spurious
qualityfailure): the aggregate gate is now!cancelled() && needs.prepare.result == 'success' && github.event.inputs.force_skip_tests != 'true'(release.yml L555-556).preparestays repository-gated (L50), so on a fork the aggregate is skipped instead of failing and openingnotify_failure; on real runs it still fails closed on any non-success component. The exactifstring is pinned by thekeeps publishing behind one fail-closed quality aggregatecontract test.
Critical-only scan of the current diff — no provable Critical found
Checked: fail-closed aggregate result mapping (all five env→loop entries pinned); artifact handoff (node_modules-pruned pack with the [[ ${#build_paths[@]} -gt 2 ]] fail-closed guard, retention-days: 3 + overwrite: true covering both re-run paths, download/unpack asserted in every consumer); immutable release_sha checkout pin with asserted needs edges for all eight consumers; shallow/full history split (validation depth 1, prepare/publish depth 0); shard completeness (every test:ci chain ends in vitest run so --shard/--passWithNoTests reach vitest); per-component + aggregate force_skip_tests gating; cancellation semantics (!cancelled() leaves the aggregate skipped so operator-cancelled runs don't mint "Release Failed" issues); workflow size within the 4096-byte allowance (1738 over the recorded baseline at this head); web-shell test changes are timing stabilizations with assertions unchanged.
Gate not met — CI pending
Test (ubuntu-latest, Node 22.x) is still in_progress on this head (as is the review-pr check); every other completed check is success or an expected skip. A pending required check cannot be treated as healthy, so this is a COMMENT rather than an APPROVE. Once the ubuntu test lane is green on this exact head, the review-side gates above are satisfied.
中文说明
在 head c42ee9a6 上完成复核:两个历史阻塞问题均已确认修复——R1-1(--passWithNoTests 零测试漏洞)由 quality_scripts 门禁中的测试文件发现 ratchet 关闭;R4-1(fork 上聚合任务误失败)由新增的 needs.prepare.result == 'success' 门控修复,且契约测试固定了完整 if 表达式。当前 diff 的 Critical-only 扫描未发现可证明的阻塞问题(fail-closed 聚合、产物交接、不可变 SHA 绑定、分片完整性、跳过门控、取消语义、体积 ratchet 均已核对)。唯一未满足的门禁:Test (ubuntu-latest, Node 22.x) 在该 head 上仍在运行,因此本次为 COMMENT 而非 APPROVE;该检查转绿后评审侧门禁即全部满足。
Maintainer verification — real local round-trip of the sharded release laneVerdict: the central claims hold, and I found nothing merge-blocking. One non-blocking gap in the new zero-test ratchet (F1) and one design-doc/code mismatch (F2) are below. Verified head
That covers the two items earlier rounds recorded as not done: the empirical 1. Build once, pack, and the artifact really is complete
Completeness audit: the build produced 13,350 files; the artifact carries 13,331. The 19 omissions are 17 2. The artifact is load-bearing and sufficient; the depth-1 checkout is safe
Depth A/B on the git-heaviest workspace tests, same commit and same artifact: a full-history worktree (9,198 commits) and the depth-1 checkout both return 3. Three shards are an exact partition of the monolithic runBoth arms ran back to back in the same consumer tree from the real lane commands, and the executed file set was reconstructed from every vitest reporter line: Per workspace as well — cli 966 = 322+323+321, core 631 = 211+210+210, web-shell 240 = 80+80+80. Empty shards are real (audio-capture / telegram / wecom / github / gitlab have fewer than 3 files), which is exactly what Wall clock on this box: monolith 429.2 s → shards 196.3 / 151.1 / 239.6 s, so the critical path is 239.6 s (1.79×) at a cost of 587 s total runner time (+36.8%, from booting 23 vitest projects three times instead of once). Your ECS dry run is a bigger win (27:17 → 9:45, 2.80×); note that vitest balances by file count over a sha1-sorted list, not by duration, so the slowest shard sets the gate (9:45 vs 4:56 there, 239 s vs 151 s here). If the gate ever needs to shrink further, splitting 4. Workflow contract: nothing dropped, one immutable source, gate still fails closed
F1 (non-blocking) — the new zero-test ratchet does not close the general caseThe ratchet you added does catch the hazard you described: remove telegram's only test file and Concretely, with So for those workspaces the pre-PR behaviour (release blocked) still becomes "release proceeds, workspace unvalidated". Ratchet-set == vitest-set on today's tree for every workspace, so nothing is silently unvalidated right now — which is why I am not treating this as blocking. Follow-up worth filing: have the ratchet consult each workspace's configured F2 (nit) — the design doc contradicts the code
Not verified locally
中文版报告维护者验证 —— 在本地对分片后的发布质量车道做了一次真实往返结论:PR 的核心主张全部成立,未发现阻塞合并的问题。 下面有一条非阻塞的缺口(F1,新加的零测试 ratchet)和一条文档与代码不一致(F2)。 验证的 head 是
这正好补上了此前评审轮次明确记为"未做"的两项: 1. 构建一次、打包、产物确实完整
完整性审计:构建实际产出 13,350 个文件,产物携带 13,331 个。缺的 19 个是 17 个 2. 产物确实是关键路径,也确实够用;depth-1 检出是安全的
对最依赖 git 的一组测试做深度 A/B(同一 commit、同一产物):完整历史 worktree(9,198 个提交)与 depth-1 检出的结果完全一致,都是 3. 三个分片是单体运行的精确划分两组 arm 在同一棵 consumer 树上前后脚运行,使用真实的车道命令,执行到的文件集合从每一行 vitest reporter 输出还原: 逐 workspace 也对得上:cli 966 = 322+323+321,core 631 = 211+210+210,web-shell 240 = 80+80+80。空分片是真实存在的(audio-capture / telegram / wecom / github / gitlab 的测试文件少于 3 个),这正是 本机墙钟:单体 429.2 秒 → 分片 196.3 / 151.1 / 239.6 秒,关键路径 239.6 秒(1.79×),代价是总 runner 时间 587 秒(+36.8%,因为 23 个 vitest 工程要启动三遍而不是一遍)。你在 ECS 上的 dry run 收益更大(27:17 → 9:45,2.80×)。需要注意:vitest 是按 文件数 在 sha1 排序后的列表上切分,而不是按耗时,所以最慢的分片决定门禁时间(那边 9:45 对 4:56,这边 239 秒对 151 秒)。如果以后还要继续压缩,拆分 4. 工作流契约:没有检查被丢掉、源码身份唯一、门禁仍然 fail-closed
F1(非阻塞)—— 新加的零测试 ratchet 没有堵住一般情况你加的 ratchet 确实能拦住你描述的那种情况:删掉 telegram 唯一的测试文件, 具体地,把 也就是说,对这些 workspace 而言,PR 之前的行为(阻断发布)仍会变成"发布照常放行、该 workspace 未被验证"。由于当前树上每个 workspace 的 ratchet 集合与 vitest 集合完全相同,眼下不存在未被验证的 workspace,所以我不把它当作阻塞项。建议的后续:让 ratchet 读取各 workspace 配置的 F2(小问题)—— 设计文档与代码不一致
本地未覆盖的部分
|
|
Thanks for the concrete round-trip verification. I agree with both follow-ups.
Nothing in this report remains merge-blocking. The current head stays unchanged; completed checks are green, with the web-shell E2E smoke and automated review still pending. |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Reviewed.
1 Suggestion-level finding(s) this review confirmed are already reported on this PR and are not repeated:
- R2-1 zero-test ratchet glob vs per-workspace vitest includes (both divergence directions) — already reported (comment 3896708378), author declined rounds 2-3
Not linted (tool limitation, not a blocker): the executable-script lint — .github/workflows/release.yml: actionlint embedded-shell source mapping is not yet supported — not linted.
1 Suggestion(s) were drafted inline past the resolved critical posting floor — the floor engaged early: the first-time-finding rate has not fallen for 2 consecutive round(s); the CLI moved them into the deferral list below (floor enforcement).
Deferred under the convergence posture (round 5, not a blocker) — the floor engaged early: the first-time-finding rate has not fallen for 2 consecutive round(s) — recorded, not requested in this round:
scripts/tests/release-workflow.test.js:408 — [review] R5-2: The release_sha source-step pin uses a substring match, so any rev argument prefixed by git rev-parse HEAD (e.g. HEAD~1 , HEAD^ ) satisfies it. If prepare's 'Resolve release …scripts/tests/release-workflow.test.js:608 — [probe] R5-1 aggregate exit-1 enforcement unpinned — inline draft blocked by overlap with comment 3896708311.github/workflows/release.yml:36 — [review] D5-1 force_skip_tests dispatch input description omits the five new component lanes the same gate skips.github/workflows/release.yml:416 — [review] D5-2 root dist bundle packed into release-quality-build but no consumer job reads it
中文说明
已审查。
本轮确认的 1 条建议级发现已在 PR 上报告过,不再重复发布(列表见上方英文部分)。
未检查(工具限制,非阻断):the executable-script lint — .github/workflows/release.yml: actionlint embedded-shell source mapping is not yet supported — not linted。
1 条 Suggestion 在已解析的 critical 发布下限之外被起草为行内评论——发布下限因首次发现速率连续 2 轮未下降而提前生效;CLI 已将其移入下方延后清单(下限强制执行)。
收敛姿态下延后(第 5 轮,非阻断)——发布下限因首次发现速率连续 2 轮未下降而提前生效——已记录,本轮不要求修改:共 4 条(原文未翻译,列表见上方英文部分)。
— qwen3.8-max via Qwen Code /review (v0.22.3)
|
@qwen-code /triage |
|
Sandboxed verification: ❌ not passed — findings reported (agent verdict) - workflow run Ran the PR in an isolated, token-free container: A/B against the base build, mock-free harness assertions, targeted gates. Advisory evidence for human reviewers — not a review, an approval, or a CI check. Scripted assertions: 502 passed · 0 failed · 502 total Flakiness gate: ✅ 4 changed test file(s) x 5 identical rounds, no divergence 中文 — 判定:❌ 不通过 · 报告了发现(agent 判定)沙箱验证在隔离、无凭证的容器中执行了该 PR 的代码(与 base 构建 A/B 对照、无 mock harness 断言、定向门禁)。仅作为评审证据,不构成评审、批准或 CI 检查。 脚本断言:502 通过 · 0 失败 · 502 总计 抖动门:✅ 4 changed test file(s) x 5 identical rounds, no divergence Verification reportPR #10619 Deep Verification (round 2) — perf(ci): shard release quality checksVerdict: findings — executed assertions pass=502 fail=0 total=502. 中文 — 判定:findings · 502/502 断言通过 · 两项遗留发现均不阻塞
Previous-finding status (round 1 → new head
|
| # | finding (round 1) | severity | status at new head |
|---|---|---|---|
| F1 | design doc says the aggregate "runs after success, failure, or cancellation"; the gate skips on cancellation | low | stands — wording unchanged, and now also omits the fork/prepare-absent skip added by c42ee9a6. Re-measured: doc quoted below vs shipped gate quoted below. Not declined by the author; I agree it remains worth fixing (docs-only). |
| F2 | pack step shipped 72 node_modules dist dirs (+61 MB, 32% of entries) | low | fixed — commit 187422f0. Re-measured end-to-end: old find expression still matches 100 dirs/72 node_modules on this tree; shipped find packs 28 dirs, 0 node_modules; tarball 14,174 entries, 0 node_modules entries, 77 MB compressed; unpack round-trip restores dist/, cli/core dist, web-templates generated (02-pack-prune-and-guard.png). |
| F3 | timing-sensitive web-shell tests flake under this box's load (adjudicated environmental) | informational | adjudicated again, no regression — A/A under load 166: head 165/165 (26.8 s), base 165/165 (23.1 s) for the two PR-touched files. The PR's relaxation (boot test 5 s → 15 s cap) holds under load; base passes equivalently. First base attempt failed on a missing nested node_modules in my scratch worktree — my harness artifact, root-caused and fixed, not a PR signal. |
Central claim and A/B
Central claim: the monolithic release quality job is replaced by a
build-once DAG whose final Quality Checks aggregate preserves the stable
publish/notify dependency and fails closed unless every component succeeded.
Round-2 focus: the head commit changed the aggregate gate to
!cancelled() && needs.prepare.result == 'success' && force_skip_tests != 'true'
and added prepare to its needs — fixing the fork-dispatch false positive
while keeping fail-closed aggregation. Secondary: (S1) the pack step no
longer ships node_modules dist dirs and fails closed on silent under-pack;
(S2) the new contract pins are load-bearing (mutation matrix).
Three-graph A/B: base (monolith quality job), mid (round-1 head
gate reconstructed from the previous report + f453f629: !cancelled()
only, needs without prepare), head (final). The aggregate script was
extracted verbatim from the parsed YAML and executed for all 4⁵=1024
component-result combinations; the JS expression evaluator was
cross-checked cell-by-cell against an independent bash transliteration of
the quality gate (16 cells) and the notify gate (64 cells).
| scenario | base | mid (round-1 gate) | head | assertion |
|---|---|---|---|---|
| S1/S2 all-green nightly / dry-run dispatch | publish ✓, quiet | same | same | equivalent |
| S3 static lane fails | q=failure, publish blocked, notify fires | same | same (script exits 1 on the failing cell) | equivalent |
| S5 one matrix leg fails | n/a (no shards) | q=failure, blocked, fires | q=failure, blocked, fires | head-only shape holds |
| S10 build lane fails, consumers skip | n/a | q=failure, blocked, fires | q=failure, blocked, fires | skipped consumers do NOT open the gate |
| S6 operator cancels run | all cancelled, quiet | same | aggregate skipped via !cancelled(), quiet |
no spurious "Release Failed" |
| S7 fork dispatch | all skipped, quiet | q=failure, notify FIRED ← the regression c42ee9a6 fixes |
all skipped, quiet | A/B flip: 9/9 assertions incl. S7.final-commit-flips-notify(fired->quiet) |
| S8 force_skip_tests=true | publish runs, quiet | same | same | emergency override preserved |
| S9 prepare fails | notify fires (prepare watched directly) | fires | notify fires, aggregate skipped by prepare gate | equivalent — prepare failures stay notified |
| S4/S11 integration / publish failure | blocked, fires | same | same | equivalent |
| S12 version_refusal | quiet | quiet | quiet | equivalent |
| S13 dry-run with failure | blocked, quiet | same | same | equivalent |
01-gate-ab-base-mid-head.png — full matrix, notify failed_jobs fragment
executed verbatim (S3 → - quality, S9 → - prepare, S11 → - publish),
and the gate/script cross-checks; 90/90.
Additional re-measured mechanics at the new head: contract suite 259/259
(release-workflow 42, package-scripts 19, workflow-size 198 — incl. the size
ratchet, 04-contract-suite-259.png); mutation matrix 9/9 killed — every
pin added since round 1 (prepare gate, five-result mapping, consumer edges,
retention, prune clause, under-pack guard, vitest-last-command, per-lane
force_skip gate, prepare-in-publish-needs) dies at its intended assertion
under exact-string mutation, with a surviving unpinned-axis control and a
first control attempt (aggregate timeout) that turned out to be pinned too
(03-mutation-matrix-9-of-9.png); 23 test:ci workspaces of 26, every
script ends in vitest run so --shard/--passWithNoTests forwarding holds;
55/55 extracted run blocks pass bash -n; repo-pinned actionlint 1.7.12
clean and live-proven (planted invalid needs → detected with
[job-needs], restored → clean); pinned shellcheck 0.11.0 over the
extracted blocks: only the known false positives (${{ matrix.shard }}
SC2296/SC1083, 55 shebang artifacts of extraction) plus one SC2164 in a
publish step proven byte-identical to base; publish/notify if+needs
byte-identical base→head; parity vs the moved base 45/45
(05-parity-vs-moved-base.png).
Corrections
None — no prior bot claim in this thread was found inaccurate.
Findings
F1 (low, non-blocking, STANDS from round 1): design doc describes aggregation semantics the code does not implement
docs/design/2026-08-31-release-quality-sharding.md, Failure semantics:
The aggregate quality job runs after success, failure, or cancellation, and
fails unless every component succeeded.
The shipped gate is
${{ !cancelled() && needs.prepare.result == 'success' && github.event.inputs.force_skip_tests != 'true' }}
with the inline YAML comment documenting both deliberate skips. The aggregate
is skipped, not run, (a) on a cancelled run and (b) when prepare never
ran (fork dispatch — the case c42ee9a6 added the prepare clause for), so
"runs after … cancellation" is wrong and the fork skip is now missing too.
The substantive invariant is unaffected and re-proven this round (publish
requires needs.quality.result == 'success'; S6/S7 cells above), so this is
a wording finding about a doc this PR adds.
suggested fix (measured)
Reword to: "The aggregate quality job runs after success or failure of its
components and fails unless every component succeeded; it stays skipped when
the run is cancelled or when prepare never ran (forks), and publishing stays
closed in all of those cases because publish requires the aggregate result
to be success."
Measured: applied in place, the release-workflow + package-scripts suites
stay green with and without the reword (no test reads this doc — the axis is
unpinned by construction), tree restored clean. The fixture that would pin
it: a contract test asserting the design doc's failure-semantics paragraph
names the shipped gate's skip conditions.
F4 (nit): .size-baseline is 1738 bytes behind the actual release.yml
.size-baseline records 59855 release.yml (bumped in e10628e1), but the
file at the new head is 61593 bytes — the last three release.yml commits
(0090c8d2 comments/pins, c42ee9a6 gate + comment, plus the main-merge
resolution) grew it again without a re-bump. The ratchet passes (61593 ≤
59855 + 4096 allowance; re-run green inside workflow-size.test.js), and
the repo's own ratchet message names the convention: "if the growth is real,
update .size-baseline in this PR and say why". Non-blocking; one-line bump
to 61593 if the author wants the ratchet tight again.
Not covered
- Per-commit verification. Depth-2 checkout:
git rev-list --count HEAD^1..HEAD^2returns 1 while the metadata lists 16 commits; the shallow
boundary makes individual commits unreachable (treated per the
shallow-boundary rule). Verified the aggregateHEAD^1..HEADdiff; the
nine post-round-1 commits were probed by their observable artifacts
(harnesses 02/03/05 target each one's claim) rather than individually
checked out. - Reviewer Test Plan steps 1–4 require dispatching real release runs on
GitHub; no token in this environment. Local contract-level equivalents
re-verified instead: one build producer + three shards + fail-closed
aggregate (harness 02), artifact overwrite pin + pack/unpack round-trip
(harness 03), publish blocked for dry-run-equivalent states (S2/S13),
fork-skip semantics (S7). The PR's local verification command was re-run:
release-workflow + package-scripts = 61 passed, 0 skipped on this
platform. Step 2's exact shard-vs-monolithic test totals and step 3's
real re-run still need a real run pair. - Shard file-partition enumeration (
vitest listhung in round 1 on the
jsdom workspaces): not retried — the shard surface changed only by the
last-command pin, which is mutation-proven (M8), and shard mechanics were
exercised through the consumer path in round 1. - Full workspace-shard lane execution on this box: round-1 F3
adjudication stands; this round re-A/A'd the PR-touched web-shell files
under load (both arms green) instead of another saturated full-lane run. - Wall-clock improvement (27:17 → ~20–25 min): needs real CI runs.
- Integration lanes: verified structurally — env/if byte-identical via
anchor, step bodies byte-identical modulo the documented checkout change
(ref → immutable SHA, depth 0 → 1) and the one wipe-comment line, and the
only git use inside them (git rev-parse HEADfor the sandbox image tag)
works at depth 1 — but not executed (bundle + docker out of scope for this
PR's claims; their test commands are unchanged). - yamllint: container refuses
pip3 install --user(permission denied,
re-attempted viascripts/lint.js --setup); actionlint + shellcheck +
bash -ncovered the workflow instead. - DAG replay calibration: still no token and no retrievable real run
artifact, so the gate simulation remains uncalibrated against an actual
GitHub run log; calibrating needs one real run's job-result set. The
expression evaluator was instead corroborated by the independent bash
transliteration (64+16 cells, zero mismatches).
Methodology
Environment: the CI verify container (shared loaded runner, load average up
to ~170 during the round, Node v22.23.2, RUNNER_TEMP=/__w/_temp), repo
pre-installed and built at HEAD. Harnesses, raw logs, extracted run blocks,
and the aggregate release.yml diff live in
tmp/pr10619-verify-20260901-133834/. The aggregate gate script and the
notify failed_jobs fragment were extracted from the parsed YAML (never
retyped) and executed under bash --noprofile --norc -eo pipefail; the DAG
simulator encodes documented GitHub semantics (implicit success() gate on
needs unless the if contains a status function; matrix result = worst
leg; run-level cancellation preempts unevaluated jobs) with three
self-check controls quoted in its output; harness 02's three scheduler
failures during development were a scheduler bug (job cascades inside one
pass skipped the cancellation point), fixed and re-run — the matrix above is
the post-fix run. The base A/A used git worktree add tmp/base-tree HEAD^1
with no local node_modules (resolution walks up to the root install; the
PR changes no lockfile) plus a symlink for web-shell's nested deps whose
input closure (packages/web-shell/package*.json) is unchanged by the PR —
the first base run failed on the missing nested store, which was my harness
artifact, root-caused and re-run. Mutations were applied to the real files
with exact-string single-occurrence replacement and restored after each run
(git status --porcelain empty at every restore; verified 10/10). Assertion
counts include expected-outcome cells as passes (e.g. the mid-arm fork
regression firing as predicted).
Flakiness gate log
rounds=5 files=4 skipped=0
file packages/web-shell/client/components/MessageList.dom.test.tsx: (cd packages/web-shell) npx --no-install vitest run ./client/components/MessageList.dom.test.tsx
file packages/web-shell/client/main-boot.test.tsx: (cd packages/web-shell) npx --no-install vitest run ./client/main-boot.test.tsx
file scripts/tests/package-scripts.test.js: (cd .) npx --no-install vitest run --config ./scripts/tests/vitest.config.ts ./scripts/tests/package-scripts.test.js
file scripts/tests/release-workflow.test.js: (cd .) npx --no-install vitest run --config ./scripts/tests/vitest.config.ts ./scripts/tests/release-workflow.test.js
per-file results (P=pass F=fail I=infra-exit, one letter per run):
packages/web-shell/client/components/MessageList.dom.test.tsx: PPPPP
packages/web-shell/client/main-boot.test.tsx: PPPPP
scripts/tests/package-scripts.test.js: PPPPP
scripts/tests/release-workflow.test.js: PPPPP
verdict: pass
summary: 4 changed test file(s) x 5 identical rounds, no divergence
--- per-invocation detail (full copy in the artifact) ---
round 1 · packages/web-shell/client/components/MessageList.dom.test.tsx: P (exit 0)
round 1 · packages/web-shell/client/main-boot.test.tsx: P (exit 0)
round 1 · scripts/tests/package-scripts.test.js: P (exit 0)
round 1 · scripts/tests/release-workflow.test.js: P (exit 0)
round 2 · packages/web-shell/client/components/MessageList.dom.test.tsx: P (exit 0)
round 2 · packages/web-shell/client/main-boot.test.tsx: P (exit 0)
round 2 · scripts/tests/package-scripts.test.js: P (exit 0)
round 2 · scripts/tests/release-workflow.test.js: P (exit 0)
round 3 · packages/web-shell/client/components/MessageList.dom.test.tsx: P (exit 0)
round 3 · packages/web-shell/client/main-boot.test.tsx: P (exit 0)
round 3 · scripts/tests/package-scripts.test.js: P (exit 0)
round 3 · scripts/tests/release-workflow.test.js: P (exit 0)
round 4 · packages/web-shell/client/components/MessageList.dom.test.tsx: P (exit 0)
round 4 · packages/web-shell/client/main-boot.test.tsx: P (exit 0)
round 4 · scripts/tests/package-scripts.test.js: P (exit 0)
round 4 · scripts/tests/release-workflow.test.js: P (exit 0)
round 5 · packages/web-shell/client/components/MessageList.dom.test.tsx: P (exit 0)
round 5 · packages/web-shell/client/main-boot.test.tsx: P (exit 0)
round 5 · scripts/tests/package-scripts.test.js: P (exit 0)
round 5 · scripts/tests/release-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-dev-bot
left a comment
There was a problem hiding this comment.
Re-reviewed at head c42ee9a6 (my approval on 0a474963 was auto-dismissed by this fix push).
- The increment is six lines each in the workflow and its pin test: the quality aggregate gains
needs: prepareand aneeds.prepare.result == 'success'gate, so a fork dispatch where prepare never ran no longer folds five skipped lanes into a quality failure that mints a bogus "Release Failed" issue. Fail-closed posture is preserved for real runs — prepare's own failure still blocks publish through the needs chain, and the test's exactif/needsassertions were updated in lockstep. - Round-4 findings on the previous head are all bot-deferred Suggestions (explicitly "not a blocker"), and the size-baseline item is within the check's 4096-byte growth allowance. 0 threads unresolved. The round-1 Critical and its 15 Suggestions were verified fixed at
0a474963and the surrounding code is unchanged here. - CI on this head has no failures; per the channel convention the call is on the review itself.
chiga0
left a comment
There was a problem hiding this comment.
增量审核 — fix(release): skip quality aggregate when prepare never ran
本次新提交: c42ee9a6 (+6/−2,仅改两个文件)
变更内容
.github/workflows/release.yml
quality聚合 job 在needs里新增了'prepare'if条件从:改为:${{ !cancelled() && github.event.inputs.force_skip_tests != 'true' }}${{ !cancelled() && needs.prepare.result == 'success' && github.event.inputs.force_skip_tests != 'true' }}
scripts/tests/release-workflow.test.js — 对应更新测试断言,与代码同步。
修复原因分析
!cancelled() 会覆盖 GitHub Actions 对 needs 的隐式 success() 检查。在 fork 分发场景下 prepare 因仓库 secret 限制而被 SKIP,5 个组件 job 随之全部 SKIP,但 quality 聚合因 !cancelled() 仍然执行,看到 5 个非 success 结果后退出 1,误触发 notify_failure 创建"Release Failed" issue。
加上 needs.prepare.result == 'success' 后:
- 正常发布(prepare 成功):behavior 不变 ✓
- Fork 调度(prepare 被 SKIP):quality 聚合跳过 ✓
边界情况:prepare 失败时
若 prepare 本身失败(而非 SKIP),needs.prepare.result == 'success' 为 false,quality 聚合将跳过而不是运行并报错。若 notify_failure 仅监听 quality == 'failure',则 prepare 失败时不会产生 "Release Failed" issue。
但这属于可接受的边界:prepare 失败已通过 CI 红灯直接可见,issue 通知是锦上添花。fix 的收益(消除 fork 噪音误报)明显大于该边角情形的代价。
CI 状态
当前 head 全部 SKIPPED(fork commit,未触发主 CI)。针对性变更可视验证:单元测试断言已同步更新。
结论
无阻断项。fix 定向、正确,测试同步更新。维持上次 Approve,本次增量通过。
|
Confidence: 4/5 — clean review at the final head; I would have approved, but the unit suite never landed green on that exact commit (an ECS runner timeout, not the diff), and the PR was merged before this run could act on either. This run was triggered at 12:59 UTC and the PR was merged by yiliang114 at 13:12 UTC, mid-review — so there is no approval or change request to post. The record below is what the pass found on Independent review of the final diff. The build-once DAG is the shape I would have proposed for #10422: one producer ( CI at the final head — the one caveat. Still in flight: the sandboxed verification run (33510803244) was started from this trigger and will post its own report when it completes; nothing further is needed from it for a merged PR. 中文说明本轮运行于 12:59 UTC 触发,PR 于 13:12 UTC 由 yiliang114 在评审进行中合入——因此不再有批准或修改请求可发。以下是本轮在 最终 diff 的独立评审。 一次构建的 DAG 正是我为 #10422 会提出的形态:一个生产者( 最终 head 的 CI——唯一的保留项。 运行 33497629312 中的 仍在进行: 沙箱验证运行(33510803244)由本次触发启动,完成后会自行发布报告;对已合入的 PR 不再需要其结论。 — Qwen Code · qwen3.8-max Reviewed at |
…10755) (QwenLM#10760) * fix(release): cap Vitest workers in the quality_scripts lane (QwenLM#10755) The 2026-09-02 nightly failed on the quality aggregate because the quality_scripts lane (added by the release-sharding split, QwenLM#10619) ran the scripts suite with vitest's default worker count — one worker per host core — on the shared ECS pool. On a 64-core runner executing several pool jobs at once, the suite's ESLint instances and bash replays spawned dozens of heavy subprocesses in parallel and 5-8 tests per run blew through the 30s test timeout non-deterministically (reproduced twice on the failing SHA: 30s timeouts across the ESLint boundary guards and workflow replays, plus a spawn-latency race in the upload-stall test). The sibling workspace_tests lane and the main CI gate already bound their vitest workers on ECS (QwenLM#10667); the new lane never inherited the bound. Apply the same tunable per-process bound to the lane and pin it in the release contract test next to the workspace lane's copy. Also deflake the upload-stall test: its 400ms kill bound raced child-spawn latency under pool load, losing attempt log lines and reading 1 or 2 attempts instead of 3; the bound is now 2s — still far under the shim's 60s hang, so the kill semantics are unchanged, and the worst-case run stays inside the test's own 30s budget. * refactor(release): alias the Vitest worker bound across release test lanes (QwenLM#10755) Review of the worker-cap fix noted that the Run Script Tests step carried a byte-identical copy of the Run Workspace Tests env block, so any future tuning (bumping the '4' default, adding another pool/thread variable) would need two synchronized edits, and editing one step while missing the other would silently restore the unbounded-worker timeout on the untouched lane — caught only reactively by a red contract test. Anchor the block at its first occurrence (&vitest_worker_bound) and alias it into the scripts lane, the same mechanism this file already uses for &release_test_env across six jobs. Reshape the contract assertions to pin the structure: the bound's four variables at the anchor definition and the bare alias at the consumer, so dropping either side turns the test red. Net five lines removed. --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
|
Released in v0.23.0. |



















What this PR does
This replaces the monolithic release quality lane with a build-once DAG. Formatting and lint run alongside the build; typechecking, script tests, and three native Vitest workspace shards consume the same generated build outputs; a final
Quality Checksaggregate preserves the existing publish and failure-notification dependency and fails closed when any component fails or is cancelled.The selected release ref is resolved to one immutable commit before validation. Every validation lane and the final publish checkout use that commit, while validation uses shallow history and the metadata/publish paths retain full history. The build artifact can also be replaced safely when all jobs in a workflow run are rerun.
Why it's needed
Release workspace tests grew from 19:30 to 27:17 in one month as the executed test count increased by 44%, accounting for most of the Quality Checks regression from 31:15 to 41:44. Running every workspace sequentially on one runner made this phase the release critical path. Three shards should reduce the quality gate to roughly 20–25 minutes without dropping any existing check.
Reviewer Test Plan
How to verify
Quality Checksaggregate succeeds only after static checks, typechecking, workspace tests, and script tests succeed.Local contract verification:
npx vitest run --config ./scripts/tests/vitest.config.ts scripts/tests/release-workflow.test.js scripts/tests/package-scripts.test.js— 51 passed, 1 platform-conditional skip.Evidence (Before & After)
UI verification: N/A. This only changes CI orchestration.
Before: workspace tests ran sequentially for 27:17 inside a 41:44 Quality Checks job.
After: the workflow contract contains one build producer, three workspace shards, independent static/typecheck/script gates, and one fail-closed aggregate. Runtime timing will be measured by the first dry-run dispatch after merge.
Tested on
Environment (optional)
Local Node.js 22 environment with the repository's existing dependencies.
Risk & Scope
Linked Issues
Closes #10422
中文说明
这个 PR 做了什么
本 PR 将串行的发布质量检查拆成一次构建、多路消费的 DAG。format 和 lint 与构建并行;typecheck、脚本测试以及三个原生 Vitest workspace 分片复用同一份构建产物;最后保留原名
Quality Checks聚合门禁,任一子任务失败或取消都会阻止发布,并继续供失败通知使用。发布 ref 会先解析为不可变的 commit SHA。所有 validation lane 和最终 publish checkout 都绑定该提交;validation 使用浅克隆,只有元数据准备和发布保留完整历史。整轮任务重跑时,同名构建 artifact 也可以安全覆盖并重新消费。
为什么需要
一个月内,执行测试数增长 44%,Release workspace tests 从 19:30 增长到 27:17,解释了 Quality Checks 从 31:15 增长到 41:44 的大部分回退。所有 workspace 在单 runner 上串行执行,已经成为发布关键路径。拆成三个分片后,预计在不减少任何检查的前提下把质量门禁降到约 20–25 分钟。
Reviewer Test Plan
如何验证
Quality Checks只有在静态检查、typecheck、workspace tests 和脚本测试全部成功后才成功。本地契约验证:
npx vitest run --config ./scripts/tests/vitest.config.ts scripts/tests/release-workflow.test.js scripts/tests/package-scripts.test.js,结果为 51 passed、1 个平台条件 skip。前后证据
UI verification: N/A。本 PR 只修改 CI 编排。
Before:workspace tests 在 41:44 的 Quality Checks job 中串行运行 27:17。
After:工作流契约包含一个构建生产者、三个 workspace shard、独立的 static/typecheck/script 门禁和一个 fail-closed 聚合。实际耗时将在合入后的首次 dry-run 中测量。
测试平台
环境
本地 Node.js 22,复用仓库现有依赖。
风险与范围
关联 Issue
Closes #10422