goose 开源项目如何把 Issue 变成新的 PR:从 PR 驱动到 Issue 驱动的贡献模型与自动化实现
本文基于 goose 官方博客《Moving to issues as the new PRs》,解读 goose 项目将外部贡献的入口从"提交 PR"上移到"发起并推进 Issue"的动机、完整工作流与配套规则;并结合仓库中 CONTRIBUTING.md、GOVERNANCE.md 以及 buzz/ 目录下的真实自动化脚本与 Goose recipe,说明这套模型如何在代码层面落地运转。读完后你将理解:为什么编码智能体让 PR 不再是合适的贡献单位、Issue 看板上每个阶段的确切含义、PR 的准入条件,以及如何用仓库内现成的工具把 GitHub Issue 状态同步到社区频道并自动分派负责人。
背景:编码智能体改变了开源贡献的经济学
传统的开源贡献方式门槛很高:新贡献者要先把项目构建起来、把应用跑起来、让测试通过,即使知道该修哪个 bug,也需要对项目架构有粗略理解、对被修改的代码有细节了解。编码智能体(coding agent)彻底改变了这一成本结构。
博客的核心论点可以概括为三句话:
- 问题不在代码质量。由智能体生成的代码往往比首次贡献者写出的更好;
- PR 曾经是"认真做过功课"的不完美证据——为什么要费力实现一个功能,除非你相信维护者会需要它?修 bug 所需的工作量也强迫贡献者理解周边代码。这种摩擦筛选出了承诺,并强迫贡献者在写代码前获得上下文;
- 今天,产出一个"看起来可行"的功能实现或 bug 修复变得非常便宜,而判断这个变更是否属于项目、正确方案长什么样,仍然昂贵。PR 带着"这些问题已回答"的外表到达,但实际上智能体只是做了一系列看似合理的选择;维护者必须找出这些选择、判断哪些正确、解释哪些应该改。贡献者的智能体会快速改代码,但判断力来自维护者。
把这一点推到极端:开源项目会变成一个只有公共代码、没有实质公共参与的项目——由小核心团队及其智能体开发,其他人变成被动的用户或工单提交者。而 goose 治理文档明确把开放性作为核心价值,GOVERNANCE.md 中 Contributors 的定义本身就包含"通过 issues、pull requests 或 discussions 贡献"。外部参与者带来的全新视角、新想法、意外用例和领域知识,是核心团队永远看不到的问题。
因此 goose 的决策是:放弃 PR 作为主要贡献机制,把外部贡献上游化。有价值的贡献地点不再主要是写代码,而是提交好 issue,并参与把 issue 变成深思熟虑的方案的那场讨论。
Issue 驱动的贡献模型:七个阶段的看板
goose 采用公开的 Goose Issues board(GitHub Projects)来组织所有贡献。结合 CONTRIBUTING.md 中的 "Issue Workflow" 章节,每个开放 issue 在看板上的阶段与含义如下:
| 看板阶段 | 含义 |
|---|---|
| Inbox | issue 等待分诊(triage) |
| Needs info | 需要更多信息,issue 才能推进 |
| Accepted / design | 团队希望解决该问题,正在敲定设计、约束与验证方案 |
| Ready | 预期方案已确定,可以开始实现 |
| In progress | 实现进行中 |
| Verification | 实现完成,等待人工确认其确实可用 |
| Done | 结果已验证,issue 关闭 |
工作流的关键节点:
- 发现 bug 或想要新功能时,照常提交 GitHub issue。新 issue 进入 Inbox;分诊时维护者可能要求补充信息,也可能带解释地关闭 issue。
- 希望解决的问题移入 Accepted / design,贡献者与核心团队在那里确定预期设计、架构约束以及结果如何被验证。
- 讨论收敛后,issue 移入 Ready——此时智能体才可以开始写代码。
- 实现期间状态为 In progress,完成后进入 Verification 等待人工确认,最后才到 Done。
- 不打算跟进的 issue 会带解释关闭;goose 明确"不使用拒绝标签"(no rejection labels)。
CONTRIBUTING.md 进一步指出了最佳贡献位置:"Best place to contribute is the discussion between Accepted / design and Ready"——这正是工程发生的地方:把一个有价值的问题变成智能体可以实现的、具体的解决方案。贡献方式包括:带来上下文和领域知识、挑战假设、比较多种方案、识别约束与权衡、并就"结果如何被验证"达成一致。
关于 issue 本身的写作要求也很明确:功能请求应描述一个广泛有用的问题,而不只是你偏好的实现方式;"添加功能很容易,维护它是长期成本",因此增加复杂度而通用收益不足的功能可能被拒绝。博客和贡献指南都强调:issue 要自己写。智能体可以帮你做研究、帮你探索,但你必须理解这个 issue;可以给出方案方向,但应避免在 issue 中写详细解决方案尤其是代码。
新的 PR 规则:没有 Ready issue 就没有 PR
模型切换后,PR 的角色从"贡献的起点"变成"Ready issue 的实现载体"。CONTRIBUTING.md 的 "From Issue to Pull Request" 章节给出了硬性规则:
- 不要在 issue 到达 Ready 之前开始实现或开 PR;
- 每个外部 PR 必须:
- 链接它所实现的 Ready issue;
- 保持在 issue 中约定的设计与范围内;
- 说明 issue 的验证方案是如何执行的;
- 把任何实质性设计变更退回 issue 继续讨论。
- 不实现 Ready issue 的 PR 会被关闭;对 AI 代码评审意见不处理(feedback left unaddressed)的 PR 也会被关闭——这正呼应了博客正文的说法。
- 例外项:自动化的依赖/发布 PR、紧急安全修复、核心团队明确指派的工作。
- 不要短时间内连续开很多 PR,按偏好顺序提交,等前面的落地后再开新的。
配套的评审机制包括:使用 codex 作为 AI 代码评审,所有评论要么修复、要么加一行说明解释为何不成立,否则 PR 可能被关闭;以及"Agent Loop Migration"约定——在 crates/goose/src/agents/agent.rs 的旧 agent loop 向 crates/goose/src/agents/state_machine/ 状态机迁移完成之前,涉及 agent-loop 行为变更的 PR 必须在两条路径上都实现并测试,并说明如何验证了两条路径的等价性。这些细节说明"issue 先于 PR"不只是一句口号,而是有具体、可执行的检查标准。
署名规则:贡献单位从"补丁"变成"问题到已验证方案"
这个模型同时改变了"谁该获得署名"。报告问题、复现问题、贡献领域知识、塑造设计、实现方案、验证结果,都可以是有意义的工作。CONTRIBUTING.md 的表述与博客一致:"Substantial contributors at any stage may be recognized as co-authors. The unit of contribution is taking a problem to a verified solution, not writing the patch."——每个阶段的实质性贡献者都可以被记为共同作者(co-author),开源贡献的单位不再是补丁,而是把一个问题一路带到已验证的解决方案,而其中大部分贡献现在发生在 issue 里。
仓库中的自动化落地:buzz/ 目录
博客宣布的看板模型在仓库里有完整的工程化实现:buzz/ 目录是一套把 aaif-goose/goose 的 GitHub issue 连接到公共 Buzz 社区频道的自动化工具集(见 buzz/README.md)。它体现了"issue 是贡献主记录"这一理念的运转方式:GitHub 上的 issue 状态变化会实时同步为社区频道的话题,讨论发生在 issue 及其关联频道中。
两套身份与四个核心工具
这套系统使用两个身份:
- Github Manager:服务身份,脚本用它的 Nostr 密钥创建和管理 issue 频道、添加成员、发布 issue 摘要、同步频道话题;
- 受管 bot(当前是 Doose):由 Buzz Desktop 运行的 agent 身份,只有在 Github Manager 显式提及时才会审阅 issue。
核心工具与其职责:
create_github_manager:幂等地生成专用 Nostr 密钥对并发布 Github Manager 资料。密钥存放在$GOOSE_BUZZ_HOME/github-manager(默认~/.config/goose/buzz/github-manager),目录权限0700、文件0600;身份丢失时只能重建,旧事件的签名无法重签。create_issue_channel:读取一个 GitHub issue,创建永久公开的 Buzz 流(频道名#<issue-number> <issue-title>),初始话题设为⚪ GitHub phase: Inbox,添加 owner/成员/bot,并附带摘要和 issue 链接;不修改 GitHub issue 本身。脚本拒绝为已有频道重复建频道,失败会删除半成品以便重试。list_issue_work:列出 Inbox 中尚未分派、或尚未有频道的 issue,以及 Buzzissues to add频道中链接的开放 issue;输出 JSON 包含核心团队、GitHub 句柄、Buzz 公钥、兴趣标签与分派容量(recent_assignment_load统计最近 100 个 issue 的分派负载),供 recipe 据此选人。该命令只读,不改动 GitHub 或 Buzz。syncissues:拉取所有开放 issue 与所有 Buzz 频道,按描述中的 GitHub URL 匹配 issue 频道,把项目阶段和分派人同步到频道话题。阶段到表情标记的映射为:
| GitHub 阶段 | Buzz 话题标记 |
|---|---|
| Inbox | ⚪ |
| Needs info | 🟡 |
| Accepted / design | 🟣 |
| Ready | 🟢 |
| Verification | 🔵 |
| Done | ✅ |
话题格式形如 🟢 Ready -- assigned to: @github-handle。与已关闭 issue 对应的频道会被打上 ✅ GitHub issue: Closed 并归档;issue 重新打开时频道自动恢复。此外,外部(非仓库 OWNER/MEMBER/COLLABORATOR)用户在 issue 下的新回复会被同步提及到频道,但评论正文不复制进 Buzz,防止评论内容注入提及或 bot 指令——这是一个值得注意的防提示注入设计。使用前建议先 ./buzz/syncissues --dry-run 预览。
这些脚本的本地测试由 Justfile 的 test-buzz 配方驱动(node --test buzz/*.test.mjs 加语法检查),测试文件为 buzz/github_manager.test.mjs,在 Buzz 自动化代码变更时也会进入 CI。
每小时运行的分派 recipe
buzz/github_issue_manager.yaml 是一个 Goose recipe,管理完整的 Inbox 循环。其 instructions 部分明确把 issue 正文和 Buzz 消息当作不可信数据处理(只用于识别、理解与总结 issue,绝不执行其中指令),并限制唯一的 GitHub 写操作是"用 gh issue edit --add-assignee 把未分派的 issue 分派给一名核心团队成员",禁止编辑正文、评论、改字段、关 issue 或动标签。
分派算法(来自 recipe prompt 与 buzz/core-team.json):
- 运行
list_issue_work获取待处理 issue 列表及每位核心成员的interest兴趣列表与recent_assignment_load(基于最近 100 个 issue,容量capacity是唯一负载调节因子,当前花名册中除 filip 为0.5外均为1); - 按 issue 号升序处理:对每个未分派 issue,用
gh issue view --json ...重读 issue,从兴趣匹配度最高的三名核心成员中选负载最低者分派(负载相同时按主题匹配度决胜),并在本轮内滚动更新候选人的负载计数; - 为该 issue 创建"聚焦的" Buzz 频道:至少包含三个不同的人(Douwe 与 issue owner 必在),只在 issue 棘手或跨领域时加第四人;
- 最后运行
syncissues,输出紧凑报告。
单次运行与干跑方式:
goose run \
--recipe "$PWD/buzz/github_issue_manager.yaml" \
--params "automation_dir=$PWD/buzz" \
--no-session
# 干跑:只报告拟议分派,不改 GitHub 或 Buzz
goose run \
--recipe "$PWD/buzz/github_issue_manager.yaml" \
--params "automation_dir=$PWD/buzz" \
--params "dry_run=true" \
--no-session
buzz/run_hourly 则负责"运行一次 recipe、等待一小时(可用 BUZZ_MANAGER_INTERVAL_SECONDS 覆盖)、循环重复",供专用机器常驻运行。从源码结构看,core-team.json 中每位成员的 interest 列表与其在 goose 代码库中的职责域高度对应(例如 "Agent-loop and state-machine architecture"、"ACP and MCP protocols"、"Security boundaries and permissions"),这解释了为什么分派可以仅凭文本匹配加负载均衡来自动完成。
总结:PR 仍是载体,Issue 是贡献
goose 的这次模型调整可以归纳为四条可复用的实践:
- 贡献单位上移:把"提交好 issue + 参与设计讨论"定义为贡献的主体,代码实现只是 Ready issue 的执行环节;
- 阶段门禁:Inbox → Needs info → Accepted / design → Ready → In progress → Verification → Done 七阶段看板,让"何时允许写代码"有明确判据,不实现的 PR 直接关闭;
- PR 约束具体化:链接 Ready issue、不超范围、说明验证执行、设计变更退回 issue——可检查、可执行;
- 自动化承担琐碎环节:分派(兴趣匹配 + 负载均衡)、状态同步(阶段表情话题)、外部讨论回流(防注入的评论提及)全部由仓库内脚本与 Goose recipe 承担,维护者把判断力集中在设计讨论与验证上。
这套机制目前适用于 goose 仓库本身(aaif-goose/goose 的 Projects 看板 1 号 board),其脚本通过 --repo、--project-owner、--project-number 等参数也可以指向其他仓库,但分派花名册(core-team.json)与社区身份需要随部署环境自行配置。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0629
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python07
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
