img2threejs Issue 分诊:09:17 的定时机器人怎么让新 Issue 不被漏看
【免费下载链接】img2threejsRebuild the object in a reference image as a code-only, procedural, quality-gated, animation-ready Three.js model. Token-efficient image-to-3D.项目地址: https://gitcode.com/gh_mirrors/im/img2threejs
每天 09:17 UTC,一个 GitHub Actions 定时任务会在后台悄悄醒来。它不发邮件、不开 Issue,也不碰你的代码,只做一件事:把仓库里「还没人看过」的打开 Issue 找出来,安静地发一个号。
这就是 img2threejs 的 Issue 分诊(Triage)机制。整套东西只有一个工作流文件 .github/workflows/issue-triage.yml 和一个零依赖脚本 scripts/issue_triage.py。它不大,但细节很值得学。
先说明白:每天 09:17 是善意提醒,不是硬性期限。别拿它当「48 小时内必有维护者回复」来安排自己的工作流。工作流里还有个小讲究:辅助逻辑只在「UTC 纪元日每第二个日子」运行,跨月边界时节奏也能保持连续,不会漂移。
机器人为什么只被允许做两件事 🎫
先想一个医院挂号。挂号员只发号、告诉你去候诊区等,诊断和开方是医生的事。img2threejs 的分诊分工就是这样画的,权限也跟着锁死——工作流只声明了issues: write,别的一概没有。
| 角色 | 允许做什么 |
|---|---|
| 机器人 | 给「打开、非 PR、零标签」的 Issue 加唯一的triage: needs-review标签,并发一条固定通知。不摘标签、不关闭、不锁定、不指派人、不打wontfix/duplicate这类终结标签 |
| 维护者 | 定优先级(priority: high/medium/low)、管贡献状态(contribution: proposed/available/claimed)、写决策评论、关闭 Issue,全部只由人来做 |
bug、enhancement这类类型标签是提交者填表单时打上的,属于第一层分类,脚本从不碰。并发方面,工作流用了仓库粒度的并发组且cancel-in-progress: false,两次触发只会排队,不会互相踩踏地写同一个 Issue。
一个 Issue 的旅程:从 09:17 到维护者桌面
跟着一个 Issue 把全流程走一遍。
第一步,进候选集。脚本分页拉全部 open 记录(per_page=100,拉到空页为止),排除 PR 和已关闭的,再给每个候选重新取一次详情快照——防止列表和详情之间状态变了。只有「打开、非 PR、零标签」的 Issue 才算候选。
第二步,历史存量不自动碰。调度运行带一个rollout-after截止(当前是2026-07-28T00:00:00Z),早于这个时间创建的 Issue 直接跳过。存量问题交给维护者:先用 dry-run 模式过一遍候选清单(只列出不写入),确认没问题再手动触发 backfill 真正落笔。模式在写死的工作流里:schedule 事件强制 backfill,手动运行则用维护者自己选的模式。
第三步,唯一的例外。如果一个 Issue 身上恰好只有triage: needs-review这一个标签,它会再被看一眼——对应「上次打标成功但评论没发出去」的场景。机器人只补发那条通知,不再重复打标签。反过来,只要有人额外加过别的标签(哪怕只是bug),它就不算恢复候选了:维护者已经进场,机器人要退开。这条边界有离线单测专门验证,比如test_skips_human_labelled_queue_issue_without_notice。
第四步,查「已读回执」。机器人的通知首行是一段 HTML 注释:
<!-- img2threejs-triage-notice:v1 --> Thanks for opening this issue. It is in the maintainer triage queue. ...写入前脚本会分页翻一遍 Issue 评论。只要回执已在,直接跳过。注意只信作者为github-actions[bot]且用户类型为Bot的评论,两个条件缺一不可——普通用户把标记原文复制进评论,骗不过它。
第五步,两步写入。先add_label,再create_notice。通知文案是固定的,里面有一句「a label is not a promise of implementation」——标签不代表承诺实现,文案层面就把预期管住了。
中途失败怎么办。三种失败场景要知道:
- 429、5xx 这类瞬时错误最多重试 3 次,间隔按 1s、2s、4s 指数退避;401、403、422 这类绝不重试,因为重试也不会有结果;
- 评论写入很特别:首次 POST 不重试。失败后先重查一次回执,确认评论确实没写进去才重发。这是防「GitHub 其实已接受写入、只是响应在路上丢了」导致重复发帖;
- 单个候选失败只把该 Issue 记进
failed桶,后续照常处理。一次运行的结果分五桶:noticed、reconciled、would_notice、skipped、failed。只要failed非空,退出码就是 1,调度层光看退出码就知道有没有人没处理完。
还有一个前置守卫:运行一开始先确认仓库里建过triage: needs-review标签(请求 404 即缺失)。缺了就中止,任何写入之前。
四个值得抄的设计取舍 💡
零依赖标准库。整个脚本只用urllib、json、argparse、dataclasses。这是仓库的约定:forge/核心不装任何 pip 依赖。好处很实在——分诊机器人是维护工具,依赖第三方包只会徒增维护成本。
先重查再重发。评论写失败的处理思路是:写入是「结果可能含糊」的操作,重发之前先读一遍状态,确认到底生效没有。这比盲目重试可靠得多,而且这条「一次失败加一次重查」的调用序列有离线单测覆盖,用内存假 API 驱动、不触网。
低优先级 Issue 不关闭。维护者改优先级、贡献状态或关闭 Issue 之前,必须先发一条决策评论:决策本身、理由、下一步。判低优先级时还必须附回访触发条件。低优先级 Issue 保持打开,给自己留了被重新审视的机会。Issue 本体就是分诊讨论记录,事后任何人翻回来都能看清「为什么给这个优先级、为什么关」。
PR 里禁用关闭关键词。实现 PR 用Refs #<number>或Related to #<number>关联 Issue,fixes、closes一律禁止。仓库另有一个专门工作流 .github/workflows/pr-issue-close-policy.yml,在 PR 事件上检测并拒绝关闭关键词。关闭权锁在维护者手里:PR 合并后,维护者把合并的 PR、验证结果、关闭理由记在 Issue 上,再手动关。
如何在自己仓库复刻分诊流程
想在自己的仓库搭一条类似管线,先抄最有价值的两点:把机器人限定在「打标签 + 发评论」两件事里,关闭和优先级留给人;让机器人给通知留一个带签名的「已读回执」,并且只认自己那个签名。之后再逐步加上恢复路径、dry-run / backfill 双模式和瞬时错误重试。整套逻辑两百来行,测试套件完全离线可跑。你可以先试一发:
python3 scripts/issue_triage.py --repository owner/repo --token "$GITHUB_TOKEN" --dry-run先 dry-run 看候选清单,确认没毛病再摘掉--dry-run。之后的事,交给每天 09:17 的那次运行。
【免费下载链接】img2threejsRebuild the object in a reference image as a code-only, procedural, quality-gated, animation-ready Three.js model. Token-efficient image-to-3D.项目地址: https://gitcode.com/gh_mirrors/im/img2threejs
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考