refactor(autofix): move the push-and-report body out of the workflow file - #9653
Conversation
…file qwen-autofix.yml was 462,656 bytes — 90% of GitHub's 500 KB start-runs limit and inside the 470,000-byte gate's warning band. Past that limit GitHub stops starting runs and says nothing: schedule ticks vanish, dispatches sit queued with zero jobs, and only PR-event runs keep working because they resolve an older copy from the PR's branch. The file crossed it once already, on 2026-08-19, and the loop went dark for a day. 'Push and report' was the largest single block in the file at 626 lines of inline shell. Its body moves to .github/scripts/autofix-push-and-report.sh byte-identically — the YAML keeps the step's if: and env:, which is where the questions of when it runs and what reaches it belong. The file drops to 422,447 bytes. Extraction moves the trust problem with it. By the time this step runs the agent and the verification gate have executed branch code on this host, so the workspace copy is branch-controlled. The step runs the trusted-base copy staged before any of that, after proving its digest — recorded in GITHUB_OUTPUT, which a disk write cannot reach — still matches, that the staged path is a regular file, and with both reads bounded so a planted FIFO is a refusal rather than a hang until timeout-minutes. A new contract test pins all of that, including that the workspace copy is never what runs; each of its assertions was checked against a mutant of the line it guards. The census assertions that counted sites across the workflow now count the script too, so an extraction cannot silently drop a site from a count that exists to pin it. The two emit_growth_audit_marker copies are compared dedented, since one is now at column 0 and its twin is still at ten. This is also the step docs/design/autofix-gate-runner-isolation.md moves into its own publish job. Carrying it as a file makes that a small diff, and deletes the staging scaffolding added here: a job that checks out the trusted base and never executes branch code can run the script where it lies. Refs #9089.
|
Thanks for the PR — this is a re-run after the approach change, so the gate reviews the content-delivery version, not the staging+digest machinery the earlier rounds defended. Template looks good ✓ Problem: real, and re-verified against the API at this pass. Direction: aligned. This keeps the autofix loop alive and opens the seam Size: no core-module paths touched ( Approach: this is the right turn. Rounds 1–4 kept hardening an on-disk copy — staged file, digest, type check, bounded reads, an Risk: no elevated risk signals — none of the changed files match the revert-correlated path patterns. It is PAT-bearing CI plumbing, so the trust chain (what the stage step reads from, how the bytes travel, what fails closed) got the full look in the code review. Moving on to code review. 🔍 中文说明感谢贡献——本次是方案变更后的 re-run,这道门审的是「内容传递」版本,而不是此前几轮一直在加固的「暂存+摘要」机制。 模板完整 ✓ 问题:真实,且本轮已重新经 API 核实。 方向:对齐。既保住 autofix 循环,也为 规模:未触及核心模块路径(仅 方案:这次转向是对的。第 1–4 轮一直在加固一个磁盘副本——暂存文件、摘要、类型判别、有界读取、带 38 项透传清单的 风险:无升级风险信号——改动文件均未命中与 revert 相关的高风险路径。由于涉及带 PAT 的 CI 管线,代码审查阶段对信任链(stage 步骤读的是什么、字节如何传递、什么会失败即拒)做了完整检查。 进入代码审查 🔍 — Qwen Code · qwen3.8-max Reviewed at |
Code reviewThe load-bearing claim of this PR is that the extracted body is a pure move, and I verified it independently rather than taking the description's word: I fetched the workflow at the merge base ( The trust chain is where an extraction like this lives or dies, and I walked it end to end in the head YAML: The fail-closed ordering holds: the The test surgery is the part I'd scrutinize hardest on a PR like this, and it's done properly: census assertions re-anchor onto the workflow+script concatenation so a site moving between the two cannot silently drop out of its count; the new contract test pins the delivery end-to-end — random delimiter, capture spelling, env binding, exec verb, the empty-capture guard before execution, the sentinel after it, and the single column-0 One residual, stated rather than hidden: a step output is fixed when its step ends — it is not unreachable while the stage step is still running. The design note in No blockers found. Testing evidence (this PR's own CI, read via API)Unattended run (
One row per check name (latest run); skipped checks omitted; failures sort first. / 每个检查名一行(取最新一次运行),省略 skipped,失败项排在最前。 Not verified: runtime behaviour of the extracted body under a live autofix round — by design of this review path (no PR code execution). The central claim of the move is byte-identity, which the static comparison above settles directly and the contract test pins against mutants; the bytes that run are byte-for-byte the bytes that already run inline from the YAML on 中文说明代码审查本 PR 的核心主张是「纯搬运」,我没有照单全收,而是独立核验:通过 API 分别取合并基线( 信任链是这类外提的生死线,我在 head YAML 中完整走了一遍: 失败即拒的顺序成立: 测试改造是这类 PR 最该盯的部分,做得干净:census 断言改锚到 workflow+脚本拼接,站点在两者之间移动不会悄悄丢出计数;新契约测试端到端钉住传递——随机定界符、捕获写法、env 绑定、执行动词、执行前的空捕获守卫、其后的哨兵、唯一的行首 一个残留,明示而非隐藏:step output 在其步骤结束时固定——并非在 stage 步骤运行期间不可触及。 未发现阻塞项。 测试证据(本 PR 自身 CI,经 API 读取)无人值守运行( (上方表格由 finalize 任务在 CI 落定后维护;本次审查时已全部落定。) 未验证:真实 autofix 轮次下搬出主体的运行时行为——这是本审查路径的设计使然(不执行 PR 代码)。搬运的中心主张是逐字节一致性,上文静态比对已直接解决、契约测试又以变异钉死;实际运行的字节与今天 — Qwen Code · qwen3.8-max Reviewed at |
|
Confidence: 5/5 — the load-bearing claim (a byte-identical move) was verified against the merge base rather than taken on trust, the trust chain holds end to end, and CI is settled green on the reviewed commit. Stepping back: my independent proposal before reading the diff was exactly this shape — extract the largest block, keep What this revision gets right that the previous approach didn't: it stops defending an object and deletes it. R1–R4's open findings were about the staging machinery — buffering that cost the live log, an env allow-list nothing pinned, test pins that existed only to hold the gate together. None of that exists anymore, and the findings that were about the PR itself rather than the gate (env contract as a rule, the "third-largest" correction, the paired census negatives, the shellcheck lane note) are addressed directly. The design note states the residual honestly — a step output is fixed when the step ends, not unreachable mid-step — and the publish-job split (#9525) is what closes it for good. CI is settled on the reviewed commit — every completed check green, nothing pending — so approval is not deferred. Approving, pinned to the reviewed commit. 中文说明退一步看:我在读 diff 之前的独立方案正是这个形状——外提最大的块、 本版相对旧方案真正做对的一点:不再守护一个对象,而是删掉它。R1–R4 的未决发现都针对暂存机制——丢失实时日志的缓冲、无人固定的 env 白名单、只为支撑门而存在的测试锚点。这些机制现已全部不存在;而真正针对本 PR 而非那道门的发现(env 约定改为规则、「第三大」的更正、成对的普查负向断言、shellcheck lane 说明)均已直接修正。设计说明诚实地写明了残留——step output 在步骤结束时固定,并非运行中不可触及—— 该提交上的 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 /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 冲突,直到移除标签或达到轮次上限。移除 |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Partially reviewed — gaps disclosed.
Not reviewed: the executable-script lint — .github/scripts/autofix-push-and-report.sh: shellcheck is not installed.
Not linted (tool limitation, not a blocker): the executable-script lint — .github/workflows/qwen-autofix.yml: actionlint embedded-shell source mapping is not yet supported — not linted.
中文说明
仅完成部分审查,审查缺口已披露。
未审查:the executable-script lint — .github/scripts/autofix-push-and-report.sh: shellcheck is not installed。
未检查(工具限制,非阻断):the executable-script lint — .github/workflows/qwen-autofix.yml: actionlint embedded-shell source mapping is not yet supported — not linted。
— qwen3.8-max via Qwen Code /review (v0.21.15)
…holes The wrapper verified the staged script in the step's inherited shell: a $GITHUB_ENV-planted PATH or BASH_FUNC_* import could swap the gate's own words, and the staged path was opened three times, so a watcher could swap the file between the digest check and the execution read. Run the whole verify-and-run sequence in one env -i clean child that opens the staged path once, verifies the digest of the captured bytes, and executes those same bytes. Also fail staging closed on any cp failure, fix the script's env contract, and pin the new guards.
|
🤖 Addressed the latest review feedback (round 1/100). What changed, and what I pushed back on: · 已处理最新评审反馈(第 1/100 轮)。改动内容与我反驳保留之处如下: Round summary — PR #9653 (address-review)DispositionsAddressed in this commit (8 findings):
Declined: none. Deferred to the next round (batch cap — the 3 Criticals plus the coupled Conflict notesNone ( VerificationCommands actually run this round (results):
Evidence recorded (probes, in
中文说明本轮小结 — PR #9653(address-review)处理结论本次提交处理(8 条发现):
拒绝(Decline):无。 顺延到下一轮(受批次上限约束——3 个 Critical 及其耦合修复已占满本轮): 冲突说明无( 验证本轮实际执行的命令(结果):
已记录的证据(探针,位于
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.
Partially reviewed — gaps disclosed. Suggestions are inline.
Not reviewed: reverse audit — stopped before round 5 by the review time budget.
Not reviewed: verification — its prompt was built, but no agent was launched with it, so the posted findings cannot be counted as verified.
Not linted (tool limitation, not a blocker): the executable-script lint — .github/workflows/qwen-autofix.yml: actionlint embedded-shell source mapping is not yet supported — not linted.
Deferred under the convergence posture (round 2, not a blocker) — recorded, not requested in this round:
.github/scripts/autofix-push-and-report.sh:380 — [probe] resanitize digest refusal carries no ::error:: annotation, unlike every gate this PR adds (deferred by the code-age rule — anchored on code unchanged since round 1's reviewed head)scripts/tests/qwen-autofix-workflow.test.js:10394 — [probe] staging digest pin is substring-only — a bare-assignment rewrite that dies under pipefail ships green (deferred by the code-age rule — anchored on code unchanged since round 1's re…
中文说明
仅完成部分审查,审查缺口已披露。 建议见行内评论。
未审查:反向审计——评审时间预算不足,未能开始第 5 轮。
未审查:验证——它的 prompt 已构建,但没有 agent 用它启动,发布的发现不能算作已验证。
未检查(工具限制,非阻断):the executable-script lint — .github/workflows/qwen-autofix.yml: actionlint embedded-shell source mapping is not yet supported — not linted。
收敛姿态下延后(第 2 轮,非阻断)——已记录,本轮不要求修改:共 2 条(原文未翻译,列表见上方英文部分)。
— qwen3.8-max via Qwen Code /review (v0.21.15)
|
🤖 AutoFix updated a stale base — the fix did not pass verification, but this PR was behind Round 3 — address-review summary (PR #9653)Test-only round: all changes are in Addressed (8 findings)Gate pins —
|
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Partially reviewed — gaps disclosed.
Not reviewed: the executable-script lint — .github/scripts/autofix-push-and-report.sh: shellcheck is not installed.
Not explored to full depth (tool budget reached): "agent reverse-audit (round 1)": none — no checks were cut short; all six layers walked within budget..
Not linted (tool limitation, not a blocker): the executable-script lint — .github/workflows/qwen-autofix.yml: actionlint embedded-shell source mapping is not yet supported — not linted.
Deferred under the convergence posture (round 3, not a blocker) — recorded, not requested in this round:
scripts/tests/qwen-autofix-workflow.test.js:12918 — [probe] upsertChildAnchor keys on a prose comment; the 'anchored at 0' doc claim silently divergesscripts/tests/qwen-autofix-workflow.test.js:10474 — [probe] workspace-copy execution ban misses prefixed/absolute spellings; not.toContain closes the classscripts/tests/qwen-autofix-workflow.test.js:10432 — [probe] the printf 'x' / %x trailing-newline marker pair is pinned nowhere; deleting it breaks every round's digestscripts/tests/qwen-autofix-workflow.test.js:10423 — [probe] gate child env list lacks the allowlist-equality pin; a BASH_ENV addition ships green (addition-side sibling of R2-3)scripts/tests/qwen-autofix-workflow.test.js:10491 — [probe] set-line inventory is column-0-anchored; an indented top-level set +e/-u evades all pinsscripts/tests/qwen-autofix-workflow.test.js:10410 — [probe] stage-side digest pin is prefix-only; the | cut tail and the GITHUB_OUTPUT append are pinned nowhere
中文说明
仅完成部分审查,审查缺口已披露。
未审查:the executable-script lint — .github/scripts/autofix-push-and-report.sh: shellcheck is not installed。
未探索到全部深度(达到工具调用预算):"agent reverse-audit (round 1)":none — no checks were cut short; all six layers walked within budget.。
未检查(工具限制,非阻断):the executable-script lint — .github/workflows/qwen-autofix.yml: actionlint embedded-shell source mapping is not yet supported — not linted。
收敛姿态下延后(第 3 轮,非阻断)——已记录,本轮不要求修改:共 6 条(原文未翻译,列表见上方英文部分)。
— qwen3.8-max via Qwen Code /review (v0.21.15)
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
|
🤖 Addressed the latest review feedback (round 3/100). What changed, and what I pushed back on: · 已处理最新评审反馈(第 3/100 轮)。改动内容与我反驳保留之处如下: Autofix review round summary — PR #9653Round: address-review (base ImplementedR3-1 — [Critical] the gate child had neither a liveness sentinel nor downstream detection (rc:3833614080)Reproduced before changing anything. A probe of the exact wrapper shape (
Behavioral harness results (extracted step bodies run against a stub staged script + stub Gate test-pin findings (all in the gate's
|
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Partially reviewed — gaps disclosed. Suggestions are inline.
Not reviewed: the executable-script lint — .github/scripts/autofix-push-and-report.sh: shellcheck is not installed.
Not linted (tool limitation, not a blocker): the executable-script lint — .github/workflows/qwen-autofix.yml: actionlint embedded-shell source mapping is not yet supported — not linted.
Deferred under the convergence posture (round 4, not a blocker) — recorded, not requested in this round:
.github/scripts/autofix-push-and-report.sh:20 — [review] 'trustworthy where it lies' contradicts the cited isolation design (publish runs no repo scripts; patch applied before push; B-3 does not refuse .github/scripts/**) — deferred by the …scripts/tests/qwen-autofix-workflow.test.js:12015 — [probe] force-push guard scans workflow alone — a force push added to the extracted script keeps all three pins green — deferred by the code-age rule (anchored on code unchanged since roun…
中文说明
仅完成部分审查,审查缺口已披露。 建议见行内评论。
未审查:the executable-script lint — .github/scripts/autofix-push-and-report.sh: shellcheck is not installed。
未检查(工具限制,非阻断):the executable-script lint — .github/workflows/qwen-autofix.yml: actionlint embedded-shell source mapping is not yet supported — not linted。
收敛姿态下延后(第 4 轮,非阻断)——已记录,本轮不要求修改:共 2 条(原文未翻译,列表见上方英文部分)。
— qwen3.8-max via Qwen Code /review (v0.21.15)
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Partially reviewed — gaps disclosed. Suggestions are inline.
2 Suggestion-level finding(s) this review confirmed are already reported on this PR and are not repeated:
- R4-2 handoff publication proof gap (qwen-autofix.yml:6440) — already reported (comment 3834639533)
- R4-3 round_reported forgeable from the outer step shell (qwen-autofix.yml:5750) — already reported (comment 3834639536)
Not reviewed: the executable-script lint — .github/scripts/autofix-push-and-report.sh: shellcheck is not installed.
Not explored to full depth (tool budget reached): "agent reverse-audit (round 3)": none — no check was cut short.; "agent reverse-audit (round 4)": none — all assigned reads completed un-truncated; scope fully walked..
Not reviewed: reverse audit — did not converge within the reverse-audit round cap of 5.
Not linted (tool limitation, not a blocker): the executable-script lint — .github/workflows/qwen-autofix.yml: actionlint embedded-shell source mapping is not yet supported — not linted.
Deferred under the convergence posture (round 5, not a blocker) — recorded, not requested in this round:
scripts/tests/qwen-autofix-workflow.test.js:10553 — [probe] execution-ban pins miss absolute-path/$(bash)/continuation interpreter spellings (script half)scripts/tests/qwen-autofix-workflow.test.js:10489 — [probe] no pin against the 128 KiB MAX_ARG_STRLEN ceiling on bash -c "$body".github/workflows/qwen-autofix.yml:3842 — [review] staging cp suppresses stderr — loses the only partial-write diagnosticscripts/tests/qwen-autofix-workflow.test.js:7634 — [review] retired divergence-ladder pins still grep bare workflow after the extractionscripts/tests/qwen-autofix-workflow.test.js:10423 — [probe] gate env -i pass-through has no enumeration pin (addition side: BASH_ENV)scripts/tests/qwen-autofix-workflow.test.js:10425 — [probe] gate launch clears 3 LD_* vars vs the upsert twin's 7; pin fossilizes the weaker formscripts/tests/qwen-autofix-workflow.test.js:15605 — [probe] PUBLISHED init/reset/gate ordering unpinned — relocation defeats the R3-1 detection.github/workflows/qwen-autofix.yml:3856 — [probe] digest line suppresses sha256sum stderr — loses the interference diagnosticscripts/tests/qwen-autofix-workflow.test.js:10482 — [probe] single-open count pin and bans require literal path adjacency — cd/quoted-brace spellings evadescripts/tests/qwen-autofix-workflow.test.js:3143 — [probe] half-migrated pair: normalizer census still counts bare workflowscripts/tests/qwen-autofix-workflow.test.js:10407 — [probe] cp and || rm -f cleanup pinned independently — attachment/order can silently rotscripts/tests/qwen-autofix-workflow.test.js:10431 — [probe] the gate's load-bearing 2>&1 stderr merge is unpinned
中文说明
仅完成部分审查,审查缺口已披露。 建议见行内评论。
本轮确认的 2 条建议级发现已在 PR 上报告过,不再重复发布(列表见上方英文部分)。
未审查:the executable-script lint — .github/scripts/autofix-push-and-report.sh: shellcheck is not installed。
未探索到全部深度(达到工具调用预算):"agent reverse-audit (round 3)":none — no check was cut short.;"agent reverse-audit (round 4)":none — all assigned reads completed un-truncated; scope fully walked.。
未审查:反向审计——在 5 轮的反审轮数上限内未收敛。
未检查(工具限制,非阻断):the executable-script lint — .github/workflows/qwen-autofix.yml: actionlint embedded-shell source mapping is not yet supported — not linted。
收敛姿态下延后(第 5 轮,非阻断)——已记录,本轮不要求修改:共 12 条(原文未翻译,列表见上方英文部分)。
— qwen3.8-max via Qwen Code /review (v0.21.15)
|
👋 Takeover released: the autofix loop will no longer engage this PR (an in-flight round, if any, completes its bounded work). Re-apply 中文说明👋 已释放:autofix 循环不再介入此 PR(在飞的一轮如有,将完成其有界工作)。重新打上 |
…bans (#9653) Address review suggestions on the push-and-report extraction: - R2-3: pin the gate child's env -i pass-through list as a sorted allow-list equality check (R9-10 precedent), so a dropped variable fails loudly instead of expanding empty. - R4-4: pin the sentinel refusal BEFORE the round_reported write — shape-only pins let the ordering rot (probe-verified mutant). - R4-6: make the execution bans continuation-tolerant; bash joins `\<newline>` before tokenizing, so line-continued spellings evaded the `[^\n|]*` middle (probe: old regex misses, new catches). - R1-6: re-scope the paired R10-8 upsert negatives to both halves, matching the positive censuses. - R3-2: correct the stale workflow-size figure (462,656 -> 463,004 bytes at the post-merge base; 90%/98% still hold). - R1-5/R1-15/R1-11: correct three disproved claims in the trust-model doc (block rank, GITHUB_OUTPUT qualifier, which call sites hold the single-open shape).
|
🤖 Addressed the latest review feedback (round 4/100). What changed, and what I pushed back on: · 已处理最新评审反馈(第 4/100 轮)。改动内容与我反驳保留之处如下: Review round summary — PR #9653Round outcome: 8 of 13 findings implemented (all Suggestion-level; no Critical/Required this round), 5 deferred to the next round with replies on their threads. One additive commit: Growth: net +87 lines this round (103 added / 16 removed, almost entirely test pins) — window budgets remain 400/400 for source and test. Implemented
Deferred to the next round (replies posted on each thread)R4-1, R4-2, R4-3, R4-5, R1-9. The first four are behavior changes to the gate wrapper step / Finalize demotion arm (live log streaming via tee; handoff publication proof; Conflict notesNone — VerificationCommands actually run and their results:
中文说明审查轮次总结 — PR #9653本轮结果:13 条发现中实现了 8 条(本轮全部为 Suggestion 级,无 Critical/Required),5 条延至下一轮并已在各自线程回复。一个增量提交: 增长:本轮净增 87 行(新增 103 / 删除 16,几乎全部为测试钉)——窗口预算 source 与 test 均仍为 400/400。 已实现
延至下一轮(已在各线程回复)R4-1、R4-2、R4-3、R4-5、R1-9。前四条是对门 wrapper 步骤 / Finalize 降级分支的行为变更(tee 实时日志流;handoff 发布证明; 冲突说明无—— 验证实际运行的命令及结果:
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/模型 |
… staged file The extraction put the body in a file and then had to defend that file: a staged copy under RUNNER_TEMP, a digest recorded in step output, a type check, bounded reads, and — after three rounds of findings against those — an env -i clean child with a liveness sentinel and a 38-entry pass-through list wrapping the whole verify-and-run sequence. Four review rounds have now been spent on that machinery, and the open findings against it are about the machinery, not about the move: output buffering that costs the live log the inline body had, an env allow-list nothing pins, and pin gaps in the tests that exist only to hold the gate together. Delete the object being guarded instead. The stage step already reads the trusted base before any branch code runs, so it captures the body as text into step output and the step runs those bytes — the delivery the inline block had, and the one upsert-deferred-issue.sh already uses. With no agent-writable copy on disk there is nothing to stage, digest, type-check or re-open, and no check→use window between the steps: R1-2's TOCTOU, R1-13's FIFO double-open, R2-2/R2-5/R2-6/R2-7's single-open pins and R4-1's buffering all describe a mechanism that no longer exists. This is not a security regression. What it drops is protection the inline block never had; what it keeps is the one property that matters — the bytes come from the trusted-base checkout through expression context, not from a path the branch can write. The step shell's inherited environment (R1-1, R1-3) is the status quo this PR preserves rather than the thing an extraction should be fixing: #9525's publish job takes the PAT out of that shell entirely, which is the real close. Kept from the gate work: the round_reported output. A loader plant that kills this shell at execve exits 0 having written nothing, so 'Finalize autofix status comment' can still tell a published round from a no-op — now without a sentinel, because the write is simply last. Also corrected, from the same review: the header no longer claims the block was the largest in the file (it is third, after 'Scan for PRs with new feedback' and 'Prepare branch and feedback'), the size is measured at this branch's merge-base (462,720), the env contract is stated as a rule rather than a list that went stale within one round, and the shellcheck note says which lane and which severity it is clean under. The R10-8 execution-ban negatives now cover the scripts as well as the workflow, and the script's own flag line is pinned as a whole rather than by spelling. Refs #9089, #9525.
|
Changed the approach rather than continuing to harden it, and released the takeover label so the loop is not working against that decision. What happened over rounds 1–4. The extraction put the step's body in a file and then had to defend that file: a staged copy under What I did instead. Deleted the object being guarded. The stage step already reads the trusted base before any branch code runs, so it now captures the body as text into step output and the step runs those bytes — This is not a security regression, and I want to be explicit about the trade. What it drops is protection the inline block never had. What it keeps is the property that actually matters: the bytes come from the trusted-base checkout through expression context, not from a path the branch can write. R1-1 and R1-3 — the step shell's inherited environment and its bare words — describe the status quo this PR preserves; fixing that is #9525's Kept from the gate work: Findings that were about this PR rather than the gate are fixed directly: R1-4 (env contract stated as a rule, not a list that went stale within a round), R1-5 (third-largest, not largest), R1-6/R1-7/R1-8 (paired census negatives cover the scripts), R1-9 (the shellcheck note names the lane and the bar this file meets), R1-11 (the staging comparison left with the staging), R1-12/R2-5 (the path ban is a comment-stripped census, so no exec verb or line continuation evades it), R1-14 (the whole Verification. 291 passed / 1 failed across the four suites; that failure fails identically on Each open thread has its disposition inline. 中文说明改变了方案,而不是继续加固它;同时摘掉了 takeover 标签,以免循环与这个决定对着干。 第 1–4 轮发生了什么。 外提把步骤主体放进文件,随后就得保卫这个文件: 我改成了什么。 删掉被守护的对象。stage 步骤本来就在任何分支代码运行之前读取可信基线,因此现在它把主体作为文本捕获进 step output,步骤直接运行这些字节—— 这不是安全回退,我想把这笔交易讲清楚。 被放弃的是内联块本来就没有的保护;被保留的是真正重要的性质:字节来自可信基线检出、经由表达式上下文抵达,而不是来自分支可写的路径。R1-1 与 R1-3 所描述的"步骤 shell 的继承环境与裸词"是本 PR 所维持的现状;修复它属于 #9525 的 从这轮门的工作中保留下来的: 真正针对本 PR 而非那道门的发现已逐条修正: R1-4(env 契约改为规则,而非一轮内就失效的清单)、R1-5(第三大而非最大)、R1-6/R1-7/R1-8(成对的普查负向断言覆盖脚本)、R1-9(shellcheck 说明点明 lane 与本文件实际达到的标准)、R1-11(暂存对比随暂存一并移除)、R1-12/R2-5(路径禁令改为剥离注释后的普查,任何执行动词或行续接都无法绕过)、R1-14(固定整个 验证。 四个套件合计 291 通过 / 1 失败;该失败在本环境的 每条未决线程都已在行内给出处置说明。 |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Partially reviewed — gaps disclosed.
Not reviewed: reverse audit — did not converge within the reverse-audit round cap of 5.
Not linted (tool limitation, not a blocker): the executable-script lint — .github/workflows/qwen-autofix.yml: actionlint embedded-shell source mapping is not yet supported — not linted.
Deferred under the convergence posture (round 6, not a blocker) — recorded, not requested in this round:
.github/workflows/qwen-autofix.yml:5660 — [probe] child-execve liveness unverified before round_reported.github/workflows/qwen-autofix.yml:5588 — [probe] step id 'push_report' unpinnedscripts/tests/qwen-autofix-workflow.test.js:11920 — [probe] force-push negative pins lost script scope.github/workflows/qwen-autofix.yml:5646 — [probe] MAX_ARG_STRLEN headroom unpinned.github/workflows/qwen-autofix.yml:3873 — [probe] capture trailing-newline invariant unpinnedscripts/tests/qwen-autofix-workflow.test.js:12871 — [probe] ban 1 misses slash-prefixed and shell-less execution spellingsscripts/tests/qwen-autofix-workflow.test.js:15514 — [review] handoff publication unproofed — PUBLISHED gate covers only fixed/noopscripts/tests/qwen-autofix-workflow.test.js:10461 — [review] body self-write of round_reported/GITHUB_OUTPUT unpinnedscripts/tests/qwen-autofix-workflow.test.js:12874 — [probe] ban 2 continuation-after-quote evasion.github/scripts/autofix-push-and-report.sh:6 — [review] header 'within a few KB of the gate' false at the introducing commitscripts/tests/qwen-autofix-workflow.test.js:111 — [review] upsert-staging negatives de-scoped by the extractionscripts/tests/qwen-autofix-workflow.test.js:12872 — [probe] brace-less $RUNNER_TEMP spellings evade all R10-8 pinsscripts/tests/qwen-autofix-workflow.test.js:111 — [review] growth-ladder retirement pins de-scoped by the extraction.github/workflows/qwen-autofix.yml:3873 — [probe] heredoc closing terminator unpinned
中文说明
仅完成部分审查,审查缺口已披露。
未审查:reverse audit — did not converge within the reverse-audit round cap of 5。
未检查(工具限制,非阻断):the executable-script lint — .github/workflows/qwen-autofix.yml: actionlint embedded-shell source mapping is not yet supported — not linted。
收敛姿态下延后(第 6 轮,非阻断)——已记录,本轮不要求修改:共 14 条(原文未翻译,列表见上方英文部分)。
— qwen3.8-max via Qwen Code /review (v0.21.15)
|
@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: 1474 passed · 1 failed · 1475 total Flakiness gate: 中文 — 判定:❌ 不通过 · 报告了发现(agent 判定)沙箱验证在隔离、无凭证的容器中执行了该 PR 的代码(与 base 构建 A/B 对照、无 mock harness 断言、定向门禁)。仅作为评审证据,不构成评审、批准或 CI 检查。 脚本断言:1474 通过 · 1 失败 · 1475 总计 抖动门: Verification reportVerification report — PR #9653Verdict: The single failed assertion is a documentation-accuracy finding (the new 中文摘要
Central claim + A/BCentral claim: the 626-line
Witness: Transport (wire oracle, real bash, runner-shaped
Mutation matrix on the contract test
Path-ban census (independent of the tests): with comment lines stripped, Targeted gates (13 scripted checks, all as predicted; logs
Full scripts suite at head: 1412/1412 ( Corrections
FindingsF-1 (low) — the new doc section's block-rank claim is wrong: fourth-largest, not third-largest.
Reproduce: Suggested fix (one line, intent-preserving)In Not covered
MethodologyEnvironment: Flakiness gate logEvidence imagesHarness scripts and raw logs are in the workflow run artifacts (7-day retention). — Qwen Code · sandboxed verification |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
LGTM, looks ready to ship. ✅
yiliang114
left a comment
There was a problem hiding this comment.
LGTM. I rechecked the exact head: the extracted body is mechanically identical to the original inline block, its trusted-base content delivery preserves the existing execution and output contract, and the PAT/head/push guards remain intact. Exact-head CI is green.
|
Released in v0.22.0. |






What this PR does
Moves the body of
review-address·Push and reportout ofqwen-autofix.ymlinto.github/scripts/autofix-push-and-report.sh. The YAML keeps the step'sif:andenv:— when it runs, and what reaches it. The body is byte-identical to the block it came from (626 lines, verified line-by-line against the merge base); the only additions are a header comment and ashellcheck disabledirective, both explained in the file.qwen-autofix.ymldrops by ~40 KB.The file is never executed from disk. The stage step reads it from the trusted-base checkout, before any branch code has run, and passes the text through step output; the step runs those bytes (
bash --norc -c "${PUSH_REPORT_SRC}"). That is the delivery the inline block already had — the workflow file's own bytes — and the oneupsert-deferred-issue.shuses. With no agent-writable copy on this shared host there is nothing to stage, digest, type-check or re-open, and no check→use window between the steps.One thing carried over from the review:
round_reported=true, written last and only after the body returns. A loader plant that kills the step shell atexecveexits 0 having written nothing, soFinalize autofix status commentcan still tell a published round from a step that no-oped.Why it's needed
GitHub stops starting runs for a workflow file over 500 KB and says nothing when it does: schedule ticks vanish, dispatches sit
queuedwith zero jobs, and only PR-event runs keep working — those resolve an older copy from the PR's own branch, so the loop looks half-alive.qwen-autofix.ymlcrossed that line on 2026-08-19 and the autofix loop went dark for a day. It was back inside the repo gate's warning band with a few KB of headroom, andPush and reportwas ~41 KB of it.It is also the step
docs/design/autofix-gate-runner-isolation.md(#9525) moves into its own credentialedpublishjob. Carrying it as a file makes that a small diff instead of a 626-line relocation.On the review history
Rounds 1–4 went into defending an on-disk copy: a staged file, a digest in step output, a type check, bounded reads, then an
env -iclean child with a liveness sentinel and a 38-entry pass-through list. The open findings were about that machinery, not about the move — buffering that cost the live log the inline body had, an env allow-list nothing pinned, and test pins that existed only to hold the gate together. This PR deletes the object being guarded instead of hardening its guard further, which retires that whole class: R1-1/R1-2/R1-3/R1-10/R1-13, R2-2/R2-3/R2-4/R2-5/R2-6/R2-7, R3-1 and R4-1…R4-6 all describe a mechanism that no longer exists.That is not a security regression. What it drops is protection the inline block never had; what it keeps is the property that matters — the bytes come from the trusted-base checkout through expression context, not from a path the branch can write. Hardening the step shell's inherited environment is #9525's
publishjob, which takes the PAT out of that shell entirely.The findings that were about this PR rather than about the gate are fixed directly: the env contract is a rule instead of a list that went stale (R1-4), the "largest block" claim is corrected to third-largest (R1-5), the paired census negatives cover the scripts (R1-6/R1-7/R1-8), the shellcheck note names the lane and the bar this file actually meets (R1-9), the staging comparison is gone from the design record along with the staging (R1-11), the flag-line pin covers every spelling by pinning the whole line set (R1-14), the
GITHUB_OUTPUTclaim carries its qualifier (R1-15), and the decaying absolute file size is gone (R3-2).Reviewer Test Plan
How to verify
The move is mechanical, so verify it as a move:
qwen-autofix-workflow.test.js: 291 passed, 1 failed across the four suites — the failure (locks the runner file-command backing files against env plants) fails identically onorigin/mainin this environment, so it is baseline, not this change.Transport was checked end to end, not just asserted: capturing the real 39 KB script through a runner-shaped
GITHUB_OUTPUTheredoc and parsing it back yields byte-identical content, and those bytes parse clean underbash --norc -n -c(38,314 bytes, against a 131,072-byteMAX_ARG_STRLEN).The contract test
delivers the push-and-report body as content, never from a path on diskpins the delivery. Rather than a spelling regex, the path ban strips comments and asserts the script's path appears exactly once in the whole workflow — the stage step'scat— so no exec verb and no line continuation can evade it. Each assertion was checked against a mutant of the line it guards:round_reportedbefore the body-uto the script'ssetlineEvidence (Before & After)
N/A — no user-visible or behavioural change; the same bytes run in the same shell.
Tested on
Environment (optional)
Node 22, repo
node_modules;shellcheck0.11.0 viascripts/lint.js.Risk & Scope
pull_request: labeled/unlabeledevent that runs this branch's workflow against a base checkout without the file captures nothing and refuses to push, with a message. Scheduled andpull_request_reviewruns resolve the workflow and the base from the same commit, so they move together and see no window. Failing closed is the intended behaviour.publish-job split itself (docs(autofix): design runner-level isolation for PAT-bearing steps #9525's step 1) — this only creates the seam. Hardening the step shell's inherited environment is that split's job, not an extraction's. The twoSC2155sites in the moved body are pre-existing and deliberately left alone; splittingexport X="$(cmd)"changes whatset -edoes at those lines, which belongs in a change reviewable as a behaviour change rather than hidden inside a move.Linked Issues
Refs #9089 — the isolation work this prepares.
Refs #9525 — the design whose first step this unblocks.
中文说明
本 PR 做了什么
把
review-address·Push and report的步骤主体从qwen-autofix.yml移到.github/scripts/autofix-push-and-report.sh。YAML 保留该步骤的if:与env:(何时运行、什么能传进来)。主体与原文逐字节一致(626 行,对照合并基线逐行核验);新增的只有文件头注释与一条shellcheck disable指令,两者都在文件内说明了理由。qwen-autofix.yml减少约 40 KB。该文件从不从磁盘执行。 stage 步骤在任何分支代码运行之前,从可信基线检出中读取它,并把文本经由 step output 传递;步骤运行的是这些字节(
bash --norc -c "${PUSH_REPORT_SRC}")。这正是原内联块本来就有的传递方式——workflow 文件自身的字节——也是upsert-deferred-issue.sh使用的方式。共享主机上既然没有 agent 可写的副本,就没有东西需要暂存、摘要、类型判别或二次打开,步骤之间也不存在 check→use 窗口。评审中保留下来的一点:
round_reported=true,在主体返回之后最后写入。若加载器植入在execve处杀死步骤 shell,它会以 0 退出且什么都没写,因此Finalize autofix status comment仍能区分"已发布报告的一轮"与"空转的步骤"。为什么需要
GitHub 对超过 500 KB 的 workflow 文件直接不再启动运行且毫无提示:schedule 不再触发、dispatch 永远
queued且零 job,只有 PR 事件仍然工作——它们从 PR 自己的分支解析出更旧、更小的副本,于是整个循环看起来"半活着"。qwen-autofix.yml曾于 2026-08-19 越线,autofix 循环停摆一天。此前它已重新进入仓库门限的告警带、只剩几 KB 余量,而Push and report占其中约 41 KB。它同时也是
docs/design/autofix-gate-runner-isolation.md(#9525)要迁入独立publishjob 的那个步骤。先把它变成文件,那次迁移就是一个小 diff。关于评审历史
第 1–4 轮都花在"保卫一个磁盘副本"上:暂存文件、step output 里的摘要、类型判别、有界读取,随后又加上带存活哨兵与 38 项透传清单的
env -i干净子进程。未决发现针对的是这套机制本身,而不是这次搬运——缓冲导致丢失了内联块原有的实时日志、无人固定的 env 白名单,以及只为支撑这道门而存在的测试锚点。本 PR 选择删掉被守护的对象,而不是继续加固它的守卫,于是整类问题一并退场:R1-1/R1-2/R1-3/R1-10/R1-13、R2-2/R2-3/R2-4/R2-5/R2-6/R2-7、R3-1 与 R4-1…R4-6 描述的机制都已不存在。这不是安全回退。被放弃的是内联块本来就没有的保护;被保留的是真正重要的性质——字节来自可信基线检出、经由表达式上下文抵达,而不是来自分支可写的路径。加固步骤 shell 的继承环境是 #9525 中
publishjob 的职责(它把 PAT 彻底移出该 shell),不是一次机械搬运该做的事。真正针对本 PR 而非那道门的发现已逐条修正:env 契约改为规则而非会失效的清单(R1-4)、"最大块"的说法更正为第三大(R1-5)、成对的普查负向断言覆盖到脚本(R1-6/R1-7/R1-8)、shellcheck 说明点明具体 lane 与本文件实际达到的标准(R1-9)、设计文档中的暂存对比随暂存一并删除(R1-11)、flag 行改为整行集合固定以覆盖所有写法(R1-14)、
GITHUB_OUTPUT的表述补上了限定条件(R1-15)、会随 main 漂移的绝对文件大小已移除(R3-2)。评审验证计划
如何验证
按"搬运"来验证:对照合并基线的内联块做逐行 diff;运行
npx vitest run scripts/tests/qwen-autofix-workflow.test.js scripts/tests/workflow-size.test.js、bash .github/scripts/check-workflow-size.sh、node scripts/lint.js --shellcheck。四个套件合计:291 通过、1 失败——该失败(
locks the runner file-command backing files against env plants)在本环境下于origin/main同样失败,属基线而非本次改动。传输链路做了端到端实测而非仅凭断言:把真实的 39 KB 脚本经 runner 形态的
GITHUB_OUTPUTheredoc 捕获再解析回来,内容逐字节一致,且这些字节在bash --norc -n -c下解析通过(38,314 字节,对应上限MAX_ARG_STRLEN为 131,072)。契约测试
delivers the push-and-report body as content, never from a path on disk固定了这一传递方式。路径禁令不用拼写正则,而是剥离注释后断言脚本路径在整个 workflow 中恰好出现一次——即 stage 步骤的cat——因此任何执行动词与行续接都无法绕过。每条断言都对其守护的那行代码做了变异验证:按路径运行文件、删除空捕获保护、把round_reported写在主体之前、给脚本set加-u、把每次运行随机的 heredoc 定界符换成固定值——五种变异全部被捕获。证据(改动前后)
N/A——无用户可见变化、无行为变化;相同的字节在相同的 shell 中运行。
测试平台
环境(可选)
Node 22,仓库
node_modules;shellcheck0.11.0(经scripts/lint.js)。风险与范围
pull_request: labeled/unlabeled事件运行本分支的 workflow 而基线检出不含该文件,捕获为空、拒绝推送并给出提示。schedule 与pull_request_review运行的 workflow 与基线来自同一提交,二者同步变更,不存在窗口。失败即拒正是预期行为。publishjob 拆分本身(docs(autofix): design runner-level isolation for PAT-bearing steps #9525 的第 1 步)——本 PR 只是打开接缝。加固步骤 shell 的继承环境属于那次拆分的职责。被搬运主体中的两处SC2155属既有问题并有意保留:拆分export X="$(cmd)"会改变那两行上set -e的行为,应放在可作为行为变更评审的改动里。关联 Issue
Refs #9089——本 PR 为之做准备的隔离工作。
Refs #9525——本 PR 解锁其第一步的设计。