fix(ci): record cd-cua-driver.yml's shipped size in the workflow size baseline - #9822
Conversation
… baseline #9587 grew cd-cua-driver.yml from 29715 to 42519 bytes (the versioned Computer Use SDK release pipeline) without updating the ratchet, and its own CI never ran the size gate. Every branch that merges current main now fails the Test job at 'Check workflow file size' before any test runs. The growth is real feature surface at 8% of GitHub's 512000-byte start-runs limit, so record it, as #9747 did for qwen-autofix.yml.
|
✅ Qwen Triage finished — CI landed green on ✅ Qwen Triage 已完成 —— |
|
Thanks for the PR!
Moving on to code review. 🔍 中文说明感谢贡献!
进入代码审查 🔍 — Qwen Code · qwen3.8-max Reviewed at |
|
Code review — the whole diff is one data line: CI evidence (fetched via the API for the reviewed commit; PR code not executed): Final CI results for
One row per check name (latest run); skipped checks omitted; failures sort first. / 每个检查名一行(取最新一次运行),省略 skipped,失败项排在最前。 No failures. The macOS/Windows Test jobs are skipped by design on PRs — 中文说明代码审查 —— 整个 diff 只有一行数据: CI 证据(通过 API 获取被审提交的结果;未执行 PR 代码):0 失败。macOS/Windows 的 Test job 在 PR 上按 — Qwen Code · qwen3.8-max Reviewed at |
|
Confidence: 5/5 — observed breakage, independently reproduced; the fix is the exact prescribed one-line change, with the number verified byte-for-byte. Everything held up under review: the problem is real ( Verdict: approve. CI on the reviewed commit shows no failures, but 中文说明置信度:5/5 —— 已观测到的故障且独立复现;修复正是被规定的一行改动,数值已逐字节核实。 审查中所有环节都站得住:问题真实( 结论:批准。被审提交的 CI 无失败,但运行门禁本身的 — 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. |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
LGTM, looks ready to ship — CI landed green after the review. ✅
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
中文说明
— qwen3.8-max via Qwen Code /review (v0.22.0)
… baseline cd-cua-driver.yml grew to 42519 bytes when the versioned Computer Use SDK release pipeline (#9587) landed, but its .size-baseline entry stayed at 29715, so the workflow-size gate fails every PR built on that main. Records the shipped size, matching the standalone fix in #9822 (folded in here so this branch is not blocked waiting on it); a later rebase past #9822 drops this commit cleanly.
…ui-core One conflict, the usual shape in this file: two independent `it`s added at the same seam — this branch's capture-tui wiring pin and main's #9789 do-not-refute pin. Both kept. Picks up the `cd-cua-driver.yml` baseline correction (#9822), so the `Test` job's step-7 gate stops failing this branch's merge ref before any dependency is installed — verified on the merged tree, the gate now answers "every workflow file is under the gate". `npm run build` clean and `vitest run --root packages/cli src/commands/review` 5130 passed / 0 failed.
The ratchet compares against the merge commit: ci.yml grew 4068 bytes on main since this branch's base (within the 4096 allowance, so no baseline update was owed by those PRs), and this PR's one-line HELPER_TESTS registration (+50) pushed the total past the allowance. Record the merged size 73900 — the same shape of fix as QwenLM#9822.
|
Released in v0.22.2. |
…wenLM#9771) * feat(autofix): keep the round status comment live during long rounds A review-address round can run for hours while the PR status comment stays frozen at "working", so on the PR page a healthy long round and a dead one look identical. Start a detached heartbeat loop with the announcement that re-PATCHes the same comment every ~10 min with elapsed time and last agent activity, and deep-link "Watch live progress" to the matrix leg's own live log. The loop lifetime is bounded to the sandboxed agent phase: the verification gate kills it before the first step that runs branch code on the host, and every kill target travels through expression context — WORKDIR is sandbox-writable, so no WORKDIR file is ever read as a kill target. Full rationale in qwen-autofix.md#af-148/af-149 and docs/design/autofix-round-heartbeat.md. * fix(autofix): harden the heartbeat self-exit and pin its behavior Review findings on the round heartbeat (PR review of the previous commit): - The orphan self-exit is now an IDENTITY check, not an existence check: WORKDIR is PR-scoped, so the next round recreates heartbeat.pid at the same path, and existence alone let a hard-killed round's orphan pass and keep PATCHing its stale body onto the comment. The loop compares the file against its own pid; removed or replaced ends it. Mirror test added. - run_loop validates every variable a tick needs up front — a launch missing a body var now fails fast instead of producing an immortal loop that never pulses (the exact failure the feature eliminates); the dead HB_START_EPOCH fallback is gone with it. - The deep-link jq filter gains a behavioral oracle (extracted verbatim, run through real jq against paginate-shaped fixtures: cross-page match, comma guard against a shorter PR number, deliberate last choice, empty input), and the timeout wrapper is pinned through a PATH shim proving gh runs under it. * fix(autofix): fail fast on a token-less heartbeat launch; correct orphan docs * fix(autofix): close the heartbeat's token paths at the kill and the gh call Round-3 review findings on the round heartbeat, each reproduced on the pool's host class before the fix: - The killers killed the loop's pid and process group, but each tick's `timeout 60 gh` subtree runs in its OWN process group (coreutils timeout default) under the loop's setsid session — a kill landing mid-tick left it alive holding the PAT for up to 60s (witnessed: group+pid kill leaves the subtree reparented with the canary token in /proc/<pid>/environ). All three killers now also kill the session; the behavioral suite pins both the escape and the fix. - The step's gh calls and every loop tick ran gh without the af-112 hermetic pins, so a planted http_unix_socket in the shared HOME's ~/.config/gh received the tick's Authorization header WITH the PAT (witnessed with the pool's gh). run_loop now mints a fresh GH_CONFIG_DIR and drops planted GH_TOKEN/GH_ENTERPRISE_TOKEN itself, post_status takes the same preamble before its first gh call, and the fail-fast check accepts the step-level GITHUB_TOKEN only. - The default age cap drops from 12h to just past the 330-minute job envelope: only a crash-leftover orphan ever reaches it, and it bounds how long that orphan holds the PAT in /proc/<pid>/environ — readable by any same-UID host process, as a sibling-read probe on this host class confirms (ptrace_scope gates attach, not this read). The af-148 record and the design doc now state that real residual profile instead of "liveness-text corruption only". * fix(autofix): degrade pre-merge rounds past the absent heartbeat script Round-4 review findings on the round heartbeat: - [Critical] The staging cp of the heartbeat script and its digest echo carried no guard, but the script is NEW in this PR: the trusted base (pre-merge main) lacks it, so any run that resolves the workflow from the PR's own ref — pull_request labeled/unlabeled on an in-repo takeover-managed PR, or workflow_dispatch on its branch — checks out the trusted base and dies on the bare cp under the runner's -eo pipefail, killing the whole round instead of degrading (witnessed: the extracted stage step exits 1 with "cp: cannot stat" on the merge-base tree). Same rule as the upsert capture in the same step: the cp carries `|| true`, the digest records only when the copy exists, and post_status now guards the empty digest — falling back to the pre-PR inline body (byte-identical to it) and skipping the heartbeat launch instead of digest-checking and running a staged copy that was never staged. Witnessed on the fixed step: exit 0 on the merge-base tree with an empty digest, and the consumer probe renders the pre-PR body there while the digest arm renders the script body. - [Suggestion] The hermetic witness logged every gh-visible credential channel except GITHUB_TOKEN — the loop's sole credential since the round-3 fail-fast. The fake gh shim now logs it and the witness asserts it reaches gh; mutation probe: broadening the loop's unset to drop GITHUB_TOKEN kept the old suite green but fails the new assertion, while in production every tick would fail authentication. * fix(autofix): pin the heartbeat's command resolution off the plantable PATH * fix(autofix): clear the staged heartbeat plant and re-verify at the loop launch * fix(autofix): clear directory plants at staging; gate the launch witness on capability * fix(ci): record ci.yml's accumulated growth in the size baseline The ratchet compares against the merge commit: ci.yml grew 4068 bytes on main since this branch's base (within the 4096 allowance, so no baseline update was owed by those PRs), and this PR's one-line HELPER_TESTS registration (+50) pushed the total past the allowance. Record the merged size 73900 — the same shape of fix as QwenLM#9822. * fix(ci): exact-size the qwen-autofix.yml baseline line after integration The parallel hardening rounds recorded 408743 while the shipped file is 409115; the gate passes either way (within allowance) but the ratchet line should record the exact size. * fix(ci): close round-8 heartbeat findings R8-1/R8-2/R8-3 R8-1: post_status mints GH_CONFIG_DIR fail-closed — a bare export masks a failing mktemp and gh falls back to the attacker-writable ~/.config/gh. R8-2: NOW_EPOCH accepts numeric overrides only — bash arithmetic expansion recursively evaluates a planted value's command substitution inside the PAT-holding body/loop. R8-3: finalize takes the gate steps' startup-channel pins (BASH_ENV, SHELLOPTS, LD_*) and pins PATH from the stage-time capture before its bare terminal gh call. Each fix carries its own witness: a planted-clock body test, a failing-mktemp behavioral probe of the step's opening block, and step-level pins; all three verified by mutation probe. * fix(ci): close round-9 finding R9-1 with an env -i finalize child The finalize step holds the bot PAT, and the startup-channel pins its previous hardening added close only the named variable channels (BASH_ENV/SHELLOPTS/LD_*): bash also imports BASH_FUNC_<name>%% env entries as functions at startup even under --norc, ahead of builtins and PATH, under attacker-chosen names no env: block can enumerate. A $GITHUB_ENV plant (reachable: the verification gate runs the branch's own build/tests on the host before this always() step, and the runner's set-env blocklist does not cover BASH_FUNC keys) could shadow the step's set/export/builtin/gh words and run with the token — probe-verified on this host for every one of those words. Close the class, not the enumeration: the whole PAT-touching body now runs through the gate's env -i clean-child form (R6-4), re-declaring only what it needs from step-level pins and expression context. HOME additionally rides the stage-time capture so a planted HOME cannot repoint gh's config search (af-112/R8-3 doctrine). Witnesses: structural adjacency-chain pin over the launch plus parent-body boundary pins, a workflow-wide clean-child count update, and a behavioral probe whose poisoned arm plants a BASH_FUNC function for every command word the old inline body resolved and requires none of them to run. Mutation probes: dropping env -i, dropping the token allowlist entry, and inserting a parent statement each fail the new tests; restored, the suite is green. * fix(autofix): bound the heartbeat pid-identity read (R10-3) A FIFO planted at heartbeat.pid (WORKDIR is sandbox-writable) blocked the identity check's cat indefinitely, so the per-tick age cap never ran again and a crash-orphan held the bot PAT past its documented bound. Wrap the read in the already-pinned coreutils timeout (conditional form keeps the documented no-timeout host fallback); a timeout kill yields empty -> identity mismatch -> the existing clean self-exit. The timeout shim witness now scopes its pin to the gh call and additionally proves the bounded read; a gated behavioral test plants a real FIFO. * fix(autofix): close R10-1/R10-2 on the gate kill block and the finalize child R10-1: the gate's heartbeat kill runs in the OUTER shell, which imports every $GITHUB_ENV plant — a BASH_FUNC_builtin%% entry shadows the `builtin` keyword itself, so `builtin kill` there is not sound (the R6-4 doctrine the gate body documents). Switch it to /usr/bin/kill, the same procps already relied on for pkill; `builtin kill` stays where it is sound, inside finalize's env -i clean child. R10-2: the finalize child's gh call carries the PAT but resolved the shared HOME's gh config — pinning HOME's path does not sanitize its contents, which gate-phase host-side branch code (same UID) can write, planting http_unix_socket to capture the Authorization header. Mirror the upsert twin: GH_HOST + RUNNER_TEMP enter the allowlist, and the child mints a hermetic GH_CONFIG_DIR fail-closed before the PATCH. Pins updated: gate statement list and kill-form pins take the absolute-path form, the finalize launch chain gains the two allowlist entries, and the probe env supplies RUNNER_TEMP for the mint. * fix(autofix): mint the hermetic gh config per call (R11-1) * fix(autofix): gate the gh-config witness on the env-log it reads (R12-1) The per-tick hermetic gh-config witness gated waitFor on the call-record count, then read gh-env.log without retry — but the fake gh writes the call record before it appends the env line, so under CPU load the wait passed in the window before the append landed and the assertion saw one line instead of two (19/30 focused runs red under single-core load, all at this assertion). Gate the wait on the env-log line count itself, so the predicate implies what the assertion reads. * fix(autofix): confirm heartbeat pid lifecycle and drain in-flight ticks before the terminal PATCH Two lifecycle races in the round heartbeat (review findings on QwenLM#9771): 1. The gate/finalize/cleanup killers TERM'd the bare pid recorded at launch — its process group and session too — after validating only that it is decimal. The gate kills the loop up to a whole agent phase before finalize lands, so the pid can be REUSED by then, and the blind block kills an unrelated process (probe-verified: mapped to an unrelated detached session, the block terminated it). Every killer now confirms the pid's /proc/<pid>/stat start time against the launch's capture (heartbeat_start_ticks, field 22) before signaling: a reused pid carries a different start time and a dead pid has no stat, so a failed check kills nothing. 2. Finalize slept a fixed 2s before the terminal PATCH, but killing the client cannot cancel a PATCH the server already accepted (probe-reproduced: WORKING accepted 1.67s in, TERMINAL submitted 3.80s in, the stale WORKING committed 6.67s in and flipped the comment back to live-looking). Each tick now stamps its start epoch into heartbeat-tick-inflight around its 60s-bounded gh call and removes it after; finalize drains until the stamp is absent or older than the 65s completion bound, and only then PATCHes. Both guard paths carry behavioral witnesses (a reused-pid victim must survive the mismatch arm and die on the matching arm; the drain must wait a near-fresh stamp out and proceed at once on an aged one), plus bounded-write/read guards against planted FIFOs on the stamp path. af-148 and the design doc record the doctrine. * fix(autofix): env-route heartbeat kill targets, cap override magnitude, and de-flake the drain witness (R15-1..R16-2) R15-1: gate the lifecycle-confirmation and drain witnesses on capability (procfs + coreutils timeout), the launchWitnessSupported shape — the gated macOS lane collects this suite and went red on both witnesses. R15-2: plant the drain witness's fresh stamp from inside the probe shell, relative to the drain's own clock, aged 63s — a wall-clock -64 plant left the stamp's sub-second age unknown, so a first drain check landing in the next second legitimately broke at once (probe: 13/40 immediate breaks at the boundary; 0/40 with the fix). R15-3: the launch-side lifecycle capture reads /proc through /usr/bin/cat, the gate twin's shadowing doctrine — a bare cat is shadowed by a $GITHUB_ENV-planted BASH_FUNC_cat%% function and a forged start time travels to every killer. R15-4: shellcheck disable directive on the intentional single-quoted stamp write ($0 is the inner bash's positional) — the repo's established SC2016 handling. R16-1: the two heartbeat outputs ride step-level env: blocks to all three killers (the STATUS_ID shape) instead of run-body interpolation, which substituted a forged output as shell syntax before the shell parsed; pins flipped to the env shape plus run-body absence, a forged $(touch proof) ticks arm witnesses the data path, and the producer numeric-validates the ticks before $GITHUB_OUTPUT. R16-2: magnitude bounds on the HB_INTERVAL_SECONDS / HB_MAX_AGE_SECONDS overrides — shape alone admitted a huge interval the loop never wakes from and a tiny age cap that kills the pulse after the first sleep; the cap floor is the 330-minute job envelope. * fix(autofix): byte-bound pid/stamp reads, digit-bound overrides, close-event witnesses, and absolute-path cleanup kills (R16-2..R17-4) --------- Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com>
What this PR does
Updates the workflow size-baseline entry for
cd-cua-driver.ymlfrom 29715 to its shipped 42519 bytes, so theCheck workflow file sizeratchet stops failing every PR that carries currentmain.Why it's needed
#9587 (the versioned Computer Use SDK release pipeline) grew
cd-cua-driver.ymlby 12804 bytes without updating.github/workflows/.size-baseline, and its own CI never exercised the size gate — somainitself is over its recorded baseline. The gate is the first step of the Test job, which means every branch that merges or rebases onto currentmainnow fails Test in ~24 seconds before a single test runs (first observed on #9794 after its takeover merge ofmain). The growth is real feature surface at 8% of GitHub's 512000-byte start-runs limit; recording it is exactly what the gate's own error message prescribes and what #9747 did forqwen-autofix.yml.Reviewer Test Plan
How to verify
On this branch,
bash .github/scripts/check-workflow-size.shprints the all-green line (✅ every workflow file is under the 470000-byte gate and within 4096 bytes of its recorded baseline); onmainit errors withcd-cua-driver.yml grew to 42519 bytes, 12804 over its recorded 29715. The vitest mirror stays green:npx vitest run scripts/tests/workflow-size.test.js— 181 passed.Evidence (Before & After)
Before (main):
##[error].github/workflows/cd-cua-driver.yml grew to 42519 bytes, 12804 over its recorded 29715 (allowance 4096)— e.g. the Test job on #9794's head after merging main, failing at 24s.After (this branch):
✅ every workflow file is under the 470000-byte gate and within 4096 bytes of its recorded baseline.Tested on
Environment (optional)
Local shell + vitest; no build needed.
Risk & Scope
cd-cua-driver.ymlitself (moving prose out) — worth doing if it keeps growing, but not required at this size.Linked Issues
Unblocks CI on #9794 and any other branch carrying
mainat or after #9587.中文说明
这个 PR 做了什么
把 workflow 体积基线中
cd-cua-driver.yml的条目从 29715 更新为实际的 42519 字节,使Check workflow file size棘轮门禁不再让所有携带当前main的 PR 失败。为什么需要
#9587(Computer Use SDK 版本化发布流水线)把
cd-cua-driver.yml撑大了 12804 字节却没有更新.github/workflows/.size-baseline,而它自己的 CI 没有跑到这个门禁——因此main本身就超出了记录的基线。该门禁是 Test job 的第一步,导致所有 merge/rebase 到当前main的分支在任何测试运行前约 24 秒即失败(最先在 #9794 的 takeover merge main 之后观察到)。该增长是真实的功能面,仅占 GitHub 512000 字节启动上限的 8%;按门禁自身报错信息的指引记录它,与 #9747 对qwen-autofix.yml的处理一致。评审验证方案
如何验证
本分支上
bash .github/scripts/check-workflow-size.sh输出全绿行;main上则报cd-cua-driver.yml grew to 42519 bytes, 12804 over its recorded 29715。vitest 镜像保持绿:npx vitest run scripts/tests/workflow-size.test.js—— 181 通过。证据(前后对比)
之前(main):
##[error].github/workflows/cd-cua-driver.yml grew to 42519 bytes, 12804 over its recorded 29715 (allowance 4096)——例如 #9794 head 在 merge main 后的 Test job,24 秒失败。之后(本分支):
✅ every workflow file is under the 470000-byte gate and within 4096 bytes of its recorded baseline。测试平台
Linux 已验证(✅);macOS/Windows 未本地验证(⚠️ ,纯数据行改动,CI 覆盖)。
环境(可选)
本地 shell + vitest;无需构建。
风险与范围
cd-cua-driver.yml本身瘦身(外移 prose)——若持续增长值得做,当前体量不必。关联 Issue
解除 #9794 及所有携带 #9587 之后
main的分支的 CI 阻塞。