@seanyao/roll 2026.514.1 → 2026.514.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,136 +1,139 @@
1
1
  # Changelog
2
2
 
3
- ## Unreleased
4
- - **Fixed**: dashboard 不再在已发版后还显示 "✓ Release ready"——只有 tag 之后存在非 docs/chore 的可发版提交时才亮,光是文档改写不会再让它误报
5
- - **Fixed**: dashboard 上 "📋 N PROPOSAL" 的动作提示改成 `see: PROPOSALS.md`,原先指向的 `roll backlog` 只看 BACKLOG.md,点过去看不到 PROPOSAL,逻辑断头
6
- - **Added**: changelog 生成时自动挡掉技术黑话
7
- - **Added**: 写 changelog 时会先参考最近几次发布的风格
8
- - **Added**: 故事跑完自动开 PR + auto-merge,CI 绿就合
9
- - **Added**: 完成的故事不再直接 push main,留下 PR 作为审计入口
10
- - **Added**: AI 代码评审可以批准 / 打回 PR — 配合 CI 形成合并双门;紧急 hotfix 在 PR body 加 `[skip-ai-review]` 即可绕开
11
- - **Added**: changelog 现在自审挡黑话
3
+ ## v2026.514.3
4
+
5
+ ### 约定与导航
6
+
7
+ - `$roll-design` 澄清需求前先自己定位产品端和业务域,问你的问题少了
8
+ - `$roll-doc` 为已有项目生成 AGENTS.md 导航骨架 新接入 Roll 不再从空白出发
9
+
10
+ ### 自动化流水线
11
+
12
+ - loop 每轮先消化开放 PR 再领新 backlog — 把队列里的 PR 当成首类工作,不是绕开的障碍 `[loop]`
13
+ - 自己开的 `loop/*` 分支不会被自己评审,避免同源 bias `[loop]`
14
+ - 24 小时内 rebase 同一个 PR 超过 3 次自动熔断,workflow 文件出错时不再无限循环 `[loop]`
15
+ - 每次 session 收尾自动删掉自己推上去的 `claude/*` 临时分支,远端不再积压"看起来要发 PR 实际不会发"的孤儿分支 `[loop]`
16
+
17
+ ## v2026.514.2
18
+
19
+ ### 自动化流水线
20
+
21
+ - 故事跑完自动开 PR,CI 过了就合入主分支 — 你不需要盯着,审计记录也完整保留 `[loop]`
22
+ - AI 评审现在有实权:可以批准或打回 PR,配合 CI 形成双重把关;真的很急可以在 PR 描述里加 `[skip-ai-review]` 临时绕过 `[loop]`
23
+ - 散落的 session 分支自动清理,远端仓库不再越来越乱 `[loop]`
24
+
25
+ ### Changelog 开始管自己
26
+
27
+ - 生成时自动过滤技术黑话,并对照历史风格保持表达一致 `[loop]`
28
+ - 写入前有一道自审:行文不达标就退回重写,不进 changelog `[loop]`
29
+ - 历史版本全部重新整理:按主题分组、合并同类项、附 `[loop]` / `[dream]` 归因标记
30
+ - Release Notes 生成规范写入 Skill:分组规则、条目合并、归因标签、措辞原则
31
+
32
+ ### 可见性
33
+
34
+ - Peer review 协商现在对所有 agent 实时可见,不再只是 claude 专属
35
+ - "Release ready" 只有真的有东西可发时才会亮,纯文档改动不再误报 `[loop]`
36
+ - PROPOSAL 的提示指向了实际有内容的地方 `[loop]`
37
+
38
+ ### 其他
39
+
40
+ - 纯文档改动直接合 main,不等 CI,合并更快
41
+ - README 新增 Evolution 章节,梳理 Roll 从工具到自主交付系统的演进脉络
12
42
 
13
43
  ## v2026.513.1
14
- - **Added**: loop 现在每轮跑在独立的 worktree 里,结束自动合回 main 并清理;跑挂时保留现场目录方便排查 — 再也不会吞掉 main 上你正在改的代码
15
- - **Added**: BACKLOG 上的 `depends-on:` 和 `manual-only:true` 标签从此有强制力,loop 选 story 前会过一遍,依赖没满足或标 manual-only 的直接跳过,不用人盯
16
- - **Fixed**: loop 多了一道并发保护 — 偶发的同一个 runner 下两个 claude 同时改状态的情况彻底堵住
17
- - **Changed**: CI unit 和 integration 测试拆到两个 runner 并行跑,墙钟时间显著下降
18
- - **Changed**: `roll`(不带参数)的 dashboard 重新设计 AI 在自动跑什么放第一眼,自治三层 + 四道防线 + Pipeline 全景 + 当前焦点 + 介入区一屏看完,不用再去 `roll loop status` 子命令翻
19
- - **Changed**: 测试运行器自动按机器核数并行,不再写死 4 个 job
20
- - **Changed**: 纯文档改动不再触发 CI 跑全套测试,文档 PR 合并节奏更快
21
- - **Fixed**: `roll peer` 在 REFINE / OBJECT 收尾时偶发的 "unbound variable" 崩溃
22
- - **Fixed**: `roll update` 后 `roll loop status` 不再误报 off,不用手动 `roll loop on` 恢复
44
+
45
+ ### Loop 更可靠了
46
+
47
+ - 每轮 story 在独立工作区里跑,完了自动合回来清理;跑挂时现场保留,不会碰主分支上你正在改的东西 `[loop]`
48
+ - `depends-on:` `manual-only:` 标签现在真的有用Loop 自己会跳过条件没满足的任务,不用再盯着 `[loop]`
49
+ - 同时只有一个 Loop 在跑,并发写入的问题修掉了 `[loop]`
50
+ - `roll update` 之后 loop 状态不再误报 off `[loop]`
51
+
52
+ ### 测试和 CI
53
+
54
+ - Unit 和集成测试并行执行,机器有多少核就用多少,等待时间降下来了 `[dream]`
55
+ - 纯文档改动不触发 CI 全套测试,合并更快 `[dream]`
56
+
57
+ ### Dashboard
58
+
59
+ - 重新设计:三层自治状态 + 四道防线 + Pipeline 全景 + 当前焦点 + 介入区,一屏看完 AI 正在做什么 `[loop]`
60
+
61
+ ### 修复
62
+
63
+ - `roll peer` 协商退出时偶发的崩溃修掉了 `[loop]`
23
64
 
24
65
  ## v2026.512.8
25
- - **Added**: `$roll-doc` — 扫描任意项目的文档现状,找出缺口并生成草稿,支持 `--dry-run`
26
- - **Added**: `roll-.dream` Scan 6 — 每晚检测文档是否跟代码脱节,发现问题自动写进重构待办
27
- - **Fixed**: 某些 SSH 配置下 loop 把"CI 查询失败"误判为"CI 工具未安装",导致测试没过就标任务完成
28
-
29
- ## v2026.512.7
30
- - **Added**: `roll alert` — 查看、确认、清除 loop 告警,不用再去翻 loop status
31
- - **Added**: macOS 系统通知 story 完成或告警写入时自动弹通知,静音模式下不打扰
32
- - **Added**: `roll ci [--wait]` — 查看当前提交的 CI 状态,或等待 CI 跑完再继续
33
- - **Fixed**: loop 现在会等 CI 通过后才标记故事完成,CI 失败则保持进行中并发出提醒
34
- - **Fixed**: changelog 更新不再产生独立 commit,并入故事完成提交,git log 更干净
35
- - **Added**: `docs/domain/` — Roll 架构领域模型文档
36
- - **Fixed**: `roll loop runs` 不再报"当前项目尚无运行记录",历史记录正常显示
37
- - **Added**: 文档目录重组 散落文档统一迁移至 `docs/guide/` `docs/practices/`
38
- - **Added**: README 精简,新增文档导航索引
39
- - **Added**: dream 每晚自动检测文档缺口,brief 新增文档覆盖率数字
40
-
41
- ## v2026.512.6
42
- - **Added**: peer review 现在也会自动弹出终端窗口,实时观察跨 AI 协商过程(mute 关闭同一开关)
43
- - **Added**: `docs/guide/en/` — loop/dream/peer 英文用户指南上线,覆盖所有子命令和使用场景
44
- - **Added**: `docs/guide/zh/` loop/dream/peer 中文用户指南上线,内容与英文版语义一致
45
-
46
- ## v2026.512.5
47
- - **Fixed**: loop 遇到 API 错误时自动重试,不再直接中断
48
-
49
- ## v2026.512.3
50
- - **Added**: BACKLOG 支持 block / defer / unblock 状态管理 — 标记卡住的任务不再占队列
51
- - **Fixed**: 自动弹窗现在能识别 Ghostty 和 iTerm2,不再强制弹出 Terminal.app
52
- - **Fixed**: loop 检测到上一轮还在跑时自动跳过,不重复启动
53
-
54
- ## v2026.512.1
55
- - **Added**: `roll loop pause` / `roll loop resume` — 想自己上手时一键暂停 loop,做完再恢复
56
- - **Added**: `roll status` 新增所有项目的 loop 状态一览 — 调度时间、待办数、是否在跑
57
- - **Fixed**: `roll loop attach` 不再黑屏,AI 干活过程实时可见
58
-
59
- ## v2026.511.7
60
- - **Added**: loop 跑起来时自动弹出一个终端窗口,看 AI 实时干活
61
- - **Added**: `roll loop mute` 关掉自动弹窗,`roll loop unmute` 恢复
62
- - **Added**: `roll loop runs` — 查看 loop 最近几次都跑了什么
63
- - **Added**: `roll loop attach` 随时接入正在跑的 loop 现场围观
64
- - **Added**: BACKLOG 任务执行中会实时显示 🔨 进度,不用等做完才知道
65
- - **Added**: `roll setup` 自动安装 tmux(macOS 通过 brew)
66
- - **Improved**: 代码巡检(dream)报告改为中文输出
67
- - **Fixed**: loop 在某些情况下完成后不正常退出
68
- - **Fixed**: loop 中途崩溃后下次启动会自动清理残留状态
69
-
70
- ## v2026.511.6
71
- - **Fixed**: 多个 loop 实例不会再因为定时重复触发而互相打架
72
-
73
- ## v2026.511.5
74
- - **Fixed**: 升级 roll 后 loop 服务自动生效,无需手动重启
75
- - **Improved**: `roll loop status` 三态显示,看得清是真没装、装了没启、还是在跑
76
-
77
- ## v2026.511.4
78
- - **Fixed**: 升级 roll 后 `roll init` 自动迁移 loop 配置,少一步手动操作
79
-
80
- ## v2026.511.3
81
- - **Fixed**: 多个项目同时跑 loop,互不干扰
82
- - **Fixed**: 在 roll 项目里运行 `roll release` 会提示改用 `scripts/release.sh`
83
-
84
- ## v2026.511.2
85
- - **Added**: `roll loop monitor` — 一屏看 loop/dream/brief 三个调度服务状态
86
- - **Fixed**: dashboard 待办数、release 状态显示问题
87
- - **Fixed**: loop 异常退出后队列卡住不再继续执行
88
- - **Improved**: 简报输出更精简,去掉空白段落和冗余信息
89
-
90
- ## v2026.511.1
91
- - **Changed**: macOS 上 loop 调度切换到 launchd,比 crontab 更稳定
92
- - **Added**: agent 跳过 TCR 节奏时自动拦回 Todo,强制重做
66
+
67
+ ### 掌控感更强了
68
+
69
+ Loop 已经能跑、能看了——这一批让你对它有更多控制权:
70
+
71
+ - `roll loop pause` / `roll loop resume` — 想自己上手时一键暂停,做完再让 AI 接着跑 `[loop]`
72
+ - `roll alert`集中查看、确认、清除 loop 产生的告警,不用翻 loop status `[loop]`
73
+ - macOS 系统通知 story 完成或有新告警时自动弹通知,静音模式下不打扰 `[loop]`
74
+ - `roll ci [--wait]` — 查看当前 CI 状态,或等 CI 跑完再继续手头的事
75
+
76
+ ### Loop 更聪明了
77
+
78
+ - Loop 现在等 CI 通过后才标 story 完成,CI 红了会保持进行中并发出提醒 `[loop]`
79
+ - API 出错时自动重试,不再直接中断 `[loop]`
80
+ - 弹窗认识 Ghostty iTerm2 了,不再强制弹 Terminal.app `[loop]`
81
+ - BACKLOG 支持 `block` / `defer` — 卡住的任务标一下就不占队列了
82
+
83
+ ### 文档体系上线
84
+
85
+ - loop / dream / peer 中英文用户指南全部上线,覆盖所有子命令和使用场景 `[dream]`
86
+ - `$roll-doc` — 扫描任意项目的文档现状,找出缺口并生成草稿 `[dream]`
87
+ - Dream 每晚检测文档是否跟代码脱节,发现问题写进重构待办 `[dream]`
88
+ - `roll status` 新增跨项目 loop 状态一览
89
+
90
+ ## v2026.511.8
91
+
92
+ ### 终于能看到 Loop 在做什么了
93
+
94
+ Loop 上线后最大的感受是「不知道它在干啥」——这一批更新专门解决这个问题:
95
+
96
+ - Loop 开跑时自动弹出终端窗口,AI 干活的过程实时可见 `[loop]`
97
+ - `roll loop attach` 随时接入正在跑的 loop 现场 `[loop]`
98
+ - `roll loop runs` 查看 loop 最近几次跑了什么、完成了哪些 `[loop]`
99
+ - BACKLOG 任务执行中实时显示进度标记,不用等做完才知道 `[loop]`
100
+ - 不想被打扰时,`roll loop mute` 关掉弹窗,`roll loop unmute` 恢复
101
+
102
+ ### Loop 更稳了
103
+
104
+ - macOS 上调度从 crontab 换成 launchd,重启后不丢 `[loop]`
105
+ - 升级 roll loop 服务自动生效,不用手动重启 `[loop]`
106
+ - 多个项目同时跑 loop,互不干扰 `[loop]`
107
+ - 崩溃或异常退出后,下次启动自动清理残留状态 `[loop]`
108
+ - 并发触发时自动跳过,不重复执行 `[loop]`
109
+
110
+ ### 其他改进
111
+
112
+ - `roll loop monitor` — 一屏查看 loop / dream / brief 三个服务状态
113
+ - `roll loop status` 三态显示:没装 / 装了没启 / 正在跑,一眼看清
114
+ - `roll init` 升级后自动迁移 loop 配置,少一步手动操作
93
115
 
94
116
  ## v2026.510.10
95
- - **Fixed**: release.sh changelog 同步时序修复 — 修正条件逻辑和执行顺序,确保每次发版时 changelog 正确更新
96
- - **Added**: roll-loop 22:00 自动执行验证 — 新增 hello_world.bats 作为 loop 定时执行的端到端存档,可回放确认调度器正常工作
97
-
98
- ## v2026.510.9
99
- - **Fixed**: CHANGELOG 改版本号分组 — 每个 release 独立 section,GitHub Release 增量内容提取正确
100
- - **Fixed**: release.yml fetch-depth: 0,确保历史 tag workflow 中可见
101
-
102
- ## v2026.510.8
103
- - **Fixed**: release.sh 自洽 — 发版流程不依赖外部调用
104
- - **Fixed**: `roll release` 改为独立调用 roll-release skill,与 release.sh 解耦
105
- - **Fixed**: GitHub Release workflow 加 fetch-depth: 0,确保历史 tag 可见
106
-
107
- ## v2026.510.7
108
- - **Fixed**: 版本比较改用 sort -V,防止旧版本号被误判为最新
109
- - **Fixed**: 版本更新后主动清缓存 — 检测到运行版本更新时自动清缓存,避免旧版本幽灵提示
110
- - **Fixed**: Kimi CLI 解析修复 — 剥离 YAML frontmatter 中的 `---` 分隔符,避免调用时解析崩溃
111
- - **Fixed**: dashboard 命令列表补全,`roll --help` 现在展示全部可用命令
112
- - **Added**: `roll release` 命令补全到 usage() 帮助中
113
-
114
- ## v2026.510.6
115
- - **Fixed**: Agent 调用层统一 — 移除所有 claude -p 硬编码,统一 agent 抽象层,支持 Claude/Kimi/Pi/Codex/OpenCode
116
- - **Fixed**: pi/codex/opencode 支持补全 — _agent_run_skill/_peer_call 穷举所有 agent,未知 agent 给明确错误
117
- - **Fixed**: kimi 非交互调用语法修正为 kimi --quiet -p,经实测验证
118
- - **Fixed**: roll-build / roll-fix Phase 12 强制触发 roll-.changelog,确保 CHANGELOG 与 BACKLOG 同步
119
- - **Added**: roll release 命令 — 在 roll CLI 内直接调用 roll-release skill
120
- - **Added**: GitHub Release 自动创建 — tag push 后由 workflow 从 CHANGELOG diff 提取内容
121
-
122
- ## v2026.510.5
123
- - **Added**: roll-loop — BACKLOG 自主执行器,支持调度、跨 Agent 路由和失败处理,让 AI 自主推进项目任务
124
- - **Added**: roll-brief — Feature完成汇报、每日晨报、按需简报,一句话掌握项目状态和发布就绪情况
125
- - **Added**: roll-.dream — 每晚代码架构健康巡检,自动产出 REFACTOR 条目,架构问题持续浮出水面
126
- - **Added**: roll-build 架构摩擦信号 — 实现遇阻时自动向 BACKLOG 写入 REFACTOR 标记,技术债持续可见
127
- - **Added**: E2E 自动沉淀 — 每个 Story 交付时自动写一个端到端测试,项目逐步积累可回放的 E2E 套件
128
- - **Added**: CI E2E 门禁 — 模板 CI 每次推送自动跑 E2E 测试,没有 E2E 时静默跳过不阻塞
129
- - **Added**: CI 红灯分诊 — 按严重程度分类 CI 失败,自动路由到 backlog 变成可执行的修复项
130
- - **Added**: roll-debug 自动修复 — 诊断后若根因在项目源码内,自动进入 TCR 修复流程并回验
131
- - **Added**: Changelog 自动生成 — 每次部署后自动更新,首次运行时回填全部历史记录
132
- - **Added**: roll status/update 显示最近更新 — 运行 `roll status` 或 `roll update` 时展示最近 3 个版本的 changelog
133
- - **Fixed**: roll-release 补齐 GitHub Release 创建步骤 — 修复版本更新提醒从不生效的问题
117
+
118
+ ### Loop、Dream、Brief 首次亮相
119
+
120
+ Roll 从「技能编排工具」变成了「自主执行系统」——三个新组件同步上线:
121
+
122
+ - `roll loop` AI 自动调度 BACKLOG,Story 一个接一个跑,不用你盯着
123
+ - `roll brief` — 每天早上自动整理昨天做了什么,帮你快速恢复上下文
124
+ - `roll-.dream` — 每晚自动巡检代码和架构,把发现的问题写成可执行的待办
125
+
126
+ ### 自动化能力
127
+
128
+ - 每个 Story 交付时自动沉淀一个 E2E 测试,项目逐步积累可回放的验收套件 `[loop]`
129
+ - CI 失败会自动分诊,按严重程度路由成 backlog 里可执行的修复项 `[loop]`
130
+ - `roll debug` 诊断出根因在源码内时,自动进入修复流程并回验 `[loop]`
131
+ - Changelog 每次部署后自动更新,不再需要手动整理 `[loop]`
132
+
133
+ ### 可见性
134
+
135
+ - `roll status` / `roll update` 运行后展示最近几个版本的更新内容
136
+ - `roll release` 命令 — 在 CLI 里直接触发发布,不用记路径
134
137
 
135
138
  ## 2026.05.09
136
139
  - **Added**: roll-peer 跨 Agent 代码评审 — 支持 Claude Code、Kimi CLI、DeepSeek TUI、Codex CLI 多工具协同评审 (by @seanyao)
package/README.md CHANGED
@@ -7,7 +7,7 @@
7
7
  ╚═╝ ╚═╝ ╚═════╝ ╚══════╝╚══════╝
8
8
  ```
9
9
 
10
- > Roll out features with AI agents — _Move fast, no sprints._
10
+ > _Agents, roll out._
11
11
 
12
12
  **[中文版 README](README_CN.md)**
13
13
 
@@ -27,6 +27,16 @@ Roll is an autonomous delivery system for software teams — AI agents pick stor
27
27
 
28
28
  _Works with Claude, Cursor, Codex, or your own agent._
29
29
 
30
+ ## Evolution
31
+
32
+ Roll didn't start as a framework. It started as a question: *what if the AI didn't just write code, but actually shipped it?*
33
+
34
+ Early versions just pushed engineering conventions to whichever AI tool you were running. Then came multi-agent support — Kimi, DeepSeek, Codex, Trae — and `roll-peer`, which let one AI challenge another's decisions before anything landed on `main`.
35
+
36
+ The real shift was `roll loop`: stories running back-to-back without human prompting, `roll-.dream` filing its own refactor tickets after nightly scans, the system generating its own work queue. What followed was building enough trust to leave it running overnight — worktree isolation, CI + AI review double gates, real-time visibility into what the agent was actually doing.
37
+
38
+ The goal from here: full delivery, end to end — with humans on the loop, not in it.
39
+
30
40
  ---
31
41
 
32
42
  ## Quick Start (30 seconds)
package/bin/roll CHANGED
@@ -4,7 +4,7 @@ set -euo pipefail
4
4
  # Roll — AI Agent Convention Manager
5
5
  # Single source of truth for how all AI coding agents behave.
6
6
 
7
- VERSION="2026.514.1"
7
+ VERSION="2026.514.3"
8
8
  ROLL_HOME="${ROLL_HOME:-${HOME}/.roll}"
9
9
  ROLL_CONFIG="${ROLL_HOME}/config.yaml"
10
10
  ROLL_GLOBAL="${ROLL_HOME}/conventions/global"
@@ -1903,7 +1903,7 @@ _agent_run_skill() {
1903
1903
  [[ -f "$skill_file" ]] || { err "Skill not found: ${skill}"; return 1; }
1904
1904
  local content; content=$(_skill_content "$skill_file")
1905
1905
  case "$agent" in
1906
- claude) claude -p --output-format stream-json "$content" ;;
1906
+ claude) claude -p --output-format text "$content" ;;
1907
1907
  kimi) kimi --quiet -p "$content" ;;
1908
1908
  deepseek) deepseek "$content" ;;
1909
1909
  pi) pi -p "$content" ;;
@@ -2963,15 +2963,73 @@ _loop_precheck_ci() {
2963
2963
  **Reason**: HEAD CI is red — loop refused to build on a broken base HEAD CI 红,loop 拒绝在破损的基础上构建
2964
2964
 
2965
2965
  **Action required**:
2966
- - Investigate and fix CI: \`gh -R $(_gh_repo_slug) run list --commit ${commit}\`
2966
+ - Investigate and fix CI: \`gh -R ${slug} run list --commit ${commit}\`
2967
2967
  - After fixing and pushing green commit: \`roll loop now\`
2968
2968
  EOF
2969
+ _loop_diagnose_open_prs "$slug" >> "$_LOOP_ALERT"
2969
2970
  _notify "roll ⚠ CI red" "loop refused to build on broken base (${short})"
2970
2971
  return 1
2971
2972
  fi
2972
2973
  return 0
2973
2974
  }
2974
2975
 
2976
+ # _loop_diagnose_open_prs <slug>
2977
+ # Appended to ALERT when CI is red on HEAD.
2978
+ # For each open PR targeting main: lists CI failing tests + changed files,
2979
+ # flags whether failures look unrelated to the PR's own changes.
2980
+ _loop_diagnose_open_prs() {
2981
+ local slug="$1"
2982
+ local prs
2983
+ prs=$(gh -R "$slug" pr list --base main --state open --json number,title,headRefName \
2984
+ --jq '.[] | [.number|tostring, .headRefName, .title] | @tsv' 2>/dev/null) || return 0
2985
+ [[ -z "$prs" ]] && return 0
2986
+
2987
+ printf '\n## Open PRs (potential fixes)\n'
2988
+ while IFS=$'\t' read -r pr_num branch pr_title; do
2989
+ printf '\nPR #%s: %s\n' "$pr_num" "$pr_title"
2990
+
2991
+ # Files changed in this PR
2992
+ local changed_files
2993
+ changed_files=$(gh -R "$slug" pr diff "$pr_num" --name-only 2>/dev/null | head -10) || changed_files="(unable to fetch)"
2994
+ printf ' Changed: %s\n' "$(echo "$changed_files" | tr '\n' ' ')"
2995
+
2996
+ # Latest CI run on the PR branch
2997
+ local run_id conclusion
2998
+ run_id=$(gh -R "$slug" run list --branch "$branch" --json databaseId,conclusion \
2999
+ --jq '[.[] | select(.conclusion != null)] | first | .databaseId' 2>/dev/null) || run_id=""
3000
+ conclusion=$(gh -R "$slug" run list --branch "$branch" --json databaseId,conclusion \
3001
+ --jq '[.[] | select(.conclusion != null)] | first | .conclusion' 2>/dev/null) || conclusion="unknown"
3002
+
3003
+ if [[ "$conclusion" == "success" ]]; then
3004
+ printf ' CI: green — blocked only by branch protection (safe to merge)\n'
3005
+ printf ' Suggest: gh pr merge %s --admin\n' "$pr_num"
3006
+ elif [[ -n "$run_id" ]]; then
3007
+ local failing_tests
3008
+ failing_tests=$(gh -R "$slug" run view "$run_id" --log-failed 2>/dev/null \
3009
+ | grep -oP '(?<=not ok \d{1,4} ).*' | head -8) || failing_tests="(unable to fetch)"
3010
+
3011
+ printf ' CI: %s\n' "$conclusion"
3012
+ printf ' Failing tests:\n'
3013
+ while IFS= read -r t; do
3014
+ [[ -n "$t" ]] && printf ' - %s\n' "$t"
3015
+ done <<< "$failing_tests"
3016
+
3017
+ # Heuristic: if no failing test mentions a changed file, flag as likely unrelated
3018
+ local related=0
3019
+ while IFS= read -r f; do
3020
+ local base; base=$(basename "$f")
3021
+ echo "$failing_tests" | grep -qi "$base" && { related=1; break; }
3022
+ done <<< "$changed_files"
3023
+ if [[ "$related" -eq 0 ]]; then
3024
+ printf ' Note: failing tests appear UNRELATED to changed files — consider manual merge\n'
3025
+ printf ' Suggest: gh pr merge %s --admin (verify tests manually first)\n' "$pr_num"
3026
+ else
3027
+ printf ' Note: failing tests may relate to PR changes — investigate before merging\n'
3028
+ fi
3029
+ fi
3030
+ done <<< "$prs"
3031
+ }
3032
+
2975
3033
  # CI gate before marking a story Done.
2976
3034
  # On CI failure: writes ALERT, returns 1 (caller keeps story 🔨 In Progress).
2977
3035
  # When gh unavailable: returns 0 (graceful skip).
@@ -3101,6 +3159,219 @@ _loop_is_manual_only() {
3101
3159
  echo "$row" | grep -qE 'manual-only:true'
3102
3160
  }
3103
3161
 
3162
+ # US-AUTO-034: PR-first inbox — loop processes open PRs before scanning BACKLOG.
3163
+ #
3164
+ # Three building blocks, kept as pure / mockable functions:
3165
+ # _loop_pr_classify pure routing decision (no side effects)
3166
+ # _loop_pr_rebase_circuit 24h sliding-window circuit breaker on rebase retries
3167
+ # _loop_pr_inbox orchestrator that walks `gh pr list` and routes
3168
+ # each open PR to skip / review / rebase
3169
+ #
3170
+ # Design notes:
3171
+ # - gh missing or any gh failure → return 0 (lenient, like FIX-026's pre-check)
3172
+ # - self-authored loop/* PRs are skipped to avoid same-source AI review
3173
+ # - latest human review of CHANGES_REQUESTED or APPROVED blocks AI review
3174
+ # (Human-review-activity guard from US-AUTO-034 AC)
3175
+ # - rebase attempts ≥3 within 24h trip the circuit breaker (writes ALERT)
3176
+
3177
+ # _loop_pr_classify <head_ref> <human_review_state> <ci_state> <mergeable_state>
3178
+ # Prints one of:
3179
+ # loop_self
3180
+ # blocked_human_request_changes
3181
+ # blocked_human_approved
3182
+ # stale
3183
+ # eligible
3184
+ # Exit 0 always — callers parse the printed token.
3185
+ _loop_pr_classify() {
3186
+ local head_ref="${1:-}"
3187
+ local human_review="${2:-}"
3188
+ local ci_state="${3:-}"
3189
+ local mergeable="${4:-}"
3190
+
3191
+ case "$head_ref" in
3192
+ loop/*) echo "loop_self"; return 0 ;;
3193
+ esac
3194
+
3195
+ case "$human_review" in
3196
+ CHANGES_REQUESTED) echo "blocked_human_request_changes"; return 0 ;;
3197
+ APPROVED) echo "blocked_human_approved"; return 0 ;;
3198
+ esac
3199
+
3200
+ if [ "$ci_state" = "failure" ] || [ "$mergeable" = "CONFLICTING" ] || [ "$mergeable" = "BEHIND" ]; then
3201
+ echo "stale"
3202
+ return 0
3203
+ fi
3204
+
3205
+ echo "eligible"
3206
+ }
3207
+
3208
+ # _loop_pr_rebase_circuit <pr_number>
3209
+ # Side effect: appends current timestamp to $_LOOP_STATE under
3210
+ # pr_state.<PR>.attempts_at, pruning entries older than 24h.
3211
+ # Exit 0: attempt allowed and recorded.
3212
+ # Exit 1: ≥3 attempts within 24h → blocked; ALERT written.
3213
+ _loop_pr_rebase_circuit() {
3214
+ local pr="$1"
3215
+ [ -n "$pr" ] || return 1
3216
+
3217
+ local state="${_LOOP_STATE:-${HOME}/.shared/roll/loop/state.yaml}"
3218
+ local now; now=$(date -u +%s)
3219
+ local cutoff=$((now - 86400))
3220
+
3221
+ # Extract existing timestamps for this PR (empty if absent).
3222
+ local existing=""
3223
+ if [ -f "$state" ]; then
3224
+ existing=$(awk -v pr="\"$pr\":" '
3225
+ $0 ~ "pr_state:" {in_pr=1; next}
3226
+ in_pr && $0 ~ pr {in_target=1; next}
3227
+ in_target && $0 ~ /attempts_at:/ {
3228
+ sub(/^[^"]*"/, ""); sub(/".*$/, ""); print; exit
3229
+ }
3230
+ in_target && /^[^[:space:]]/ {in_target=0}
3231
+ ' "$state" 2>/dev/null)
3232
+ fi
3233
+
3234
+ # Prune stale timestamps (>24h ago).
3235
+ local fresh=""
3236
+ local ts
3237
+ for ts in $existing; do
3238
+ case "$ts" in
3239
+ ''|*[!0-9]*) continue ;;
3240
+ esac
3241
+ if [ "$ts" -ge "$cutoff" ]; then
3242
+ fresh="${fresh:+$fresh }$ts"
3243
+ fi
3244
+ done
3245
+
3246
+ # Count attempts within window; ≥3 means this would be the 4th retry blocked.
3247
+ local count=0
3248
+ for ts in $fresh; do count=$((count + 1)); done
3249
+
3250
+ if [ "$count" -ge 3 ]; then
3251
+ mkdir -p "$(dirname "${_LOOP_ALERT:-/dev/null}")" 2>/dev/null || true
3252
+ cat > "${_LOOP_ALERT}" <<EOF
3253
+ # ALERT — PR rebase circuit breaker tripped
3254
+
3255
+ **Time**: $(date '+%Y-%m-%d %H:%M')
3256
+ **PR**: #${pr}
3257
+ **Reason**: PR #${pr} rebased ${count}× within 24h without CI resolution — possible workflow file error PR rebase 多次未解决,可能是 workflow 文件错误
3258
+
3259
+ **Action required**:
3260
+ - Check PR CI logs and workflow files for breakage
3261
+ - Resolve manually, then: \`roll loop now\`
3262
+ EOF
3263
+ return 1
3264
+ fi
3265
+
3266
+ # Record this attempt and persist.
3267
+ fresh="${fresh:+$fresh }$now"
3268
+ _loop_pr_state_write "$pr" "$fresh" "$state"
3269
+ return 0
3270
+ }
3271
+
3272
+ # Internal: rewrite $state with pr_state.<pr>.attempts_at = "<fresh-ts-list>".
3273
+ # Minimal YAML writer — we own the schema and only need this one field family.
3274
+ _loop_pr_state_write() {
3275
+ local pr="$1"
3276
+ local attempts="$2"
3277
+ local state="$3"
3278
+
3279
+ mkdir -p "$(dirname "$state")" 2>/dev/null || true
3280
+ [ -f "$state" ] || : > "$state"
3281
+
3282
+ local tmp; tmp=$(mktemp)
3283
+ awk -v pr="\"$pr\":" -v attempts="$attempts" '
3284
+ BEGIN { in_pr=0; in_target=0; written=0 }
3285
+ /^pr_state:/ { in_pr=1; print; next }
3286
+ in_pr && $0 ~ pr {
3287
+ in_target=1
3288
+ print " " pr
3289
+ print " attempts_at: \"" attempts "\""
3290
+ written=1
3291
+ next
3292
+ }
3293
+ in_target && /attempts_at:/ { next } # skip old value, already written
3294
+ in_target && /^[^[:space:]]/ { in_target=0 }
3295
+ { print }
3296
+ END {
3297
+ if (!in_pr) {
3298
+ print "pr_state:"
3299
+ print " " pr
3300
+ print " attempts_at: \"" attempts "\""
3301
+ } else if (!written) {
3302
+ print " " pr
3303
+ print " attempts_at: \"" attempts "\""
3304
+ }
3305
+ }
3306
+ ' "$state" > "$tmp" && mv "$tmp" "$state"
3307
+ }
3308
+
3309
+ # _loop_pr_inbox
3310
+ # Walks open PRs and routes each by classification.
3311
+ # Lenient on gh unavailability — returns 0 so the loop continues to BACKLOG.
3312
+ _loop_pr_inbox() {
3313
+ command -v gh >/dev/null 2>&1 || return 0
3314
+
3315
+ local slug; slug=$(_gh_repo_slug 2>/dev/null) || return 0
3316
+ local prs_json
3317
+ prs_json=$(gh -R "$slug" pr list --state open \
3318
+ --json number,headRefName,author,title \
3319
+ 2>/dev/null) || return 0
3320
+ [ -n "$prs_json" ] || return 0
3321
+ [ "$prs_json" = "[]" ] && return 0
3322
+
3323
+ local count; count=$(echo "$prs_json" | jq 'length' 2>/dev/null || echo 0)
3324
+ [ "${count:-0}" -gt 0 ] || return 0
3325
+
3326
+ local i=0
3327
+ while [ "$i" -lt "$count" ]; do
3328
+ local num head_ref
3329
+ num=$(echo "$prs_json" | jq -r ".[$i].number")
3330
+ head_ref=$(echo "$prs_json" | jq -r ".[$i].headRefName")
3331
+
3332
+ # Fetch CI + review state for this PR.
3333
+ local view_json
3334
+ view_json=$(gh -R "$slug" pr view "$num" \
3335
+ --json reviews,mergeStateStatus,statusCheckRollup \
3336
+ 2>/dev/null) || { i=$((i + 1)); continue; }
3337
+
3338
+ local human_review ci_state mergeable
3339
+ human_review=$(echo "$view_json" | jq -r '
3340
+ [.reviews[]? | select(.authorAssociation != "BOT" and .authorAssociation != "APP")]
3341
+ | last // {} | .state // ""' 2>/dev/null)
3342
+ mergeable=$(echo "$view_json" | jq -r '.mergeStateStatus // ""' 2>/dev/null)
3343
+ ci_state=$(echo "$view_json" | jq -r '
3344
+ if (.statusCheckRollup | length) == 0 then ""
3345
+ elif any(.statusCheckRollup[]?; .conclusion == "FAILURE") then "failure"
3346
+ elif all(.statusCheckRollup[]?; .conclusion == "SUCCESS" or .conclusion == "SKIPPED") then "success"
3347
+ else "pending" end' 2>/dev/null)
3348
+
3349
+ local verdict
3350
+ verdict=$(_loop_pr_classify "$head_ref" "$human_review" "$ci_state" "$mergeable")
3351
+
3352
+ case "$verdict" in
3353
+ loop_self|blocked_human_request_changes|blocked_human_approved)
3354
+ : # skip — explained by verdict; nothing to do this cycle
3355
+ ;;
3356
+ stale)
3357
+ _loop_pr_rebase_circuit "$num" || true
3358
+ # Actual rebase work delegated to _loop_pr_rebase_stale when present
3359
+ # (kept out of this cycle to avoid touching git remote in tests).
3360
+ command -v _loop_pr_rebase_stale >/dev/null 2>&1 \
3361
+ && _loop_pr_rebase_stale "$num" "$head_ref" || true
3362
+ ;;
3363
+ eligible)
3364
+ # Hand off to review (US-AUTO-035 supplies the actual decision).
3365
+ command -v _loop_pr_review_external >/dev/null 2>&1 \
3366
+ && _loop_pr_review_external "$num" || true
3367
+ ;;
3368
+ esac
3369
+
3370
+ i=$((i + 1))
3371
+ done
3372
+ return 0
3373
+ }
3374
+
3104
3375
  # US-CL-004: changelog 风格守门 Phase 1 — mechanical linter.
3105
3376
  #
3106
3377
  # _changelog_lint_bullet <bullet-text>
@@ -3156,7 +3427,7 @@ _changelog_style_anchors() {
3156
3427
  /^## v/ { ver++; if (ver > 3) exit; printing = 1; next }
3157
3428
  /^## / { printing = 0 }
3158
3429
  printing && /^- / { print }
3159
- ' "$changelog" | head -c 1500
3430
+ ' "$changelog" | head -c 1500 || true
3160
3431
  }
3161
3432
 
3162
3433
  # US-CL-005: changelog 风格守门 Phase 2 — self-audit gate.
@@ -34,6 +34,10 @@
34
34
  - Requests made in conversation are NOT AC — capture with `roll-idea` first.
35
35
  - Any new Story/Fix requires design doc + user confirmation before TCR starts.
36
36
  - Do not commit without user approval unless explicitly told to auto-commit.
37
+ - **Goal First**: Before any implementation, state verifiable success criteria.
38
+ Transform vague tasks: "add validation" → "write test for invalid input, then make it pass".
39
+ Multi-step work: list steps with verify checkpoints (step → verify: how to check).
40
+ Weak criteria ("make it work") require human clarification before starting.
37
41
  - **TCR**: Test -> Green = Commit / Red = Revert. No WIP commits.
38
42
  - Before implementing: confirm exact files, test strategy, and commit message
39
43
  draft with user. Do not write code until approved.
@@ -83,3 +87,9 @@ Confirm each phase clean before proceeding to the next.
83
87
  - `components/ui/` is shadcn-generated — never edit manually.
84
88
  - Tailwind utility classes only. No inline styles, no CSS modules.
85
89
  - Icons: Lucide React.
90
+
91
+ ## 8. Where to Look
92
+ - **Domain model**: `docs/domain/context-map.md` — Bounded Contexts and relationships
93
+ - **Story details**: `docs/features/` — AC, implementation specs, dependencies
94
+ - **Design decisions**: `docs/domain/` — DDD models, architecture records
95
+ - When `docs/domain/` or `docs/features/` don't exist yet, run `$roll-doc` to bootstrap.
@@ -25,3 +25,8 @@ src/
25
25
  - **TCR**: Mandatory.
26
26
  - **Security**: Input validation (zod), Rate limiting, Secrets rotation.
27
27
  - **Workspace**: `BACKLOG.md` + `docs/features/`.
28
+
29
+ ## 5. Where to Look
30
+ - **Domain model**: `docs/domain/context-map.md` — Bounded Contexts and relationships
31
+ - **Story details**: `docs/features/` — AC, implementation specs, dependencies
32
+ - **Design decisions**: `docs/domain/` — DDD models, architecture records
@@ -27,3 +27,8 @@ tests/
27
27
  - **TCR**: Mandatory.
28
28
  - **Distribution**: `bin` in `package.json`, test `npm i -g`.
29
29
  - **Workspace**: `BACKLOG.md` + `docs/features/`.
30
+
31
+ ## 5. Where to Look
32
+ - **Domain model**: `docs/domain/context-map.md` — Bounded Contexts and relationships
33
+ - **Story details**: `docs/features/` — AC, implementation specs, dependencies
34
+ - **Design decisions**: `docs/domain/` — DDD models, architecture records
@@ -24,3 +24,8 @@ src/
24
24
  - **TCR**: Mandatory.
25
25
  - **Testing**: Unit (hooks/logic) >80%, E2E (Playwright).
26
26
  - **Workspace**: `BACKLOG.md` + `docs/features/`.
27
+
28
+ ## 5. Where to Look
29
+ - **Domain model**: `docs/domain/context-map.md` — Bounded Contexts and relationships
30
+ - **Story details**: `docs/features/` — AC, implementation specs, dependencies
31
+ - **Design decisions**: `docs/domain/` — DDD models, architecture records
@@ -27,3 +27,8 @@ api/
27
27
  - **TCR**: Mandatory.
28
28
  - **Testing**: Unit >80%, E2E for critical flows.
29
29
  - **Workspace**: `BACKLOG.md` + `docs/features/`.
30
+
31
+ ## 5. Where to Look
32
+ - **Domain model**: `docs/domain/context-map.md` — Bounded Contexts and relationships
33
+ - **Story details**: `docs/features/` — AC, implementation specs, dependencies
34
+ - **Design decisions**: `docs/domain/` — DDD models, architecture records
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seanyao/roll",
3
- "version": "2026.514.1",
3
+ "version": "2026.514.3",
4
4
  "description": "Roll — Roll out features with AI agents",
5
5
  "scripts": {
6
6
  "test": "bash tests/run.sh"
@@ -286,3 +286,74 @@ After successful deploy in `$roll-build` / `$roll-fix`:
286
286
  **Post-Deploy:**
287
287
  - `$roll-.changelog` - Sync external changelog
288
288
  ```
289
+
290
+ ---
291
+
292
+ ## 7. Release Notes 生成模式(GitHub Release 正文)
293
+
294
+ `CHANGELOG.md` 里的 `## Unreleased` 条目是**原始数据**,每条对应一个故事或修复。
295
+ 发版时(`release.sh` 打 tag 前,或手动 `roll release notes`),把这些散装 bullet 整理成**给人读的 Release 正文**。
296
+
297
+ 这是两个不同的产物:
298
+ - `CHANGELOG.md` — 机器写、给 `roll update` 之后展示用,条目可以多
299
+ - GitHub Release 正文 — 给用户 / 关注者读,要分组、要有温度、要简洁
300
+
301
+ ### 7.1 何时触发
302
+
303
+ - `release.sh` 在 commit 之前调用本 skill 并传入 `--release-notes` flag
304
+ - 或用户手动说"帮我整理 Release Notes"
305
+
306
+ ### 7.2 分组规则
307
+
308
+ 把当前版本的 bullet 按**用户感知**归入以下分组,每组最多 5 条,超出的合并:
309
+
310
+ | 分组标题 | 归入条件 |
311
+ |---|---|
312
+ | 自动化流水线 | PR 自动化、Auto-merge、CI 触发、分支管理 |
313
+ | 可见性 | Dashboard、弹窗、实时输出、状态显示 |
314
+ | 稳定性 | 崩溃修复、并发问题、状态误报 |
315
+ | 工程和测试 | CI 速度、测试并行、文档工作流 |
316
+ | 新功能 | 全新命令或用户可感知的新能力 |
317
+
318
+ 分组顺序:**影响日常使用的放前面**,纯工程改进放后面。
319
+ 分组少于 2 条时可以合并到相邻组,不强制保留空组。
320
+
321
+ ### 7.3 合并相似条目
322
+
323
+ 同一功能点的多个 bullet(如"开 PR"和"Auto-merge"都在描述同一条流水线)合并为一条,用一句话说清楚整体效果,不要列两条。
324
+
325
+ 判断标准:**用户感知到的是同一件事,就合并。**
326
+
327
+ ### 7.4 归因标签
328
+
329
+ 在每条末尾加标签,说明是谁做的:
330
+
331
+ | 标签 | 归因依据 |
332
+ |---|---|
333
+ | `[loop]` | 对应 `US-AUTO-*` / `FIX-*` / `US-CL-*` 故事,由 Loop 自动执行 |
334
+ | `[dream]` | 对应 `REFACTOR-*` 故事,由 Dream 夜间扫描发现并推入 Backlog |
335
+ | (无标签) | 人工提交,或无法确定来源 |
336
+
337
+ ### 7.5 措辞原则
338
+
339
+ - 用**第二人称**("你不需要盯着"、"你可以随时查看")
340
+ - **主语是用户的感受**,不是系统的行为("不再误报" > "修复了误报逻辑")
341
+ - **故意模糊实现细节**:不写函数名、文件名、Phase 编号
342
+ - **有温度**:可以用"真的很急"、"不再越来越乱"这样口语化的表达
343
+ - 每条一行,简洁优先
344
+
345
+ ### 7.6 输出格式
346
+
347
+ ```markdown
348
+ ## <分组标题>
349
+
350
+ - <条目> `[loop]`
351
+ - <条目> `[dream]`
352
+ - <条目>
353
+
354
+ ## <分组标题>
355
+
356
+ - ...
357
+ ```
358
+
359
+ 不需要 `**Added**` / `**Fixed**` 前缀,分组标题已经承担了语义分类的职责。
@@ -491,6 +491,19 @@ OrderPricingService:
491
491
  Domain: Order Context > Order Aggregate > OrderItem Entity
492
492
  ```
493
493
 
494
+ ### AGENTS.md Where to Look — 指针维护
495
+
496
+ After completing any Domain Slice (User Story level), check if the project's `AGENTS.md` has a `## Where to Look` section with a `docs/domain/` pointer. If missing, append one line:
497
+
498
+ ```markdown
499
+ - **Domain model**: `docs/domain/context-map.md` — Bounded Contexts and relationships
500
+ ```
501
+
502
+ Rules:
503
+ - Idempotent: only append if the pointer line is not already present
504
+ - Do not modify any other content in AGENTS.md
505
+ - Skip silently if `docs/domain/` does not yet exist for this project
506
+
494
507
  ---
495
508
 
496
509
  ## Clarify Phase
@@ -505,10 +518,20 @@ Domain: Order Context > Order Aggregate > OrderItem Entity
505
518
  - Contains ambiguous terms like "优化一下", "改一下", "加个东西", "做个设计"
506
519
  - Could be interpreted in multiple ways
507
520
 
521
+ **Pre-Clarify: three-step product localization (always run first, silently)**
522
+
523
+ Before listing questions, internally determine:
524
+ 1. **产品端 (product end)**: web / mobile / API / CLI / other — which surface does this touch?
525
+ 2. **角色 (role)**: who initiates this action? (end user / admin / system / external)
526
+ 3. **业务域 (domain)**: which business domain does this belong to?
527
+
528
+ Already-localized dimensions become context prefix in the output, not open questions.
529
+
508
530
  **Output format:**
509
531
 
510
532
  ```
511
533
  🎯 Clarified Intent: {1-2 sentences}
534
+ 🗺 Context: {product end} · {role} · {domain} ← omit if all three are unknown
512
535
 
513
536
  📏 Complexity: {small|medium|large}
514
537
 
@@ -124,6 +124,29 @@ For each gap:
124
124
  | Module with no README | `<dir>/README.md` |
125
125
  | No `docs/domain/` entries | `docs/domain/context-map.md` |
126
126
  | No conventions doc | `docs/CONVENTIONS.md` |
127
+ | Missing `AGENTS.md` `## Where to Look` section | `AGENTS.md` (append or create) |
128
+
129
+ **AGENTS.md Where to Look bootstrap:**
130
+
131
+ When `AGENTS.md` has no `## Where to Look` section, generate and append one:
132
+
133
+ 1. Scan which doc directories actually exist: `docs/domain/`, `docs/features/`, `docs/practices/`, etc.
134
+ 2. Generate pointer lines **only for directories that actually exist** — never fabricate pointers to missing paths
135
+ 3. If `docs/domain/context-map.md` exists, read it to extract Bounded Context names for a one-line summary
136
+ 4. Append the section to the end of `AGENTS.md` with the standard draft header
137
+ 5. **Idempotency**: if `## Where to Look` already present, do not overwrite or duplicate — skip this gap
138
+
139
+ Draft output format:
140
+
141
+ ```markdown
142
+ > **Draft** — auto-generated by roll-doc on YYYY-MM-DD. Review before treating as authoritative.
143
+
144
+ ## Where to Look
145
+ - **Domain model**: `docs/domain/context-map.md` — Contexts: {list from context-map, or "see file"}
146
+ - **Story details**: `docs/features/` — AC, implementation specs, dependencies
147
+ ```
148
+
149
+ Only include lines for directories that already exist in the project.
127
150
 
128
151
  **Every generated file starts with this exact header line:**
129
152
 
@@ -84,6 +84,33 @@ exact stuck-red state FIX-026 traces to).
84
84
  - `gh` missing or repo unparseable → graceful skip (`_loop_precheck_ci`
85
85
  returns 0); the post-build `_loop_enforce_ci` remains the strict gate.
86
86
 
87
+ ### Step 1.6 — PR Inbox (US-AUTO-034)
88
+
89
+ Before scanning BACKLOG, process open PRs first. PRs are also units of work:
90
+ external contributors and human teammates expect their PRs to be reviewed and
91
+ moved forward, not starved while loop opens new fronts.
92
+
93
+ Call `_loop_pr_inbox` after the pre-run CI check passes. It walks
94
+ `gh pr list --state open` and routes each PR by classification:
95
+
96
+ | Classification | Action |
97
+ |---|---|
98
+ | `loop_self` (head ref starts with `loop/`) | Skip — let GitHub auto-merge handle it; never AI-review your own commit |
99
+ | `blocked_human_request_changes` | Skip — last human review requested changes; wait for the author to push fixes |
100
+ | `blocked_human_approved` | Skip — let GitHub auto-merge after CI is green |
101
+ | `stale` (CI failed or branch behind/conflicting) | Try `_loop_pr_rebase_stale` after the circuit breaker allows it |
102
+ | `eligible` (clean external PR, no blocking review) | Invoke `_loop_pr_review_external` — the actual decision is provided by US-AUTO-035's GitHub Action |
103
+
104
+ **Rebase circuit breaker** — `_loop_pr_rebase_circuit <pr>` records each rebase
105
+ attempt under `pr_state.<PR>.attempts_at` in `state.yaml`, pruning entries older
106
+ than 24 h. Once ≥3 attempts land within 24 h, further rebases are blocked and an
107
+ ALERT is written (typical cause: a broken workflow file makes CI never run,
108
+ which would otherwise drive infinite rebase loops).
109
+
110
+ **Lenient on infrastructure** — `gh` missing, repo unparseable, or any
111
+ `gh` API failure → `_loop_pr_inbox` returns 0 and the loop falls through to
112
+ Step 2 (BACKLOG scan). Same posture as the pre-run CI check.
113
+
87
114
  ### Step 2 — Scan BACKLOG
88
115
 
89
116
  Read `BACKLOG.md`. Collect all rows where Status = `📋 Todo`, in order: