fix(ci): neutralize legacy ##[ commands in autofix stdout echoes - #9871
Conversation
A review finding about workflow-command injection carried its payload strings (::error::forged, ##[add-matcher]) verbatim as example text. Prepare echoed feedback.md raw, the runner parsed the mid-line ##[add-matcher] and failed the step before the agent ran, and the consecutive-failure breaker burned the takeover window of #9761 in about 70 minutes while reporting it as a 100/100 round cap. The existing neutralization covered only the modern :: syntax; the runner also parses the legacy ##[name] form, even mid-line. Extend every untrusted-content echo in the autofix family (prepare feedback echo, both artifact dump loops, the gate's failure/handoff/no-action echoes, the deferred-findings dumps and upsert re-emit loops) to neutralize both prefixes, and pin the census plus a behavioral ##[ case in the contract tests.
|
Re-run requested — this pass reviews the full four-commit head (original fix, spelling unification, main merge, pairing-property test); the first pass only covered the initial commit. Template looks good ✓ Problem: observed, not theoretical. Measured on #9761 — reviewer feedback carrying a quoted legacy-form payload ( Direction: aligned — this removes a deterministic round-burner from the repo's own autofix pipeline. CI-workflow internals, no product surface, so no CHANGELOG signal applies. Size: core-module gate not applicable — no Approach: scope feels right, and the re-run arc made it tighter. Fixing only the prepare echo would just move the crash to the next dump loop that re-echoes the same payload in the same round, so sweeping the full census of stdout-bound echo sites is the minimal fix. The follow-up commits unified the family on one canonical two-expression spelling (the two re-emit loops had carried a bash-expansion variant) and replaced the census's shape enumeration with a pairing property, closing the Risk: no elevated risk signals — changes confined to Moving on to code review. 🔍 中文说明应 re-run 请求重跑——本次审查覆盖完整的四个 commit(原始修复、拼写统一、main 合并、配对性质测试);首次审查只覆盖了第一个 commit。 模板完整 ✓ 问题:已观测到,非理论问题。在 #9761 实测——评审反馈中引用的 legacy 形式 payload( 方向:对齐——这消除了仓库自身 autofix 流水线中一个确定性的轮次消耗源。CI 工作流内部,无产品面,CHANGELOG 信号不适用。 规模:核心模块门不适用——未触及任何 方案:范围合理,且 re-run 过程使其更收敛。只修 prepare 回显只会把崩溃挪到同一轮里再次回显同一 payload 的下一个 dump 循环,因此覆盖全部面向 stdout 的回显点才是最小修复。后续 commit 将整个家族统一到一个规范的双表达式拼写(两个重发循环此前带的是 bash 替换变体),并把清点的形状枚举替换为配对性质,堵上了第二轮评审发现的 风险:无升级风险信号——改动限于 进入代码审查 🔍 — Qwen Code · qwen3.8-max Reviewed at |
Code reviewIndependent proposal first (title + incident only): extend the existing I re-verified the census against THIS head (read-only, via the API — no PR code checked out or executed):
The pairing property is the stronger test shape, and the right one to land: it fails on a half-copy in ANY spelling — including spellings nobody has enumerated yet — instead of racing new shapes, and because the census counts are file-wide, even a NEW site carrying a half-guard unbalances them. The one direction it cannot see is a brand-new file outside the four census files, which the test's own comment states plainly; that is inherent to any static census, and it is exactly what the declined centralization suggestion was about. On that decline: I verified the ground it stands on — the workflow does stage these scripts as trusted-base content (sha256-pinned The substitution itself is sound: its outputs ( No blockers found. Test evidenceUnattended CI run — PR code is never built or run here; the evidence below is the PR's own CI at the reviewed commit, fetched once via the API. It has fully settled:
Skipped (classification-driven for a CI-only change): Test (macos-latest, Node 22.x), Test (windows-latest, Node 22.x), Integration Tests (CLI, No Sandbox). / 已跳过(CI-only 改动的分类结果):macOS/windows 单测与无沙箱集成测试。 The check that matters here is No sandboxed-lane line, and why: the one link no lane could exercise is the Actions runner's own parser — neither Real-scenario testing: N/A — CI-workflow internals with no user-visible surface, and this is an unattended run. 中文说明代码审查先给独立方案(只看标题与事故):扩展现有的 我对当前 head 重新核对了清点(只读,经 API——未检出、未执行任何 PR 代码):
配对性质是更强的测试形状,也是应该落地的形状:它对任何拼写的半拷贝都会失败——包括尚未被枚举过的拼写——而不是追着新形状跑;且因为清点计数是文件级的,携带半拷贝的新站点同样会使计数失衡。它唯一看不到的方向是四个清点文件之外的全新文件,测试自己的注释已直说这一点;这对任何静态清点都是固有的,也正是被拒绝的集中化建议想覆盖的方向。关于那次拒绝:我核实了其依据——工作流确实把这些脚本作为可信基内容暂存(同一文件里就有 sha256 钉扎的 替换本身可靠:其输出( 未发现阻塞项。 测试证据无人值守 CI 运行——本环境从不构建或运行 PR 代码;以下证据是 PR 自身在受审 commit 上的 CI,经 API 一次性读取。已全部落定:上方表格为各项检查结论。 这里的关键检查是 不给出沙箱通道触发行的原因:唯一没有任何通道能演练的环节是 Actions runner 自身的解析器—— 真实场景测试:N/A——CI 工作流内部,无用户可见面,且本次为无人值守运行。 — Qwen Code · qwen3.8-max Reviewed at |
|
Confidence: 5/5 — clean on the second pass too; the review-fix cycle made a good fix visibly better. Stepping back: this re-run reviewed all four commits, not just the first. The incident is real and now doubly evidenced — measured on #9761, and visible in this very thread, where the takeover on this PR burned five consecutive pre-agent rounds to the same mid-line bracket-form payload riding echoed review text. The fix is still the minimal one: defuse BOTH command syntaxes at every site where reviewer- or agent-written content reaches step stdout, in one canonical spelling. What changed since the first pass is that the two holes the review lane found got closed mechanically rather than by convention. The re-emit loops now carry the same two-expression sed as everyone else, so the family has exactly one spelling; and the census no longer enumerates banned shapes — it asserts the pairing property, so a half-copy in any spelling, present or future, unbalances a count and fails the contract. That is the piece I'd thank the author for in six months: the next syntax change fails in the test, not in a round. The one suggestion that was declined — centralizing the neutralizer into a shared program — was declined for the right reason, and I verified it against the workflow itself: these scripts run as trusted-base, sha256-pinned content, and a runtime-loaded defuse program from the agent-writable checkout would have given the agent an off switch for its own guard. Every census number re-checked against this head through the API (7 + 6 + 2 + 1, expression counts balanced, zero leftovers, every remaining raw cat flowing to a file, a substitution, or the step summary). CI on the reviewed commit settled green, including the ubuntu unit job that executes exactly these contract tests. Verdict: approve. Nothing pending — the approval lands now, pinned to the reviewed commit. 中文说明置信度:5/5 —— 第二遍依然干净;评审-修复循环让这个本就正确的修复明显更好了。 回顾全局:本次 re-run 审查了全部四个 commit,而不只是第一个。事故是真实的,且现在有双重证据——#9761 上的实测,以及本线程内可见的事实:本 PR 的接管在同一个行中括号形式 payload(随被回显的评审文本进入)上连续烧掉了 5 轮 agent 运行前的轮次。修复仍然是最小的:在所有评审者/agent 内容到达步骤 stdout 的站点,用一个规范拼写同时失效两种命令语法。 与第一遍相比的变化是:评审通道发现的两个漏洞被机械性地堵上了,而不是靠约定。重发循环现在与其他所有站点使用同一个双表达式 sed,家族里只有一种拼写;清点不再枚举被禁形状——它断言配对性质,因此任何拼写的半拷贝,无论现在还是将来,都会使计数失衡并让契约测试失败。这是六个月后我会感谢作者的部分:下一种语法变化会先在测试里失败,而不是在某一轮里。唯一被拒绝的建议——把消毒器集中成共享程序——被拒绝的理由是正确的,我也对照工作流本身核实了:这些脚本以可信基、sha256 钉扎的内容运行,若从 agent 可写的检出区运行时加载失效程序,等于给 agent 一个关掉自身防线的开关。 所有清点数字都已通过 API 对照本 head 复核(7 + 6 + 2 + 1,表达式计数均衡,零残留,每一处剩余的原始 cat 都流向文件、命令替换或 step summary)。受审 commit 上的 CI 已全部变绿,包括恰好执行这些契约测试的 ubuntu 单测任务。 结论:批准。无待决项——批准现在落下,钉在受审 commit 上。 — 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 /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. Suggestions are inline.
Not reviewed: build-and-test — the 'Test (macos-latest, Node 22.x)' check was skipped in CI and is the lane that runs the changed scripts test suite under BSD sed, which the local GNU-sed run did not cover.
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.
中文说明
仅完成部分审查,审查缺口已披露。 建议见行内评论。
未审查:build-and-test — the 'Test (macos-latest, Node 22.x)' check was skipped in CI and is the lane that runs the changed scripts test suite under BSD sed, which the local GNU-sed run did not cover。
未检查(工具限制,非阻断):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.22.0)
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. ✅
|
🤖 AutoFix crashed before it could evaluate the feedback (attempt 1/100) — it will retry on the next scan. AutoFix failed before producing a verified commit (the run crashed or timed out before it could explain why). 中文说明🤖 AutoFix 在评估反馈之前崩溃(第 1/100 次尝试)—— 将在下次扫描时重试。 Run log: https://github.com/QwenLM/qwen-code/actions/runs/32712448580 🧠 Handled by Qwen Code · model/模型 |
|
🤖 AutoFix crashed before it could evaluate the feedback (attempt 2/100) — it will retry on the next scan. AutoFix failed before producing a verified commit (the run crashed or timed out before it could explain why). 中文说明🤖 AutoFix 在评估反馈之前崩溃(第 2/100 次尝试)—— 将在下次扫描时重试。 Run log: https://github.com/QwenLM/qwen-code/actions/runs/32714543876 🧠 Handled by Qwen Code · model/模型 |
|
🤖 AutoFix crashed before it could evaluate the feedback (attempt 3/100) — it will retry on the next scan. AutoFix failed before producing a verified commit (the run crashed or timed out before it could explain why). 中文说明🤖 AutoFix 在评估反馈之前崩溃(第 3/100 次尝试)—— 将在下次扫描时重试。 Run log: https://github.com/QwenLM/qwen-code/actions/runs/32715790281 🧠 Handled by Qwen Code · model/模型 |
|
🤖 AutoFix crashed before it could evaluate the feedback (attempt 4/100) — it will retry on the next scan. AutoFix failed before producing a verified commit (the run crashed or timed out before it could explain why). 中文说明🤖 AutoFix 在评估反馈之前崩溃(第 4/100 次尝试)—— 将在下次扫描时重试。 Run log: https://github.com/QwenLM/qwen-code/actions/runs/32716551463 🧠 Handled by Qwen Code · model/模型 |
|
🤖 AutoFix stopped after 5 consecutive rounds that pushed nothing (failed rounds, timeouts, gate rejections, or stops under instruction). Retrying at the same per-round budget is not converging — this usually means the PR is too large or conflicts with a fast-moving AutoFix failed before producing a verified commit (the run crashed or timed out before it could explain why). 中文说明🤖 AutoFix 已停止:连续 5 轮未能推送任何内容(失败轮次、超时、验证门拒绝或按指示停止)。以相同的单轮预算重试并不收敛 —— 这通常意味着 PR 过大,或与快速变动的 Run log: https://github.com/QwenLM/qwen-code/actions/runs/32718508030 🧠 Handled by Qwen Code · model/模型 |
Review feedback on #9871: the two upsert re-emit loops neutralized with a hybrid spelling (bash expansion for :: plus a single-syntax sed for ##[) while the other fourteen sites used the canonical two-expression sed, establishing two implementations of one normalization. A future edit fixing one spelling could miss the other and recreate the single-syntax half-guard that let #9761 through. Unify both loops on the canonical sed and consolidate the pinned test shapes. The census test's comment also promised more than its assertions deliver: it pins the known enumeration, it does not auto-detect new un-neutralized echoes. Reword it to state the actual guarantee, ban the ##[-only sed and the bare bash expansion alongside the existing ::-only ban, and fold the script-side re-emit loop into the census.
|
@qwen-code /takeover stop |
|
👋 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(在飞的一轮如有,将完成其有界工作)。重新打上 |
|
@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. Suggestions are inline.
Not reviewed: build-and-test — the 'Test (macos-latest, Node 22.x)' check was skipped in CI and is the lane that runs the changed scripts test suite under BSD sed, which the local GNU-sed run did not cover.
Not reviewed: the executable-script lint — .github/scripts/autofix-push-and-report.sh: shellcheck is not installed.
Not reviewed: the executable-script lint — .github/scripts/run-autofix-review-verification.sh: shellcheck is not installed.
Not reviewed: the executable-script lint — .github/scripts/upsert-deferred-issue.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 2, not a blocker) — recorded, not requested in this round:
scripts/tests/qwen-autofix-workflow.test.js:13908 — [probe] behavioral test embeds the live ##[add-matcher] payload; a failing run re-injects it into CI stdout
中文说明
仅完成部分审查,审查缺口已披露。 建议见行内评论。
未审查:build-and-test — the 'Test (macos-latest, Node 22.x)' check was skipped in CI and is the lane that runs the changed scripts test suite under BSD sed, which the local GNU-sed run did not cover。
未审查:the executable-script lint — .github/scripts/autofix-push-and-report.sh: shellcheck is not installed。
未审查:the executable-script lint — .github/scripts/run-autofix-review-verification.sh: shellcheck is not installed。
未审查:the executable-script lint — .github/scripts/upsert-deferred-issue.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。
收敛姿态下延后(第 2 轮,非阻断)——已记录,本轮不要求修改:共 1 条(原文未翻译,列表见上方英文部分)。
— qwen3.8-max via Qwen Code /review (v0.22.0)
|
🤖 AutoFix crashed before it could evaluate the feedback (attempt 1/100) — it will retry on the next scan. AutoFix failed before producing a verified commit (the run crashed or timed out before it could explain why). 中文说明🤖 AutoFix 在评估反馈之前崩溃(第 1/100 次尝试)—— 将在下次扫描时重试。 Run log: https://github.com/QwenLM/qwen-code/actions/runs/32735943107 🧠 Handled by Qwen Code · model/模型 |
|
🤖 AutoFix crashed before it could evaluate the feedback (attempt 2/100) — it will retry on the next scan. AutoFix failed before producing a verified commit (the run crashed or timed out before it could explain why). 中文说明🤖 AutoFix 在评估反馈之前崩溃(第 2/100 次尝试)—— 将在下次扫描时重试。 Run log: https://github.com/QwenLM/qwen-code/actions/runs/32738254462 🧠 Handled by Qwen Code · model/模型 |
|
🤖 AutoFix crashed before it could evaluate the feedback (attempt 3/100) — it will retry on the next scan. AutoFix failed before producing a verified commit (the run crashed or timed out before it could explain why). 中文说明🤖 AutoFix 在评估反馈之前崩溃(第 3/100 次尝试)—— 将在下次扫描时重试。 Run log: https://github.com/QwenLM/qwen-code/actions/runs/32740992248 🧠 Handled by Qwen Code · model/模型 |
|
🤖 AutoFix crashed before it could evaluate the feedback (attempt 4/100) — it will retry on the next scan. AutoFix failed before producing a verified commit (the run crashed or timed out before it could explain why). 中文说明🤖 AutoFix 在评估反馈之前崩溃(第 4/100 次尝试)—— 将在下次扫描时重试。 Run log: https://github.com/QwenLM/qwen-code/actions/runs/32743179103 🧠 Handled by Qwen Code · model/模型 |
|
🤖 AutoFix stopped after 5 consecutive rounds that pushed nothing (failed rounds, timeouts, gate rejections, or stops under instruction). Retrying at the same per-round budget is not converging — this usually means the PR is too large or conflicts with a fast-moving AutoFix failed before producing a verified commit (the run crashed or timed out before it could explain why). 中文说明🤖 AutoFix 已停止:连续 5 轮未能推送任何内容(失败轮次、超时、验证门拒绝或按指示停止)。以相同的单轮预算重试并不收敛 —— 这通常意味着 PR 过大,或与快速变动的 Run log: https://github.com/QwenLM/qwen-code/actions/runs/32745156411 🧠 Handled by Qwen Code · model/模型 |
|
⏸️ Takeover paused: this PR reached its round cap (100/100). Comment 中文说明⏸️ 托管已暂停:本 PR 达到轮次上限(100/100)。评论 |
…shapes The three shape bans only matched the no--e spellings, so the canonical line's natural half-copies (sed -e 's/::/;;/g' alone, or the ##[-only -e mirror) passed every ban while guarding only one syntax. Assert the pairing property instead: every occurrence of either substitution expression must belong to the canonical two-expression pair, so a half-guard in ANY spelling unbalances the count and fails the census — verified by probe against all four half-guard shapes. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Partially reviewed — gaps disclosed.
Not reviewed: build-and-test — the 'Test (macos-latest, Node 22.x)' check was skipped in CI and is the lane that runs the changed scripts test suite under BSD sed, which the local GNU-sed run did not cover.
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.
中文说明
仅完成部分审查,审查缺口已披露。
未审查:build-and-test — the 'Test (macos-latest, Node 22.x)' check was skipped in CI and is the lane that runs the changed scripts test suite under BSD sed, which the local GNU-sed run did not cover。
未检查(工具限制,非阻断):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.22.0)
|
@qwen-code /triage |
|
Sandboxed verification: The verification job did not complete (checkout, runner, or setup error) and produced no report. See the workflow run for details. 中文 — 判定:
|
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.
Approved. Every untrusted echo site now routes through one canonical two-expression neutralizer (s/::/;;/g + s/##[/##[/g), closing the legacy mid-line ##[ parse that #9761 exploited; the census test pins exact site counts per file, the pairing assertion catches any surviving single-syntax half-guard in any spelling, and the behavioral upsert test proves a ##[add-matcher] payload is defused end to end. Mergeable confirmed.
wenshao
left a comment
There was a problem hiding this comment.
Not reviewed: build-and-test — the 'Test (macos-latest, Node 22.x)' check was skipped in CI and its suite did not run locally.
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.
中文说明
未审查:build-and-test — the 'Test (macos-latest, Node 22.x)' check was skipped in CI and its suite did not run locally。
未检查(工具限制,非阻断):the executable-script lint — .github/workflows/qwen-autofix.yml: actionlint embedded-shell source mapping is not yet supported — not linted。
— gpt-5.6-sol via Qwen Code /review (v0.22.0)
|
Released in v0.22.2. |
What this PR does
Extends the workflow-command neutralization the autofix pipeline applies whenever it echoes reviewer- or agent-written content into a job log, so it defuses BOTH command syntaxes the Actions runner parses: the modern
::name::form and the legacy##[name]form — the latter is parsed even mid-line. Every echo site in the autofix family now passes content through the same two-expression neutralizer, and the contract tests pin a full census of those sites plus a behavioral case proving a##[payload comes out defused.Why it's needed
Measured on #9761: a review finding about workflow-command injection carried its payload strings (
::error::forged,##[add-matcher]) verbatim as example text. The autofix prepare step echoes the assembled feedback file to the job log for diagnostics; the runner parsed the mid-line##[add-matcher], tried to load the rest of the line as a problem-matcher file, and failed the step before the agent ever ran. Five consecutive rounds crashed within ~2 minutes each, the consecutive-failure breaker fired, and the takeover window was reported as exhausted at the 100/100 round cap — ~70 minutes after the first crash. The existing neutralization covered only the::syntax, so every one of these echoes was one quoted##[away from the same failure: any review comment containing command-shaped text can deterministically burn rounds of a takeover PR this way.Reviewer Test Plan
How to verify
npx vitest run --config ./scripts/tests/vitest.config.ts scripts/tests/qwen-autofix-workflow.test.js. The new neutralization case pins the census of every untrusted echo (six in the workflow, six in the verification gate script, two in the deferred-findings script, plus both upsert re-emit loops) and fails if any site drops back to the single-syntax form; the deferred-findings behavioral case feeds a##[add-matcher]payload through the real script and asserts the neutralized form appears and the raw form never does.;;error;;forged/##[add-matcher]instead of being parsed, so the prepare step survives and the round reaches the agent.mapfilebuiltin); verified identical on the base tree.Evidence (Before & After)
N/A — CI-workflow internals, no user-visible surface.
Tested on
Environment (optional)
Contract tests only (
vitestagainst the workflow/script sources; the behavioral case executes the real deferred-findings script under a stubbedgh).Risk & Scope
::as;;and##[as##[(full-width bracket) — a cosmetic change to diagnostic log text only. The files themselves (feedback, agent stop notes, PR comment bodies) are untouched, so nothing the agent reads or the PR thread renders changes.Linked Issues
Follow-up to the incident measured on #9761 (no auto-close).
中文说明
本 PR 做了什么
扩展 autofix 流水线在把评审者/agent 产出内容回显进 job 日志时所使用的 workflow 命令消毒,使其同时失效 Actions runner 会解析的两种命令语法:现代
::name::形式与 legacy##[name]形式——后者即使在行中也会被解析。autofix 家族中所有回显点现在都经过同一个双表达式消毒器,契约测试对这些回显点做了全量清点,并新增一个行为性用例证明##[payload 输出时已被失效。为什么需要
在 #9761 上实测:一条关于 workflow 命令注入的评审发现,正文里逐字带着其 payload 字符串(
::error::forged、##[add-matcher])作为示例文本。autofix 的 prepare 步骤会把组装好的反馈文件回显到 job 日志以便排查;runner 解析了行中的##[add-matcher],把该行的剩余部分当作 problem-matcher 文件去加载,导致步骤在 agent 运行之前就失败。连续 5 轮每轮约 2 分钟内崩溃,连续失败断路器触发,接管窗口被报成 100/100 轮次上限耗尽——距首次崩溃仅约 70 分钟。既有消毒只覆盖了::语法,因此这些回显点距离同样的故障只差一个被引用的##[:任何包含命令形状文本的评审评论都可以确定性地以这种方式烧掉接管 PR 的轮次。评审者测试计划
如何验证
npx vitest run --config ./scripts/tests/vitest.config.ts scripts/tests/qwen-autofix-workflow.test.js。新增的消毒清点上例钉住了所有不可信回显点(工作流 6 处、验证门脚本 6 处、deferred-findings 脚本 2 处,外加两个 upsert 重发循环),任何一处退回单语法形式即失败;deferred-findings 行为用例通过真实脚本喂入##[add-matcher]payload,断言输出中出现已失效形式且原始形式绝不出现。;;error;;forged/##[add-matcher]而不会被解析,prepare 步骤得以存活,轮次能够到达 agent。mapfile内建命令);已在 base 树上确认相同。证据(前后对比)
N/A —— CI 工作流内部改动,无用户可见界面。
测试环境
环境(可选)
仅契约测试(
vitest针对工作流/脚本源码;行为用例在 stub 的gh下执行真实的 deferred-findings 脚本)。风险与范围
::渲染为;;、##[渲染为##[(全角括号)——仅是诊断日志文本的观感变化。文件本身(反馈、agent 停止说明、PR 评论正文)完全不受影响,agent 读取的内容与 PR 线程渲染的内容均不变。关联 Issue
跟进在 #9761 上实测到的事故(不自动关闭)。