feat(triage): add duplicate / already-fixed gate (Stage 1-pre) - #10292
feat(triage): add duplicate / already-fixed gate (Stage 1-pre)#10292yiliang114 wants to merge 9 commits into
Conversation
A PR opened after its linked issue was already fixed by a merged PR stays open forever: no existing gate looks at the linked issue's state, and triage has no close action at all. Add a deterministic Stage 1-pre check that resolves the linked issue's closer via GraphQL and, only when the PR's production diff is fully subsumed by the merged fix, posts a bilingual terminal comment and closes the PR. Any remaining delta requests changes instead; ambiguity escalates to the maintainer. Register the gate in SKILL.md so it is not treated as a fabricated policy.
|
Re-run at the new head Template looks good ✓ Problem: observed, and re-verified today — #10278 merged at 11:17:15Z, #10270 was closed as COMPLETED one second later, #10285 was opened ~55 minutes after that and hung until a manual close while its triage run posted nothing. No existing stage looks at the linked issue's state; the gap is real and this gate fills it. Direction: aligned — unchanged from the prior pass. Triage gains its first close authority, registered explicitly in the skill's no-fabricated-policies rule, with escalation as the default on any ambiguity. Size: not applicable — 3 files under Approach: since the last pass six commits answered the round-2 findings one by one — linkage scoped to same-repo closing references, a reopen guard on the close exit, test pins for the state dispatch and the ambiguity prohibition, the Risk: no elevated risk signals — no changed file matches a high-risk path. Moving on to code review. 🔍 中文说明在新 head 模板完整 ✓ 问题:已观测到,今日复核——#10278 于 11:17:15Z 合并,#10270 一秒后以 COMPLETED 关闭;#10285 约 55 分钟后创建,一直挂到被手动关闭,期间其 triage 运行没有任何输出。现有阶段均不检查关联 issue 的状态,缺口真实存在,正是本 gate 要补的。 方向:对齐——与上一轮一致。triage 首次获得关闭权限,已显式注册进 skill 的"禁止虚构政策"规则,任何歧义默认升级人工。 规模:不适用—— 方案:上一轮之后共六个提交逐条回应第二轮审查意见——关联范围限定为同仓库关闭引用、关闭出口加 reopen 守卫、状态分发与"歧义不关闭"禁令补测试固化、SKILL.md 去掉 production 限定词使两个文件口径一致、混合 issue 状态定义固定优先级、关联解析措辞不再过度承诺意图识别。diff 未夹带无关改动。 风险:无升级风险信号——改动文件均未命中高风险路径。 进入代码审查 🔍 — Qwen Code · qwen3.8-max Reviewed at |
Code review (re-run at the new head)The prior pass approved What the new commits add, and what I checked:
Live re-verification at this head (read-only API calls): the closer query returns One interaction left unresolved by design (author's call, documented in the thread): the two new request-changes exits feed the pre-existing exit-agnostic re-run suppression, so a bot CHANGES_REQUESTED review from any terminal gate suppresses later terminal reviews on re-run. That rule predates this PR and the standing review still gates the PR; it belongs where the suppression rule lives, not here. The composition gap from the prior pass persists in the same shape: the live end-to-end exercise (dispatch run 33075297788 against #10285, which posted a Testing evidenceUnattended CI run — no PR code was built or executed here; the evidence is the PR's own CI read via the API plus my live re-execution of the gate's query fragments above. Real-scenario (tmux) testing: N/A — skill-guidance markdown with no user-visible product behavior. At the reviewed SHA the PR's CI is green:
One row per named check (latest run); 135 classification-skipped checks and bot-orchestration checks omitted. / 每个具名检查一行(取最新一次运行);省略 135 项分类跳过与机器人编排类检查。 中文说明代码审查(新 head 上重跑)上一轮批准了 新提交的内容与我的核对:
在线复验(只读 API 调用,本 head):closer 查询对 #10270 返回 一处有意不解决的交互(作者决定,已在讨论串说明):两条新的 request-changes 出口并入既有的"出口无关"重跑抑制——任何终止门禁留下的机器人 CHANGES_REQUESTED review 都会抑制后续终止 review。该规则早于本 PR,既有 review 仍在把关 PR;它应该在抑制规则所在处处理,不属于本 PR。 上一轮的组合缺口以同样形态存在:端到端实弹演练(对 #10285 的 dispatch 运行 33075297788,发出的是 测试证据无人值守 CI 运行——此处未构建或执行任何 PR 代码;证据为通过 API 读取的 PR 自身 CI,加上我对 gate 查询构件的在线复跑(见上)。真实场景(tmux)测试:N/A——skill 指引 markdown,无用户可见的产品行为。被审查 SHA 上 PR 的 CI 全绿: — Qwen Code · qwen3.8-max Reviewed at |
|
Confidence: 4/5 — the six post-approval commits each close a named round-2 finding and are pinned in CI; the one reservation is unchanged from the prior pass and fails safe (no composed end-to-end run at the current head yet). Going back to the independent proposal: what shipped still matches it, and the hardening rounds made it strictly better — same-repo scoping, the reopen guard, and the fixed precedence were real holes a hostile or merely unlucky input could have hit, and each is now closed and test-pinned. What continues to impress is the disposition discipline: every deferred finding has a named, verified-open tracking issue (#10314, #10320–#10324 — re-verified open today), and the one remaining interaction (new request-changes exits feeding the exit-agnostic re-run suppression) was closed as out of scope with the reasoning in the thread, not silently dropped. Six months from now this reads like a decision log, not a pile of guidance. The reservation is the composition gap named in Stage 2: the live end-to-end exercise ran at the first commit, and at CI is green on this head (no pending PR-CI runs; the only red check is the bot's own review pipeline, pre-existing infra), so approval is posted now, pinned to the reviewed commit. This approval supersedes the CHANGES_REQUESTED reviews this account left at 中文说明回到独立方案:最终落地的版本依然与之相符,而且历轮加固让它变得更好——同仓库范围限定、reopen 守卫、固定优先级都是真实漏洞(恶意或仅仅倒霉的输入都可能踩中),如今逐一堵上并用测试固化。持续令人欣赏的是延期处理的纪律性:每条延期意见都有经核实 open 的具名跟踪 issue(#10314、#10320–#10324——今日复核仍为 open),唯一遗留的交互(新的 request-changes 出口并入"出口无关"的重跑抑制)以讨论串中的理由按超出范围关闭,而不是悄悄丢弃。半年后回看,这份内容像一份决策记录,而不是一堆指引的堆砌。 保留意见仍是 Stage 2 指出的组合缺口:端到端实弹演练跑在第一个提交上, 该 head 的 CI 全绿(无未完成的 PR CI 运行;唯一红色检查是机器人自身的 review 流水线,属既有基础设施问题),因此现在提交批准,并钉在被审查的提交上。本批准取代本账号在 — 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. |
E2E test report — live gate exercise against #10285The gate was tested end to end via Observed behavior (bot review on #10285,
Local dry run of the same decision tree (read-only, steps 1–4) reached the identical verdict beforehand, so the agent's live behavior matches the designed path. The model-name injection also worked on the branch ref (signature shows the dispatched model, not the default literal). Target PR was closed again afterwards with a maintainer note explaining the test. |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
LGTM, looks ready to ship — CI landed green after the review. ✅
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Partially reviewed — gaps disclosed.
Not explored to full depth (tool budget reached): "agent 5": running scripts/tests/qwen-triage-workflow.test.js to confirm no existing pin breaks (no node_modules in worktree; static slice-boundary analysis done instead….
Not reviewed: reverse audit — stopped before round 7 by the review time budget.
— qwen3.8-max via Qwen Code /review (v0.22.2)
- Define subsumption over the entire diff (added lines present AND deleted lines absent in the default branch) so deletions-only and tests-only diffs can never reach the close branch. - Run Stage 1-pre only for PRs targeting the default branch; backports to release/* branches legitimately carry changes already on the default branch. - Extract linked issues via GitHub's closingIssuesReferences instead of a keyword grep that missed 6 of 9 closing-keyword forms and matched substrings like "prefixes". - Pin all three invariants in qwen-triage-workflow.test.js. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
- Name both Stage 1-pre request-changes exits in the terminal gate exception and the footer rule, give each an explicit `gh pr review --request-changes` command like 1a/1b, and note the duplicate-close exit posts its terminal comment and closes instead of submitting a review; extend SKILL.md's rejection exception clause to cover them. - Closer query: window the LAST 20 CLOSED_EVENTs and take only the most recent close's closer (older closes belong to reopen cycles), null-guard the jq filter, and spell out that a failed or empty query means the closer is unresolved (a PR number or missing issue must not hard-error). - Add the per-issue state loop that assigns $N and produces the state/stateReason the branch bullets consume. Co-authored-by: Qwen-Coder <qwen-coder @alibabacloud.com>
|
@qwen-code /triage |
|
Sandboxed verification: ✅ passed — merge-ready (agent verdict) - workflow run Ran the PR in an isolated, token-free container: A/B against the base build, mock-free harness assertions, targeted gates. Advisory evidence for human reviewers — not a review, an approval, or a CI check. Scripted assertions: 225 passed · 0 failed · 225 total Flakiness gate: ✅ 1 changed test file(s) x 5 identical rounds, no divergence 中文 — 判定:✅ 通过 · 可合入(agent 判定)沙箱验证在隔离、无凭证的容器中执行了该 PR 的代码(与 base 构建 A/B 对照、无 mock harness 断言、定向门禁)。仅作为评审证据,不构成评审、批准或 CI 检查。 脚本断言:225 通过 · 0 失败 · 225 总计 抖动门:✅ 1 changed test file(s) x 5 identical rounds, no divergence Verification reportVerdict: merge-ready — 225/225 scripted assertions passed (0 failures). Verified head: 中文摘要
Central claim and A/BCentral claim: the Stage 1-pre gate's invariants (linkage via GitHub closing references, default-branch-only scope, full-diff subsumption, never-close on tests-only/empty production delta) are load-bearing in the skill text and pinned by the new tests. The artifact is agent guidance (markdown) plus one executable query, so the A/B holds the test file constant and varies only the markdown.
The base reds are the intended assertions, not setup artifacts — each failure is of the form Corroborating harnesses on the executable surface (all captured):
No mutant killed anything beyond its intended test (attribution matches the invariants the commits claim to pin), the combination row shows the two subsumption guards are independently observable (no layered masking), and the unmutated control plus every restored-clean check is the positive control for the runner. The new tests are not vacuous. Full targeted gate (same vitest config and files the repo's Corrections
Findings
Not covered
MethodologyCI verify container ( 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.
Partially reviewed — gaps disclosed.
7 Suggestion-level finding(s) this review confirmed are already reported on this PR and are not repeated:
- R1-9 close-exit comment fresh-POST idempotency — still stands, author deferred to tracking issue #10320 (round-1 thread, comment 3872958112)
- R1-10 close path does not re-read head/issue state before acting — still stands, author deferred to tracking issue #10320 (round-1 thread, comment 3872958122)
- R1-11 remaining-delta branch has no terminal exit (re-run fixed point) — still stands, author deferred to tracking issue #10323 (round-1 thread, comment 3872958133)
- R1-12 contents-API per-file subsumption cost and ~1 MiB silent ceiling — still stands, author deferred to tracking issue #10322 (round-1 thread, comment 3872958140)
- R1-13 no executable test for the closer pipeline/filter — still stands, author deferred to tracking issue #10321 (round-1 thread, comment 3872958150)
- R1-14 decision tree not total over the closer query's output space — still stands, author deferred to tracking issue #10321 (round-1 thread, comment 3872958157)
- R1-15 close exit leaves no PR review so the silent-rerun notify misreports — still stands, author deferred to tracking issue #10324 (round-1 thread, comment 3872958166)
Not explored to full depth (tool budget reached): "agent 5": executed run of the added stage 1-pre duplicate gate tests (worktree has no node_modules, vitest unresolvable); asserted/negated substrings verified staticall….
Not reviewed: reverse audit — stopped before round 7 by the review time budget.
— qwen3.8-max via Qwen Code /review (v0.22.2)
The closingIssuesReferences extraction kept only the bare issue number, so a cross-repo closing reference resolved against this repo's same-numbered unrelated issue and could drive the wrong-issue gate branches. Filter the extraction to references whose repository matches the triaged repo and state that cross-repo closing references are skipped. Co-authored-by: Qwen-Coder <qwen-coder @alibabacloud.com>
An explicit /triage re-run on a PR a maintainer reopened after a 1-pre duplicate-close re-derives identical inputs and closes again, indefinitely overriding the deliberate reopen. Before the close exit posts and closes, require that no stage=1-pre comment exists yet on the open PR; if one does, escalate to the maintainer instead of re-closing. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
The per-issue state dispatch (OPEN / CLOSED NOT_PLANNED / CLOSED COMPLETED) and the "never close on ambiguity" bullet had no test witness: deleting or inverting either kept the suite green. Add toContain pins binding each state to its action and one pinning the ambiguity escalation rule. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
…L.md The exit summary qualified both sides with 'production' (remaining production delta -> request-changes; entire production diff fully subsumed -> close), contradicting pr-workflow.md's operational definition: request-changes fires on ANY remaining delta including non-production additions, and a diff with NO production changes is never fully subsumed. A tests-only PR got opposite instructions from the two files. Align SKILL.md with pr-workflow.md and pin the boundary in the suite. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
… Stage 1-pre The per-issue loop legend said OPEN -> proceed to 1a while the aggregate bullets said any closed-as-completed issue enters the closer flow, so 'fixes #101 and fixes #102' with #101 OPEN, #102 CLOSED-COMPLETED had two contradictory outcomes. Make the loop collect-only and state one fixed precedence (not-planned > completed > all-open), so an OPEN issue never short-circuits a CLOSED one. Pin the precedence. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
…tion The doc promised linkage is 'input to verify against the issue's actual state, never as proof by itself', but the issue state check is the only verification, so an accidental prose linkage (e.g. 'resolves #123's closer') still drove the branches off an unrelated issue. Reword to state the truth: there is no deterministic intent check, the linkage decides which issues are read, and the blast radius is bounded because close additionally requires the diff to be fully subsumed by the default branch (true only when the change already landed), so an accidental linkage reaches at worst a visible, reversible request-changes or escalation. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
|
Qwen Code review did not complete successfully. The review pipeline failed before a review could be posted. A transient error is retried automatically; if you are seeing this, retry with |
|
@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 /triage |
1 similar comment
|
@qwen-code /triage |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
LGTM, looks ready to ship. ✅
|
Sandboxed verification: ✅ passed — merge-ready (agent verdict) - workflow run Ran the PR in an isolated, token-free container: A/B against the base build, mock-free harness assertions, targeted gates. Advisory evidence for human reviewers — not a review, an approval, or a CI check. Scripted assertions: 83 passed · 0 failed · 83 total Flakiness gate: ✅ 1 changed test file(s) x 5 identical rounds, no divergence 中文 — 判定:✅ 通过 · 可合入(agent 判定)沙箱验证在隔离、无凭证的容器中执行了该 PR 的代码(与 base 构建 A/B 对照、无 mock harness 断言、定向门禁)。仅作为评审证据,不构成评审、批准或 CI 检查。 脚本断言:83 通过 · 0 失败 · 83 总计 抖动门:✅ 1 changed test file(s) x 5 identical rounds, no divergence Verification reportVerdict: merge-ready — 83/83 scripted assertions passed (0 failures). Verified head: 中文 — 判定:✅ 通过 · 可合入(agent 判定)这是第二轮验证(上一轮验证的是
Previous-finding status (follow-up round)Previous round: verified head
The previous round's Correction (PR body says "Markdown-only change to skill files" while the diff also adds tests) still applies: the body is unchanged and the test addition grew from 36 to 98 lines. Central claim and A/BCentral claim: the Stage 1-pre gate's invariants — linkage via GitHub closing references scoped to this repo, default-branch-only scope, full-diff subsumption, never-close on tests-only/ambiguous, state dispatch with fixed precedence, and the reopen guard — are load-bearing in the skill text and pinned by the new tests. The artifact is agent guidance (markdown) plus executable queries, so the A/B holds the test file constant and varies only the markdown.
The base reds are the intended assertions, not setup artifacts — all 10 failure messages are Corroborating harnesses on the changed surface (all captured):
Every mutant killed exactly its intended test — no mutant killed anything else (attribution matches the invariants the commits claim to pin), no survivors, and the combination row shows the two subsumption guards are independently observable (no layered masking). Every failure message is an Full targeted gate (same vitest config and files the repo's Corrections
Findings
Not covered
MethodologyCI verify container ( Flakiness gate logEvidence imagesHarness scripts and raw logs are in the workflow run artifacts (7-day retention). — Qwen Code · sandboxed verification |
|
Sandboxed verification: ✅ passed — merge-ready (agent verdict) - workflow run Ran the PR in an isolated, token-free container: A/B against the base build, mock-free harness assertions, targeted gates. Advisory evidence for human reviewers — not a review, an approval, or a CI check. Scripted assertions: 116 passed · 0 failed · 116 total Flakiness gate: ✅ 1 changed test file(s) x 5 identical rounds, no divergence 中文 — 判定:✅ 通过 · 可合入(agent 判定)沙箱验证在隔离、无凭证的容器中执行了该 PR 的代码(与 base 构建 A/B 对照、无 mock harness 断言、定向门禁)。仅作为评审证据,不构成评审、批准或 CI 检查。 脚本断言:116 通过 · 0 失败 · 116 总计 抖动门:✅ 1 changed test file(s) x 5 identical rounds, no divergence Verification report<!-- qwen-triage:verify --> Sandboxed verification: ✅ passed — merge-ready (agent verdict) - workflow publishes the run link 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: 116 passed · 0 failed · 116 total 中文 — 判定:✅ 通过 · 可合入(agent 判定)第三轮验证。上一轮验证的 head 与本轮相同(
Verification reportVerdict: merge-ready — 116/116 scripted assertions passed (0 failures). Verified head: Previous-finding status (follow-up round)Previous round: verified head
Central claim and A/BCentral claim: the Stage 1-pre gate's invariants — same-repo linkage via GitHub closing references, default-branch-only scope, full-diff subsumption (adds present AND deletes absent, never for tests-only diffs), state dispatch with fixed precedence, never-close on ambiguity, and the reopen guard — are load-bearing in the skill text and pinned by the ten new tests. The artifact is agent guidance plus executable queries, so the A/B holds the test file constant and varies only the markdown.
Witness: Mutation matrix ( Wire-oracle harnesses on the changed surfaceAll filters extracted verbatim from head
Consistency, parse gates, and previous-finding re-measurement
Full targeted gate ( Corrections
Findings
Not covered
MethodologyCI verify container ( Evidence imagesHarness scripts and raw logs are in the workflow run artifacts (7-day retention). — Qwen Code · sandboxed verification Flakiness gate logEvidence imagesHarness scripts and raw logs are in the workflow run artifacts (7-day retention). — Qwen Code · sandboxed verification |




















What this PR does
Adds a new Stage 1-pre gate to the triage skill's PR workflow: before the template check, triage now resolves the PR's linked issues (
Fixes/Closes/Resolves #N) and, when one was closed as completed by a merged PR, compares this PR's production diff againstmain. If the merged fix fully subsumes the diff, triage posts a bilingual terminal comment and closes the PR as a duplicate — the first and only place triage closes a PR. Any remaining delta gets a request-changes asking the author to rebase and reduce the PR to that delta; an unresolvable closer escalates to the maintainer instead of closing. The gate is also registered in the skill's "No fabricated policies" rule so the agent treats the close authority as explicitly granted.Why it's needed
A PR opened after its linked issue was already fixed stays open forever today: no existing gate looks at the linked issue's state, and the PR workflow has no close action at all. This happened with #10285 — opened ~55 minutes after #10270 was closed as fixed by the merged #10278. Its triage run (33070765162) posted nothing (no stage comment, no review), leaving the duplicate PR hanging until a maintainer closed it by hand. Note: that particular silence was a masked agent-API failure — the run log shows the model call returned
[API Error: Connection error ... ETIMEDOUT], which the workflow's response check classified as a successful response. Stage 1-pre does not prevent that failure shape (agent guidance cannot run in a session that cannot reach the model; the fix for it belongs in the workflow's response check, out of scope here). The gap Stage 1-pre covers is the orthogonal one: on a healthy triage run, nothing inspects the linked issue's state, so a true duplicate stays open. The gate is deliberately conservative: it only auto-closes when every production line the PR adds already exists onmain, because #10285 itself shows a "duplicate" can carry a genuine remaining delta (extra model patterns) that must not be silently dropped.Reviewer Test Plan
How to verify
10278 true(the merged fixing PR). The REST timeline alternative was tested and rejected: itsclosedevent carriescommit_id: nulleven when a merged PR closed the issue.workflow_dispatchre-run against a staged one) exercises the full path; the close comment carries thestage=1-premarker so the Duplicate Guard dedups re-runs.Evidence (Before & After)
Before: #10285 (duplicate of merged #10278, linked issue #10270 already closed) — triage run 33070765162 completed
successwith zero stage comments or reviews (the "response" was an[API Error: ... ETIMEDOUT]string the response check counted as success); PR stayed OPEN until closed manually.After (live re-run 33075297788 at this PR's head): Stage 1-pre resolves #10270's closer via GraphQL (
10278 true), then detects #10285's genuine remaining delta (thevlalternatives and theglm-5.3-flashvideo/pdf modalities are not onmain) and posts astage=1-preCHANGES_REQUESTED review asking the author to rebase and reduce the PR to that delta. It does NOT close #10285, because #10285 is not fully subsumed — exactly the conservative behavior this gate is designed for. A duplicate with no remaining delta takes the close branch instead.Tested on
Environment (optional)
Markdown-only change to skill files; no build/typecheck surface. The embedded
gh api graphqlsnippet was executed against the live repo API on macOS.Risk & Scope
mainbefore closing, requesting changes instead when any delta remains, and escalating on any ambiguity (manual close, unresolvable closer).qwen3.7-maxliteral.Linked Issues
None — follow-up to the manual close of #10285.