Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions .github/workflows/qwen-autofix.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ task-oriented guides — what a maintainer types and what happens next — see:
- [70. review-address · Report dry-run / failure — -c drops any partial multi-byte sequence a byte-level head -c may have split, so the…](#af-070)
- [71. review-address · Report dry-run / failure — Bilingual companion. Repo convention is English first, Chinese in a collapsed <details>.…](#af-071)
- [72. review-address · Report dry-run / failure — Flip the status comment out of "working" so a finished round never leaves a live-looking…](#af-072)
- [73. review-address · Report dry-run / failure — Idle (silent-sandbox) timeouts are EXCLUDED from the cumulative timeout cap.…](#af-073)

---

Expand Down Expand Up @@ -2090,3 +2091,68 @@ publishing" and report a successful round as a failed one. An empty
'stale' (prepare itself crashed) still finalises — that IS this job's
round, and it is exactly the case that must not stay "working".
```

<a id="af-073"></a>

### 73. review-address · Report dry-run / failure — Idle (silent-sandbox) timeouts are EXCLUDED from the cumulative timeout cap.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Critical] The new af-073 anchor is added without a Contents-table entry, and that fails this repo's existing design-record consistency test scripts/tests/workflow-size.test.js ("lists every section in the contents table"): the test asserts the TOC link targets equal every <a id="af-N"> anchor in this file. This diff adds anchor af-073 but the table still ends at entry 72, so the suite fails — the PR's own Test (ubuntu-latest, Node 22.x) lane is red with this exact assertion right now.

Witness:

PR arm at 536d2096f9:
FAIL scripts/tests/workflow-size.test.js > lists every section in the contents table
AssertionError: expected [ 'af-001', …, 'af-072' ] to deeply equal [ …, 'af-073' ]
Tests 1 failed | 56 passed (57)

BASE arm (same test, input files at merge base 0dd518f950): Tests 57 passed (57)
Live CI: gh pr checks 9673 → Test (ubuntu-latest, Node 22.x) fail

Add the entry for section 73 to the Contents block (after the entry-72 line, ~line 126), matching the house style — the test's regex only requires the - [73. ...](#af-073) shape:

- [73. review-address · Report dry-run / failure — Idle (silent-sandbox) timeouts are EXCLUDED from the cumulative timeout cap.…](#af-073)
中文说明

新增的 af-073 锚点没有对应的目录条目,这会使仓库既有的设计档案一致性测试 scripts/tests/workflow-size.test.js("lists every section in the contents table")失败:该测试断言目录链接目标与本文件中的每个 <a id="af-N"> 锚点一一对应。本次 diff 添加了锚点 af-073,但目录仍止于第 72 条,因此该测试套件失败——本 PR 自己的 Test (ubuntu-latest, Node 22.x) 通道现在正是因为这条断言而变红。

(证据见上方代码块:PR 侧该测试 1 failed | 56 passed;换回合并基线的输入文件后 57/57 全绿;线上 CI 同一断言失败。)

修复:在 Contents 区块(第 72 条之后,约第 126 行)为第 73 节添加条目,沿用既有风格——测试的正则只要求 - [73. ...](#af-073) 这一形状。

— qwen3.8-max via Qwen Code /review (v0.21.15)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Critical] R1-1: The new af-073 anchor is added without a Contents-table entry, and that fails this repo's existing design-record consistency test scripts/tests/workflow-size.test.js ("lists every section in the contents table"): the test asserts the TOC link targets equal every <a id="af-N"> anchor in this file. This diff adds anchor af-073 but the table still ends at entry 72, so the suite fails — the PR's own Test (ubuntu-latest, Node 22.x) lane is red with this exact assertion.

Witness: at the reviewed commit the file holds 71 anchors but 70 contents-table entries — the difference is exactly af-073. Running the scripts/tests suite here: FAIL scripts/tests/workflow-size.test.js > lists every section in the contents table, 1 failed | 1519 passed (1520). At the merge base the same sets are equal (70/70) and the test passes, so the failure is introduced by this diff even though the test file itself is unchanged.

Add the entry for section 73 to the Contents block (after the entry-72 line, ~line 126), matching the house style — the test's regex only requires the - [73. ...](#af-073) shape:

- [73. review-address · Report dry-run / failure — Idle (silent-sandbox) timeouts are EXCLUDED from the cumulative timeout cap.…](#af-073)
中文说明

新增的 af-073 锚点没有对应的目录条目,这会使仓库既有的设计档案一致性测试 scripts/tests/workflow-size.test.js("lists every section in the contents table")失败:该测试断言目录链接目标与本文件中的每个 <a id="af-N"> 锚点一一对应。本次 diff 添加了锚点 af-073,但目录仍止于第 72 条,因此该测试套件失败——本 PR 自己的 Test (ubuntu-latest, Node 22.x) 通道现在正是因为这条断言而变红。

证据:在被审提交上,文件有 71 个锚点但只有 70 条目录条目,差集恰为 af-073。在本工作树运行 scripts/tests 套件:FAIL scripts/tests/workflow-size.test.js > lists every section in the contents table,1 failed | 1519 passed (1520)。在合并基线上两组集合相等(70/70)且测试通过,因此该失败由本 diff 引入——尽管测试文件本身未被修改。

修复:在 Contents 区块(第 72 条之后,约第 126 行)为第 73 节添加条目,沿用既有风格——测试的正则只要求 - [73. ...](#af-073) 这一形状(见上方代码块)。

— qwen3.8-max via Qwen Code /review (v0.21.15)


In `review-address` · `Report dry-run / failure`.

```text
TIMEOUT_WINDOW_CAP exists to stop a PR that is too big to finish a
round inside the agent's time budget; its remedy says so ("split or
reduce the PR, or raise the agent time budget AND its step backstop").
An idle timeout is a different failure entirely: run-agent.mjs's idle
watchdog kills the round after QWEN_IDLE_TIMEOUT_MS (20m) because the
sandbox produced no output at all — the four observed hangs (#8663 x2,
#8761 r3, #8763 r4) each printed their last byte at docker container
entry and then sat silent. Nothing about the PR caused it, and the
breaker's own headline already told the reader that "no budget increase
can cure" it. Counting a failure whose prescribed remedy is
inapplicable is what parked healthy PRs.

Measured on 2026-08-21, over the preceding 14 days: 119 timeouts, of
which 58 (49%) were idle. 51 windows tripped this cap, every one of
them at exactly N=3. Of the 12 open PRs then carrying
autofix/needs-human, 9 had been stopped here — #8332 at 24 rounds,
#8368 at 28, #8276 at 16, all still producing pushed rounds when they
were parked. With idle rounds counted, the fleet timeout rate was
8.5% per round, so a window accumulated three of them in ~35 rounds by
arithmetic alone, independent of whether the PR was stuck. Excluding
idle drops the rate to 4.3%, which needs ~69 rounds — beyond the
deepest window ever observed (22/100).

The escape hatch that makes the exclusion safe: an idle round pushes
nothing and matches none of CONSEC_FAIL's streak-reset needles
("Addressed the latest review feedback", "no changes needed", "AutoFix
could not start", "updated a stale base"), so a persistently wedged
sandbox still terminates the PR at CONSECUTIVE_FAILURE_CAP. What no
longer terminates it is idle rounds INTERLEAVED with real progress —
which is the intended change: that PR is not stuck, the runner is.

Two consequences inside the block. IDLE_N's needle became the full
emitted headline prefix ('AutoFix ran out of time before finishing
(idle-timeout') rather than a bare 'idle-timeout' substring: IDLE_N is
now subtracted from TIMEOUT_N, so it MUST be a subset of it, and a
loose needle could otherwise match provider error text that
API_ERROR_DETAIL puts on the same first line and drive the difference
negative. And the all-idle remedy branch is gone as unreachable: the
guard now fires only when BUDGET_TIMEOUT_N alone reaches the cap, so a
tripped window always holds at least TIMEOUT_WINDOW_CAP genuine budget
timeouts — idle rounds can outnumber budget ones in it, but the budget
remedy applies because those budget timeouts exist, not because they
are the majority.

Idle rounds stay visible through a job-log ::warning:: rather than a PR
comment — the signal belongs to whoever owns the runners, and infra
noise should not spend a comment on someone's PR. The census and its
warning run outside the cap's terminal guard: the all-idle shape stops
via the consecutive breaker with that breaker's headline, and the
terminal run's log is exactly where the wedged runner must be named.

The same exclusion applies to the prepare step's PRIOR_TIMEOUTS census
(af-049): its budget warning tells the agent to narrow scope — the
budget remedy again — and an idle round never exhausted any budget, so
it must not steer the narrowing. Idle rounds are excluded there with
the same needle the cap census uses.
```
73 changes: 47 additions & 26 deletions .github/workflows/qwen-autofix.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
name: 'Qwen Autofix'

# One workflow for the whole autonomous-fix lifecycle:
Expand Down Expand Up @@ -245,7 +245,9 @@
# pushed rounds in between, so the consecutive cap never fired and the PR
# kept walking into the same wall; #7846 the same, twice. Counted over the
# current counting window (window-scoped like every other census), so a
# re-arm clears it along with the round counter.
# re-arm clears it along with the round counter. Counts BUDGET timeouts
# only: silent-sandbox (idle) timeouts are infra, not PR size, and are
# excluded — see qwen-autofix.md#af-073.
TIMEOUT_WINDOW_CAP: '3'
# Do not claim more issues when too many existing autofix PRs are still open.
MAX_OPEN_AUTOFIX_PRS: '5'
Expand Down Expand Up @@ -4991,6 +4993,9 @@
# Time-budget exhaustions SINCE THE LAST SUCCESSFUL ROUND mean
# the standard address-everything prompt is not converging at
# Full rationale → qwen-autofix.md#af-049
# Idle (silent-sandbox) timeouts are excluded like in the cap
# census: the narrowing advice targets budget exhaustion, and an
# infra-killed round never had any budget to exhaust (af-073).
PRIOR_TIMEOUTS="$(jq -r --arg ab "${AUTOFIX_BOT}" --arg key "${LIVE_REARM_KEY}" '
[ .[] | select((.user.login // "") == $ab)
| select((.body // "") | contains("<!-- autofix-eval "))
Expand All @@ -4999,7 +5004,7 @@
] | sort_by(.created_at)
| map((.body | gsub("\r"; "") | split("\n")[0]))
| (map(test("Addressed the latest review feedback|no changes needed")) | rindex(true) // -1) as $lastok
| [ .[($lastok + 1):][] | select(contains("AutoFix ran out of time before finishing")) ] | length' "${WORKDIR}/ic.json" 2> /dev/null || true)"
| [ .[($lastok + 1):][] | select(contains("AutoFix ran out of time before finishing") and (contains("AutoFix ran out of time before finishing (idle-timeout") | not)) ] | length' "${WORKDIR}/ic.json" 2> /dev/null || true)"
if [[ "${PRIOR_TIMEOUTS}" -ge 1 ]]; then
echo
echo '## Budget warning: previous round(s) ran out of time'
Expand Down Expand Up @@ -6674,9 +6679,31 @@
# full agent budget with nothing to show (observed on #7929:
# three timeouts with successes in between; #7846 twice). The
# census reuses PRIOR_HEADS, so it is window-scoped exactly
# like the consecutive one and a re-arm clears it. Only
# overrides a would-be RETRY: a round already terminal keeps
# its own headline (the consecutive breaker included).
# like the consecutive one and a re-arm clears it. Only the
# cap gate below overrides a would-be RETRY: a round already
# terminal keeps its own headline (the consecutive breaker
# included). The idle census and its warning run OUTSIDE that
# guard: the all-idle shape terminates via the consecutive
# breaker above, and that terminal run's job log is exactly
# where the wedged runner must be named.
# Idle (silent-sandbox) timeouts are EXCLUDED from this cap:
# a wedged runner/docker is not this PR being too big, an idle
# round dies at QWEN_IDLE_TIMEOUT_MS having produced no byte
# (a fraction of a real round), and no budget increase cures
# one — so this breaker's remedy does not apply to them. A
# persistently wedged sandbox stays bounded by
# CONSECUTIVE_FAILURE_CAP, which an idle round DOES feed.
# Full rationale → qwen-autofix.md#af-073
IDLE_N="$(grep -c 'AutoFix ran out of time before finishing (idle-timeout' <<< "${PRIOR_HEADS}" || true)"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] R1-3: Still stands — reported in rounds 1-2, deferred by the autofix loop, re-verified at this commit. The idle-vs-budget reclassification this PR introduces is applied at only one of the census sites that count the same timeout headline: the sibling af-049 PRIOR_TIMEOUTS site in the prepare step (~L4996-5010) still selects every first line containing AutoFix ran out of time before finishing since the last pushed round — the idle headline matches that needle — and reports idle rounds to the agent as budget exhaustions: "N round(s) since the last successful round exhausted the agent time budget before finishing anything". Per af-049's own doc the consequence is behavioral: "From the second attempt on, tell the agent to narrow." This PR's exclusion is what makes the misattribution persistent: pre-diff such windows parked at 3 cumulative timeouts; post-diff an idle-failing PR that keeps pushing runs on, and every idle round re-fires the false "exhausted the budget" attribution — steering the agent to narrow scope for a wedged runner (the idle watchdog kills the round at 20 min of silence; the ~130-min budget was never exhausted). Exclude the idle cause at the sibling site — e.g. and (contains("(idle-timeout") | not) in the jq select — or amend the af-049 wording so infra-killed rounds are not reported as budget exhaustion; either fork carries its own test obligation.

Witness: running the prepare step's jq verbatim against a fixture of one pushed round + one pure idle round yields PRIOR_TIMEOUTS=1 and emits ## Budget warning: previous round(s) ran out of time / 1 round(s) since the last successful round exhausted the agent time budget before finishing anything. (a two-idle fixture yields PRIOR_TIMEOUTS=2).

中文说明

[Suggestion] R1-3:仍然成立 —— 已在第 1-2 轮报告,被 autofix 循环顺延,本轮在该提交上重新核实。本 PR 引入的 idle/预算重分类只应用在了统计同一超时标题的普查点之一:prepare 步骤里的姊妹点位 af-049 PRIOR_TIMEOUTS(约 L4996-5010)仍然选取自上一次推送轮以来所有包含 AutoFix ran out of time before finishing 的首行 —— idle 标题恰好匹配该匹配串 —— 从而把 idle 轮次当作预算耗尽上报给 agent:"N round(s) since the last successful round exhausted the agent time budget before finishing anything"。按 af-049 自己的档案,其后果是行为性的:"从第二次尝试起,告诉 agent 收窄范围。"本 PR 的排除恰恰使这一错误归因变得持续:改动前此类窗口会在累计 3 次超时时停摆;改动后,一个持续失败但仍在推送的 idle 型 PR 会继续跑下去,每一次 idle 都会再次触发这条"耗尽预算"的假归因 —— 引导 agent 为一个挂死的 runner 收窄范围(idle 看门狗在 20 分钟无输出时就杀掉该轮;约 130 分钟的预算根本未被耗尽)。建议在姊妹点位同样排除 idle 成因 —— 例如在 jq select 中加 and (contains("(idle-timeout") | not) —— 或者修订 af-049 的措辞,使被基础设施杀掉的轮次不再以预算耗尽上报;两种分叉各有自己的测试义务。

见证:对一个"一次推送轮 + 一次纯 idle 轮"的夹具逐字运行 prepare 步骤的 jq,得 PRIOR_TIMEOUTS=1,并发出 ## Budget warning: previous round(s) ran out of time / 1 round(s) since the last successful round exhausted the agent time budget before finishing anything.(两次 idle 的夹具得 PRIOR_TIMEOUTS=2)。

— qwen3.8-max via Qwen Code /review (v0.21.15)

if [[ "${AGENT_TIMEOUT:-}" == 'idle-timeout'* ]]; then
IDLE_N=$(( IDLE_N + 1 ))
fi
Comment on lines +6698 to +6700

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] R1-4: Still stands — reported in rounds 1-2, deferred by the autofix loop. Idle classification now lives in three independently-pinned places — this current-round prefix test, the census grep needle, and the headline assembly (CAUSE=/HEADLINE=) fed by run-agent.mjs's detail template — and the composition test tying the run-agent.mjs emission shape to the workflow's expectations is still absent at this commit. The static pins added since lock the workflow-internal pairs only (CAUSE text ↔ census needle; idle needle ⊇ timeout needle); the replay harness's IDLE_HEAD remains a hand-maintained literal. A format change on the run-agent.mjs side — rewording the idle-timeout (no output for Nms — …) sentinel or reshaping the CAUSE contribution — breaks no test: the fixture keeps echoing the old shape, so the workflow's idle exclusion silently stops classifying (idle rounds counted as budget timeouts again, or the increment never firing), and the regression surfaces only in production counting windows. Add the deferred composition test: extract the idle sentinel/detail template from .qwen/skills/autofix/scripts/run-agent.mjs and assert the harness's idle prefix and the workflow's 'idle-timeout'* prefix test both match it — or pin the run-agent emission string and the workflow needle to one shared constant.

中文说明

[Suggestion] R1-4:仍然成立 —— 已在第 1-2 轮报告,被 autofix 循环顺延。idle 分类现在存在于三个独立钉住的位置 —— 本处的当前轮前缀判断、普查 grep 匹配串、以及由 run-agent.mjs 的 detail 模板喂给的标题拼装(CAUSE=/HEADLINE=)—— 而把 run-agent.mjs 的发出形态与 workflow 侧期望钉在一起的组合测试,在该提交上依然缺失。此后新增的静态钉只锁住了 workflow 内部的两对关系(CAUSE 文案 ↔ 普查匹配串;idle 匹配串 ⊇ timeout 匹配串);回放 harness 里的 IDLE_HEAD 仍是手工维护的字面量。若 run-agent.mjs 一侧变更格式 —— 改写 idle-timeout (no output for Nms — …) 哨兵或重塑 CAUSE 的贡献 —— 不会有任何测试失败:夹具继续回放旧形态,于是 workflow 的 idle 排除会悄无声息地停止分类(idle 轮次重新被计为预算超时,或自增根本不触发),回归只会在生产计数窗口里浮现。建议补上被顺延的组合测试:从 .qwen/skills/autofix/scripts/run-agent.mjs 提取 idle 哨兵/detail 模板,断言 harness 的 idle 前缀与 workflow 的 'idle-timeout'* 前缀判断都能与之匹配 —— 或者把 run-agent 的发出字符串与 workflow 匹配串钉到同一个共享常量上。

— qwen3.8-max via Qwen Code /review (v0.21.15)

# Excluding idle from the cap must not hide it. The job log is
# the right surface: it reaches the operator without spending a
# PR comment on infra noise.
if [[ "${IDLE_N}" -gt 0 ]]; then
echo "::warning::#${PR}: ${IDLE_N} silent-sandbox (idle) timeout(s) this counting window — excluded from the ${TIMEOUT_WINDOW_CAP}-timeout cap; check the sandbox image and the runner docker daemon"
Comment on lines +6704 to +6705

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] R3-1: Of this idle-census ::warning:: — the only observability af-073 leaves for excluded idle timeouts — the tests pin only the ::warning::#N: M silent-sandbox prefix; the ${TIMEOUT_WINDOW_CAP} interpolation and the guidance tail (check the sandbox image and the runner docker daemon) are asserted nowhere. Verified mutants that ship the suite green: corrupting the tail text, and swapping ${TIMEOUT_WINDOW_CAP}${CONSECUTIVE_FAILURE_CAP}, which logs "excluded from the 5-timeout cap" where the code must say 3 — misstating the cap on the very channel designated as the sole idle signal. (The illustrative ${TIMEOUT_N} swap is not the observable mutant here — TIMEOUT_N is first assigned after this echo; the cap-variable swap above is the demonstrated one.) Same three-site gap as the sibling comments (R3-1). Pin the tail in mixedTrips:

expect(mixedTrips.log).toContain(
  `excluded from the ${timeoutCap}-timeout cap; check the sandbox image and the runner docker daemon`,
);

Witness: tail mutant and cap-swap mutant each ship the existing suite green; with the pin added the cap-swap mutant fails — expected excluded from the 3-timeout cap; check the sandbox image and the runner docker daemon, received ::warning::#1: 1 silent-sandbox (idle) timeout(s) this counting window — excluded from the 5-timeout cap; ….

中文说明

[Suggestion] R3-1:这条 idle 普查 ::warning:: 是 af-073 为被排除的 idle 超时留下的唯一可观测信号,但测试只钉住了 ::warning::#N: M silent-sandbox 前缀;${TIMEOUT_WINDOW_CAP} 插值和指引尾部(check the sandbox image and the runner docker daemon)没有任何断言。已验证可全绿出船的变异:破坏尾部文案;以及把 ${TIMEOUT_WINDOW_CAP} 换成 ${CONSECUTIVE_FAILURE_CAP},后者会打出 "excluded from the 5-timeout cap",而代码必须说 3 —— 恰好在这个被指定为唯一 idle 信号的通道上把上限值说错。(示意性的 ${TIMEOUT_N} 替换在此处不是可观测变异 —— TIMEOUT_N 在这条 echo 之后才首次赋值;上面这个上限变量替换才是实证过的变异。)与同组评论(R3-1)属同一三处缺口。建议在 mixedTrips 中按上方代码块钉住尾部。

见证:尾部变异与上限替换变异各自让现有套件全绿;补上断言后,上限替换变异失败 —— 期望 excluded from the 3-timeout cap; check the sandbox image and the runner docker daemon,实际收到 ::warning::#1: 1 silent-sandbox (idle) timeout(s) this counting window — excluded from the 5-timeout cap; …

— qwen3.8-max via Qwen Code /review (v0.21.15)

fi
if [[ "${MARK_ROUND}" != "${MAX_ROUNDS}" ]]; then
# Needle matches the emitted headline verbatim — first lines
# can embed provider error text (API_ERROR_DETAIL puts up to
Expand All @@ -6686,38 +6713,32 @@
if [[ -n "${AGENT_TIMEOUT:-}" ]]; then
TIMEOUT_N=$(( TIMEOUT_N + 1 ))
fi
# Idle (silent-sandbox) timeouts share the census — they burn
# the same full budget — but no budget increase cures them, so
# when the window contains any, the breaker says so.
IDLE_N="$(grep -c 'idle-timeout' <<< "${PRIOR_HEADS}" || true)"
if [[ "${AGENT_TIMEOUT:-}" == 'idle-timeout'* ]]; then
IDLE_N=$(( IDLE_N + 1 ))
fi
if [[ "${TIMEOUT_N}" -ge "${TIMEOUT_WINDOW_CAP}" ]]; then
# IDLE_N's needle is TIMEOUT_N's needle extended with the
# idle cause's opening token — every line matching it also
# matches TIMEOUT_N's, so IDLE_N can never exceed TIMEOUT_N
# and the subtraction below can never go negative.
BUDGET_TIMEOUT_N=$(( TIMEOUT_N - IDLE_N ))
if [[ "${BUDGET_TIMEOUT_N}" -ge "${TIMEOUT_WINDOW_CAP}" ]]; then
MARK_ROUND="${MAX_ROUNDS}"
# The headline states what the census MEASURED — the
# window's cumulative count — not "stopped after N
# timeouts": the round that trips this can itself have
# failed differently (a gate rejection landing on a window
# that already carries the cap — the exact rollout state
# of #7929/#7846).
# No all-idle branch here: the guard above only fires when
# BUDGET_TIMEOUT_N alone reaches the cap, so a tripped
# window always holds at least TIMEOUT_WINDOW_CAP genuine
# budget timeouts — idle rounds can outnumber budget ones
# in it, but the budget remedy is always the right one.
IDLE_CLAUSE=''
IDLE_CLAUSE_ZH=''
if [[ "${IDLE_N}" -gt 0 ]]; then
IDLE_CLAUSE=" ${IDLE_N} of those were silent-sandbox (idle) timeouts that no budget increase can cure — investigate the sandbox image and runner docker daemon for those."
IDLE_CLAUSE_ZH="其中 ${IDLE_N} 次是静默 sandbox(idle)超时,提高预算也治不了 —— 请针对这些排查 sandbox 镜像与 runner 的 docker daemon。"
fi
# Mirror the round-level split: when EVERY counted timeout
# was idle, the closing remedy must not prescribe the
# budget increase the clause above just declared useless.
REMEDY='split or reduce the PR (or raise the agent time budget AND its step backstop together)'
REMEDY_ZH='拆分或缩减该 PR(或同时提高 agent 时间预算与其步骤兜底)'
if [[ "${IDLE_N}" -ge "${TIMEOUT_N}" ]]; then
REMEDY='investigate the sandbox image and runner docker daemon'
REMEDY_ZH='排查 sandbox 镜像与 runner 的 docker daemon'
IDLE_CLAUSE=" The window also holds ${IDLE_N} silent-sandbox (idle) timeout(s), which no budget increase can cure and which do NOT count toward this cap — investigate the sandbox image and runner docker daemon separately."
IDLE_CLAUSE_ZH="本窗口另有 ${IDLE_N} 次静默 sandbox(idle)超时,提高预算也治不了,且不计入本上限 —— 请另行排查 sandbox 镜像与 runner 的 docker daemon。"
Comment on lines +6737 to +6738

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] R3-1: The ${IDLE_N} count interpolated into this cap-trip clause is pinned by no test — a ${IDLE_N}${TIMEOUT_N} swap in either the EN or the ZH clause ships the whole suite green (both mutants verified: 215/215 pass). In this PR's own mixedTrips shape (2 budget priors + 1 idle prior + budget-timeout current round: TIMEOUT_N=4, IDLE_N=1), the mutant posts "The window also holds 4 silent-sandbox (idle) timeout(s)" — and 本窗口另有 4 次静默… in the ZH half — on a window holding exactly one idle round, sending whoever owns the runners to hunt a wedged fleet four times the real scale, while mixedTrips stays green because it only asserts the do NOT count toward this cap phrase. This is one of three sites of the same gap — the stop-message text this PR adds interpolates counts the suite never pins; the siblings sit on the headline's second count and on the idle-census warning. Pin the clause count numerically in both languages, e.g. in mixedTrips:

expect(mixedTrips.headline).toContain('also holds 1 silent-sandbox');
expect(mixedTrips.headlineZh).toContain('本窗口另有 1 次静默');

Witness: both clause mutants ship Tests 215 passed (215); with the two pins added the mutants fail — expected '…The window also holds 4 silent-sandbox…' to contain 'also holds 1 silent-sandbox'.

中文说明

[Suggestion] R3-1:插入到本熔断停机子句中的 ${IDLE_N} 计数没有任何测试钉住 —— 把英文或中文子句里的 ${IDLE_N} 换成 ${TIMEOUT_N},整个测试套件依然全绿(两个变异均已验证:215/215 通过)。在本 PR 自己的 mixedTrips 形态下(2 次预算超时历史 + 1 次 idle 历史 + 当前轮预算超时:TIMEOUT_N=4IDLE_N=1),该变异会发出 "The window also holds 4 silent-sandbox (idle) timeout(s)"(中文半边为 本窗口另有 4 次静默…)—— 而窗口里实际只有一次 idle,等于让负责 runner 的人按四倍的规模去排查,而 mixedTrips 依然为绿,因为它只断言了 do NOT count toward this cap 这一措辞。这是同一缺口的三处之一 —— 本 PR 新增的停机文案插入了若干计数,而测试套件从未钉住它们;另外两处分别在标题的第二个计数与 idle 普查警告上。建议在两种语言里把该计数用数字钉住,例如在 mixedTrips 中加入上方代码块中的两条断言。

见证:两个子句变异均让 Tests 215 passed (215) 全绿通过;补上两条断言后变异失败 —— expected '…The window also holds 4 silent-sandbox…' to contain 'also holds 1 silent-sandbox'

— qwen3.8-max via Qwen Code /review (v0.21.15)

fi
HEADLINE="🤖 AutoFix stopped: this counting window now contains ${TIMEOUT_N} time-budget exhaustions (pushed rounds in between included; this round itself may have failed differently). That is ${TIMEOUT_N} full agent runs that pushed nothing.${IDLE_CLAUSE} A human should ${REMEDY}, then comment \`${RETRY_COMMAND}\` to re-arm. Until then future scans will skip this PR."
HEADLINE_ZH="🤖 AutoFix 已停止:当前计数窗口内已累计 ${TIMEOUT_N} 次时间预算耗尽(含其间推送过的轮次;本轮本身可能以别的方式失败)。即 ${TIMEOUT_N} 次完整 agent 运行没有推送任何内容。${IDLE_CLAUSE_ZH}应由人工${REMEDY_ZH},然后评论 \`${RETRY_COMMAND}\` 重新武装。在此之前,后续扫描将跳过本 PR。"
HEADLINE="🤖 AutoFix stopped: this counting window now contains ${BUDGET_TIMEOUT_N} agent time-budget exhaustions (pushed rounds in between included; this round itself may have failed differently). That is ${BUDGET_TIMEOUT_N} full agent runs that pushed nothing.${IDLE_CLAUSE} A human should split or reduce the PR (or raise the agent time budget AND its step backstop together), then comment \`${RETRY_COMMAND}\` to re-arm. Until then future scans will skip this PR."

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] R3-1: Of the two ${BUDGET_TIMEOUT_N} interpolations in this headline, only the first is pinned — this second one ("That is … full agent runs that pushed nothing") and its ZH twin ("即 … 次完整 agent 运行没有推送任何内容") are asserted nowhere (grep confirms full agent runs / 次完整 appear nowhere in the test file). A ${BUDGET_TIMEOUT_N}${TIMEOUT_N} mutant on this occurrence ships green (verified: 215/215), and in the mixedTrips shape (TIMEOUT_N=4, BUDGET_TIMEOUT_N=3) it posts "That is 4 full agent runs that pushed nothing" — re-inflating with an idle round exactly the count the exclusion was written to correct, and calling an idle round a "full agent run" when af-073 documents that it dies in a fraction of one — while mixedTrips only checks the first sentence's wording (3 agent time-budget exhaustions, 3 次时间预算耗尽, and the 4 次时间预算耗尽 negative). This is the diff's own stated mutant class — the test comment at the static pins says it intends to kill exactly this mutation — one interpolation over. Same three-site gap as the sibling comments (R3-1). Pin the second sentence both ways, e.g.:

expect(mixedTrips.headline).toContain(`That is ${timeoutCap} full agent runs`);
expect(mixedTrips.headline).not.toContain(`That is ${timeoutCap + 1} full agent runs`);
expect(mixedTrips.headlineZh).toContain(`即 ${timeoutCap} 次完整`);

Witness: the second-occurrence mutant ships Tests 215 passed (215); with the pins added it fails — expected '🤖 AutoFix stopped: …' to contain '3 full agent runs' (received That is 4 full agent runs that pushed nothing).

中文说明

[Suggestion] R3-1:这条标题里有两处 ${BUDGET_TIMEOUT_N} 插值,但只有第一处被测试钉住 —— 第二处("That is … full agent runs that pushed nothing")及其中文对应("即 … 次完整 agent 运行没有推送任何内容")没有任何断言覆盖(grep 确认测试文件中不存在 full agent runs / 次完整)。对这一处施加 ${BUDGET_TIMEOUT_N}${TIMEOUT_N} 变异,套件全绿(已验证:215/215);在 mixedTrips 形态下(TIMEOUT_N=4BUDGET_TIMEOUT_N=3)会发出 "That is 4 full agent runs that pushed nothing" —— 把本次排除刚刚纠正下去的计数用一次 idle 轮重新吹大,而按 af-073 的记载,idle 轮连一轮的零头都跑不到,却被称作 "完整 agent 运行" —— 而 mixedTrips 只检查了第一句的措辞(3 agent time-budget exhaustions3 次时间预算耗尽 以及对 4 次时间预算耗尽 的负向断言)。这正是本 diff 自己声明要杀的变异类 —— 静态钉处的测试注释写明意图钉住这种变异 —— 只是漏了一处插值。与同组评论(R3-1)属同一三处缺口。建议按上方代码块双向钉住第二句。

见证:第二处插值的变异让 Tests 215 passed (215) 全绿通过;补上断言后失败 —— expected '🤖 AutoFix stopped: …' to contain '3 full agent runs'(实际收到 That is 4 full agent runs that pushed nothing)。

— qwen3.8-max via Qwen Code /review (v0.21.15)

HEADLINE_ZH="🤖 AutoFix 已停止:当前计数窗口内已累计 ${BUDGET_TIMEOUT_N} 次时间预算耗尽(含其间推送过的轮次;本轮本身可能以别的方式失败)。即 ${BUDGET_TIMEOUT_N} 次完整 agent 运行没有推送任何内容。${IDLE_CLAUSE_ZH}应由人工拆分或缩减该 PR(或同时提高 agent 时间预算与其步骤兜底),然后评论 \`${RETRY_COMMAND}\` 重新武装。在此之前,后续扫描将跳过本 PR。"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] HEADLINE_ZH is rewritten to interpolate ${BUDGET_TIMEOUT_N}, but no test asserts the ZH headline's count: the harness sentinel returns only $HEADLINE, and the pairing anchors carry no number, so the budget-count guarantee is pinned for English alone. Mutation ${BUDGET_TIMEOUT_N}${TIMEOUT_N} on this line ships green (verified: 215/215 passed); on the mixedTrips window the posted bilingual comment's ZH half would then say 已累计 4 次时间预算耗尽 while the EN half says 3 — the re-inflated number the exclusion just corrected, surviving in the other language. Add "$HEADLINE_ZH" to the harness sentinel tuple and assert mixedTrips's ZH headline contains 3 次时间预算耗尽 (and not 4 次), mirroring the existing EN assertion.

Witness: probe extending the sentinel tuple with "$HEADLINE_ZH" observed ZH 已累计 4 次时间预算耗尽 against EN's 3 under the mutation (baseline: both 3).

中文说明

HEADLINE_ZH 被改写为插值 ${BUDGET_TIMEOUT_N},但没有任何测试断言中文标题里的计数:harness 哨兵只返回 $HEADLINE,配对锚点也不含数字,因此"预算计数"这一保证只在英文侧被钉住。把这行的 ${BUDGET_TIMEOUT_N} 变异为 ${TIMEOUT_N} 可以全绿通过(已验证:215/215 通过);在 mixedTrips 窗口下,发布的双语评论中文一侧将写 已累计 4 次时间预算耗尽,而英文一侧写 3——刚被排除逻辑纠正掉的重新膨胀数字,在另一种语言里存活下来。请把 "$HEADLINE_ZH" 加入 harness 哨兵元组,并断言 mixedTrips 的中文标题包含 3 次时间预算耗尽(而非 4 次),与现有英文断言对称。

(证据见上:探针在该变异下观测到中文侧 4 对英文侧 3;基线两侧均为 3。)

— qwen3.8-max via Qwen Code /review (v0.21.15)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] R1-8: HEADLINE_ZH is rewritten to interpolate ${BUDGET_TIMEOUT_N}, but no test asserts the ZH headline's count: the harness sentinel returns only $HEADLINE, and the pairing anchors carry no number, so the budget-count guarantee is pinned for English alone. Mutation ${BUDGET_TIMEOUT_N}${TIMEOUT_N} on this line ships green (probe-verified: extending the sentinel tuple with "$HEADLINE_ZH" observed ZH 已累计 4 次时间预算耗尽 against EN's 3 on the mixedTrips window under the mutation, baseline both 3, suite 215/215) — the posted bilingual comment's ZH half would then say 4 while the EN half says 3, the re-inflated number the exclusion just corrected surviving in the other language. Add "$HEADLINE_ZH" to the harness sentinel tuple and assert mixedTrips's ZH headline contains 3 次时间预算耗尽 (and not 4 次), mirroring the existing EN assertion.

中文说明

HEADLINE_ZH 被改写为插值 ${BUDGET_TIMEOUT_N},但没有任何测试断言中文标题里的计数:harness 哨兵只返回 $HEADLINE,配对锚点也不含数字,因此"预算计数"这一保证只在英文侧被钉住。把这行的 ${BUDGET_TIMEOUT_N} 变异为 ${TIMEOUT_N} 可以全绿通过(探针已验证:把哨兵元组扩展为含 "$HEADLINE_ZH" 后,mixedTrips 窗口在该变异下观测到中文侧 已累计 4 次时间预算耗尽、英文侧为 3,基线两侧均为 3,套件 215/215)——发布的双语评论中文一侧将写 4 而英文一侧写 3,刚被排除逻辑纠正掉的重新膨胀数字在另一种语言里存活下来。请把 "$HEADLINE_ZH" 加入 harness 哨兵元组,并断言 mixedTrips 的中文标题包含 3 次时间预算耗尽(而非 4 次),与现有英文断言对称。

— qwen3.8-max via Qwen Code /review (v0.21.15)

fi
fi
fi
Expand Down
Loading
Loading