ci: install tmux and zip tooling on the Linux test lane, and pin it - #8792
Conversation
Two suites go quiet on a runner without their tooling. capture-tui's real-tmux block is `describe.skipIf(!hasTmux)`-gated, so on the hosted ubuntu image — which ships no tmux — every real-tmux behavior skips inside a green required check. The install-script packaging suite needs zip and unzip the same way. This installs both where apt exists, bounded (timeout-minutes) and advisory (continue-on-error absorbs the bound), so a stalled mirror or dpkg lock neither hangs the job toward its 60-minute cap nor reds the check. A step that silently stops installing is the same outage as no step at all, so it comes with a pin that reads the workflow the way bash does rather than the way it looks. The parser is quote-aware (a `#` inside a message is literal; a `;` inside one does not split statements), continuation-aware (only an odd run of trailing backslashes continues a line, and a comment never continues), and wrapper-aware (an apt-get behind sudo/env/nice/ timeout/time/nohup, with or without their options, or inside a subshell, is still an apt-get). On top of that it pins what actually keeps the step working: the if-condition whole (a one-character `&&`→`||` took the already-installed branch on a lane with zip but no tmux), the elif whole (a near-miss falsifier like `false2 &&` killed the install branch), the else fallback (deleting it removed the only signal on a lane with neither tmux nor sudo), exact package tokens (`powerline-tmux` is not tmux), the flags that make it work unattended (-y, sudo) and the ones that make it a no-op (-s, --download-only and friends exit 0 having installed nothing), that nothing may hard-fail the step in any errexit spelling or through a subshell exit, that every apt-get REACHES a `|| echo` guard (an AND-list is transparent, a pipeline or a `;` is not), and that every annotation reaches stdout unredirected with `::warning::` at line start — a workflow command the runner cannot see is just log noise. Split out of #8388, whose capture-tui command is what made the tmux half load-bearing. Each pin was mutation-verified against this workflow file.
The tmux/zip install step and the 400-line workflow pin it comes with have no coupling to the capture-tui command itself: they keep the Linux lane's tooling honest for two suites, one of which happens to be this PR's. They were also generating the largest share of review findings here — 28 of the last 78 — while being third-order to what this PR ships, so they land on their own where they can be reviewed for what they are. Until #8792 merges, this PR's real-tmux suite skips on CI exactly as it did before the step existed. That is a reporting loss, not a behaviour change: the suite runs locally and in any lane that already has tmux, and every real-tmux behaviour it covers is also probe-verified in the PR description.
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
@wenshao Stopping at the template gate — the description's substance is solid, but the headings don't match .github/pull_request_template.md, so this is a formatting pass rather than a rework.
- Missing required headings: the body uses
## What,## Why the pin is 400 lines, and## Testing; the template asks for## What this PR does,## Why it's needed,## Reviewer Test Plan(with### How to verify,### Evidence (Before & After),### Tested on),## Risk & Scope, and## Linked Issues. See the template. The bilingual<details>section is already there ✓. - Everything is already written — it just needs re-flowing: the mutation-verification table is exactly Reviewer Test Plan material, and the bounded/advisory design discussion is the Risk & Scope content.
- Linked Issues matters here: this step was split out of #8388, and the tmux half pre-lands that PR's tooling need (the gated real-tmux suite is not on
mainyet). A reviewer assessing this PR needs that link up front, not only inside the Chinese section.
Once the body follows the template, re-run the gate with @qwen-code /triage.
中文说明
@wenshao 这个 PR 停在模板关卡 —— 描述内容本身很扎实,但标题结构与 .github/pull_request_template.md 不符,所以这一轮只是补格式,不是返工。
- 缺少必需标题:正文目前用
## What、## Why the pin is 400 lines、## Testing;模板要求## What this PR does、## Why it's needed、## Reviewer Test Plan(含### How to verify、### Evidence (Before & After)、### Tested on)、## Risk & Scope、## Linked Issues。见模板。双语<details>部分已有 ✓。 - 内容都已具备,只需归位:变异验证表就是 Reviewer Test Plan 的内容,bounded/advisory 的设计讨论可直接作为 Risk & Scope。
- Linked Issues 在这里很关键:该步骤拆自 #8388,tmux 一半实际是为该 PR 预落地工具需求(被保护的 real-tmux 套件尚未进入
main)。评审者需要一进来就看到这一关联,而不是只在中文说明里。
正文按模板调整后,用 @qwen-code /triage 重新触发关卡即可。
— Qwen Code · qwen3.8-max
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 /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: 73 passed · 2 failed · 75 total 中文 — 判定:❌ 不通过 · 报告了发现(agent 判定)沙箱验证在隔离、无凭证的容器中执行了该 PR 的代码(与 base 构建 A/B 对照、无 mock harness 断言、定向门禁)。仅作为评审证据,不构成评审、批准或 CI 检查。 脚本断言:73 通过 · 2 失败 · 75 总计 Verification reportPR 8792 deep verification —
|
| arm | ci.yml | oracle | result |
|---|---|---|---|
| HEAD | step present (shipped) | pin suite exit code | 5/5 passed, exit 0 |
| BASE (control) | step hunk reverted to HEAD^1 |
pin suite exit code | 5/5 failed, exit 1 |
Witness: 01-ab-head-vs-base.png. The base arm failing as predicted is the load-bearing proof: the pin detects the exact regression it exists for (the step disappearing in a future refactor). Raw logs: logs/01-head-suite.txt, logs/02-base-arm-step-removed.txt.
Mutation matrix (the PR's own claim, replayed)
The PR description claims 20 mutations each turn the suite red. Each was applied textually to .github/workflows/ci.yml (scoped to the install step region), the suite run, and the file restored — 22 PR-table mutants (two table rows split into a/b variants) plus 11 of my own probes:
| class | mutants | result |
|---|---|---|
| PR table (M01–M20b) | 22 | 22/22 red, as claimed |
| adversarial escapes (X01–X08) | 8 | 8/8 red (incl. false && dead-install prefix, apt-get→apt, guard→|| true, step moved below tests, condition→macOS) |
| tolerance probes (T01–T03) | 3 | 3/3 green, as required (wrapper-wrapped update, reworded warning with ;/parens in message, dropped --no-install-recommends) |
escape X07 (return 0 first run line) |
1 | GREEN — escape (F1) |
escape X11 (exec true first run line) |
1 | GREEN — escape (F1) |
Witness: 04-mutation-matrix-live.png (full live rerun). Raw: logs/04-mutation-matrix-v2.txt, mutants/matrix-results.json, harness mutants/matrix.mjs (rerunnable).
Methodology note (transparency): a first matrix run showed 6 deviations; all six traced to a harness bug — run: |- and timeout-minutes: 5 are not unique in ci.yml, so a whole-file replace mutated an earlier step. After scoping every edit to the install-step region, all 22 PR mutants went red. The v1→v2 flip is itself evidence the deviations were harness artifacts, not pin gaps. (logs/03-mutation-matrix.txt = buggy v1, kept.)
The step itself behaves as specified
The run: block was extracted verbatim via a YAML parser and executed under bash --noprofile --norc -eo pipefail (the runner's strictest realistic shell) across five environment cells. Stubs stand in for the environment (apt, mirrors, preinstalled tools — real apt needs root, unavailable here); the script under test runs for real in every cell. Witness: 02-step-execution-cells.png. Raw: logs/06-step-execution.txt, harness harness/run-step.mjs.
| cell | environment | oracle | result |
|---|---|---|---|
| live-else | this container as-is (no sudo/tmux/zip) — no stubs | exit 0; one ::warning::tmux/zip unavailable… at stdout line start; clean stderr |
pass (5 checks) |
| stub-install | stub sudo+apt-get, exit 0 | apt args exactly update -qq then install -y -qq --no-install-recommends tmux zip unzip, both via sudo; no annotation |
pass (6 checks) |
| stub-fail | stub apt-get exits 100 | || echo guard fires: install-failure annotation, exit still 0 |
pass (4 checks) |
| stub-present | stub tmux/zip healthy | silent success, no annotation | pass (3 checks) |
| stub-broken | stub tmux -V exits 1 |
advisory annotation only, exit still 0 | pass (2 checks) |
Syntactic gates: bash -n clean; shellcheck --shell=bash --enable=all --severity=style clean (only finding was SC2148, the missing-shebang artifact of extracting an embedded block — excluded, then 0 findings). Repo gates: actionlint clean across all workflows, shellcheck gate exit 0, prettier and eslint clean on both changed files. Actionlint gate proven live: a planted bad ${{ … && }} expression was flagged before removal.
Findings
F1 — the pin's hard-fail blacklist misses return and exec (Suggestion)
The pin explicitly forbids any statement being exit/false and any set enabling errexit ("NOTHING may hard-fail the step"). Two siblings of that same class escape it, while each silently disables the whole step under the runner's shell:
| mutant | suite at HEAD | real behavior under bash -e |
|---|---|---|
return 0 as first run line |
green (escape) | bash errors return: can only 'return' from a function or sourced script, aborts under -e (exit 2) → step fails → continue-on-error absorbs → check green, nothing installed, and no ::warning:: emitted anywhere |
exec true as first run line |
green (escape) | shell replaced by true, exit 0 → check green, nothing installed, zero output |
This is the zero-signal variant of the failure mode the file exists to prevent — worse than the exit 1 case the pin does catch, because not even the else-branch warning survives. Measured, not inferred: bash -eo pipefail -c 'return 0; echo AFTER' never reaches AFTER (exit 2); bash -eo pipefail -c 'exec true; echo AFTER' exits 0 silently (also note: without -e, bash continues past a failed return, so the escape is specific to the runner's -e shell). Siblings break/continue outside a loop were probed and do not abort under -e (AFTER still runs) — they are not escapes and are excluded.
Reproduce:
node tmp/pr8792-verify-20260809-074359/harness/escape-demo.sh # mutants vs head pin (green) and vs patched pin (red)Measured candidate fix (line 354 of the test)
- /^(exit(\s+\d+)?|false)$/,
+ /^(exit(\s+\d+)?|false|return(\s+\d+)?|exec(\s.*)?)$/,Applied in a scratch copy and driven through the same harnesses:
| cell | head pin | patched pin |
|---|---|---|
| pristine ci.yml | 5 passed | 5 passed (no over-pin) |
X07 return 0 mutant |
5 passed (escape) | 1 failed | 4 passed |
X11 exec true mutant |
5 passed (escape) | 1 failed | 4 passed |
Logs: logs/05-fix-candidate-{head,x07,x11}.txt. The head suite is green with and without the patch on the pristine file, so the axis is currently unpinned — the fix should ship together with a mutant fixture (e.g. assert the suite goes red when return 0 is spliced into the step) to pin the new axis.
Witness: 03-escape-return-exec.png. Severity is Suggestion rather than blocking: the shipped ci.yml is correct, the pin already exceeds ordinary practice, and the escapes require a one-line edit inside the step — but that is precisely the edit-class the pin advertises catching.
Claim traces (secondary claims, all confirmed)
- Wiring: step 31
Install tmux and zip toolingsits before step 32Run tests and generate reportsin jobtest; step 32 runsnpm run test:ci, which chainstest:scripts=vitest run --config ./scripts/tests/vitest.config.tswhose include glob collects the new test. The pin runs in CI, in the same job, after the install it pins. (A pin CI never executes would pin nothing — checked first.) - Lane premise: the published ubuntu-24.04 runner-image toolset (image 20260720.247.2, fetched externally) lists no tmux (motivation holds), while
zip 3.0,unzip 6.0andsudoare present — so on the real lane the step takes the elif branch and installs tmux (zip/unzip no-op installs), and the zip half of the step is defensive exactly as the PR frames it. - Zip suite "throws, never silently skips":
install-script.test.jsthrows at module load whenCIis set and zip/unzip are missing — verified in-vivo: the full suite run in this container (CI=true, no zip) failed collection of exactly that file with that throw, while 955/955 tests passed. Attribution proved pre-existing: the throw is byte-identical at base, the PR makes zero changes to that file, and this container simply lacks zip. On the Test lane (zip shipped) it cannot fire. If anything this is a live demonstration of the loud-failure the step exists to keep green. - Code comment "(qwen-autofix.yml does the same for tmux)" — accurate:
Install tmuxstep at line 949 of that workflow (with a stricter policy:::error::on failure, appropriate to that lane). - Single-commit PR:
git rev-list HEAD^1..HEAD^2enumerates exactly one commit, matching the snapshot'scommitsarray oid — per-commit attribution was trivially complete. - Parser semantics: all three rules the parser comments claim were probe-verified against real bash (
;#…\splice,\\escaped backslash, comment-never-continues, quoted-line fold) plus the two escape mechanisms — 6/6 scripted probes passed (logs/08-semantics-probes.txt).
Gates
| gate | scope | result |
|---|---|---|
full test:scripts suite (51 files, includes the 4 other suites that parse ci.yml) |
HEAD | 955/955 tests passed; 1 file fails collection = pre-existing env throw above |
| actionlint (all workflows) | HEAD | clean; liveness proven via planted violation |
| shellcheck repo gate | HEAD | exit 0 (pre-existing warnings only in unrelated scripts/test-rewind-e2e.sh) |
shellcheck on extracted step, --enable=all --severity=style |
HEAD | clean |
bash -n on extracted step |
HEAD | clean |
prettier --check / eslint on the two changed files |
HEAD | clean |
Not covered
- yamllint — the binary could not be installed in this container (
pip3permission-denied, no pip module for--user). Mitigations: actionlint (which parses every workflow YAML) passed, theyamlpackage parsedci.ymlsuccessfully in every harness, and the lane image ships yamllint 1.38.0 so the real gate runs on the lane. - Real
apt-get installexecution — this container runs as non-root, so the install branch was exercised via PATH stubs (script logic, branch selection, argument tokens, guard behavior all executed for real) and only the else branch ran fully live. The author reports a Debian-container install; that specific claim was not re-run here. The lane image does ship sudo+apt-get, so the elif branch is the one the lane takes. - This PR's own CI job logs (
Install tmux and zip toolingvisible in theTest (ubuntu-latest)run, per the Reviewer Test Plan) — no GitHub token in this environment; substituted with the external image-toolset verification above. Reviewer Test Plan step "run the pin → 5 passed" was executed verbatim and confirmed. - Windows/macOS lanes — the step is gated
runner.os == 'Linux'and the pin is platform-independent text parsing (not in the Windows exclude list); no lane-specific behavior to verify there. - Per-commit behavior beyond the single commit — N/A (one commit, verified above).
Methodology
Verification ran inside the CI verify container (node:22-bookworm, user node, CI=true) at merge-ref checkout 827d376 (base HEAD^1, head HEAD^2 = 8052b31…). Harnesses live under tmp/pr8792-verify-20260809-074359/ and are rerunnable: mutants/matrix.mjs (mutation matrix; edits ci.yml in place and restores from mutants/ci.head.yml), harness/run-step.mjs (5-cell step execution), harness/ab-demo.sh, harness/escape-demo.sh, harness/semantics-probes.sh. Raw per-cell logs are in logs/. The step's bash was always extracted from ci.yml by a YAML parser, never retyped. Every mutant edit was region-scoped after a v1 harness bug (non-unique needles hitting earlier steps) produced six false deviations; the corrected matrix reproduces 32/33 predictions across two independent live runs. Evidence images were produced with scripts/verify-capture.mjs from live reruns, not from log replay.
Evidence images
Harness scripts and raw logs are in the workflow run artifacts (7-day retention).
— Qwen Code · sandboxed verification
|
Gate re-run against the post-review head — the PR has moved well beyond the commit my last pass reviewed (
Moving on to code review. 🔍 中文说明针对 review 后的最新 head 重跑关卡 —— PR 已远远超出我上一轮审查的提交(
进入代码审查。🔍 — Qwen Code · qwen3.8-max Reviewed at |
Code reviewIndependent baseline first: for "keep the Linux lane from silently losing its tmux/zip coverage" I would write the step this PR ships — an The workflow step holds up under close reading:
The pin (1203 lines) is elaborate, but it is not performative: every rule carries the mutation that escaped an earlier version, and the bash model underneath (quote/comment/continuation-aware logical lines, wrapper unwrapping with options, whole-token package matching, guard-reachability walk where No critical blockers. Non-blocking, recorded so nothing is silently dropped:
Test evidence — the PR's own CI on
|
| Check | Conclusion |
|---|---|
Classify PR |
✅ success |
Desktop Shell (ubuntu-22.04) |
✅ success |
Desktop Shell (windows-2022) |
✅ success |
Post Coverage Comment (ubuntu-latest, 22.x) |
✅ success |
Remind on force-push |
✅ success |
Test (ubuntu-latest, Node 22.x) |
✅ success |
route |
✅ success |
web-shell E2E Smoke (ubuntu-latest, Node 22.x) |
✅ success |
One row per check name (latest run); skipped checks omitted; failures sort first. / 每个检查名一行(取最新一次运行),省略 skipped,失败项排在最前。
No red checks on this commit. The skips are pre-existing by design: the macOS/Windows test jobs run only in the merge queue, and build-cli / integration lanes were routed off for this CI-only change — none of those gates is touched by this diff.
Sandboxed verification would settle what this run cannot: @qwen-code /verify — that the two apt-get install branches (root and sudo) actually install on a tool-less lane, and that the current 1203-line suite still has teeth against mutation of the new two-branch step shape. The earlier sponsored verify run targeted 8052b31, before the root branch, the budget fix, and the pin tripling, so its result no longer covers this diff.
Real-scenario testing
N/A — CI infrastructure plus a test file; nothing user-visible or TUI. Evidence carried by this comment: the PR's own CI check results and the job-log excerpts above.
中文说明
代码审查:先写独立方案 —— 我会写出的步骤与本 PR 一致(已安装 → 安装 → 警告的三分支,加 timeout 与 continue-on-error,置于 profile 步骤之后、测试之前)。本 head 超出我方案之处是双 lane 拆分(无 sudo 的 root 容器,然后是免密 sudo runner)与内部 apt 时限——两者都来自 review 且都正确。
- 分支顺序正确且实际互斥:先探测,root 容器走
[ "$(id -u)" = '0' ](此类 lane 不带 sudo,链条也确实不碰 sudo——钉子按分支强制了这一点),进入 sudo lane 前先测sudo -n true可用性(command -v sudo通过但 sudo 不可用正是当年挂住步骤的原因)。 - 守卫形态在 GitHub 的
bash -e -o pipefail下安然无恙(job 日志可见该 shell 行):每个分支都以 errexit 豁免形态收尾;内部时限 280s 小于 300s 步骤上限,命令级超时先于步骤级触发——步骤级超时没有|| echo,注释也写明了。 run:块完全不含${{ }}插值,无来自 PR 可控值的注入面;if:只读工作流内部输出。- 钉子的机制细读成立且非表演性:每条规则都注明了它封堵的逃逸,底层 bash 模型有自己的 fixture 套件(17 个测试,每个助手函数都经探针验证)。按分支钉是对双 lane 拆分的正确回应:每个分支必须独立安装全部三个包、sudo 期望正确、恰好一个 update 在恰好一个 install 之前、内部时限之和低于步骤预算。分支体允许名单封住了"无钉之处执行外来语句"这一类。我在 shipped 配对上找逃逸,没找到。
- 无关键阻塞项。不阻塞但记录在案:五轮 review 产生的 13 条非 Critical 行内建议被 autofix 的 Critical-only 过滤器延后(更深的对抗性逃逸:聚簇
-o短选项、--only-upgrade、-c配置文件、working-directory键、echo -n、背景化的结尾&、env 值展开)——针对的是钉子在恶意编辑下的深度,不是 shipped 步骤的缺陷,留待人工跟进;对 maintainer 自己的同仓库 PR 这是合理位置。常设风格问题(review 中的 R7-2):oracle 是不咨询 bash 的约 1200 行 bash 词法重实现——fixture 套件缓解了它,但该深度是否应成为未来钉子的模板是团队问题。变异表仍是作者自述。
测试证据:CI 在所审提交上已完全落定且为绿——Qwen Code CI 工作流 success,无进行中的 pull_request 事件运行。job 日志显示本 PR 的两半都真实执行:钉子套件在单元测试中通过(17 个测试),安装步骤在 bash -e -o pipefail 下运行,于 self-hosted runner 走"已安装"分支并应答 tmux 3.4。坦白说明:本 PR 的 CI 只执行了探测分支,两个 apt-get 分支未在任何 lane 运行;Debian 容器里的安装验证是作者自述,此处未独立复跑。无红色检查;跳过项均为既有设计(macOS/Windows 测试 job 仅在 merge queue 运行,build/集成 lane 被路由跳过),与本 diff 无关。
沙箱验证:@qwen-code /verify 可定夺本次运行无法定夺之事——两个 apt-get 分支在无工具 lane 上确实安装,且当前 1203 行套件对新的双分支步骤形态的变异仍有牙齿。更早的 sponsored verify 运行针对 8052b31,早于 root 分支、预算修复与钉子三倍扩充,其结论已不覆盖本 diff。
真实场景测试:N/A —— CI 基础设施加测试文件,无用户可见或 TUI 变更。
— Qwen Code · qwen3.8-max
Reviewed at 9e86d4b3ca09b03838c5b9b2e779e2b91149569e · re-run with @qwen-code /triage
|
Confidence: 4/5 — clean review across every stage on the post-review head; the point deducted is for evidence no static run can produce (the two apt-get branches have never executed on a tool-less lane, and the pin's mutation table rests on the author's word for this head). Stepping back: the step is what I would have written, and in one respect better — my baseline single-install branch would have broken on root-container lanes (no The 1203-line pin remains the part I'd think hardest about before maintaining it in six months. Reading it closely, the effort is earned rather than performative — every rule is annotated with the mutation that escaped a weaker version, the bash model underneath has its own fixture suite, and five rounds of review kept finding real escapes that each fix closed. But it is now the deepest workflow pin in the repo by a wide margin, and whether that depth should be the template for future pins is a team question, not a merge blocker for this one: it guards a step whose failure mode is silent coverage loss inside a green required check, which is precisely what shallow assertions miss. The 13 deferred non-Critical suggestions (deeper adversarial escapes) are recorded in the thread for human follow-up and none of them is a defect in the shipped step. CI is fully green and settled on the reviewed commit, the pin suite passed inside it (17 tests), and the step itself ran live on the routed lane. My earlier approval on LGTM — approving, pinned to the reviewed commit. ✅ 中文说明信心:4/5 —— 在 review 后的最新 head 上各阶段审查干净;扣掉的一分是任何静态运行都无法产生的证据(两个 apt-get 分支从未在无工具的 lane 上执行过,且钉子的变异表在本 head 上仍是作者自述)。 退一步看:这个步骤正是我会写的样子,某方面还更好——我方案里的单一安装分支会在 root 容器 lane 上坏掉(无 sudo 二进制 → exit 127 → 守卫触发 → 什么都没装),而本 head 的双分支拆分恰好封住了这一点。有界、咨询性、与相邻步骤同样门控、在已带工具的 lane 上空操作,且内部 apt 时限低于步骤上限,镜像源卡住时警告注解仍能发出。diff 中每处改动都服务于既定目标,无顺手改动;我上一轮之后落地的四条 review 发现各自修复,且修复方式随即被钉子锁定。 1203 行的钉子仍是我考虑六个月后维护它时最用力的部分。细读之下,这份用力是挣来的而非表演性的——每条规则都注明了它针对的逃逸变异,底层 bash 模型有自己的 fixture 套件,五轮 review 持续找到真实逃逸且每次修复都封堵。但它现在已是仓库中最深的工作流钉子,该深度是否应成为未来钉子的模板是团队问题,不是本 PR 的合并阻塞:它守护的步骤,其失败模式是绿色必需检查里的静默覆盖丢失,正是浅层断言会漏掉的那一类。13 条被延后的非 Critical 建议(更深的对抗性逃逸)已记录在线程中留待人工跟进,均非 shipped 步骤的缺陷。 CI 在所审提交上完全落定且为绿,钉子套件在其中通过(17 个测试),步骤本身也在被路由的 lane 上真实执行。我此前在 LGTM —— 已批准,钉在所审提交上。✅ — Qwen Code · qwen3.8-max Reviewed at |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
LGTM, looks ready to ship. ✅
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Reviewed — no blockers. Suggestions are inline. Not linted (tool limitation, not a blocker): the executable-script lint — .github/workflows/ci.yml: actionlint embedded-shell source mapping is not yet supported — not linted.
中文说明
已审查——无阻断问题。 建议见行内评论。 未检查(工具限制,非阻断):the executable-script lint — .github/workflows/ci.yml: actionlint embedded-shell source mapping is not yet supported — not linted。
— qwen3.8-max via Qwen Code /review (v0.21.8)
|
@qwen-code /takeover |
|
🤝 Takeover engaged: the autofix loop now manages this PR — it will address new review feedback and resolve base conflicts until the label is removed or the round cap is reached. Remove the 中文说明🤝 已接管:autofix 循环现在管理此 PR —— 将持续处理新的评审反馈与 base 冲突,直到移除标签或达到轮次上限。移除 |
Thirteen findings on this PR, all against the pin rather than the step, and two of them were the pin being WRONG rather than weak — those matter most, because a pin that reds a legitimate edit is worse than one that misses. Wrong: the redirect check matched `>` anywhere in an echo statement, including inside the quoted message, so a semantics-preserving reword was rejected — exactly what this file says it must not pin. And the sudo requirement demanded literal `sudo apt-get` adjacency, which rejects `sudo -n apt-get`, the sibling workflow's own convention. Both now ask the question at the right level: quoted spans are blanked before any question about shell syntax, and "runs through sudo" is asked of the raw statement. Weak, each mutation-verified against ci.yml: `&>` was split as a separator, so `echo '::warning::…' &> /dev/null` tore into a clean echo and an unchecked redirect; apt's `-o APT::Get::Simulate=true` reached simulate mode through a door the token blacklist never saw; the package pin matched the whole logical line, so a package dropped from the install survived if its token appeared in the guard's message; `apt-get update` was required by nothing; `-y` and sudo were checked only on the FIRST install statement, and nothing kept a foreign command off the install's chain to short-circuit it; the step's `shell:` key — the one key that decides which interpreter parses the entire block — was unpinned; the closing `fi` was unpinned, so deleting it passed 5/5 while bash rejects the block and the step runs nothing; and the else branch's annotation was a substring check unanchored to any emitting command, so `: '::warning::…'` muted it. The ~180 lines of bash-semantics helpers are the ORACLE every pin reasons through, and they had no tests of their own. They do now — a table per rule, each rule traceable to a mutation that escaped an earlier version. Writing them corrected one of my own assumptions: `echo a \` + newline + `b` joins to `echo a b`, not `echo ab`, because the space before the backslash survives. Also corrected the rationale in both files: they asserted in the present tense that a real-tmux suite exists in this tree. It does not — it arrives with #8388, and the tmux half of this step is deliberately pre-landed for it. Saying so is the honest version.
|
@qwen-code /review |
|
Qwen Code review request accepted. Review is queued in workflow run. |
…l fixtures Four gaps survived the first pass, each probe-verified against the suite before the fix: - an always-failing `apt-get install` prefixed in the same && chain short-circuited the real install with every pin green, because every per-statement pin binds to a statement on the line and none sees the skip — now the step must carry exactly one install statement; - `apt-get update` could be deleted or moved behind the install — now it must exist AND precede the install in statement order; - `return` and `exec` were missing from the hard-fail blacklist, the same family as `exit`/`false` under the runner's `bash -e`: either leaves the required check green with nothing installed and not even the else-branch warning emitted (sandboxed verification's F1) — now blacklisted, with an end-to-end splice fixture pinning the axis; - the guard walker's operator alphabet still split on the `&` of `&>` although statementsOf no longer does, reddening a guarded `apt-get … &> /dev/null || echo` — both now share the exemption. Also finished the bash-model fixtures round one asked for: a quote spanning a newline, and `timeout -k 1 5 sudo …` wrapper stripping. Writing them exposed one oracle bug the round's own failure scenario had measured: `set -- -e` assigns positional parameters, it does not enable errexit — `--` now ends option parsing for the check. Mutation battery against ci.yml: the five escape mutants (prefixed install, return 0, exec true, update deleted, update reordered) all flip red; the five tolerated forms (sudo -n sibling, dropped --no-install-recommends, wrapper-wrapped update, &> redirect with the guard intact, reworded warning) all stay green.
|
🤖 Addressed the latest review feedback (round 1/100). What changed, and what I pushed back on: · 已处理最新评审反馈(第 1/100 轮)。改动内容与我反驳保留之处如下: Round summaryThis round verified all thirteen round-1 inline findings against the exact code at HEAD (b3006bb already answered them), found and closed four gaps that survived that pass, and fixed F1 from sandboxed verification ( Feedback triageFixed in this commit (b513c97), each probe-verified red BEFORE the fix:
Verified already fixed at HEAD — no change needed this round:
Deferred — cannot be addressed from this mode:
ChangesOne file: Mutation battery (scratch runs against ci.yml, restored after each)
Bash probes backing the oracle fixes: Verification
中文说明本轮摘要本轮对照 HEAD 的精确代码逐条核验了一轮的 13 条行内发现(b3006bbbd0 已对它们作出回应),发现并堵上了那一轮之后仍存活的四处缺口,并修复了沙箱验证报告中的 F1( 反馈分类在本提交(b513c9704a)中修复,且每一条都在修复前经过探针验证为红:
已核验在 HEAD 上修复——本轮无需改动:
延后——本模式无法处理:
改动单个文件: 变异电池(对 ci.yml 的临时运行,每次运行后还原)
支撑 oracle 修复的 bash 探针: 验证
Base-conflict check · 基分支冲突检查: no conflict with main. · 与 main 无冲突。 Re-review when you have a moment. After round 100 this bot stops and leaves the PR for a human. · 有空请复审;第 100 轮后本 bot 停止并将 PR 交给人工。 🧠 Handled by Qwen Code · model/模型 |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Reviewed — no blockers. Suggestions are inline. Not linted (tool limitation, not a blocker): the executable-script lint — .github/workflows/ci.yml: actionlint embedded-shell source mapping is not yet supported — not linted.
中文说明
已审查——无阻断问题。 建议见行内评论。 未检查(工具限制,非阻断):the executable-script lint — .github/workflows/ci.yml: actionlint embedded-shell source mapping is not yet supported — not linted。
— qwen3.8-max via Qwen Code /review (v0.21.8)
Seven of sixteen findings, chosen by one question: would an honest edit hit this? Each is mutation-verified against ci.yml. - apt's long option spellings reached simulate mode past a `-o`-anchored regex (`--option APT::Get::Simulate=true`, `--simulate=yes`), as did `--version`/`--help`, which print and exit having installed nothing. - The chain allowlist checked only a statement's leading word, so `&& sudo apt-get remove -y tmux` appended after the install passed while undoing it. It checks the subcommand now. - `apt-get update` was pinned as load-bearing but never required to run through sudo, though that is the whole reason the install is. - A quoted redirect target (`>'/dev/null'`) muted an annotation: blanking quoted spans leaves the operator, so the pin matches the operator itself. - The already-installed branch's BODY was unpinned — emptying it to `:` left a broken-but-installed tmux undetected on the lane that takes it. - The step was pinned before the test step but not AFTER the `ci_profile` step its `if:` reads; above it the condition is always false and the step silently never runs. - The separator alphabet was encoded three times and the copies had already drifted. One definition now, used by all three consumers: a model of bash that depends on which function you ask is not a model. The nine I did not take are recorded on their threads with reasons. Most are adversarial rewrites — swapping branch bodies, wrapping the block in a never-executing loop, nesting a conditional inside a pinned branch, hiding the chain in a heredoc — and this file cannot win that game: anyone who can restructure the step can also delete it. It guards accidental drift, which is the failure that actually happens. One asks for a composite action shared with qwen-autofix.yml; that is a repo-wide refactor and does not belong in this PR.
doudouOUC
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/ci.yml: actionlint embedded-shell source mapping is not yet supported — not linted.
中文说明
已审查。 建议见行内评论。 未检查(工具限制,非阻断):the executable-script lint — .github/workflows/ci.yml: actionlint embedded-shell source mapping is not yet supported — not linted。
— deepseek-v4-flash via Qwen Code /review (v0.21.8)
doudouOUC
left a comment
There was a problem hiding this comment.
Round 1 review (deepseek-v4-flash) found 0 Critical, 4 Suggestions on .github/workflows/ci.yml.
Findings:
- [Suggestion] R3-1
ci.yml:478: Usesudo -ninstead ofsudoso passwordless-sudo failures fail fast instead of hanging stdin for 5 minutes. - [Suggestion] R3-2
ci.yml:470: Pre-flight sudo usability withsudo -n true 2>/dev/nullin theelifcondition;command -v sudoalone is not enough. - [Suggestion] R3-3
ci.yml:463: Step-level timeout kills the|| echoguard before it can emit::warning::; consider a command-leveltimeoutwrapper so the guard runs. - [Suggestion] R3-4
ci.yml:469: Already-installed branch only verifiestmux -V; addzip -v/unzip -vchecks with their own warning guards.
— qwen-review subagent, round 1 only (deepseek-v4-flash)
…hree tools Four review findings, all about the step rather than its pin, and all four taken — they are the shapes a real runner produces. `sudo` becomes `sudo -n`, the convention nine other steps in this file and the sibling workflow already use: without it, a runner without passwordless sudo gets a password PROMPT on stdin and the step hangs to its timeout instead of falling through to the warning. The `elif` tests sudo's USABILITY (`sudo -n true`) rather than its presence: `command -v sudo` passes on a runner where sudo exists and cannot be used, which takes the install branch straight into that hang. The apt calls carry their own `timeout 280` inside the step's 5-minute bound. When the STEP timeout fires there is no `|| echo` — the lane loses its tooling and says nothing. A shorter command-level bound lets the guard run and the annotation reach the check UI. The already-installed branch verifies zip and unzip too, not just tmux: it is taken when all three are present, so a broken-but-present zip failed the packaging suite with no warning explaining why. The pins moved with the step: "runs through sudo" now walks the wrapper prefix (a bounded install is `timeout 280 sudo -n apt-get …`, and requiring sudo FIRST would have redded it), the elif equality carries the new condition, and all three tool probes are pinned to exist AND to be guarded. That last pin needed a second pass: matching by substring reported the zip probe present after deletion, because `unzip -v` contains `zip -v` — it is word-anchored now, and each of the three deletions turns it red.
doudouOUC
left a comment
There was a problem hiding this comment.
Code Review — qwen deepseek-v4-flash
Verdict: Comment — 4 Suggestions, 0 Criticals.
Findings
R3-1 — Use sudo -n instead of sudo (.github/workflows/ci.yml:478)
sudo without -n can hang on self-hosted runners without passwordless sudo. The project convention is sudo -n (9 other usages in ci.yml). Suggested fix: change both sudo apt-get to sudo -n apt-get.
R3-2 — Pre-flight sudo usability in elif condition (.github/workflows/ci.yml:470)
command -v sudo passes even when sudo is present but not usable. Suggested fix: add sudo -n true 2>/dev/null to the elif condition.
R3-3 — Step-level timeout prevents ::warning:: guard (.github/workflows/ci.yml:463)
When the step-level timeout fires, the || echo guard never executes and the ::warning:: annotation is lost. Suggested fix: wrap apt-get commands with timeout 280.
R3-4 — Already-installed branch only verifies tmux (.github/workflows/ci.yml:469)
Only tmux -V is verified, not zip -v or unzip -v. Suggested fix: add zip -v and unzip -v checks with their own || echo '::warning::' guards.
Model: deepseek-v4-flash · Reviewed SHA: 6d80258
doudouOUC
left a comment
There was a problem hiding this comment.
Code Review — qwen deepseek-v4-flash
Verdict: Comment — 4 Suggestions, 0 Criticals.
Findings
R3-1 — Use sudo -n instead of sudo (.github/workflows/ci.yml:478)
sudo without -n can hang on self-hosted runners without passwordless sudo. The project convention is sudo -n (9 other usages in ci.yml). Suggested fix: change both sudo apt-get to sudo -n apt-get.
R3-2 — Pre-flight sudo usability in elif condition (.github/workflows/ci.yml:470)
command -v sudo passes even when sudo is present but not usable. Suggested fix: add sudo -n true 2>/dev/null to the elif condition.
R3-3 — Step-level timeout prevents ::warning:: guard (.github/workflows/ci.yml:463)
When the step-level timeout fires, the || echo guard never executes and the ::warning:: annotation is lost. Suggested fix: wrap apt-get commands with timeout 280.
R3-4 — Already-installed branch only verifies tmux (.github/workflows/ci.yml:469)
Only tmux -V is verified, not zip -v or unzip -v. Suggested fix: add zip -v and unzip -v checks with their own || echo '::warning::' guards.
Model: deepseek-v4-flash · Reviewed SHA: 6d80258
|
🔀 Base updated: red check(s) [Test (ubuntu-latest, Node 22.x)] pass on current main — merged current main via update-branch; CI will re-run. 中文说明🔀 已更新 base:红色检查 [Test (ubuntu-latest, Node 22.x)] 在当前 main 上通过 —— 已通过 update-branch 合入当前 main,CI 将重新运行。 |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Reviewed. Suggestions are inline. Not reviewed: reverse audit — reached the 5-round hard cap while round 5 was still reporting new findings; two-consecutive-dry convergence was not reached. Not explored to full depth (tool budget reached): This PR adds a CI step in .github/workflows/ci.yml inst...: could not run npx vitest run scripts/tests/capture-tmux-ci.test.js (no node_modules in the review worktree; full monorepo install out of budget) — so the suit…; This PR adds a CI step in .github/workflows/ci.yml inst...: could not execute npx vitest run scripts/tests/capture-tmux-ci.test.js — the review worktree has no node_modules and a full monorepo install exceeded my too…. Not linted (tool limitation, not a blocker): the executable-script lint — .github/workflows/ci.yml: actionlint embedded-shell source mapping is not yet supported — not linted.
中文说明
已审查。 建议见行内评论。 未审查:reverse audit — reached the 5-round hard cap while round 5 was still reporting new findings; two-consecutive-dry convergence was not reached。 未探索到全部深度(达到工具调用预算):This PR adds a CI step in .github/workflows/ci.yml inst...:could not run npx vitest run scripts/tests/capture-tmux-ci.test.js (no node_modules in the review worktree; full monorepo install out of budget) — so the suit…;This PR adds a CI step in .github/workflows/ci.yml inst...:could not execute npx vitest run scripts/tests/capture-tmux-ci.test.js — the review worktree has no node_modules and a full monorepo install exceeded my too…。 未检查(工具限制,非阻断):the executable-script lint — .github/workflows/ci.yml: actionlint embedded-shell source mapping is not yet supported — not linted。
— qwen3.8-max via Qwen Code /review (v0.21.8)
|
🤖 Addressed the latest review feedback (round 5/100). What changed, and what I pushed back on: · 已处理最新评审反馈(第 5/100 轮)。改动内容与我反驳保留之处如下: Round-6 summary — PR #8792All 13 inline findings from the round-5 automated review are resolved in Dispositions
One extra hole found while implementing the allowlist, closed in the same Verification evidenceEvery filed escape was probe-verified against the new suite by mutating Verification
中文说明第 6 轮总结 — PR #8792第 5 轮自动审查的 13 条行内发现全部已在代码中解决(单个提交: 各项处置
实施 allowlist 时另发现一个洞,同批关闭:以允许词命名的函数定义 验证证据每个已归档逃逸都经过实测:就地变异 验证
Base-conflict check · 基分支冲突检查: no conflict with main. · 与 main 无冲突。 Re-review when you have a moment. After round 100 this bot stops and leaves the PR for a human. · 有空请复审;第 100 轮后本 bot 停止并将 PR 交给人工。 🧠 Handled by Qwen Code · model/模型 |
|
🔀 Base updated: red check(s) [Test (ubuntu-latest, Node 22.x)] pass on current main — merged current main via update-branch; CI will re-run. 中文说明🔀 已更新 base:红色检查 [Test (ubuntu-latest, Node 22.x)] 在当前 main 上通过 —— 已通过 update-branch 合入当前 main,CI 将重新运行。 |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Reviewed. Suggestions are inline. Not reviewed: reverse audit — reached the 5-round hard cap while round 5 was still reporting new findings; two-consecutive-dry convergence was not reached. Not explored to full depth (tool budget reached): PR #8792 adds a bounded, advisory CI step installing tmux...: actionlint could not be executed locally (binary not installed on this runner); assessed statically instead — the new run block uses only syntax already passing…; PR #8792 adds a bounded, advisory CI step installing tmux...: actionlint/shellcheck not run locally (binaries absent; CI's lint.js --setup installs them) — the step's shell idioms mirror pre-existing steps that pass thos…; PR #8792 adds a bounded, advisory CI step installing tmux...: no live ubuntu-latest lane execution — install-branch runtime behavior (hosted-runner sudo/apt paths) verified by reading, not by running GitHub-hosted runners.. Not linted (tool limitation, not a blocker): the executable-script lint — .github/workflows/ci.yml: actionlint embedded-shell source mapping is not yet supported — not linted.
中文说明
已审查。 建议见行内评论。 未审查:reverse audit — reached the 5-round hard cap while round 5 was still reporting new findings; two-consecutive-dry convergence was not reached。 未探索到全部深度(达到工具调用预算):PR #8792 adds a bounded, advisory CI step installing tmux...:actionlint could not be executed locally (binary not installed on this runner); assessed statically instead — the new run block uses only syntax already passing…;PR #8792 adds a bounded, advisory CI step installing tmux...:actionlint/shellcheck not run locally (binaries absent; CI's lint.js --setup installs them) — the step's shell idioms mirror pre-existing steps that pass thos…;PR #8792 adds a bounded, advisory CI step installing tmux...:no live ubuntu-latest lane execution — install-branch runtime behavior (hosted-runner sudo/apt paths) verified by reading, not by running GitHub-hosted runners.。 未检查(工具限制,非阻断):the executable-script lint — .github/workflows/ci.yml: actionlint embedded-shell source mapping is not yet supported — not linted。
— qwen3.8-max via Qwen Code /review (v0.21.8)
|
🤖 AutoFix updated a stale base — the fix did not pass verification, but this PR was behind What I found before stopping: See the Qwen Autofix agent step logs for model/tool output. Run log: https://github.com/QwenLM/qwen-code/actions/runs/31394772225 🧠 Handled by Qwen Code · model/模型 |
|
🔀 Base updated: red check(s) [Test (ubuntu-latest, Node 22.x)] pass on current main — merged current main via update-branch; CI will re-run. 中文说明🔀 已更新 base:红色检查 [Test (ubuntu-latest, Node 22.x)] 在当前 main 上通过 —— 已通过 update-branch 合入当前 main,CI 将重新运行。 |
|
🤖 Reviewed the latest feedback — no changes needed. Why, point by point: · 已审阅最新反馈——无需改动。逐点说明原因如下: Autofix review round: no action takenThis round found no actionable feedback, so no code changes were made and nothing was committed. Why: The PR has completed five change-producing review rounds and is now in Critical-only mode. All feedback received since the last evaluation is non-Critical and was deferred to the audit record (13 inline suggestions plus 1 review summary). Per the round rules, deferred items are not code-modified, resolved, or replied to in this round; they remain open for human follow-up. Round inputs checked:
Deferred (not addressed, still open for humans):
中文说明Autofix 审查轮次:未采取任何操作本轮未发现可执行的反馈,因此没有修改任何代码,也没有提交任何内容。 原因: 该 PR 已完成 5 个产生改动的审查轮次,目前处于仅处理 Critical 的模式。自上次评估以来收到的所有反馈均为非 Critical,已被延后至审计记录(13 条行内建议加 1 条审查总结)。按照本轮规则,延后的条目在本轮中不修改代码、不关闭线程、也不回复评论;它们保持开放,留待人工跟进。 本轮检查的输入:
已延后(未处理,保持开放待人工跟进):
Deferred non-Critical feedbackCritical-only mode is active after 5 change-producing rounds. The workflow excluded the non-Critical feedback below from this round's actionable sections; the items remain open for human follow-up. Maintainer feedback is deferred only after its author has used 2 regular feedback batches in this window's Critical-only tail; authors at that budget, if any, are named below. (
中文说明完成 5 个产生改动的轮次后进入仅处理 Critical 的模式。本轮可执行区域已排除下方非 Critical 反馈;这些条目保持开放,留待人工跟进。维护者反馈仅在其本人于本窗口 Critical-only 阶段已使用 2 批常规反馈预算后才会延后;达到预算的作者(如有)在下方点名。(评论 Base-conflict check · 基分支冲突检查: no conflict with main. · 与 main 无冲突。 🧠 Handled by Qwen Code · model/模型 |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Reviewed. Suggestions are inline.
Not reviewed: reverse audit — reached the 5-round hard cap while round 5 was still reporting new findings; two-consecutive-dry convergence was not reached.
Not explored to full depth (tool budget reached): PR #8792 adds a bounded, advisory CI step to .github/work...: none — all planned checks completed. (A full vitest run was impossible without node_modules. The harness measurement is a deterministic alternative covering the…; PR #8792 adds a bounded, advisory CI step to .github/work...: verifying the exit code of apt-get install -y --only-upgrade <not-installed package> as root (no root/passwordless sudo on this runner) — the uncertainty is d…; You are review agent reverse-audit — Reverse audit agen...: none — all candidate checks completed within budget..
Not linted (tool limitation, not a blocker): the executable-script lint — .github/workflows/ci.yml: actionlint embedded-shell source mapping is not yet supported — not linted.
中文说明
已审查。 建议见行内评论。
未审查:reverse audit — reached the 5-round hard cap while round 5 was still reporting new findings; two-consecutive-dry convergence was not reached。
未探索到全部深度(达到工具调用预算):PR #8792 adds a bounded, advisory CI step to .github/work...:none — all planned checks completed. (A full vitest run was impossible without node_modules. The harness measurement is a deterministic alternative covering the…;PR #8792 adds a bounded, advisory CI step to .github/work...:verifying the exit code of apt-get install -y --only-upgrade <not-installed package> as root (no root/passwordless sudo on this runner) — the uncertainty is d…;You are review agent reverse-audit — Reverse audit agen...:none — all candidate checks completed within budget.。
未检查(工具限制,非阻断):the executable-script lint — .github/workflows/ci.yml: actionlint embedded-shell source mapping is not yet supported — not linted。
— qwen3.8-max via Qwen Code /review (v0.21.8)
|
@qwen-code /triage |
|
Sandboxed verification: ✅ passed — merge-ready (agent verdict) - workflow run Ran the PR in an isolated, token-free container: A/B against the base build, mock-free harness assertions, targeted gates. Advisory evidence for human reviewers — not a review, an approval, or a CI check. Scripted assertions: 122 passed · 0 failed · 122 total 中文 — 判定:✅ 通过 · 可合入(agent 判定)沙箱验证在隔离、无凭证的容器中执行了该 PR 的代码(与 base 构建 A/B 对照、无 mock harness 断言、定向门禁)。仅作为评审证据,不构成评审、批准或 CI 检查。 脚本断言:122 通过 · 0 失败 · 122 总计 Verification reportPR 8792 deep verification (round 2) —
|
| # | finding | severity | status at new head |
|---|---|---|---|
| F1 | the pin's hard-fail blacklist misses return and exec — splicing either as the first run line silently disabled the whole step under the runner's bash -e while the pin stayed green |
Suggestion | fixed. HARD_FAIL is now `/^(exit(\s.*)? |
Central claim and A/B
Central claim: the pin suite (scripts/tests/capture-tmux-ci.test.js, now 17 tests: 11 bash-model + 6 pin) is load-bearing — green on the shipped ci.yml, red on any edit that would silently stop the Linux lane installing tmux/zip/unzip or change the step's advisory/bounded contract.
| arm | ci.yml | oracle | result |
|---|---|---|---|
| HEAD | step present (shipped) | suite exit code | 17/17 passed, exit 0 |
| BASE (control) | git show HEAD^1:.github/workflows/ci.yml (no step) |
suite exit code | 6 failed | 11 passed, exit 1 — existence test fails with a clean assertion (expected -1 to be greater than -1), the other five pin tests fail on the absent step |
Witness: 01-ab-head-vs-base.png. The base arm failing as predicted is the load-bearing proof. Raw logs: logs/01-head-suite.txt, logs/02-base-arm-step-removed.txt. Harness: harness/ab.sh.
Mutation matrix (52 mutants, replayed at the new head)
Every mutant was applied only inside the install-step region of .github/workflows/ci.yml, the suite run, and the pristine file restored. The PR description's 20-row table was adapted to the current step shape (22 mutants, M01–M20b), plus the two round-1 escapes re-measured, plus 21 new-architecture escapes (X01–X23) and 7 tolerance probes (T01–T07) for legitimate edits that must stay green:
| class | mutants | result |
|---|---|---|
| PR table mutants (M01–M20b) | 22 | 22/22 red |
round-1 F1 escapes: return 0, exec true (X01–X02), plus exit $? (X03) |
3 | 3/3 red (was the F1 escape; fixed) |
new-architecture escapes (X04–X23): root branch reaching for sudo, branch bodies/conditions swapped, probe deletion, body emptied to :, foreign sleep 295, inner bounds 400+400 > 300s budget, timeout 0, guard→|| true, per-branch package drop, update-after-install, second install prefixed, APT_CONFIG env, fi deleted, stray statement after fi, step moved below tests / above ci_profile, runner.os→macOS, shell: pwsh, unbounded apt call |
20 | 20/20 red |
tolerance probes (T01–T07): warning reworded with ;/parens kept ::warning::, --no-install-recommends dropped, DEBIAN_FRONTEND env prefix, version-pinned tmux=1:3.5-1build1, bounds tightened 140→120, extra comment, install-failure message reword |
7 | 7/7 green, as required |
| pristine control after the run | 1 | green (17/17) |
Witness: 02-mutation-matrix-live.png (full live run). Data: mutants/matrix-results.json, harness mutants/matrix.mjs (rerunnable), log logs/03-matrix-run.txt.
Methodology note (transparency): the first matrix run had one invalid cell — X20 (step moved above ci_profile) searched for unquoted id: ci_profile while the file ships id: 'ci_profile', so the harness inserted the step at the top of the file, producing a YAML parse error that reddened the suite for the wrong reason. The mutant was fixed, the dry-run confirmed the moved file parses and the pin fails with the intended assertion (expected 4 to be greater than 5), and the whole matrix was re-run; the published numbers are from the corrected run (0 deviations). All other cells of run 1 matched run 2 byte-for-byte.
Step execution cells (the step itself, verbatim)
The run: block was extracted via a YAML parser and executed under bash --noprofile --norc -eo pipefail (the runner's default shell) in eight lane shapes. Stubs stand in for the environment (apt behaviour, sudo policy, uid, preinstalled tools); the script under test runs for real in every cell, through the real timeout binary. Witness: 03-step-execution-cells.png. Raw: logs/04-step-cells.txt, harness harness/run-step.mjs.
| cell | environment | result |
|---|---|---|
| live-else | this container as-is, no stubs (uid 1000, no tmux/zip/sudo; unzip present) | pass (5 checks): exit 0, exactly one ::warning::tmux/zip unavailable… at stdout line start, clean stderr, no apt call |
| root-install | stub id→0, healthy apt-get |
pass (7): update -qq then install -y -qq --no-install-recommends tmux zip unzip, no sudo anywhere, no annotation |
| root-apt-fail | uid 0, apt-get exits 100 | pass (4): guard fires (::warning::tmux/zip install failed…), exit still 0, chain short-circuits after the failing update |
| sudo-install | uid 1000, passwordless sudo | pass (6): sudo -n true probe first, both apt calls delegated as -n apt-get update -qq / -n apt-get install …, apt never invoked bare |
| sudo-unusable | sudo present, sudo -n true exits 1 |
pass (3): no hang, falls through to else warning, no apt delegation attempted |
| all-present | three healthy tool stubs | pass (4): exit 0, no annotation, no :: workflow command on stdout (tmux -V prints its version by design), no apt call |
| broken-tmux | present, tmux -V exits 1 |
pass (2): advisory annotation only, exit still 0 |
| broken-zip | present, zip -v exits 1 |
pass (2): advisory annotation only, exit still 0 |
Total: 33/33 checks. The live cell is also a direct sample of this lane's runtime: the verify container itself (node:22-bookworm, no tmux/zip/sudo) takes the else branch and emits the annotation, exit 0 — the step cannot red the check here.
Findings
None. No blocking or suggestion-level defects survived the matrix, the cells, or the gates. The two items below are description/comment corrections, not code problems.
Corrections
- The PR body's test-plan count is stale. "Run the pin → 5 passed" was true at round 1's head. The current head carries 17 tests (11 bash-model tests added in round 2 plus the round-2 splice test); executing the exact command from the Reviewer Test Plan yields
Test Files 1 passed (1) / Tests 17 passed (17). The plan's mutation-table instructions remain valid verbatim. - Step comment wording ("ships neither") understates the throw condition. The ci.yml comment says the install-script suite "throws on a CI host that ships neither" zip/unzip; the actual condition throws when either is missing (
zipAvailablerequires both) — measured live: this container ships unzip but not zip, and the suite threw. The comment's conclusion ("a silent skip there is impossible") holds regardless; no behaviour is affected.
Informational (not findings): shellcheck --enable=all --severity=style on the extracted block reports two NOTE-level items, both on the new root-branch condition [ "$(id -u)" = '0' ] — SC2292 (prefer [[ ]]) and SC2312 ($(id -u) inside [ ] masks its return value). Both are benign by design (a failing id falls through to the next branch, matching the advisory contract), below warning severity, and outside the repo's shellcheck gate scope (embedded run blocks are not tracked shell files).
Not covered
- Real
apt-get installexecution — this container runs as uid 1000 with no sudo, so the install branches were driven through PATH stubs (branch selection, exact argv tokens, guard behaviour, wrapper pass-through all executed for real); only the else branch ran fully live. The author's Debian-container install claim was not re-run here. The ubuntu-latest lane ships sudo + apt-get (round-1 external image check), so the sudo branch is the one the real Test lane takes. - This PR's own CI job logs (
Install tmux and zip toolingvisible in theTest (ubuntu-latest)run, per the Reviewer Test Plan) — no GitHub token in this environment. Substituted by the live-lane-sample cells above and the wiring trace below. - yamllint — the container has no pip module at all (
python3 -m pip: "No module named pip";pip3: permission denied), so the pinned yamllint 1.35.1 could not be installed (same restriction as round 1). Mitigations: actionlint (which parses every workflow YAML) passed with liveness proven; theyamlpackage parsedci.ymlsuccessfully in every harness; CI runs yamllint on the lane (ci.yml lint job). - Per-commit attribution — the checkout is depth 2 (
.git/shallowlists both parents);git rev-list HEAD^1..HEAD^2returns1(a shallow-boundary artifact) against the snapshot's 14 commits, so individual commits were unreachable. The aggregateHEAD^1..HEADdiff (exactly two files:ci.yml+49,capture-tmux-ci.test.js+1203) is what this report verifies. - Hung-apt wall clock — a stalled apt call is bounded by
timeout 140inside the 300s step cap; waiting out a real 140s kill was out of budget. Thetimeoutwrapper passed through in every root/sudo cell, and the worst-case duration+kill-after model was measured directly (P12 below). - Windows/macOS lanes — the step is gated
runner.os == 'Linux'; the pin is platform-independent text parsing and not in the Windows exclude list. - Repo-wide workspace unit suites (
npm test --workspaces) — out of scope: the diff touches no package code; CI covers those in its own job.
Claim traces (secondary claims, confirmed)
- Wiring: the pin file matches the vitest include glob (
scripts/tests/**/*.test.{js,ts}, not in any exclude list),test:scriptsis chained fromtest:ci, and theRun tests and generate reportsstep runsnpm run test:ci— so the pin executes in the same Test job, after the install step it pins (step order and theci_profileordering are themselves pinned, mutants X19/X20). Confirmed live: the full suite log shows✓ scripts/tests/capture-tmux-ci.test.js (17 tests). hasTmuxpremise: the gated real-tmux suite is still not on this tree (the onlyhasTmuxmentions are the pin file's own comments) — the "pre-landed for feat(review): capture-tui — rendering claims get pixels, not prose (Phase 2) #8388" rationale in the step comment is accurate at this head.install-script.test.jsthrows, never silently skips: reproduced live in this container (CI=true, zip missing): the full suite collected 1016/1016 passing tests while exactly that file failed collection with the loud module-load throw. Attribution proven pre-existing: the file is byte-identical atHEAD^1and the PR touches only two files. On the Test lane (zip + unzip shipped) it cannot fire — if anything this is a live demonstration of the outage the step exists to prevent.- Sibling convention:
qwen-autofix.ymlstill installs tmux the same way (Install tmuxsteps at lines 979/3652,sudo -n apt-get … || { … }), matching the step's comment. - Bash model: 13/13 probes against real bash (
logs/05-semantics-probes.txt), including: comments never continue (;#note \splice), escaped-backslash non-continuation, quoted-newline fold, trailing&&/||/|continuation vs&background,return 0under-eaborting,exec truesilent exit,breakoutside a loop NOT aborting (excluded from HARD_FAIL correctly),set -- -enot enabling errexit,&>as a redirection. P2b additionally measured that the M19 splice shape (then;#note \) is a bash syntax error (rc=2) — bash rejects the whole block there; the pin catches the mutant either way. P12 measuredtimeout -k 1 1against a TERM-ignoring process at 2002ms wall (duration + kill-after, exit 137 on this coreutils — nonzero either way, so the step's|| echoguard fires), corroborating thetimeoutSecondsworst-case model the budget pin relies on.
Gates
| gate | scope | result |
|---|---|---|
full test:scripts suite |
HEAD | 1016/1016 tests passed (51 files); 1 pre-existing env collection throw (install-script.test.js, attribution above) |
| actionlint (all workflows, repo flags) | HEAD | clean; liveness proven — a planted bad ${{ && }} expression in a scratch workflow was flagged, then removed |
shellcheck repo gate (scripts/lint.js --shellcheck) |
HEAD | exit 0; all 2429 note/style lines are pre-existing in unrelated packages (packages/cua-driver, packages/desktop); the PR adds no shell files and the embedded block is outside the gate's file list |
shellcheck on extracted step (--enable=all --severity=style) + bash -n |
HEAD | zero warning-or-higher findings (2 informational notes, see Corrections); bash -n clean; liveness proven via planted unquoted variable |
eslint (lint:ci equivalent, repo + integration-tests, --max-warnings 0) |
HEAD | clean; liveness proven — a planted unused variable in a scripts/tests/*.js file was flagged. (Note: the run flagged scratch files this verification itself placed under tmp/, which is gitignored but absent from eslint's global ignores — CI never has tmp/ at lint time; pre-existing repo behaviour, not a PR issue.) |
prettier --check on both changed files |
HEAD | clean |
Witness for probes + vacuity: 04-semantics-and-vacuity.png. Vacuity detail: V1 (revert HARD_FAIL to the round-1 regex → splice test red with the intended assertion, model tests unaffected) and V2 (runsThroughSudo → always-false → both the sudo model test and the ci.yml sudo-branch pin fail) — 9/9 checks, proving the new tests pin what they claim.
Methodology
Verification ran inside the CI verify container (node:22-bookworm, uid 1000, no tmux/zip/sudo, unzip present, CI=true) at merge-ref checkout b392f83e9c (base HEAD^1 = 9bbb11ac, head HEAD^2 = 9e86d4b3). Harnesses live under tmp/pr8792-verify-20260810-174233/ and are rerunnable: harness/ab.sh, mutants/matrix.mjs (region-scoped edits, pristine restored from mutants/ci.head.yml after every mutant), harness/run-step.mjs (8 cells), harness/semantics-probes.sh, harness/vacuity.mjs. The step's bash was always extracted from ci.yml by a YAML parser, never retyped. Three harness defects were found and fixed mid-round, each disclosed above with its re-run: the X20 needle (invalid cell, whole matrix re-run), step-cell log appending + an over-strict stdout oracle (33/33 after fix), and two over-specific probe assertions (P2 reconstructed without a confounding syntax error; P12 asserting wall-time, the model under test, rather than timeout's exit code). Counts include only final-run results; expected-red control outcomes are encoded as passing assertions. Evidence images were produced with scripts/verify-capture.mjs from live runs. The working tree was left clean (git status empty).
Evidence images
Harness scripts and raw logs are in the workflow run artifacts (7-day retention).
— Qwen Code · sandboxed verification
|
🤖 Reviewed the latest feedback — no changes needed. Why, point by point: · 已审阅最新反馈——无需改动。逐点说明原因如下: Autofix review round: no actionable feedback — no changes made This round ran in Critical-only mode (active after five change-producing review rounds on this PR). In this mode, non-Critical automated-reviewer suggestions are deferred to an audit record for human follow-up, and only Critical findings, failed checks, maintainer requests, and base-conflict resolution remain actionable. Triage result for this round:
Result: no code changes and no commits this round. A 中文说明Autofix 审查轮次:无可处理反馈 — 未做任何改动 本轮运行于 仅处理 Critical 模式(该 PR 已完成五个产生改动的审查轮次后自动激活)。在此模式下,自动审查器提出的非 Critical 建议会被延后到审计记录中留待人工跟进,只有 Critical 级别的问题、失败的检查、维护者的请求以及与基础分支的冲突解决仍然属于可处理范围。 本轮分诊结果:
结果:本轮没有任何代码改动,也没有任何提交。如果维护者希望重新考虑那些被延后的建议,可以评论 Deferred non-Critical feedbackCritical-only mode is active after 5 change-producing rounds. The workflow excluded the non-Critical feedback below from this round's actionable sections; the items remain open for human follow-up. Maintainer feedback is deferred only after its author has used 2 regular feedback batches in this window's Critical-only tail; authors at that budget, if any, are named below. (
中文说明完成 5 个产生改动的轮次后进入仅处理 Critical 的模式。本轮可执行区域已排除下方非 Critical 反馈;这些条目保持开放,留待人工跟进。维护者反馈仅在其本人于本窗口 Critical-only 阶段已使用 2 批常规反馈预算后才会延后;达到预算的作者(如有)在下方点名。(评论 Base-conflict check · 基分支冲突检查: no conflict with main. · 与 main 无冲突。 🧠 Handled by Qwen Code · model/模型 |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
LGTM, looks ready to ship. ✅
yiliang114
left a comment
There was a problem hiding this comment.
LGTM. The step design is right for the failure mode it targets: a missing tool used to silently remove coverage inside a green required check, so the install is installed-and-pinned rather than gated — bounded (5-minute step cap, per-apt 140s bounds summing under it so the || echo ::warning:: guard always gets to run), advisory (continue-on-error, sudo -n to fail fast instead of hanging on a password prompt), and every branch ends in a ::warning:: annotation at line start on stdout so a lane that permanently loses the tooling shows it in the check UI. The pre-landing of tmux for #8388's skipIf-gated real-tmux suite is deliberate and documented. The pin is genuinely well-built: it parses the step the way bash does (quote-aware so # and ; inside messages don't mislead, continuation-aware, wrapper-aware through sudo/env/nice/timeout/subshells), pins the if-condition whole, the bounded+advisory step keys, branch reachability, the no-op flags (-s/--download-only), errexit in every spelling, whole-token package args, and that no branch may hard-fail the required check. CI green on head including the new pin. Nothing blocks merge.
|
Released in v0.21.10. |
|
Consolidated response to the 52 open review threads (rounds 2–7). Individual replies hit GitHub's secondary rate limit earlier in this work; one comment carries the same information, and the threads are resolved individually. What was real, and is fixedThe inner bounds could exceed the step cap. Two sequential Root lanes were also unreachable: Where I am drawing the line, and whyThe rest are escapes: a construct that would slip past the pin if someone wrote the step that way. Each is real. I am not taking them, and the reason is visible in the artifact itself — this PR now carries 1203 lines of pin for a 40-line shell step, across 14 commits and seven review rounds, and each round produces another dozen. The pin's job is to catch accidental drift: a flag dropped, a condition mistyped, a package renamed, a branch deleted, a bound that no longer fits. Every one of those is covered and mutation-verified. What the remaining findings describe is a step deliberately restructured to defeat its own test — a heredoc hiding the chain, a never-executing loop around the block, an I would rather this PR merge with a pin a reader can hold in their head than keep adding rounds to a file that is now thirty times the size of its subject. If the remaining escapes matter to the reviewer, the better answer is not more regex — it is a smaller step (a composite action shared with Findings recorded as declined with this reasoning: R2-2, R2-6 (×3), R2-8, R2-9, R2-13, R4-1 through R4-5, R5-1 (×3), R6-x and R7-x escape variants. Findings already closed by the commits on this branch: the quoted-target bypass, the wrapper-option strip, the 中文说明对 52 条未解决线程(第 2–7 轮)的合并答复:逐条回复此前触发过 GitHub 二次限流;线程已逐个 resolve。 真实且已修:两段串行 其余不再跟进的理由:它们都是"逃逸"——把步骤写成某种形状就能绕过钉子。每条都成立,但本 PR 现在是 1203 行钉子守 40 行 shell 步骤,14 个提交、七轮评审,每轮再产出十余条。钉子的职责是拦住意外漂移(少个标志、条件笔误、包名改动、分支被删、界不再匹配),这些都已覆盖并逐一变异验证。剩下的描述的是"刻意改写步骤以击败它自己的测试"——能这么做的人同样可以直接删掉步骤,没有钉子挡得住。若这些逃逸确实重要,正确答案不是更多正则,而是更小的步骤(与 |
A verifier ruling on "the panel clips at 80 columns" reads the layout code and imagines a terminal; measured on this repo, the imagining is where rendering verdicts go wrong. `qwen review capture-tui` makes the terminal real and the evidence a file: it drives a command in a throwaway tmux and hands back what actually rendered — `.ans` pane text always, a `.png` when freeze is available, and a manifest saying which rung it reached and why. Isolation is structural, not disciplined. Every tmux call is scoped to a per-run private server (`-L`), so a capture cannot see — let alone resize or kill — the user's own sessions; the measured failure mode of desktop-automation verification was exactly "drives the user's own windows". `kill-server` reaps everything the capture started, a signal handler reaps before re-raising, and the pane holder carries its own watchdog so an unreaped server still self-terminates. Evidence is never invented. The manifest records the rung and the reason it stopped there, so a verdict can say what it stands on. A png is credited only when this run's render demonstrably produced it; artifact paths are stamped by identity, so nothing this run did not write is ever deleted or claimed; and a path already occupied by a foreign file is refused up front rather than overwritten. The trailing-space fidelity that column claims depend on follows the tmux version — `-N` where it means real spaces, `-T` where padding must be trimmed, neither where `-N` would fabricate them. Refusals are a contract: exit 3, a reason on stderr and the same reason as JSON on stdout, and no stale evidence left beside it. An environment that cannot produce evidence says so instead of producing something weaker and calling it the same thing. Recreated from #8388 to collapse 373 commits and rebase onto current main. The CI tooling step and its workflow pin are #8792; the shell-registry deflake is #8795.








What this PR does
Installs
tmux,zipandunzipon the Linux test lane, and adds a test that pins the step so it cannot silently stop installing. The step is bounded (timeout-minutes: 5) and advisory (continue-on-error: true), so a stalled mirror or a dpkg lock neither hangs the job toward its 60-minute cap nor reds the required check.Why it's needed
Two suites go quiet without their tooling, and quiet is the problem.
capture-tui's real-tmux block isdescribe.skipIf(!hasTmux)-gated, and the hosted ubuntu image ships no tmux — so on that lane every real-tmux behaviour skips inside a green required check. The install-script packaging suite needszip/unzipthe same way. A missing tool does not fail anything; it just quietly removes coverage.That same property is why the step needs a pin. If it stops installing — a refactor, a typo in a condition, a flag that turns the install into a no-op — nothing goes red: the suites it feeds skip instead. So the pin reads the workflow the way bash does rather than the way it looks: it is quote-aware (a
#inside a message is literal, and a;inside one does not split statements), continuation-aware (only an odd run of trailing backslashes continues a line, and a comment never continues), and wrapper-aware (anapt-getbehindsudo/env/nice/timeout/time/nohup, with or without their options, or inside a subshell, is still anapt-get).On that base it pins what actually keeps the step working: both branch conditions whole, the
elsefallback, exact package tokens, the flags that make the install unattended (-y,sudo) and the ones that make it a no-op (-s,--download-only, …), that nothing may hard-fail the step in any errexit spelling or through a subshell exit, that everyapt-getreaches a|| echoguard (an AND-list is transparent; a pipeline or a;is not), and that every annotation reaches stdout, unredirected, with::warning::at line start — a workflow command the runner cannot see is just log noise.Reviewer Test Plan
How to verify
Run the pin:
npx vitest run --config ./scripts/tests/vitest.config.ts scripts/tests/capture-tmux-ci.test.js→ 5 passed.To confirm the pin has teeth rather than trusting the count, apply any mutation from the table below to
.github/workflows/ci.yml, re-run the command, and restore the file. Each one should turn the suite red — that is the whole point of the file, since none of these mutations would fail anything else in CI.The step itself is visible on this PR's own
Test (ubuntu-latest)job: it appears asInstall tmux and zip toolingand its log shows either the install or a::warning::explaining why the tooling is unavailable.Evidence (Before & After)
N/A — no user-visible or TUI change. The behavioural evidence is the mutation table: each mutation was applied to
ci.yml, the suite run, and the file restored.-sadded to the install-ydroppedsudodroppedtmux→powerline-tmux&&→||in the first branchelif false2 && …elsebranch deleted|| :forcing the first branchtimeout-minutesremovedcontinue-on-errorremovedsudo -n> /dev/null>&2::warning::set -u -e/set -o pipefail -eadded(exit 1)addedsleep 1 & exit 1added;#note \comment spliceTested on
The pin is a YAML/text test and runs anywhere; it was run on macOS. The step it pins is Linux-only by its own
runner.os == 'Linux'condition, and its real behaviour — apt present, tmux installed,tmux -Vanswering — was exercised in a Debian container. Windows is N/A: the step does not run there.Environment (optional)
Unit test only for the pin; a Debian bookworm container for the install path.
Risk & Scope
apt-geton the Linux lane, which costs time and depends on a mirror. That is why it is bounded and advisory — if it stalls or fails, the job continues and the affected suites degrade to skipping, exactly as they do today. The tradeoff is deliberate: a required check must not go red because a package mirror had a bad minute.tmux -V || echo '::warning::'branch is for. Self-hosted runners that already carry the tools take the first branch and install nothing.sudo+apt-get, the step emits a::warning::and changes nothing.Linked Issues
Split out of #8388 — the
capture-tuicommand there is what makes the tmux half load-bearing, and its gated real-tmux suite is not onmainyet, so this step pre-lands that tooling need. No coupling in the other direction: this PR stands alone and #8388 does not depend on it to merge.中文说明
这个 PR 做了什么
在 Linux 测试 lane 上安装
tmux、zip、unzip,并加一个测试钉住该步骤,使其不能悄悄停止安装。步骤是有界的(timeout-minutes: 5)且咨询性的(continue-on-error: true)——镜像源卡住或 dpkg 锁既不会把 job 拖到 60 分钟上限,也不会让必需检查变红。为什么需要
两个套件在缺少工具时会静默,而"静默"正是问题所在。
capture-tui的真实 tmux 块由describe.skipIf(!hasTmux)门控,而 hosted ubuntu 镜像不带 tmux——于是在该 lane 上,所有真实 tmux 行为都在绿色的必需检查里跳过。zip 打包套件同理。缺少工具不会让任何东西失败,只是悄悄拿走覆盖率。同样的性质决定了这个步骤需要钉子。一旦它停止安装——重构、条件里的笔误、把安装变成空转的标志——不会有任何东西变红:它供养的套件只是跳过。所以钉子按 bash 的真实语义读工作流,而不是按它的外观:引号感知(消息里的
#是字面量,消息里的;不切分语句)、续行感知(只有奇数个结尾反斜杠才续行,注释永不续行)、wrapper 感知(sudo/env/nice/timeout/time/nohup之后的 apt-get,带不带选项、在不在子 shell 里,都仍然是 apt-get)。在此基础上,它钉住真正让步骤生效的东西:两个分支条件的全文、
else兜底、精确包名 token、让安装无人值守生效的标志(-y、sudo)与让它空转的标志(-s、--download-only等)、任何 errexit 拼法或子 shell exit 都不得硬失败、每个 apt-get 必须能到达|| echo守卫(AND 列表是透明的,管道或;不是),以及每条注解必须未重定向地进入 stdout 且::warning::位于行首——runner 看不见的 workflow command 只是日志噪音。复核测试计划
如何验证
运行钉子:
npx vitest run --config ./scripts/tests/vitest.config.ts scripts/tests/capture-tmux-ci.test.js→ 5 通过。要确认钉子有牙齿而不是只看通过数,把下表任一变异应用到
.github/workflows/ci.yml,重跑命令,再还原文件。每一个都应让套件变红——这正是该文件存在的意义,因为这些变异不会让 CI 中的任何其他东西失败。步骤本身可在本 PR 自己的
Test (ubuntu-latest)job 中看到:名为Install tmux and zip tooling,其日志要么显示安装过程,要么显示解释工具为何不可用的::warning::。证据(前后对比)
N/A——无用户可见或 TUI 变更。行为证据是上面的变异表:每个变异都实际应用到
ci.yml、跑套件、再还原。测试环境
钉子是 YAML/文本测试,在任何平台都能跑,本次在 macOS 上运行。被钉住的步骤按其自身的
runner.os == 'Linux'条件仅在 Linux 运行,其真实行为——apt 存在、tmux 安装成功、tmux -V有应答——在 Debian 容器中实测。Windows 为 N/A:该步骤不在其上运行。运行环境(可选)
钉子仅单元测试;安装路径使用 Debian bookworm 容器。
风险与范围
apt-get,需要时间且依赖镜像源。这正是它有界且咨询性的原因——若卡住或失败,job 继续,受影响的套件退化为跳过,与今天的行为一致。这个权衡是刻意的:必需检查不应因为镜像源坏了一分钟而变红。tmux -V || echo '::warning::'分支的职责。已自带工具的自托管 runner 走第一分支,不会安装任何东西。sudo+apt-get的 lane 上,该步骤发出::warning::并不改变任何东西。关联 Issue
拆自 #8388——那里的
capture-tui命令正是让 tmux 这一半成为承重项的原因,且其被门控的真实 tmux 套件尚未进入main,因此本步骤为该需求预落地。反方向无耦合:本 PR 独立成立,#8388 的合并并不依赖它。