- 修复 — 当 X,应该 Y,实际 Z。示例:
#1074 — 当
gh auth status退出码为 1 且当前账号有效时,Runner 应该解析出该 账号;实际却因退出码让交付失败。 - 变更 — 当 X,用户应该能够 Y;今天不能,因为 Z。示例:
#1048 — 用户从
发布镜像启动 runner 时,应该得到一个配好模型 provider 的
runner;当时还没有任何代码读取
ORBI_PI_*变量。
ai-blocked 现场)。这一页覆盖完整的自动链路和项目的 GitHub 工作流
词汇。
完整链路
状态机(七个交付状态;review/fix 循环留在同一个 PR 上——只有它的 head 可以前进):ai-fix-needed,失败 comment 写入 Issue
和 PR(带完整现场),下一个 tick 在同一 run、branch、worktree、PR 上
继续(不重新创建 PR、不重复领取)。worktree 缺失根本不是失败:resume
会从远端交付分支重建它并继续推进(Issue #807——worktree 只是远端状态
的本地缓存,branch 和 PR 都在 GitHub 上)。只有 AI 无法安全判断或修复的外部
前置条件(无可恢复现场、base 分支变更、审查超轮、PR 未合并被关闭、
交付分支已从远端删除——没有任何东西可以用来重建 worktree)
才 fail fast:Issue 被标记 ai-blocked 并写明为什么不能自动恢复,自动
循环从此不再碰它——人工决定下一步。
运行中的交付修正
无需取消正在进行的交付,也可以修正方向。当 Issue 处于ai-in-progress 时,可以评论新的决定,或直接编辑 Issue body。这两个渠道
是等价的:Runner 会在下一次 steering 轮询时拾取它们(间隔由
steering_poll_seconds 控制,默认 60 秒),并带着修正重启当前 session。
重启次数由 steering_max_rounds 限制(默认 3 次);达到上限后,后续修正
不会再重启这次交付,journal 会记录 steering_limit_reached。让当前交付完成,
再把修订后的范围放入后续交付。
只有受信任的仓库角色——OWNER、MAINTAINER、MEMBER 或 COLLABORATOR——的
评论可以 steering。公开评论者的评论会按设计被忽略,因为允许任意评论者
控制交付会暴露 prompt injection 入口。初始受信任评论时间线受
issue_comments_limit 限制(默认 200 条):更早的评论会被丢弃,注入的上下文
会明确说明省略了多少条。
死循环守卫与评论去重(Issue #825):恢复接续可能绑定新的 run id
(一次可恢复失败就够了),而 PR body 只在创建时写入一次,所以 resume
校验接受本交付线曾用过的任一 run marker——marker 集合读自 Issue 的
受信任评论(与恢复现场同一信任过滤,公开评论里复制的 marker 不计入;
branch、base、head、精确 URL 校验全部保留,#45/#89 的防串 PR 语义不变)。
同一 (run_id, 失败指纹) 的失败评论全程只发一次(隐藏的
<!-- orbi:fail=... --> 标记为键),后续相同失败原位递增该评论的隐藏
:次数 后缀(journal 记录重复);同一 (run_id, 失败指纹) 连续失败
达到 3 次(现场块——新开的 PR、完成的评审轮——代表交付线前进,会重置
连击)即判定死循环而非抖动,升级为 ai-blocked 并在评论里写明连续
次数与「前置条件未变化」。
链路规则:
- 一个任务 = 一个 run = 一个 feature branch = 一个 worktree = 一个 PR。 review/fix 循环期间 PR 编号永远不变;只有它的 head 可以前进。
- 只有两个 opened-PR 状态会被自动拾取:
ai-pr-opened(等待 review) 和ai-fix-needed(等待下一个审查会话)。ai-ready作为新工作被 领取;ai-blocked从不自动恢复。携带回填的ai-in-progress标签的 交付仍会被拾取(Issue #178:review 中被杀的 Runner 会留下该标签—— 正向的 opened-PR 限定已保证 implement 阶段的 Issue 不会匹配该扫描)。 - review/fix 循环有界(5 轮)。已完成轮数存在受信任现场里
(
review_round字段,Issue #788):每条轮次评论携带更新后的现场 块,下一 tick 从中读出推进后的计数(人工在 block 之后把 Issue 改回 ai-fix-needed 的恢复仍会重置预算,Issue #483)。超轮仍有 findings 时 Issue 标记ai-blocked(超轮是人工决策);可恢复失败(Issue #50) 保持ai-fix-needed。无论哪种,PR、branch 和 worktree 原样保留。 评审会话自己也守卫预算(Issue #878):开工前其 prompt 会读本交付 (同一run_id)的轮次评论,当同一失败原因已连续出现在两轮及以上 时,直接发点名重复的 findings verdict,不再重复同一修复路径—— 同一堵墙最多撞两轮。 - tick 内零等待(Issue #788):旧进程内等待循环(
wait_for_delivery、 对 CI/mergeability 的轮询、release_ci_wait_seconds/mergeable_wait_seconds)已移除。所有等待都走 waiting 原语——写状态、 返回、释放槽位,下一 tick 重读(delivery_ci_pending、merge_gate_ci_pending、merge_gate_mergeable_unknown)。可恢复 扫描直接跳过 CI 仍 pending 的候选(Issue #1473),pending 的 PR 因此不再占掉一整个 tick。崩溃恢复 路径就是主路径。 - base 前进由 task branch 上对最新
origin/<base>的普通git merge吸收(冲突手工解决),然后重跑完整测试。不 force push、不自动解决 冲突、不 push 保护分支。 - 吸收契约(Issue #877,Runner 机器校验):一轮评审开始时 head 已落后
origin/<base>或 PR 有冲突时,该轮结束必须满足二者之一——分支包含origin/<base>(git merge-base --is-ancestor为真),或 verdict 为findings且写明「本轮尝试吸收但放弃」及具体原因。head 仍不含 base 的pass会在轮次评论里被 Runner 点名为违反吸收契约(review_absorb_abandoned);既有 5 轮有界语义不变。 - Git transport(Issue #114、#580):git 数据操作走配置的传输方式
(
git_transport:默认ssh——git over SSH——,或https走ghcredential helper),GitHub API 操作留在ghtoken 上——完整的双通道边界与启动前检查见运维; 凭据见安全。
Reviewing with a different model(用不同的模型评审)
独立评审不必运行在写代码的同一个模型上:review_pi_provider、
review_pi_model、review_pi_thinking 为评审会话单独选择
provider/模型/思考级别,评审者因此不会继承实现者的盲区。每个键独立
回落到实现侧的对应值(review_pi_provider → pi_provider、
review_pi_model → pi_model、review_pi_thinking →
pi_thinking),只覆盖其中一个也有效。两个选择都必须存在于配置的
pi_providers 目录中,并在启动时分别校验——评审选择非法会以
review provider selection invalid fail fast。本文不推荐也未实测
任何具体配对;能力与回落机制才是契约。
合并账本:external_commits 与 commits
Runner 合并交付 PR 时,Orbi merged PR: Issue 评论只展示用户真正
要行动的三件事——PR 链接、review 结果与目标分支——并把
merged-as-is 事实(Issue #833)收进一个折叠的 Run details 块:
Run details 行保持 key=value 形式(等号,不是冒号),因为它们是
机器读的锚点:orbi-cloud 的 mergeCommentFields 读取器与本仓库的
comment-reader 契约精确匹配 external_commits=K、review_rounds=N
与 run_id=…。Orbi: merged milestone 行以同样的 key=value 形式
带上这些事实。
commits—— 合并时 PR 分支相对 base 的全部 commit 数(比例的分母)。external_commits—— 不在引擎自己推送历史里的 commit 数(开 PR 时的 head 加上 Runner 自己推的每一轮 review/fix head;绝不检查 git 作者 身份)。0= merged as-is:合并前没有人改过交付分支的一行。计数在 合并后通过 merge commit 的父链读取(M^1为 base 端、M^2为 PR head),减去引擎记录的推送线:最后一个引擎推送 head;外部接管 (Issue #608)时再排除接管的贡献者 head(推送线的起点),贡献者的 commit 保持为外部。external_commits=unknown—— 推送历史无法证实(重建的 worktree 丢了 run-state 记录;某轮 fix 推送在记录之前回合已结束且历史不完整;或 记录的 head/base 不是合并 head 的祖先)。squash 或 rebase 合并 (Issue #1480)没有M^2,两项指标同样为unknown。数不出来的历史 绝不写成0。
pushed_head,
Issue #219 文件)——交付推送、verdict 覆盖的 review/fix 推送、以及
回合开始时对上一 session 已推送但回合在 re-freeze 记录之前结束的
head 的采纳——同一 worktree 的接续都能恢复这份历史;全新的 worktree
从无记录开始,退化为 unknown。外部接管在 claim 时一次性记录接管的
起点 head(pushed_base),且其回合开始的采纳永不运行:worktree 本来
就始于贡献者的 head。记录本身是 bypass:state 文件缺失或损坏只记
pushed_head_unrecorded 日志,交付继续。
交付标签
GitHub 标签是外部状态机。它们不是 commit 创建的——每个仓库要初始化 (见快速开始):
仓库策略可能在 Orbi 的评审和检查都通过后仍阻塞合并。Orbi 交付 PR 后停止
在
ai-awaiting-merge;Issue 评论写出精确 head 和维护者要做的一件事(适用时
包含设置 URL)。维护者完成策略变更或给出所需批准后,下一 tick 恢复,仍由
Orbi 自己合并 PR。若 preflight 没有已知的 FAILED 动作,或是其他合并失败,
仍进入 ai-blocked。
人工验收门禁(ai-human-review)
产品主线是无人值守:默认流水线人在环外运行,且本门禁默认关闭——开关 关闭时交付、评审、merge、release 的行为与 Issue #763 之前逐字节一致。 host 配置打开门禁(orbi.toml 里 human_review_gate = true——这是部署
方的信任决策,不是仓库可写策略)。打开后每个交付在 PR 打开时向 Issue
发一条验收清单评论,分两栏:
- 栏一 · 本次 run 已验证的(证据,不是待办):PR head 的 CI 检查 状态(Issue #1613:引擎不再解析本地测试日志)、覆盖层(仓库测试套件)、 改动文件数。
- 栏二 · 机器验不了的最小集合,按每个交付的改动路径与可用证据生成 (不是硬编码的”人类专属”模板):业务意图(测试只证明实现与测试一致, 不证明测试与意图一致)、UI 视觉与文案、外部环境验证,以及 head 的检查 未通过或 diff 不可读时的证据缺失条目。
ai-ready(opened-PR 标签保留作 resume 锚),槽位释放,此后每个 tick
只花一次 label 读。等待不消耗评审轮次预算,也不碰 ai-fix-needed。
人确认清单后打 ai-human-review,下一 tick 照常评审与合并(verdict 绑定
与 --match-head-commit 零改动)。
栏二为空是目标状态:清单明确写「无需人工介入」,交付不要求该标签直接
通过。模型能力提升时,栏二条目迁进栏一——清单评论带机器可解析的
<!-- orbi:human-review {...} --> 块(schema 1),迁移可以做工具化,
工件形态不变。清单不声称任何正确性保证:栏一是已验证内容的证据,栏二是
人仍需确认的最小集合。
薄 ticket 闸门
对一个含糊的 ticket 自信交付,比不交付更糟:烧掉一次 run、开出一个没人 能接受的 PR,还把你真正要问的问题埋掉。闸门默认关闭,仓库用.github/orbi.toml 里的 clarify_thin_tickets = true 打开;未启用的仓库
行为与之前完全一致。
闸门打开时,一次 fresh claim 会先问一次 Pi session(--no-tools,无
worktree、无分支、不交付)判定 ticket 正文的三项检查:可观察结果
(用户会看到或做得了什么不同的)、验收条件(怎么区分完成与未完成)、
单一结果(一个 fix 或一个 change,而不是几件互不相关的事)。判定通过
则一切不变,交付照常开始。
判定不通过则在任何工作存在之前中止本次尝试:只发一条评论列出全部缺失项
与修复动作,把该仓库的认领标签(默认 ai-ready,可经 dispatch_label 改名)
换成 ai-needs-detail,本 tick 结束——无
worktree、无分支、无 Pi session、无 PR,槽位留给下一个 Issue,该票与
ai-blocked 一样等人。重新编辑 Issue 并加上该认领标签才获得下一次机会,
届时判定从头再跑一遍。
所有错误路径都是旁路——闸门绝不会弄丢一次交付。模型报错或超时、答案不可
用、评论或标签写入失败,都只记 clarify_check_skipped journal 行,交付照常继续。
判定与中止分别对应 Operations 里的
clarify_check_skipped 和 clarify_needs_detail 行。
票派发:content、dev、ops
ai-ready 是所有任务类型的执行开关;第二个标签选择类型
(Issue #537)。共三类——派发时每个 Issue 选定其一:
ops 语义(Issue #537):
- 信任边界是(凭据,票面)——不是命令白名单。 会话原样获得 runner
的全部环境(gh 凭据、
CLOUDFLARE_API_TOKEN、AWS keys 等,外加 deploy home 的.orbi/env文件);凭据给多宽是用户的决定,runner 不做二次裁剪。 - 票面即授权。 会话只执行票面写明的 ops 动作(合并哪个 PR、部署 哪个环境);票面之外的动作回帖说明并停。人工审批门(environment approval、Release 批准)不由会话代行——这是提示词约定,不是执法代码。
- 交付 = 证据(Issue #526 的事实文化):每一步把真实命令与真实
输出 / API 返回原文贴到票上;未执行的步骤如实标注未执行。纯 ops 运行
(无 commit)以 Runner 关闭 Issue 并摘除
ai-in-progress收尾—— 无 PR。附带代码 commit 则照常走 PR 路径(审查 + 合并)。
ai-ready + ai-ops-only。
dev 票带不动这个指令:dev 的交付判据是 task branch 上有新提交,
照办不提交的 agent 会以 the agent delivered no commit 失败,票被
烧成 ai-blocked。ops 剧本的证据纪律(真实命令、原文输出、未验证项
如实标注)就是调研交付物本身;无 commit 的 ops 运行正常收尾(关票、
摘除 ai-in-progress),调研后继续落地的代码仍走正常 PR 流程。
Issue #537 之前,ai-ops-only 标签派发的是内容代理(#530 只改了名,
模式未变——名字承诺 ops,模式仍是纯内容);现在 content 路径派发到
ai-content-only,ai-ops-only 是完整执行的 ops 路径。
Run marker 与 run_id
每个任务 attempt 生成一个run_id(8 位 hex,例如 e07383c2),该
attempt 的每一步复用同一个值;同一 Issue 的 retry 生成新的。同一个 id
出现在:
- 该 attempt 的每条 journal 行(前缀
[e07383c2]); - Issue/PR 评论:可见字段
run_id=e07383c2+ 隐藏机器可读 marker<!-- orbi:run=e07383c2 -->; - 里程碑与场景评论还携带隐藏的 runner marker
<!-- runner=1a2b3c4d -->: 写出评论的部署指纹——执行 checkout 的 HEAD;非 editable 安装为包版本; 探测失败为unknown(live progress 评论与 PR 评论不带它); Orbi opened PR场景评论携带隐藏的带版本 run 现场<!-- orbi:scene:v1 {json} -->(src/orbi/scene.py):这是 resume 扫描解析的机器可读恢复记录——现场损坏与现场缺失是两个不同的失败 分支(Issue #786);- 稳定的按 Issue 命名的 feature branch(
orbi/<owner>-<repo>-issue-<N>); - worktree 名(
.worktrees/orbi-<owner>-<repo>-issue-<n>-e07383c2),以及 Pi session 目录和 run 产物; - PR body——稳定 marker
<!-- orbi:run=e07383c2 -->是 PR 契约 的一部分;Runner 拒绝没有它的 PR。
失败记录:orbi:failure:v1
引擎通过失败通道上报的每一次失败都是机器可读的:详细的
Orbi: blocked / Orbi: fix needed 评论与其旁的短 milestone 都在
run marker 旁携带恰好一个隐藏的带版本 block:
outcome 为 blocked(终态,由人决策)或 fix_needed(下一 tick 恢复
同一个 run)。reason_code 说明交付为何停止;action_code 说明读者该
做什么;retry_safe 表示仅重新入队能否在没有人工修复的情况下成功。两个
集合都是封闭的、由引擎拥有(src/orbi/failure.py);分类器无法识别的
异常映射为 unclassified,block 永不省略;在本功能之前写出的评论完全
没有 block——读取方会忽略它(parse 返回 None)。
动作:
requeue 给 Issue 重新加 ai-ready(或点击重试)——无需修复;
fix_ticket 修复失败并重跑该 Issue;decide 需要人工决策;
check_credentials 检查并修复凭据后重试;wait_quota 等配额窗口重置后
重试。人类可读的标题、**Reason:** 与 **Action:** 行与 block 并列;
milestone 的可见摘要就是同一条有长度上限、不含本机路径的理由——完整的
命令 stderr 绝不会被截成第一个词。
PR body 契约:Fixes #N
PR 描述必须包含 Fixes #<issue-number>(可以放在首行)。GitHub 读的是
body(不是 PR title),PR merge 到默认分支时原生关闭 Issue。Runner 在
PR 验收时校验该关键词,缺失即 fail fast。这个校验失败会记录在对应 Issue
上,不会让 runner tick 进程退出。
分阶段计划不能用一张 Issue 和一个 PR 承载多个独立 runtime outcome。应使用
Epic 或父 Issue 做协调,把每个阶段拆成子 Issue;每张子票各自有一个交付 PR,
并写入对应的 Fixes #<子票号>。这样既保留 GitHub 原生关票,也不需要新增第二
种 PR 正文语法。如果现有 PR 写的是 Part of #N,维护者应在该 PR 已经完整
交付一个子票结果时改成精确的 Fixes #N,或者先把剩余阶段拆成子票再重试。
Epic、Release task 与 P0 优先级
项目用普通 GitHub 原语组织多任务工作——没有 DAG、没有优先级数字、没有 独立队列:-
Epic:协调 Issue,把一组相关任务归在一起(例如 v0.1 release
checklist)。它带
ai-epic标签。Epic 本身不是可直接执行的任务: 实际工作拆成独立的ai-readyIssue,每个一个 runtime outcome、一个 PR、一次 review、一次 merge。Runner 从不领取 Epic(Issue #93): 普通领取扫描跳过它并记录结构化日志epic_not_claimed issue=N repo=...(不领取、不改标签、不建 worktree、 不启动 run、不占 slot),重启恢复扫描也排除它。Epic 的子票声明唯一 来源是 GitHub 原生sub-issues关系:所有子 Issue 必须 CLOSED(或其 PR 已 merged),且没有 open 的原生blockedBy。正文中的引用(包括Children/Child Issues段落)不构成声明。没有 sub-issues 或子票、依赖 状态畸形时 fail-closed;Runner 每个 tick 重试并留下幂等审计评论。随后 同一个 tick reconcile open Milestone:title 精确匹配的已发布 Release (draft 不算)且当前没有 open Issue 才关闭,其余情况保持 open。最后 同一个 tick 还会扫一遍 open PR(Issue #746):head 为稳定交付命名 (orbi/<repo>-issue-<N>)且源票已被人工关闭的 PR,会在 PR 上收到 一条幂等的报告评论——所有 resumable 扫描都只查state=open的票,没有 这个扫描该 PR 会永久悬空。合并/关闭由人决定:Runner 绝不自动合, 也绝不越过人的关闭决定去自动关。 -
Release task:带
ai-release标签的ai-readyIssue(Issue #98)。它不是开发任务。三个普通扫描会记录release_not_claimed并跳过它;只有 release fallback 扫描使用allow_release=True。在领取前,_pick_from_scan会执行 Milestone 完整性门禁:只要所属 Milestone 还有 其他 open Issue(open_issues > 1),Runner 就记录release_milestone_incomplete,让 release 保持ai-ready,并在之后的 tick 重试。这是可恢复的等待,永远不会变成ai-blocked。Milestone 查询 无法评估时也会跳过领取,并记录release_milestone_check_failed。该领取层 发生在process_issue和所有 release 状态机等待之前,因此排在 open ticket 后面的 release 没有wait_started,累积的release_waited_seconds为零。 领取后,process_issue才会把它路由到确定性的 release 状态机而不是run_pi——没有 Pi 会话、没有 PR。状态机幂等且可恢复(重启后恢复同一 run id/worktree),按顺序执行这些步骤:- 严格解析 Issue 正文的
## Release声明:version、base_branch、scope(#N列表)或scope_from_milestone, 以及可选的version_file(默认pyproject.toml,也可为package.json或none)。声明不携带任何测试契约 (Issue #569):测试验收就是 release commit 的 GitHub Actions CI 结果(#268 CI-wait 门)。字段缺失、重复或未知 都 fail fast。 - 冻结 base——release commit 就是
origin/<base_branch>(在 base-sync 锁下 fetch)。在任何门禁等待之前,校验声明(或默认) 的version_file确实存在于该冻结树的根目录(Issue #740): 不匹配在认领瞬间 fail fast,错误信息列出根目录实际存在的受支持 文件和none选项,不会在烧完门禁和范围核验后才以 FileNotFoundError 失败。 - 执行发布前门禁。只有上述领取层允许领取后,门禁层的
release_waiting_deliveries才可能等待带有ai-in-progress、ai-pr-opened或ai-fix-needed的 open Issue(release Issue 本身 除外)。release 有 Milestone 时,只有同一 Milestone 内的 Issue 会阻塞 门禁(Issue #671);没有 Milestone 时仍按全仓扫描。只有ai-ready的 Issue 不会阻塞该门禁。这个有界等待使用RELEASE_DELIVERIES_WAIT_SECONDS;超时就是 delivery wait timeout, release 进入ai-blocked。随后要求 release commit 的 CI 全绿 (no check runs passes 带明确证据; pending check 是中间态——门禁等待最终结论,每次轮询一条release_waiting_cijournal 行和一次进度评论更新,上限release_ci_wait_seconds(默认 1800 秒);等待超时以独立原因 失败,绝不报成 CI 失败)。open PR 刻意不是门禁(Issue #608, 维护者裁决):open PR 是队列状态,不是发布前提——是否存在 open PR、多少个、谁提的,与发版无关;滞留 PR 由交付循环自身的 接管/对账机制处理。因真实gh失败而查不了的门禁算失败门禁。 - 逐项验证 scope:
gh pr view必须报告MERGED,否则gh issue view必须报告CLOSED且stateReason为COMPLETED——NOT_PLANNED关闭(重复/不做)的票在 Scope 证据里 标注为排除、绝不进入 Changelog,Changelog 的 PR 链接只在 PR 位于 带 tag 的 commit 区间内时才写入(Issue #1492)。Changelog 本身源自带 tag 的 commit 区间(prev_tag..release_commit),绝不源自 Milestone Issue 列表:Milestone scope 只决定能否发版,不决定 notes 内容。 对于 Milestone-derived scope,open_milestone_evidence不等待:仍 open 的 Milestone Issue 会被 排除,并记录为NOT released (still open in milestone <name>): <item>。 禁止解析复选框——每个 scope 项都对照 live API 检查。 - 测试验收就是 release commit 的 CI-wait 门:版本 bump 之后,门禁会对
该 commit 重跑(步骤 3 的
release_ci_wait_seconds等待),CI 红或 等待超时走既有可恢复失败路径(ai-blocked)。没有本地测试执行 (Issue #569)——release 流程即 bump → push → CI-wait → tag。 如果 CI 红或等待超时,既有失败路径会阻止发布;版本 bump 可以保留在main,这是可接受的终态。bump 是幂等的,修复 CI 后重试 release 会安全地再次准备所请求的版本。 - 打 tag:远端 tag 不存在时,在 release commit 上创建 annotated
tag 并普通 push(绝不
--force)。已存在时必须精确指向 release commit——不匹配 fail fast。已存在的 tag 绝不移动、覆盖或 force-push。 - 发布 GitHub Release(幂等),notes 带完整验证证据:version、
tag、release commit、逐项 scope 证据、门禁证据、测试证据和 run
marker。文档站 Release 页不由状态机写入(Issue #1483):仓库
自己的
.github/workflows/release-docs.yml(Issue #1482)在 Release 发布时用其 body 生成docs/release-<version>.mdx与docs/zh/release-<version>.mdx,把新版本插到docs/docs.json的Releases/发布导航分组首位(中英文两处,最新在前),并 转移(latest)标题标记。自 Issue #1096 起两个分组各嵌套一个 折叠的Earlier releases(历史版本)子分组——自动同步把新版本 插到可见页首位,并将超过最新三个版本的可见条目移入该子分组, 让可见分组始终保持最新三个版本。幂等:已有页面内容一致时不重复 生成、不覆盖,无改动时不提交;已有页面内容不一致则 fail fast—— workflow 运行失败,绝不静默跳过。 - 给 release Issue 打上
ai-merged并关闭它(交付终态转换);merged 事件同时清除ai-ready与ai-in-progress,发布后的 Issue 只剩ai-merged(Issue #1526)。 - 关闭 title 与发布 version 精确一致的 Milestone(Issue #214),
然后写成功评论(release URL、tag、commit、证据、Milestone
证据)。只按 title 精确匹配(不猜测、不模糊匹配、绝不关别的
Milestone):0 个 open Issue 时关闭;已关闭
是幂等成功(不重开);Milestone 缺失或仍有 open Issue 则 fail
fast,带上 version、Milestone 编号/链接和 open issue 列表(绝不
静默跳过)。release 票自己永不算未完成工作(Issue #808):它正被
本次发版关闭,issue 索引落后关票几秒(Issue #754)造成的
「只剩 release 票自己」的陈旧读既不阻止也不拖延关闭;其余任何
open Issue 仍然拒绝关闭。
任何失败:单独进入
ai-blocked(release 是人工决策 点——不自动重试),失败评论带 run marker 和具体原因,异常向上传 播让 tick fail fast。
ai-release标签是类型标记,不是交付状态:它不属于交付状态机、 不是 ready 扫描的排除条件,Runner 从不增删它——只有人在派发时 (与ai-ready一起)添加。 - 严格解析 Issue 正文的
-
P0 优先级:普通
p0GitHub 标签标记紧急 Issue(生产故障)。它 不是交付状态——只决定 ready 领取顺序,从不改变 Issue 粒度、任何 交付状态或终态语义,Runner 也从不增删它。ready 领取顺序固定:ai-ready+p0→ai-ready+bug→ 普通ai-ready(三次gh issue list扫描,共享完全相同的排除条件和 blockedBy 语义)。 P0 遵守所有现有排除规则和单 slot 约束:被阻塞的 P0 被跳过(回退到 bug/普通扫描),在途的 P0 由重启扫描接回。P0 运行失败单独进入ai-blocked(领取标签被移除;ai-ready残留被所有 ready 扫描排除), 所以没有任何 tick 重新领取——没有无限重试。没有优先级数字或加权队列。
blockedBy 关系(gh issue edit N --add-blocked-by M);Runner 读该字段并跳过有未关闭 blocker 的 Issue。
Issue body 里的 Depends on #N 行不被解析。
仓库级交付策略(.github/orbi.toml)
仓库可以把交付策略作为代码版本化。claim 时 Runner 通过 GitHub
contents API 读取仓库默认分支 tip 上的 .github/orbi.toml——
绝不读任务分支、绝不读本地 worktree——并按按键分域把白名单键应用
到宿主 orbi.toml 之上。没有该文件的仓库行为与之前完全一致(零变化)。
位置固定,只有文件名可用 [[repositories]] 条目的可选字段
config_path(默认 .github/orbi.toml)覆盖。
只允许交付策略键:
active_milestone 时,它胜过宿主
配置,也就是 claim 扫描使用的值。orbi milestone set <title> 把新值
写到同一个地方——仓库策略声明该键时写策略(对默认分支做 contents
PUT),否则写宿主 orbi.toml 的那一行——并打印 wrote: 指明目标
(Issue #1306)。
host-only 身份/安全键(见
安全)、坏 TOML 或类型错误都会让 claim fail-fast 并
在错误里列出违规键,Issue 进入 ai-blocked——一个仓库的坏文件绝不
影响其他池。其余不认识的键则被忽略:journal 记一行
repo_config_keys_ignored 指明该键与引擎版本,run 评论带
repo_config_ignored,claim 用已知键继续(Issue #1329)。该文件由交付
宿主实际运行的引擎版本读取,而它往往比写出该文件的代码更旧,所以新策略
键必须能被旧引擎安全忽略:它默认保持功能加入前的行为,不认识它的引擎照
常交付。host-only 集合同样包括宿主调度:max_concurrency
永远不能下放到仓库文件——并发 slot 池属于部署宿主。
dispatch_label 不得是交付生命周期标签
(ai-in-progress、ai-merged 等):那些恒为宿主常量。阻塞落在
claim 选中的那个 Issue 上;dispatch_label 不是 ai-ready 的仓库在
文件本身坏掉时没有 ai-ready 候选 Issue 可阻塞,此时 fail-fast 原因
只在 journal 的 repo_config_invalid 行(不改标签、不发评论)。run 评论记录
repo_config: <sha>;与上一 run 的 sha 不同时附 repo_config_changed
标注,且当前一版内容可读时附有效键值 diff repo_config_diff,让变更
始终在 run 上可见。传输原因(限流、网络)导致的读取失败只记一行
repo_config_read_failed 并回退宿主配置——该降级在 run 评论上表现为
缺少 repo_config 字段。
等待 Milestone 决策时的唯一回复
整条链路安静下来时,Runner 会说明为什么,而不是静默等待。两种空闲状态 通过同一个决策通知呈现——一张带有指纹orbi-milestone-advance old=<active> candidates=<titles> 的 Issue——两者
都由票内同一条命令回答:在该通知上以受信任评论形式发出的
/milestone <版本>:
两种通知都是幂等的(每种等待只有一张 open 通知,绝不是每 tick 一张),
等待结束后自动关闭并留下回执,而且都不会自行行动:Runner 绝不自己创建
release 票,也绝不在没有回复的情况下推进 Milestone。回复执行的是确定性
三步命令
/milestone(milestone → release ticket →
active_milestone),详见 Operations;不涉及任何模型调用。