feat(autofix): auto-manage the bot's own fork PRs without a label - #7243
Conversation
A fork PR the autofix bot itself opened (its codex flow pushes to qwen-code-dev-bot/qwen-code) is the bot's own generated work — same author, same code provenance, and the bot holds write+ — so it is trust-equal to an in-repo bot PR. Requiring a manual autofix/takeover label on it was redundant: in-repo bot PRs are auto-managed with no label, and the takeover label exists to authorize EXTERNAL (human) fork authors, not the bot's own. Now a fork authored by AUTOFIX_BOT with 'Allow edits from maintainers' is admitted and managed WITHOUT a label: - Scan: fork candidates are unioned from bot-prs.json (the bot's own forks — --author AUTOFIX_BOT, so no label needed) AND the takeover-labeled list (non-bot forks, explicit opt-in). Both still require allow-edits and pass the per-candidate live write+ gate. - Eligibility: the fork chain no longer demands the takeover label when the author is the bot (the author check already exempts it); it still demands allow-edits + a live write+ author + a matching live head repo. autofix/skip still opts any such PR out. Non-bot forks are unchanged — they still need the explicit label. Tests: the fork-candidate union admits a bot fork (no label) + a labeled human fork, dropping no-allow-edits/in-repo/skip; the eligibility replay makes a bot fork with allow-edits eligible without a label and discards it without allow-edits. 62/62 + 12/12.
|
Thanks for the PR! Template looks good ✓ (minor: "Why it's needed" is covered under "What this PR does" + "Why it's safe" rather than its own heading — substance is all there, no action needed). Problem: observed and well-evidenced. #7208 and #7220 are concrete bot codex-flow fork PRs that sat unprocessed until someone manually applied Direction: aligned. The bot's own fork PRs are trust-equal to in-repo bot PRs (same author, same code provenance, bot holds Size: not applicable (no core module paths touched). Approach: the scope feels right — two surgical changes (union the bot's forks into the scan candidate pool, relax the eligibility fork-chain for bot-authored forks) plus matching test updates. No scope creep, no drive-by refactors. The Moving on to code review. 🔍 中文说明感谢贡献! 模板完整 ✓(小瑕疵:"Why it's needed" 的内容分散在 "What this PR does" 和 "Why it's safe" 中,但实质内容齐全,无需修改)。 问题:已观测且有据可查。#7208 和 #7220 是具体的 bot codex 流程 fork PR,因缺少 方向:对齐。bot 自己的 fork PR 与 in-repo bot PR 同等信任(同作者、同代码来源、bot 持有 规模:不适用(未触及核心模块路径)。 方案:范围合理——两处精准修改(将 bot fork 并入扫描候选池、放宽 bot 作者 fork 的资格链)加上对应的测试更新。无范围蔓延,无顺手重构。jq 并集中的 进入代码审查 🔍 — Qwen Code · qwen3.7-max Reviewed at |
Code ReviewIndependent proposal: I'd do exactly what this PR does — union Comparison with the diff: the PR matches this approach precisely. Verified the full eligibility chain at address time:
No correctness bugs, security holes, or regressions found. No new abstractions or utilities — the change extends existing jq and bash patterns in place. Comments updated to match. TestingThis is a GitHub Actions workflow change — there's no CLI behavior to drive in tmux. The behavioral replay tests are the real-scenario verification here: they extract the exact jq filters and bash eligibility logic verbatim from the workflow YAML and replay them against fixtures. Key behavioral coverage in the 62 tests:
中文说明代码审查独立方案: 我会做和这个 PR 完全一样的事——将 与 diff 对比: PR 完全匹配此方案。验证了 address 时的完整资格链:
无正确性 bug、安全漏洞或回归。无新抽象或工具——变更就地扩展现有 jq 和 bash 模式。注释已同步更新。 测试这是 GitHub Actions 工作流变更——没有 CLI 行为可在 tmux 中驱动。行为回放测试即为真实场景验证:它们从工作流 YAML 中逐字提取 jq 过滤器和 bash 资格逻辑,对夹具回放。
关键行为覆盖:fork 候选并集 jq 准入 bot 无标签 fork(#20)和带标签人类 fork(#9),丢弃无 allow-edits 的 bot fork(#19)、in-repo bot PR(#18)和 skip fork(#7)。资格回放确认带 allow-edits 的 bot fork 无标签即合格,无 allow-edits 则丢弃。 — Qwen Code · qwen3.7-max Reviewed at |
|
Confidence: 5/5 — clean across every stage; the problem is real, the fix is minimal, and the safety envelope is preserved. This is a textbook small PR: two surgical edits that remove a redundant label requirement for the bot's own fork PRs, with matching test coverage. The independent proposal I sketched before reading the diff was identical to what landed — union the bot's forks into the scan pool, relax the fork-chain condition for bot authors. No simpler path exists; the change is already at the minimum. What I verified:
If I had to maintain this in six months, I'd thank the author — the comments explain the two-source union clearly, and the test fixtures document the admission/discard matrix. 中文说明置信度:5/5 — 每个阶段都干净;问题真实、修复最小、安全边界完整保留。 这是一个教科书级的小 PR:两处精准修改,移除对 bot 自己 fork PR 的冗余标签要求,配套测试覆盖。我在读 diff 之前草拟的独立方案与最终实现完全一致——将 bot fork 并入扫描池、放宽 bot 作者的 fork 链条件。不存在更简路径;变更已是最小。 验证内容:
— Qwen Code · qwen3.7-max Reviewed at |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
LGTM, looks ready to ship. ✅
Code Coverage Summary
CLI Package - Full Text ReportCore Package - Full Text ReportFor detailed HTML reports, please see the 'coverage-reports-22.x-ubuntu-latest' artifact from the main CI run. |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Reviewed — no blockers. Suggestions are inline.
— qwen3.8-max-preview via Qwen Code /review
| expect(runRecheck(botFork).passed).toBe(true); | ||
| // Remove allow-edits and the same bot fork discards (cannot push). | ||
| expect(runRecheck({ ...botFork, maintainerCanModify: false }).passed).toBe( | ||
| false, | ||
| ); |
There was a problem hiding this comment.
[Suggestion] No negative test guards the complement of the new bot exemption — a NON-bot fork with allow-edits enabled but without the autofix/takeover label. botFork proves the bot passes without the label and forkPr proves a human passes with it, but nothing asserts that a human fork without the label is still rejected when allow-edits is on. — Concrete cost: if the author gate above (line 2053) is ever relaxed, a non-bot fork could be admitted with no failing test to catch it.
| expect(runRecheck(botFork).passed).toBe(true); | |
| // Remove allow-edits and the same bot fork discards (cannot push). | |
| expect(runRecheck({ ...botFork, maintainerCanModify: false }).passed).toBe( | |
| false, | |
| ); | |
| expect(runRecheck(botFork).passed).toBe(true); | |
| // Remove allow-edits and the same bot fork discards (cannot push). | |
| expect(runRecheck({ ...botFork, maintainerCanModify: false }).passed).toBe( | |
| false, | |
| ); | |
| // A NON-bot fork with allow-edits but no takeover label still discards — | |
| // the bot exemption is author-specific. | |
| expect( | |
| runRecheck({ ...botFork, author: { login: 'maint-fork' } }).passed, | |
| ).toBe(false); |
— qwen3.8-max-preview via Qwen Code /review
| # elif ends the ladder for eligible forks, so anything below it | ||
| # would be unreachable for exactly the PR class we fetch and push. | ||
| elif [[ "${LIVE_XREPO}" != "false" && "${LIVE_TAKEOVER}" != "true" ]]; then INELIGIBLE='fork head without takeover' | ||
| elif [[ "${LIVE_XREPO}" != "false" && "${LIVE_TAKEOVER}" != "true" && "${LIVE_AUTHOR}" != "${AUTOFIX_BOT}" ]]; then INELIGIBLE='fork head without takeover (a non-bot fork needs the label)' |
There was a problem hiding this comment.
[Suggestion] This fork-specific elif is now provably unreachable. The author gate above (line 2053, LIVE_AUTHOR != AUTOFIX_BOT && LIVE_TAKEOVER != "true") already rejects every PR that could satisfy this condition: any PR reaching here has author == bot OR takeover == true, which contradicts this branch's author != bot && takeover != true. The bot exemption added by this PR removed the last reachable case (previously bot forks without the label were caught here). — Concrete cost: the diagnostic 'fork head without takeover (a non-bot fork needs the label)' can never appear in a log (a non-bot fork is reported from line 2053 with no fork indication), and a future maintainer editing fork-label policy here would see no effect since the real gate is line 2053. If kept as defense-in-depth, say so in a comment so it isn't mistaken for an accidental dead branch.
| elif [[ "${LIVE_XREPO}" != "false" && "${LIVE_TAKEOVER}" != "true" && "${LIVE_AUTHOR}" != "${AUTOFIX_BOT}" ]]; then INELIGIBLE='fork head without takeover (a non-bot fork needs the label)' | |
| # Non-bot forks without takeover are already rejected by the author | |
| # gate above; this branch is unreachable defense-in-depth. |
— qwen3.8-max-preview via Qwen Code /review
|
Released in v0.20.1. |
|
Agent run timed out after 1800000ms ❌ failed |
What this PR does
Auto-manages the autofix bot's own fork PRs without requiring a label.
The bot's codex flow opens PRs from its own fork (
qwen-code-dev-bot/qwen-code). Those are the bot's own generated work — same author, same code provenance, and the bot holdswrite+— so they are trust-equal to an in-repo bot PR, which is already auto-managed with no label. Requiring a manualautofix/takeoverlabel on them was redundant: the takeover label exists to authorize external (human) fork authors, not the bot itself. That's exactly why #7208 and #7220 (bot codex-flow fork PRs with actionable review suggestions) sat unprocessed until a label was applied by hand.Now a fork authored by the autofix bot with "Allow edits from maintainers" is admitted and managed without a label:
bot-prs.json(the bot's own forks — the list is--author AUTOFIX_BOT, so no label needed) and the takeover-labeled list (non-bot forks, explicit opt-in). Both still require allow-edits and pass the per-candidate livewrite+gate.write+author, and a matching live head repo.Unchanged:
autofix/skipstill opts any such PR out; non-bot forks still need the explicitautofix/takeoverlabel.Why it's safe
A bot-authored fork adds no trust surface beyond an in-repo bot PR:
authorcannot be spoofed (it is the account that opened the PR), the fork is the bot's own (only the bot pushes to it), the executed-code provenance is identical to in-repo bot PRs, and the address run's constraints (coreTools allowlist, docker sandbox, severed hooks, trusted-base runner staging) are unchanged. Thewrite+gate and allow-edits requirement remain.Reviewer Test Plan
How to verify
npx vitest run scripts/tests/qwen-autofix-workflow.test.js— 62/62. Behavioral coverage:npx vitest run scripts/tests/qwen-fleet-shepherd-workflow.test.js— 12/12.run:block passesbash -n.Tested on
Risk & Scope
autofix/skipis the opt-out; allow-edits + livewrite+remain hard gates.Linked Issues
Follow-up to #7213 (fork takeover). Motivated by #7208 / #7220 sitting unprocessed for want of a label.
中文说明
本 PR 做了什么
让 autofix bot 自己 fork 的 PR 无需标签即自动托管。
bot 的 codex 流程会从自己的 fork(
qwen-code-dev-bot/qwen-code)开 PR。那是 bot 自己生成的工作 —— 同一作者、同样的代码来源、bot 持有write+—— 与 in-repo bot PR 同等信任,而后者本就无需标签自动托管。给它们打autofix/takeover标签是多余的:该标签是给外部(人类) fork 作者的显式授权,不是给 bot 自己的。这正是 #7208、#7220(带可执行 review suggestion 的 bot codex fork PR)一直没被处理、直到手动打标签的原因。现在:由 autofix bot 作者、且勾选 "Allow edits from maintainers" 的 fork,无需标签即被纳入并托管:
bot-prs.json(bot 自己的 fork —— 列表是--author AUTOFIX_BOT,无需标签)与 takeover 标签列表(非 bot fork,显式 opt-in)并集取得;两者仍需 allow-edits 并逐候选过实时write+门。write+作者 + 实时 head 仓库匹配。不变:
autofix/skip仍可让任意此类 PR 退出;非 bot fork 仍需显式autofix/takeover标签。为什么安全
bot 作者的 fork 不增加超出 in-repo bot PR 的信任面:
author无法伪造(就是开 PR 的账号)、fork 是 bot 自己的(只有 bot 推送)、被执行代码来源与 in-repo bot PR 一致、address 运行的约束(coreTools 白名单、docker 沙箱、断 hook、可信基座 runner staging)均不变。write+门与 allow-edits 要求保留。评审验证
npx vitest run scripts/tests/qwen-autofix-workflow.test.js—— 62/62:fork 候选并集 jq(逐字提取、双夹具回放)准入 bot 无标签 fork([Bug] API请求超时:无法完成html重构(啥活没产,就干了我input 43万个tokens) #20)与带标签人类 fork(API Error: Streaming setup timeout after 45s #9),丢弃无 allow-edits 的 bot fork、in-repo bot PR、skip fork;资格回放使带 allow-edits 的 bot fork 无标签即合格、无 allow-edits 则丢弃。npx vitest run scripts/tests/qwen-fleet-shepherd-workflow.test.js—— 12/12。run:块过bash -n。风险与范围
autofix/skip为退出开关,allow-edits + 实时write+仍是硬门。关联 Issue
#7213(fork 托管)的后续;由 #7208 / #7220 因缺标签未被处理驱动。