ci: auto-minimize comments from org-blocked users - #7899
Conversation
Adds a scheduled workflow that runs every hour to scan recent issue/PR comments and minimize any from users blocked at the org level. This cleans up spam comments that were posted before a block was applied. The workflow: 1. Fetches the org's blocked-user list via REST API 2. Queries recent comments (last 2h) via GraphQL 3. Matches comment authors against the blocked list 4. Minimizes unmatched comments as OFF_TOPIC via GraphQL Also triggerable manually via workflow_dispatch with a configurable lookback window.
|
Thanks for the PR! Template looks good ✓ (the "Evidence (Before & After)" section is N/A for a CI-only change, correctly noted under "Tested on"). Problem: observed operational burden — the PR cites a specific incident (25 spam comments from danialzivehdadr requiring manual minimization after the org block). Real, recurring maintenance task. Direction: aligned. Automating post-block comment cleanup is straightforward repo hygiene. Doesn't touch any sensitive subsystem. The workflow is scoped to Size: not applicable — files under Approach: the scope feels right. The updated implementation is cleaner than what was described in the PR body — it uses a local Risk: no elevated risk signals — none of the changed files match high-risk paths. Moving on to code review. 🔍 中文说明感谢贡献! 模板完整 ✓("Evidence (Before & After)" 对 CI 变更为 N/A,已在 "Tested on" 中正确标注)。 问题:已观测到的运维负担——PR 引用了具体事件(danialzivehdadr 的 25 条垃圾评论在 org 拉黑后仍需手动逐条 minimize)。真实且反复出现的维护任务。 方向:对齐。自动化拉黑后的评论清理是基本的仓库卫生工作。不涉及任何敏感子系统。Workflow 仅限于 规模:不适用——仅 方案:范围合理。更新后的实现比 PR 描述中更干净——使用本地 风险:无升级风险信号——变更文件均未匹配高风险路径。 进入代码审查 🔍 — Qwen Code · qwen3.8-max-preview Reviewed at |
Code ReviewIndependent proposal: a single scheduled workflow that reads a blocklist, runs one GraphQL query for recent issue+PR comments, matches authors in shell with exact case-insensitive comparison, and minimizes via Comparison with the diff: the PR's implementation matches this exactly. The dead first GraphQL query from the earlier revision is gone — the current code runs a single query, extracts unminimized comments with Findings: none blocking. Minor observations (non-blocking, no action required):
Security model is sound: TestingCI-only change — no user-visible behavioral change. Real-scenario tmux testing: N/A. CI evidence for
One row per check name (latest run); skipped checks omitted. / 每个检查名一行(取最新一次运行),省略 skipped。 All checks green. Windows/macOS tests were skipped (expected for CI-only changes classified by the PR classifier). The helper test ( 中文说明代码审查独立方案: 一个定时 workflow,读取 blocklist,用一次 GraphQL 查询获取最近的 issue+PR 评论,在 shell 中用精确大小写不敏感匹配,然后通过 与 diff 对比: PR 的实现与此完全一致。早期版本中的死查询已删除——当前代码只运行一次查询,用 发现: 无阻塞项。 次要观察(非阻塞,无需操作):
安全模型健全: 测试纯 CI 变更——无用户可见行为变化。tmux 实测:N/A。 CI 证据见上表,所有检查通过。Windows/macOS 测试被跳过(CI 变更的预期行为)。helper 测试作为 — Qwen Code · qwen3.8-max-preview Reviewed at |
|
Confidence: 5/5 — the previous review's only finding (dead GraphQL query) is fixed, the implementation is now exactly what I'd write independently, CI is green, and the security invariants are pinned by a test. Clean, focused automation. One workflow, one blocklist file, one test that guards the security model, one line in CI to register it. The motivation is concrete (25 manually-minimized spam comments), the design is minimal (local file over org API, single query, shell matching, minimize-only), and the code reads well. If I had to maintain this in six months I'd thank the author — the YAML header documents its own limitations, the test catches permission drift, and there's nothing to untangle. The PR body's "How it works" section still describes the org blocks API approach, but the implementation correctly uses the simpler local-file approach. Cosmetic mismatch, not worth a round-trip. 中文说明置信度:5/5 — 上次审查的唯一发现(死 GraphQL 查询)已修复,实现与我的独立方案完全一致,CI 全绿,安全不变量由测试固定。 干净、聚焦的自动化。一个 workflow、一个 blocklist 文件、一个守护安全模型的测试、CI 中一行注册。动机具体(25 条手动 minimize 的垃圾评论),设计最小化(本地文件优于 org API、单次查询、shell 匹配、仅 minimize),代码可读性好。半年后维护这个文件我会感谢作者——YAML 头部记录了自身限制,测试捕获权限漂移,没有需要解开的纠缠。 PR 正文的 "How it works" 部分仍描述 org blocks API 方案,但实现正确使用了更简单的本地文件方案。表面不一致,不值得为此多一轮修改。 — Qwen Code · qwen3.8-max-preview Reviewed at |
The org blocked-users API requires admin:org scope which the CI bot PAT doesn't have. Switch to a plain-text blocklist file at .github/spam-blocklist.txt — one username per line, case-insensitive, # for comments. No special scopes needed. Also adds danialzivehdadr as the first entry.
|
🔀 Base updated: red check(s) [Test (ubuntu-latest, Node 22.x), review-pr] pass on current main — merged current main via update-branch; CI will re-run. 中文说明🔀 已更新 base:红色检查 [Test (ubuntu-latest, Node 22.x), review-pr] 在当前 main 上通过 —— 已通过 update-branch 合入当前 main,CI 将重新运行。 |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Reviewed. Suggestions are inline. 1 Suggestion-level finding(s) could not be anchored to a changed line and were dropped; nothing further to act on here.
中文说明
已审查。 建议见行内评论。 1 条建议级发现无法锚定到改动行,已丢弃;此处无需进一步处理。
— qwen3.7-max via Qwen Code /review
|
🤖 Addressed the latest review feedback (round 1/10). What changed, and what I pushed back on: · 已处理最新评审反馈(第 1/10 轮)。改动内容与我反驳保留之处如下: Review feedback addressedAddressed all four Suggestion-level findings from the automated reviewer on the 1. Empty/all-comment blocklist aborts the script silently (rc:3663155998) — fixedFinding: Under Verified: Reproduced in an isolated Change: Appended 2.
|
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Reviewed. Suggestions are inline.
中文说明
已审查。 建议见行内评论。
— qwen3.7-max via Qwen Code /review
| - name: 'Minimize comments from blocklisted users' | ||
| env: | ||
| GH_TOKEN: '${{ secrets.CI_BOT_PAT }}' |
There was a problem hiding this comment.
[Suggestion] ~120 lines of non-trivial bash logic (blocklist parsing, GraphQL querying, case-insensitive matching, minimization with error tracking) are embedded directly in the workflow YAML without tests, deviating from the project's established convention of extracting workflow scripts into .github/scripts/<name>.mjs with paired .test.mjs files (e.g. pr-safety-precheck.mjs, classify-release-notes.mjs, ci-flaky-rerun.mjs). — Failure scenario: a future edit to the blocklist parsing pipeline or matching logic could silently break username matching; the workflow would exit 0 and report "Unminimized comments from blocklisted users: 0" — indistinguishable from a legitimately clean run — while spam comments remain visible.
| - name: 'Minimize comments from blocklisted users' | |
| env: | |
| GH_TOKEN: '${{ secrets.CI_BOT_PAT }}' | |
| - name: 'Minimize comments from blocklisted users' | |
| env: | |
| GH_TOKEN: '${{ secrets.CI_BOT_PAT }}' | |
| LOOKBACK_HOURS: '${{ inputs.hours || ''2'' }}' | |
| run: node .github/scripts/auto-minimize-spam.mjs |
中文说明
[Suggestion] 约 120 行非平凡的 bash 逻辑(黑名单解析、GraphQL 查询、大小写不敏感匹配、带错误追踪的最小化操作)直接嵌入在 workflow YAML 中,没有测试,偏离了项目的既定惯例——将 workflow 脚本提取到 .github/scripts/<name>.mjs 并配套 .test.mjs 测试文件。—— 失败场景:未来对黑名单解析管道或匹配逻辑的编辑可能悄然破坏用户名匹配;workflow 会以 exit 0 退出并报告 "Unminimized comments from blocklisted users: 0"——与正常运行完全无法区分——而垃圾评论仍然可见。
— qwen3.7-max via Qwen Code /review
There was a problem hiding this comment.
Declined — not worth the diff growth in this PR.
The suggestion to extract the bash into .github/scripts/auto-minimize-spam.mjs with paired tests is well-founded as a convention matter, but it would be a major rewrite that significantly expands this PR's scope. The maintainer (@wenshao) already verified the verbatim bash with a 31-check scenario harness (including a 5-mutation teeth check) and live read-only runs against the repository, then gave LGTM. That harness provides the regression coverage this suggestion seeks.
If the mjs convention is desired here, a follow-up PR is the better vehicle — it keeps this PR focused and avoids re-litigating logic that has already been thoroughly validated.
中文说明
拒绝 — 在本 PR 中不值得增加这么多 diff。
将 bash 提取到 .github/scripts/auto-minimize-spam.mjs 并配套测试的建议,从惯例角度来说完全合理,但这将是一次重大重写,会显著扩大本 PR 的范围。维护者(@wenshao)已通过 31 项场景 harness(含 5 个变异杀伤力检查)和对仓库的真实只读运行充分验证了原版 bash 脚本,并给出了 LGTM。该 harness 已提供了本建议所寻求的回归覆盖。
如果此处需要遵循 mjs 惯例,后续 PR 是更好的载体——既保持本 PR 的聚焦,也避免对已充分验证的逻辑进行重新审查。
Maintainer verification — real local runs @
|
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Reviewed. Suggestions are inline.
中文说明
已审查。 建议见行内评论。
— qwen3.7-max via Qwen Code /review
- Change hours input type from string to number (project convention) - Extract duplicated step-summary writes into write_summary() function - Capture gh stderr (2>&1) and include it in failure warnings - Document coverage limits in header comment
|
🤖 Addressed the latest review feedback (round 2/10). What changed, and what I pushed back on: · 已处理最新评审反馈(第 2/10 轮)。改动内容与我反驳保留之处如下: Review feedback addressedInline findings
Issue-level findings (maintainer @wenshao)
Failed checks
Verification
中文说明已处理的审查反馈行内发现
议题级发现(维护者 @wenshao)
失败的检查
验证
Base-conflict check · 基分支冲突检查: no conflict with main. · 与 main 无冲突。 Re-review when you have a moment. After round 10 this bot stops and leaves the PR for a human. · 有空请复审;第 10 轮后本 bot 停止并将 PR 交给人工。 🧠 Handled by Qwen Code · model/模型 |
|
🔀 Base updated: red check(s) [Test (ubuntu-latest, Node 22.x), review-pr] pass on current main — merged current main via update-branch; CI will re-run. 中文说明🔀 已更新 base:红色检查 [Test (ubuntu-latest, Node 22.x), review-pr] 在当前 main 上通过 —— 已通过 update-branch 合入当前 main,CI 将重新运行。 |
|
🤖 Reviewed the latest feedback — no changes needed. Why, point by point: · 已审阅最新反馈——无需改动。逐点说明原因如下: No action takenNo review comments (inline, issue-level, or review bodies) were present in this round's feedback — there is nothing to address. The four failed CI checks (Test, web-shell E2E Smoke, Post Coverage Comment, review-pr) are unrelated to this PR's changes. This PR adds only two files under
The CI failures are attributable to the base branch or CI environment, not to this PR. 中文说明未采取任何操作本轮反馈中没有任何评审评论(行内评论、Issue 级别评论或评审正文)——没有需要处理的内容。 四个失败的 CI 检查(Test、web-shell E2E Smoke、Post Coverage Comment、review-pr)与本 PR 的更改无关。本 PR 仅在
CI 失败归因于基础分支或 CI 环境,而非本 PR。 Base-conflict check · 基分支冲突检查: no conflict with main. · 与 main 无冲突。 🧠 Handled by Qwen Code · model/模型 |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
中文说明
— qwen3.7-max via Qwen Code /review
|
🔀 Base updated: red check(s) [Test (ubuntu-latest, Node 22.x)] pass on current main — merged current main via update-branch; CI will re-run. 中文说明🔀 已更新 base:红色检查 [Test (ubuntu-latest, Node 22.x)] 在当前 main 上通过 —— 已通过 update-branch 合入当前 main,CI 将重新运行。 |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
中文说明
— qwen3.7-max via Qwen Code /review
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Reviewed. Suggestions are inline.
中文说明
已审查。 建议见行内评论。
— qwen3.7-max via Qwen Code /review
|
🤖 Addressed the latest review feedback (round 4/10). What changed, and what I pushed back on: · 已处理最新评审反馈(第 4/10 轮)。改动内容与我反驳保留之处如下: Review feedback — round summaryrc:3666120433 — Regression tests for workflow invariants → ImplementedAdded
Registered the test in the rc:3666057899 — Batch GraphQL mutations (N+1 pattern) → DeclinedThe per-comment
Not worth the diff growth for this use case. Verification
中文说明审查反馈 — 本轮总结rc:3666120433 — 工作流不变量的回归测试 → 已实现按照
已在 rc:3666057899 — 批量 GraphQL mutation(N+1 模式) → 已拒绝逐条评论调用
对于此用例,不值得增加 diff 体量。 验证
Base-conflict check · 基分支冲突检查: no conflict with main. · 与 main 无冲突。 Re-review when you have a moment. After round 10 this bot stops and leaves the PR for a human. · 有空请复审;第 10 轮后本 bot 停止并将 PR 交给人工。 🧠 Handled by Qwen Code · model/模型 |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
中文说明
— qwen3.7-max via Qwen Code /review
|
🤖 Reviewed the latest feedback — no changes needed. Why, point by point: · 已审阅最新反馈——无需改动。逐点说明原因如下: No action requiredThe only review feedback on this PR is an automated comment from No code changes were made this round. 中文说明无需操作本 PR 收到的唯一审查反馈是 本轮未做任何代码更改。 Base-conflict check · 基分支冲突检查: no conflict with main. · 与 main 无冲突。 🧠 Handled by Qwen Code · model/模型 |
|
@qwen-code /verify |
|
Sandboxed verification: 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. 沙箱验证在隔离、无凭证的容器中执行了该 PR 的代码(与 base 构建 A/B 对照、无 mock harness 断言、定向门禁)。仅作为评审证据,不构成评审、批准或 CI 检查。 Scripted assertions: 49 passed · 0 failed · 49 total 脚本断言:49 通过 · 0 失败 · 49 总计 Verification report (report.md)Harness scripts and raw logs are in the workflow run artifacts (7-day retention). — Qwen Code · sandboxed verification |
|
🤖 Reviewed the latest feedback — no changes needed. Why, point by point: · 已审阅最新反馈——无需改动。逐点说明原因如下: Autofix review round — no action takenNo code changes were made this round. The only feedback newer than the last evaluation is an automated sandboxed-verification report from There are no maintainer reviews, no inline comments, no failed checks, and no still-red checks to act on, so there is nothing to fix, decline, or escalate. 中文说明Autofix 评审轮次 —— 未采取任何操作本轮未做任何代码改动。 唯一一条比上次评估更新的反馈,是 当前没有维护者评审、没有行内评论、没有失败的检查、也没有持续飘红的检查需要处理,因此没有任何需要修复、拒绝或上报的事项。 Base-conflict check · 基分支冲突检查: no conflict with main. · 与 main 无冲突。 🧠 Handled by Qwen Code · model/模型 |
doudouOUC
left a comment
There was a problem hiding this comment.
No issues found. LGTM! ✅
中文说明
未发现问题。LGTM!✅
— qwen3.7-max via Qwen Code /review
Mined #7836 R2, #7885 and #7899 for method. Checked each candidate against the current text first; six had zero coverage, the rest were already there (harness teeth-checks, pin dereferencing, boundary probing, and the follow-up round's "re-measure, never diff"). The one that corrects the skill's own core method, from #7836: - **Before calling a survivor vacuous, escalate to a finer mutation.** A whole-file revert is blunt enough to remove the PRECONDITION a test depends on, so a good test goes green because its scenario no longer occurs — from the outside, identical to a test that asserts nothing. A `finally`-cleanup test survived reverting four production files and died to deleting one line. Coarse survived + fine killed ⇒ the test is fine and the mutation was wrong. A false "your test is vacuous" costs the author more than a missed survivor does. From #7836, the root cause shared by both of its blockers: - **When the same predicate is checked in two places, verify they see the same state.** A guard duplicated across a process boundary is two implementations of one question that diverge when their INPUTS differ. One settings key made a route ask sessionExistsInAnyState() with an unpinned runtime dir while the child asked with a pinned one, turning a clean 409 into a 500 plus a process.exit(1) that killed every session on the channel. Includes the temporal half: lazily-created backing files leave a window where a just-created entity is invisible to any on-disk existence check. - **Measure the blast radius on bystanders.** The caller's own error code understates a shared-state failure; the number that matters is an unrelated session going 200 -> 404 and a workspace list going 2 -> 0. From #7885, which took a performance claim apart: - **Isolate the slice the mechanism can actually affect.** A speedup claim is two claims: the mechanism works, and the thing it speeds up matters. `--ignore-scripts` isolated what an npm download cache can touch — 36s of a 226s install — so the ceiling was 20s and the real saving 15%, not the claimed 75%. Then check it against the whole job: 33s off 14m37s. - **A mechanism that persists something has a cost — price it.** 219 MB per lockfile hash into a pool at 9.98 GB of a 10 GB cap, with 39 distinct lockfile states in 30 days: at the cap every entry evicts by LRU, including entries other jobs need and its own. - **Test the scarier consequences and report which do NOT hold.** The write-path finding was real; code injection was disproved (npm integrity-checks a tampered cache and refetches) and privilege escalation was disproved (chown -R does not follow symlinks). A finding that names what it is not is harder to wave away. - **Verify third-party actions from their own manifest.** The PR said the cache dir was discarded after the job; `action.yml` declares `post: dist/save/index.js` with `post-if: success()`, which uploads it as root with credentials intact — the opposite of the claim, and the whole finding. From #7899: - **To exercise real production data safely, interpose a refusing proxy on the write path.** Wrap the client so every mutating call hard- fails, then run the shipped script verbatim: real counts, mechanical guarantee of no side effects. Mutation-verified 9/9, each with landing proof against line-accurate anchors. 89/89 tests; prettier and eslint clean. The skill is now 546 lines, up from 392 — the growth is deliberate and called out in the PR body's risk section.
…ked-user-comments # Conflicts: # .github/workflows/ci.yml
|
🤖 Addressed the latest review feedback (round 5/10). What changed, and what I pushed back on: · 已处理最新评审反馈(第 5/10 轮)。改动内容与我反驳保留之处如下: Autofix review round — PR #7899Feedback triageNo new review feedback this round: Conflict resolution (
|
|
🔀 Base updated: red check(s) [Test (ubuntu-latest, Node 22.x)] pass on current main — merged current main via update-branch; CI will re-run. 中文说明🔀 已更新 base:红色检查 [Test (ubuntu-latest, Node 22.x)] 在当前 main 上通过 —— 已通过 update-branch 合入当前 main,CI 将重新运行。 |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
.github/workflows/auto-minimize-spam.yml: actionlint embedded-shell source mapping is not yet supported — not linted; the executable-script lint — .github/workflows/ci.yml: actionlint embedded-shell source mapping is not yet supported — not linted.
中文说明
.github/workflows/auto-minimize-spam.yml: actionlint embedded-shell source mapping is not yet supported — not linted; the executable-script lint — .github/workflows/ci.yml: actionlint embedded-shell source mapping is not yet supported — not linted。
— qwen3.8-max-preview via Qwen Code /review
Maintainer verification (round 2) — real local runs @
|
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Reviewed — no blockers. 1 Suggestion-level finding(s) could not be anchored to a changed line and were dropped; nothing further to act on here. Not linted (tool limitation, not a blocker): the executable-script lint — .github/workflows/auto-minimize-spam.yml: actionlint embedded-shell source mapping is not yet supported — not linted; the executable-script lint — .github/workflows/ci.yml: actionlint embedded-shell source mapping is not yet supported — not linted.
中文说明
已审查——无阻断问题。 1 条建议级发现无法锚定到改动行,已丢弃;此处无需进一步处理。 未检查(工具限制,非阻断):the executable-script lint — .github/workflows/auto-minimize-spam.yml: actionlint embedded-shell source mapping is not yet supported — not linted; the executable-script lint — .github/workflows/ci.yml: actionlint embedded-shell source mapping is not yet supported — not linted。
— qwen3.8-max-preview via Qwen Code /review
|
🤖 Reviewed the latest feedback — no changes needed. Why, point by point: · 已审阅最新反馈——无需改动。逐点说明原因如下: No actionable feedback this round.
中文说明本轮没有需要处理的反馈。
Deferred non-Critical feedbackCritical-only mode is active after 5 change-producing rounds. Any items listed below stay open for human follow-up; do not modify code, resolve threads, or reply on their behalf.
中文说明完成 5 个产生改动的轮次后,进入仅处理 Critical 的模式。以上内容保持开放,留待人工跟进;不要为其修改代码、解决线程或代为回复。 Base-conflict check · 基分支冲突检查: no conflict with main. · 与 main 无冲突。 🧠 Handled by Qwen Code · model/模型 |
|
@qwen-code /triage |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
LGTM, looks ready to ship. ✅
…unds (QwenLM#8010) * feat(verify-pr): add four techniques from maintainer verification rounds Two hand-written maintainer rounds contained methods the skill could not have produced. Checked each against the current text before adding it; these four had no coverage at all. From QwenLM#7914 (live daemon A/B on the artifact-recording change): - Run every control on BOTH arms. That round's sharpest finding came from a control whose only job was to validate the BASE probe — "the empty list is a real absence, so have the model call record_artifact and watch an entry appear". Run on head as well, it showed the curated title being silently discarded. The control was not hunting for a bug; running it symmetrically is what found one. - A new writer into a shared store is an ordering change. The PR added write_file as a second writer into the artifact list; the bug was not in the new writer but in the collision, where a pre-existing first-writer-wins merge began discarding record_artifact's curated title and description while still reporting success. Enumerate the other writers, exercise the collision in both orders, and check what the loser is told — and separate the pre-existing cause from the PR's contribution so the author is not blamed for the policy. From QwenLM#7998 (ink cursor fix, real-terminal A/B): - When the oracle is an instrument, corroborate it with a mechanism that does not use that instrument. The hardware cursor row came from `tmux display-message -p '#{cursor_y}'`, then from a marker printed after the TUI exits — which lands wherever the cursor actually was. Two agreeing instruments turn a measurement into evidence; one tool's report about the system is not the system. - Re-run the generator on committed generated artifacts and diff. That round re-ran `npx patch-package ink` and found byte-different hunk headers, proving the .d.ts hunks were hand-written rather than regenerated as the description claimed. Also strengthens Not covered: proving a limitation is environmental requires an A/A control (boot base and head identically, show both fail the same way), because "seems environmental" and a real regression look identical in a report. Mutation-verified 4/4, each with landing proof. Two initially reported `landed: False` — the assertions match the whitespace-normalised text while the rules wrap across lines in the source, so the replace never fired and the green result proved nothing. Re-run against line-accurate anchors, both kill. 89/89 tests; prettier and eslint clean. * feat(verify-pr): teach the timing-race and scenario-arrival checks Third maintainer round mined for method (QwenLM#7934 R4). The blocker it found had zero coverage in the skill — `timer`, `wall-clock`, `flake`, `retry`, `duration`, `deterministic` all returned 0, and the one `race` hit was a substring of "trace". - **Timing-triggered assertions have a threshold — measure it, do not sample it.** A new guard (`expect(false).toBe(true)` after an abort loop) turned a vacuous pass into a deterministic failure, because the case triggers its abort from `setTimeout(..., 1000)` while the query's duration is set by CLI startup rather than the server. Natural completion measured 730-2151 ms, so every box on the fast side of 1000 ms fails. The rule says to measure the operation's natural duration with the trigger disabled and compare it to the timer, because a green run only proves this box was slow enough. - **A speed-correlated failure is not flake, and a retry budget does not absorb it.** Random flake becomes a pass under `retry: 2`; this failed 5/5 runs on all three attempts. The two get opposite verdicts, so the kind has to be established before the verdict is written. Stated plainly in the skill: the verify job runs on a shared, loaded runner — the regime where such a test PASSES. Repetition cannot reproduce a fast-machine failure there; only computing the margin can. A rule that said "run it more times" would be useless in this lane. - **The failure one level before vacuity: the scenario never reached the code under test.** The vacuity check asks whether an assertion can fail; this asks whether the code ever ran. Four abort cases fired during CLI process startup, so the fake server saw zero requests and a suite named for mid-stream aborts never streamed — with every assertion green. Instrument the seam and assert the count is non-zero. Mutation-verified 5/5, each with landing proof against line-accurate anchors. 89/89 tests; prettier and eslint clean. Skill is 472 lines, up from 392. * feat(verify-pr): six more techniques, from three maintainer rounds Mined QwenLM#7836 R2, QwenLM#7885 and QwenLM#7899 for method. Checked each candidate against the current text first; six had zero coverage, the rest were already there (harness teeth-checks, pin dereferencing, boundary probing, and the follow-up round's "re-measure, never diff"). The one that corrects the skill's own core method, from QwenLM#7836: - **Before calling a survivor vacuous, escalate to a finer mutation.** A whole-file revert is blunt enough to remove the PRECONDITION a test depends on, so a good test goes green because its scenario no longer occurs — from the outside, identical to a test that asserts nothing. A `finally`-cleanup test survived reverting four production files and died to deleting one line. Coarse survived + fine killed ⇒ the test is fine and the mutation was wrong. A false "your test is vacuous" costs the author more than a missed survivor does. From QwenLM#7836, the root cause shared by both of its blockers: - **When the same predicate is checked in two places, verify they see the same state.** A guard duplicated across a process boundary is two implementations of one question that diverge when their INPUTS differ. One settings key made a route ask sessionExistsInAnyState() with an unpinned runtime dir while the child asked with a pinned one, turning a clean 409 into a 500 plus a process.exit(1) that killed every session on the channel. Includes the temporal half: lazily-created backing files leave a window where a just-created entity is invisible to any on-disk existence check. - **Measure the blast radius on bystanders.** The caller's own error code understates a shared-state failure; the number that matters is an unrelated session going 200 -> 404 and a workspace list going 2 -> 0. From QwenLM#7885, which took a performance claim apart: - **Isolate the slice the mechanism can actually affect.** A speedup claim is two claims: the mechanism works, and the thing it speeds up matters. `--ignore-scripts` isolated what an npm download cache can touch — 36s of a 226s install — so the ceiling was 20s and the real saving 15%, not the claimed 75%. Then check it against the whole job: 33s off 14m37s. - **A mechanism that persists something has a cost — price it.** 219 MB per lockfile hash into a pool at 9.98 GB of a 10 GB cap, with 39 distinct lockfile states in 30 days: at the cap every entry evicts by LRU, including entries other jobs need and its own. - **Test the scarier consequences and report which do NOT hold.** The write-path finding was real; code injection was disproved (npm integrity-checks a tampered cache and refetches) and privilege escalation was disproved (chown -R does not follow symlinks). A finding that names what it is not is harder to wave away. - **Verify third-party actions from their own manifest.** The PR said the cache dir was discarded after the job; `action.yml` declares `post: dist/save/index.js` with `post-if: success()`, which uploads it as root with credentials intact — the opposite of the claim, and the whole finding. From QwenLM#7899: - **To exercise real production data safely, interpose a refusing proxy on the write path.** Wrap the client so every mutating call hard- fails, then run the shipped script verbatim: real counts, mechanical guarantee of no side effects. Mutation-verified 9/9, each with landing proof against line-accurate anchors. 89/89 tests; prettier and eslint clean. The skill is now 546 lines, up from 392 — the growth is deliberate and called out in the PR body's risk section. * feat(verify-pr): decomposed fixes, contextual limits, destination counts From QwenLM#7862 R4. Three additions, and a deliberate stop. - **When one fix bundles two changes, build the intermediate variants.** An A/B against base proves the pair works and says nothing about what each half does. That round compiled a third build with only the ordering change reverted, and the three-row table showed the halves do different jobs: moving `initialized = true` after the fallible work converts a 2,999-and-climbing backlog flood into a fail-safe retry, while `reduce()` restores liveness. Either alone leaves a channel that floods or wedges — a conclusion the two-cell A/B cannot reach. - **A limit measured in isolation does not transfer to the real call site.** The same `Math.max` spread threw between 110k and 130k elements inside a deep async stack, well below a standalone micro-benchmark. Bisect thresholds through the real code path and quote the harness; a limit taken from documentation or a toy loop is a guess about the system under test. - **Count at the destination, not at the component boundary.** The mirror of the scenario-arrival rule added earlier: envelopes the adapter emitted and prompts that reached the agent are different numbers, and every gate lives between them. A count taken at the seam can be right while the feature is silently dropped downstream. Declined from the same report, to protect prompt budget rather than because they are wrong: siblings-as-convention-oracle (the lockfile version table across five channels), degenerate fixtures that cannot distinguish two sort keys, and naming the condition under which a cosmetic finding becomes real. Each is a good technique; none is worth another rule competing for attention with the ones already here. The skill is now 578 lines, up from 392 on main (+47%) across this branch. That growth is the main risk on this PR and further additions should wait until a live round shows the current set changes behaviour. Mutation-verified 3/3 with landing proof. One mutation initially SURVIVED — it deleted text sitting AFTER the asserted phrase, so the assertion still matched and the green proved nothing; re-run against the phrase itself, it kills. 89/89 tests; prettier and eslint clean. * test(scripts): drop stale technique count from verify test name (QwenLM#8010) * fix(triage): correct verify-skill worked examples and verdict path (QwenLM#8010) Address review feedback on the verification-techniques skill: - Make the npm-cache worked example's numbers close: separate the 20 s download-slice ceiling (36 s to 16 s) from the 15% end-to-end saving (226 s to 193 s) rather than conflating them. - Stop overstating the tarball experiment: one tarball was poisoned, and the 2262-entry integrity coverage is a separate static fact. - Give the speed-correlated-failure rule a contract-legal verdict path by encoding the margin as a scripted assertion, and mark the load/idle sweep as the local-mode variant. - Fix the one bullet that broke its 2-space list continuation. - Pin the new contract-encoding clause in the workflow test. --------- Co-authored-by: wenshao <wenshao@example.com> Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com>
|
Released in v0.21.2. |





What this PR does
Adds a scheduled GitHub Actions workflow (
auto-minimize-spam.yml) that runs every hour to scan recent issue/PR comments and automatically minimize any from users blocked at the org level.Why it's needed
When a spam user is blocked at the org level, their existing comments remain visible on PR/issue pages. Currently these must be manually minimized one by one (as we just did for 25 comments from danialzivehdadr). This workflow automates that cleanup and also catches any comments posted in the window between the spam and the block action.
How it works
GET /orgs/{org}/blocksOFF_TOPICvia GraphQLminimizeCommentmutationThe workflow is also triggerable manually via
workflow_dispatchwith a configurable lookback window (default 2 hours).Security model
CI_BOT_PATfor API access (same token used by other triage workflows)QwenLM/qwen-codeonly (if: github.repository == 'QwenLM/qwen-code')Reviewer Test Plan
How to verify
Tested on
CI-only change.
Risk & Scope
Linked Issues
N/A
中文说明
添加定时 workflow,每小时扫描最近 2 小时的 issue/PR 评论,自动 minimize 被 org 拉黑用户的评论。解决拉黑后已有评论仍需手动清理的问题。也支持手动触发,可配置回看时间窗口。