@seanyao/roll 2026.513.1 → 2026.514.2

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,127 +1,128 @@
1
1
  # Changelog
2
2
 
3
+ ## v2026.514.2
4
+
5
+ - Changelog 历史版本全部重新整理:按主题分组、合并同类项、用词更贴近用户视角,附 `[loop]` / `[dream]` 归因标记
6
+ - Changelog Skill 新增 Release Notes 生成规范:分组规则、条目合并、归因标签、措辞原则
7
+ - README 新增 Evolution 章节,梳理 Roll 从工具到自主交付系统的演进脉络
8
+
9
+ ## v2026.514.1
10
+
11
+ ### 自动化流水线
12
+
13
+ - 故事跑完自动开 PR,CI 过了就合入主分支 — 你不需要盯着,审计记录也完整保留 `[loop]`
14
+ - AI 评审现在有实权:可以批准或打回 PR,配合 CI 形成双重把关;真的很急可以在 PR 描述里加 `[skip-ai-review]` 临时绕过 `[loop]`
15
+ - 散落的 session 分支自动清理,远端仓库不再越来越乱 `[loop]`
16
+
17
+ ### Changelog 开始管自己
18
+
19
+ - 生成时自动过滤技术黑话,并对照历史风格保持表达一致 `[loop]`
20
+ - 写入前有一道自审:行文不达标就退回重写,不进 changelog `[loop]`
21
+
22
+ ### 可见性
23
+
24
+ - Peer review 协商现在对所有 agent 实时可见,不再只是 claude 专属
25
+ - "Release ready" 只有真的有东西可发时才会亮,纯文档改动不再误报 `[loop]`
26
+ - PROPOSAL 的提示指向了实际有内容的地方 `[loop]`
27
+
28
+ ### 其他
29
+
30
+ - 纯文档改动直接合 main,不等 CI,合并更快
31
+
3
32
  ## v2026.513.1
4
- - **Added**: loop worktree 隔离 Phase 2 — `_write_loop_runner_script` 现在让每轮 cron 在独立的 `loop/cycle-<ts>-<pid>` worktree 里跑 claude,结束后 ff-merge 回 main + 自动清理;失败保留 worktree + 写 ALERT。loop 不再吞 main 的 WIP,多轮之间也完全隔离。claude 仍保留 story selection 权(SKILL.md 不变)
5
- - **Added**: loop worktree 隔离 Phase 1 — `bin/roll` 新增 7 个 `_worktree_*` helpers(path / create 幂等 / cleanup / fetch lenient / submodule init / merge_back ff-only / alert),覆盖 loop 在独立 worktree 跑 story 的完整生命周期;零行 runner.sh 改动,loop 自己也能跑;US-AUTO-037 (manual-only) 之后把这些 helpers 接入 `_write_loop_runner_script`
6
- - **Added**: loop 依赖闸门 — BACKLOG 行末尾的 `` `depends-on:US-X,US-Y` `` 和 `` `manual-only:true` `` 标签从此具有强制力;loop 选 story 前先用 `_loop_check_depends_on` / `_loop_is_manual_only` 两个 helper 过一遍,未满足依赖或带 manual-only 标的 story 直接跳过并写 `skipped` runs.jsonl 记录,不再需要靠人盯标签
7
- - **Fixed**: loop 并发保护补丁 — 2026-05-13 14:37 实测同 runner 下出现并发 claude 会话同时改 state.yaml / BACKLOG / git;inner script 现新增二级 LOCK(PID + start-ts 4h 双校验),守住 claude 调用现场,外层 LOCK 即使被旁路也兜底
8
- - **Changed**: CI unit / integration 双 job 并行 `tests/run.sh` 接受可选目录参数;`.github/workflows/ci.yml` `strategy.matrix` 把 509 unit + 70 integration 用例分到两个 runner 并行跑(REFACTOR-009 Phase 1B);Phase 2 测试瘦身拆到 REFACTOR-010
9
- - **Changed**: `roll`(无参)dashboard 重设计 — 自治优先六块布局:① Identity(项目名 + 版本 + agent + git 状态)② AI 自治主视觉(Loop / Dream / Peer 三层 + 四道防线,框线高亮)③ Pipeline 全景(Idea/Backlog/Build/Verify/Release 五段计数)④ Current Focus · DoD(in-progress story 的 [AC] [CI] 信号,其余 4 项标注待接入)⑤ Human × AI 介入区(ALERT/PROPOSAL/Release ready,空时显示"AI 自驱中")⑥ Schedules & Last Brief。把"AI 自动跑什么"放第一眼,不再埋在 `roll loop status` 子命令里
10
- - **Changed**: CI 测试运行器 (`tests/run.sh`) 动态检测核数(`nproc`/`sysctl`)替换硬编码 `--jobs 4`,可用 `ROLL_TEST_JOBS` 覆盖;bats-core submodule 缺失时给出清晰报错
11
- - **Changed**: GitHub Actions CI 跳过 docs-only PR(`paths-ignore: **.md, docs/**`),文档改动不再触发全套 bats
12
- - **Fixed**: `roll peer` 在 REFINE/OBJECT 退出路径上的 "resolution: unbound variable" 风险 — cmd_peer 显式初始化 `local resolution=""`,并补齐 AGREE/REFINE/OBJECT/ESCALATE/UNKNOWN 五条退出路径的回归测试
13
- - **Fixed**: `roll update` 后 `roll loop status` 误报 off — `_install_launchd_plists` reload 路径改用 `launchctl bootout` + `bootstrap`(不动 overrides db),消除 macOS Sonoma+ 上 no-`-w` unload/load 把 label `enabled` 标记吞掉的副作用;不再需要手动 `roll loop on` 恢复
33
+
34
+ ### Loop 更可靠了
35
+
36
+ - 每轮 story 在独立工作区里跑,完了自动合回来清理;跑挂时现场保留,不会碰主分支上你正在改的东西 `[loop]`
37
+ - `depends-on:` `manual-only:` 标签现在真的有用Loop 自己会跳过条件没满足的任务,不用再盯着 `[loop]`
38
+ - 同时只有一个 Loop 在跑,并发写入的问题修掉了 `[loop]`
39
+ - `roll update` 之后 loop 状态不再误报 off `[loop]`
40
+
41
+ ### 测试和 CI
42
+
43
+ - Unit 和集成测试并行执行,机器有多少核就用多少,等待时间降下来了 `[dream]`
44
+ - 纯文档改动不触发 CI 全套测试,合并更快 `[dream]`
45
+
46
+ ### Dashboard
47
+
48
+ - 重新设计:三层自治状态 + 四道防线 + Pipeline 全景 + 当前焦点 + 介入区,一屏看完 AI 正在做什么 `[loop]`
49
+
50
+ ### 修复
51
+
52
+ - `roll peer` 协商退出时偶发的崩溃修掉了 `[loop]`
14
53
 
15
54
  ## v2026.512.8
16
- - **Added**: `$roll-doc` — legacy 项目文档自动化技能:四阶段扫描(索引 + 缺口分析 + 草稿补全 + 报告),支持 `--dry-run` / `--force`,适用任何项目
17
- - **Added**: `roll-.dream` Scan 6 — 文档新鲜度检测(滞后文档 / 未记录 ENV 变量 / 架构文档缺失),依赖 roll-doc,发现写入 REFACTOR 条目
18
- - **Fixed**: loop CI gate 在 SSH config 改写 github.com 为 IP 的环境下失灵 — `gh` 自动识别失败被静默吞掉,loop 把 "gh 出错" 误判为 "gh 未装",在 CI 红的情况下继续把 story 标 ✅ Done;现从 git remote 推导 `owner/repo` 强制传 `-R`,gh 调用失败 = ALERT,loop 起跑前先验 HEAD CI 红绿,红则拒绝 build
19
-
20
- ## v2026.512.7
21
- - **Added**: `roll alert` — 查看、确认、清除 loop 告警,不用再去翻 loop status
22
- - **Added**: macOS 系统通知 story 完成或告警写入时自动弹通知,静音模式下不打扰
23
- - **Added**: `roll ci [--wait]` — 查看当前提交的 CI 状态,或等待 CI 跑完再继续
24
- - **Fixed**: loop 现在会等 CI 通过后才标记故事完成,CI 失败则保持进行中并发出提醒
25
- - **Fixed**: changelog 更新不再产生独立 commit,并入故事完成提交,git log 更干净
26
- - **Added**: `docs/domain/` — Roll 架构的 DDD 领域模型文档(5 个 Bounded Context + 自治操作 Aggregate 设计)
27
- - **Fixed**: `roll loop runs` 不再报"当前项目尚无运行记录",历史记录正常显示
28
- - **Added**: 文档目录重组 methodology、skill 选择指南、loop 验证记录迁移至 `docs/guide/` `docs/practices/`,根目录不再有散落文件
29
- - **Added**: README 大幅精简并新增文档导航索引 — 首页更清晰,所有指南一表可查
30
- - **Added**: dream 每晚自动检测文档缺口,brief 新增文档覆盖率数字
31
-
32
- ## v2026.512.6
33
- - **Added**: peer review 现在也会自动弹出终端窗口,实时观察跨 AI 协商过程(mute 关闭同一开关)
34
- - **Added**: `docs/guide/en/` — loop/dream/peer 英文用户指南上线,覆盖所有子命令和使用场景
35
- - **Added**: `docs/guide/zh/` loop/dream/peer 中文用户指南上线,内容与英文版语义一致
36
-
37
- ## v2026.512.5
38
- - **Fixed**: loop 遇到 API 错误时自动重试,不再直接中断
39
-
40
- ## v2026.512.3
41
- - **Added**: BACKLOG 支持 block / defer / unblock 状态管理 — 标记卡住的任务不再占队列
42
- - **Fixed**: 自动弹窗现在能识别 Ghostty 和 iTerm2,不再强制弹出 Terminal.app
43
- - **Fixed**: loop 检测到上一轮还在跑时自动跳过,不重复启动
44
-
45
- ## v2026.512.1
46
- - **Added**: `roll loop pause` / `roll loop resume` — 想自己上手时一键暂停 loop,做完再恢复
47
- - **Added**: `roll status` 新增所有项目的 loop 状态一览 — 调度时间、待办数、是否在跑
48
- - **Fixed**: `roll loop attach` 不再黑屏,AI 干活过程实时可见
49
-
50
- ## v2026.511.7
51
- - **Added**: loop 跑起来时自动弹出一个终端窗口,看 AI 实时干活
52
- - **Added**: `roll loop mute` 关掉自动弹窗,`roll loop unmute` 恢复
53
- - **Added**: `roll loop runs` — 查看 loop 最近几次都跑了什么
54
- - **Added**: `roll loop attach` 随时接入正在跑的 loop 现场围观
55
- - **Added**: BACKLOG 任务执行中会实时显示 🔨 进度,不用等做完才知道
56
- - **Added**: `roll setup` 自动安装 tmux(macOS 通过 brew)
57
- - **Improved**: 代码巡检(dream)报告改为中文输出
58
- - **Fixed**: loop 在某些情况下完成后不正常退出
59
- - **Fixed**: loop 中途崩溃后下次启动会自动清理残留状态
60
-
61
- ## v2026.511.6
62
- - **Fixed**: 多个 loop 实例不会再因为定时重复触发而互相打架
63
-
64
- ## v2026.511.5
65
- - **Fixed**: 升级 roll 后 loop 服务自动生效,无需手动重启
66
- - **Improved**: `roll loop status` 三态显示,看得清是真没装、装了没启、还是在跑
67
-
68
- ## v2026.511.4
69
- - **Fixed**: 升级 roll 后 `roll init` 自动迁移 loop 配置,少一步手动操作
70
-
71
- ## v2026.511.3
72
- - **Fixed**: 多个项目同时跑 loop,互不干扰
73
- - **Fixed**: 在 roll 项目里运行 `roll release` 会提示改用 `scripts/release.sh`
74
-
75
- ## v2026.511.2
76
- - **Added**: `roll loop monitor` — 一屏看 loop/dream/brief 三个调度服务状态
77
- - **Fixed**: dashboard 待办数、release 状态显示问题
78
- - **Fixed**: loop 异常退出后队列卡住不再继续执行
79
- - **Improved**: 简报输出更精简,去掉空白段落和冗余信息
80
-
81
- ## v2026.511.1
82
- - **Changed**: macOS 上 loop 调度切换到 launchd,比 crontab 更稳定
83
- - **Added**: agent 跳过 TCR 节奏时自动拦回 Todo,强制重做
55
+
56
+ ### 掌控感更强了
57
+
58
+ Loop 已经能跑、能看了——这一批让你对它有更多控制权:
59
+
60
+ - `roll loop pause` / `roll loop resume` — 想自己上手时一键暂停,做完再让 AI 接着跑 `[loop]`
61
+ - `roll alert`集中查看、确认、清除 loop 产生的告警,不用翻 loop status `[loop]`
62
+ - macOS 系统通知 story 完成或有新告警时自动弹通知,静音模式下不打扰 `[loop]`
63
+ - `roll ci [--wait]` — 查看当前 CI 状态,或等 CI 跑完再继续手头的事
64
+
65
+ ### Loop 更聪明了
66
+
67
+ - Loop 现在等 CI 通过后才标 story 完成,CI 红了会保持进行中并发出提醒 `[loop]`
68
+ - API 出错时自动重试,不再直接中断 `[loop]`
69
+ - 弹窗认识 Ghostty iTerm2 了,不再强制弹 Terminal.app `[loop]`
70
+ - BACKLOG 支持 `block` / `defer` — 卡住的任务标一下就不占队列了
71
+
72
+ ### 文档体系上线
73
+
74
+ - loop / dream / peer 中英文用户指南全部上线,覆盖所有子命令和使用场景 `[dream]`
75
+ - `$roll-doc` — 扫描任意项目的文档现状,找出缺口并生成草稿 `[dream]`
76
+ - Dream 每晚检测文档是否跟代码脱节,发现问题写进重构待办 `[dream]`
77
+ - `roll status` 新增跨项目 loop 状态一览
78
+
79
+ ## v2026.511.8
80
+
81
+ ### 终于能看到 Loop 在做什么了
82
+
83
+ Loop 上线后最大的感受是「不知道它在干啥」——这一批更新专门解决这个问题:
84
+
85
+ - Loop 开跑时自动弹出终端窗口,AI 干活的过程实时可见 `[loop]`
86
+ - `roll loop attach` 随时接入正在跑的 loop 现场 `[loop]`
87
+ - `roll loop runs` 查看 loop 最近几次跑了什么、完成了哪些 `[loop]`
88
+ - BACKLOG 任务执行中实时显示进度标记,不用等做完才知道 `[loop]`
89
+ - 不想被打扰时,`roll loop mute` 关掉弹窗,`roll loop unmute` 恢复
90
+
91
+ ### Loop 更稳了
92
+
93
+ - macOS 上调度从 crontab 换成 launchd,重启后不丢 `[loop]`
94
+ - 升级 roll loop 服务自动生效,不用手动重启 `[loop]`
95
+ - 多个项目同时跑 loop,互不干扰 `[loop]`
96
+ - 崩溃或异常退出后,下次启动自动清理残留状态 `[loop]`
97
+ - 并发触发时自动跳过,不重复执行 `[loop]`
98
+
99
+ ### 其他改进
100
+
101
+ - `roll loop monitor` — 一屏查看 loop / dream / brief 三个服务状态
102
+ - `roll loop status` 三态显示:没装 / 装了没启 / 正在跑,一眼看清
103
+ - `roll init` 升级后自动迁移 loop 配置,少一步手动操作
84
104
 
85
105
  ## v2026.510.10
86
- - **Fixed**: release.sh changelog 同步时序修复 — 修正条件逻辑和执行顺序,确保每次发版时 changelog 正确更新
87
- - **Added**: roll-loop 22:00 自动执行验证 — 新增 hello_world.bats 作为 loop 定时执行的端到端存档,可回放确认调度器正常工作
88
-
89
- ## v2026.510.9
90
- - **Fixed**: CHANGELOG 改版本号分组 — 每个 release 独立 section,GitHub Release 增量内容提取正确
91
- - **Fixed**: release.yml fetch-depth: 0,确保历史 tag workflow 中可见
92
-
93
- ## v2026.510.8
94
- - **Fixed**: release.sh 自洽 — 内联 agent 检测和 changelog 同步,发版流程不依赖外部调用
95
- - **Fixed**: roll release 命令重构 — 改为独立调用 roll-release skill,与 scripts/release.sh 解耦
96
- - **Fixed**: GitHub Release workflow 加 fetch-depth: 0,确保历史 tag 可见
97
-
98
- ## v2026.510.7
99
- - **Fixed**: 版本比较改用 sort -V,防止旧版本号被误判为最新
100
- - **Fixed**: 版本更新后主动清缓存 — 检测到运行版本更新时自动清缓存,避免旧版本幽灵提示
101
- - **Fixed**: Kimi CLI 解析修复 — 剥离 YAML frontmatter 中的 `---` 分隔符,避免调用时解析崩溃
102
- - **Fixed**: dashboard 命令列表补全,`roll --help` 现在展示全部可用命令
103
- - **Added**: `roll release` 命令补全到 usage() 帮助中
104
-
105
- ## v2026.510.6
106
- - **Fixed**: Agent 调用层统一 — 移除所有 claude -p 硬编码,统一 agent 抽象层,支持 Claude/Kimi/Pi/Codex/OpenCode
107
- - **Fixed**: pi/codex/opencode 支持补全 — _agent_run_skill/_peer_call 穷举所有 agent,未知 agent 给明确错误
108
- - **Fixed**: kimi 非交互调用语法修正为 kimi --quiet -p,经实测验证
109
- - **Fixed**: roll-build / roll-fix Phase 12 强制触发 roll-.changelog,确保 CHANGELOG 与 BACKLOG 同步
110
- - **Added**: roll release 命令 — 在 roll CLI 内直接调用 roll-release skill
111
- - **Added**: GitHub Release 自动创建 — tag push 后由 workflow 从 CHANGELOG diff 提取内容
112
-
113
- ## v2026.510.5
114
- - **Added**: roll-loop — BACKLOG 自主执行器,支持调度、跨 Agent 路由和失败处理,让 AI 自主推进项目任务
115
- - **Added**: roll-brief — Feature完成汇报、每日晨报、按需简报,一句话掌握项目状态和发布就绪情况
116
- - **Added**: roll-.dream — 每晚代码架构健康巡检,自动产出 REFACTOR 条目,架构问题持续浮出水面
117
- - **Added**: roll-build 架构摩擦信号 — 实现遇阻时自动向 BACKLOG 写入 REFACTOR 标记,技术债持续可见
118
- - **Added**: E2E 自动沉淀 — 每个 Story 交付时自动写一个端到端测试,项目逐步积累可回放的 E2E 套件
119
- - **Added**: CI E2E 门禁 — 模板 CI 每次推送自动跑 E2E 测试,没有 E2E 时静默跳过不阻塞
120
- - **Added**: CI 红灯分诊 — 按严重程度分类 CI 失败,自动路由到 backlog 变成可执行的修复项
121
- - **Added**: roll-debug 自动修复 — 诊断后若根因在项目源码内,自动进入 TCR 修复流程并回验
122
- - **Added**: Changelog 自动生成 — 每次部署后自动更新,首次运行时回填全部历史记录
123
- - **Added**: roll status/update 显示最近更新 — 运行 `roll status` 或 `roll update` 时展示最近 3 个版本的 changelog
124
- - **Fixed**: roll-release 补齐 GitHub Release 创建步骤 — 修复版本更新提醒从不生效的问题
106
+
107
+ ### Loop、Dream、Brief 首次亮相
108
+
109
+ Roll 从「技能编排工具」变成了「自主执行系统」——三个新组件同步上线:
110
+
111
+ - `roll loop` AI 自动调度 BACKLOG,Story 一个接一个跑,不用你盯着
112
+ - `roll brief` — 每天早上自动整理昨天做了什么,帮你快速恢复上下文
113
+ - `roll-.dream` — 每晚自动巡检代码和架构,把发现的问题写成可执行的待办
114
+
115
+ ### 自动化能力
116
+
117
+ - 每个 Story 交付时自动沉淀一个 E2E 测试,项目逐步积累可回放的验收套件 `[loop]`
118
+ - CI 失败会自动分诊,按严重程度路由成 backlog 里可执行的修复项 `[loop]`
119
+ - `roll debug` 诊断出根因在源码内时,自动进入修复流程并回验 `[loop]`
120
+ - Changelog 每次部署后自动更新,不再需要手动整理 `[loop]`
121
+
122
+ ### 可见性
123
+
124
+ - `roll status` / `roll update` 运行后展示最近几个版本的更新内容
125
+ - `roll release` 命令 — 在 CLI 里直接触发发布,不用记路径
125
126
 
126
127
  ## 2026.05.09
127
128
  - **Added**: roll-peer 跨 Agent 代码评审 — 支持 Claude Code、Kimi CLI、DeepSeek TUI、Codex CLI 多工具协同评审 (by @seanyao)
package/README.md CHANGED
@@ -74,6 +74,22 @@ roll loop on # optional: let the agent work unattended
74
74
 
75
75
  ---
76
76
 
77
+ ## Evolution
78
+
79
+ 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?*
80
+
81
+ The first version was almost embarrassingly small — a way to push engineering conventions to whatever AI tool you happened to be running. But it needed to be trustworthy before it could be useful, so the early weeks went into making Roll self-maintaining: one-command releases, self-updating installs, a clean npm presence.
82
+
83
+ The next step was making Roll genuinely multi-agent. Kimi, DeepSeek, Codex, Trae — each integrated with its own skill preferences and model bindings. `roll-peer` came from a simple insight: agents shouldn't just review their own work. Have one AI challenge another's design decisions before anything lands on `main`. That turned out to be the first glimpse of what Roll was actually becoming.
84
+
85
+ The real shift happened when `roll loop` went live. Stories started running back-to-back without any human prompt. `roll-.dream` began filing its own refactor tickets after nightly scans. The system had started generating its own work queue — not just executing tasks, but surfacing the next ones.
86
+
87
+ What followed was learning to trust that autonomy: real-time terminal windows, worktree isolation so loop runs never touch your in-progress work, a CI + AI review double gate so nothing merges until it's actually ready. The kind of loop you can leave running overnight and wake up to something mergeable.
88
+
89
+ The goal from here: full delivery, end to end — with humans on the loop, not in it.
90
+
91
+ ---
92
+
77
93
  ## Contributing
78
94
 
79
95
  PRs welcome. Keep them focused on one thing. For larger changes, open an issue first.
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.513.1"
7
+ VERSION="2026.514.2"
8
8
  ROLL_HOME="${ROLL_HOME:-${HOME}/.roll}"
9
9
  ROLL_CONFIG="${ROLL_HOME}/config.yaml"
10
10
  ROLL_GLOBAL="${ROLL_HOME}/conventions/global"
@@ -609,6 +609,36 @@ cmd_setup() {
609
609
 
610
610
  echo ""
611
611
  info "Next: run ${BOLD}roll init${NC} inside a project to initialize it. 下一步:在项目目录运行 roll init"
612
+
613
+ echo ""
614
+ _print_pr_pipeline_hint
615
+ }
616
+
617
+ # ─── PR pipeline hint ────────────────────────────────────────────────────────
618
+ # US-AUTO-035: print the one-time branch-protection command that flips repo
619
+ # from path A (CI gate only) to path C (CI + AI review double gate). Reading
620
+ # this hint is opt-in; the command is destructive (changes branch protection)
621
+ # so it is never run automatically.
622
+ _print_pr_pipeline_hint() {
623
+ cat <<'HINT'
624
+
625
+ Optional — enable AI review as a hard merge gate (path C).
626
+ 可选 —— 启用 AI 评审作为合并双门(路径 C)。
627
+
628
+ Run once per repo (requires admin token), then claude-code-review.yml
629
+ approvals become a required merge gate alongside CI:
630
+ 每个仓库执行一次(需要管理员 token),之后 claude-code-review.yml 的
631
+ approve 将与 CI 一起成为合并必经的双门:
632
+
633
+ gh api -X PATCH repos/<owner>/<repo>/branches/main/protection \
634
+ -f required_pull_request_reviews.required_approving_review_count=1
635
+
636
+ Escape hatch: add [skip-ai-review] to a PR body, or include
637
+ SKIP_AI_REVIEW in any commit message, to bypass AI review for that PR.
638
+ 紧急通道:在 PR body 加 [skip-ai-review],或在任一 commit message
639
+ 里包含 SKIP_AI_REVIEW,可对该 PR 绕过 AI 评审。
640
+
641
+ HINT
612
642
  }
613
643
 
614
644
  # ═══════════════════════════════════════════════════════════════════════════════
@@ -2118,15 +2148,31 @@ for _attempt in 1 2 3; do
2118
2148
  fi
2119
2149
  done
2120
2150
 
2121
- # Post-claude: merge back if we used an isolated worktree.
2151
+ # Post-claude: publish cycle branch. Doc-only changes (BACKLOG/docs) merge
2152
+ # immediately via --admin; code changes use auto-merge (CI gate required).
2153
+ # When \`gh\` is unavailable, fall back to the legacy ff-merge path.
2122
2154
  if [ "\$_USE_WORKTREE" = "1" ]; then
2123
2155
  if [ "\$_exit" -eq 0 ]; then
2124
- if ( cd "${project_path}" && _worktree_merge_back "\$BRANCH" ); then
2156
+ if ( cd "\$WT" && _loop_is_doc_only_change ); then
2157
+ ( cd "\$WT" && _loop_publish_doc_pr "\$BRANCH" "doc: loop cycle \${CYCLE_ID}" )
2158
+ else
2159
+ ( cd "\$WT" && _loop_publish_pr "\$BRANCH" "loop cycle \${CYCLE_ID}" )
2160
+ fi
2161
+ _publish_status=\$?
2162
+ if [ "\$_publish_status" -eq 0 ]; then
2125
2163
  _worktree_cleanup "\$WT" "\$BRANCH"
2126
- echo "[loop] cycle \${CYCLE_ID}: merged and cleaned up"
2164
+ echo "[loop] cycle \${CYCLE_ID}: published; worktree cleaned"
2165
+ elif [ "\$_publish_status" -eq 2 ]; then
2166
+ if ( cd "${project_path}" && _worktree_merge_back "\$BRANCH" ); then
2167
+ _worktree_cleanup "\$WT" "\$BRANCH"
2168
+ echo "[loop] cycle \${CYCLE_ID}: gh unavailable; merged via ff and cleaned up"
2169
+ else
2170
+ _worktree_alert "cycle \${CYCLE_ID}: gh unavailable AND merge_back failed; worktree preserved at \$WT"
2171
+ echo "[loop] cycle \${CYCLE_ID}: gh+merge_back both failed; worktree preserved at \$WT"
2172
+ fi
2127
2173
  else
2128
- _worktree_alert "cycle \${CYCLE_ID}: merge_back failed; worktree preserved at \$WT (branch \$BRANCH)"
2129
- echo "[loop] cycle \${CYCLE_ID}: merge_back failed; worktree preserved at \$WT"
2174
+ _worktree_alert "cycle \${CYCLE_ID}: PR publish failed; worktree preserved at \$WT (branch \$BRANCH)"
2175
+ echo "[loop] cycle \${CYCLE_ID}: PR publish failed; worktree preserved at \$WT"
2130
2176
  fi
2131
2177
  else
2132
2178
  _worktree_alert "cycle \${CYCLE_ID}: claude exited \$_exit; worktree preserved at \$WT (branch \$BRANCH)"
@@ -3055,6 +3101,172 @@ _loop_is_manual_only() {
3055
3101
  echo "$row" | grep -qE 'manual-only:true'
3056
3102
  }
3057
3103
 
3104
+ # US-CL-004: changelog 风格守门 Phase 1 — mechanical linter.
3105
+ #
3106
+ # _changelog_lint_bullet <bullet-text>
3107
+ # Stdout: one violation tag per line; empty = bullet passes.
3108
+ # Exit: 0 always (callers read the output stream, not the exit code).
3109
+ #
3110
+ # Violation tags:
3111
+ # backtick-identifier `…` contains `_` or `()` (e.g. `_foo`, `bar()`)
3112
+ # file-suffix `.md`/`.sh`/`.yml`/`.ts`/`.bats` outside backticks
3113
+ # internal-word Phase N / Step N / Helper / Schema / Fixture / Refactor
3114
+ # over-length > 50 visible chars (UTF-8 codepoints; 中文按字符计)
3115
+ # path-fragment docs/ / bin/ / tests/ / scripts/ outside backticks
3116
+ #
3117
+ # Backticks are treated as the "user-quoted" zone — content there is assumed
3118
+ # to be a real user command (e.g. `roll edit notes.md`) and is excluded from
3119
+ # the file-suffix / path-fragment checks.
3120
+ _changelog_lint_bullet() {
3121
+ local bullet="$1"
3122
+ local stripped
3123
+ stripped=$(printf '%s' "$bullet" | sed -E 's/`[^`]*`//g')
3124
+
3125
+ if printf '%s' "$bullet" | grep -qE '`[^`]*(_|\(\))[^`]*`'; then
3126
+ echo "backtick-identifier"
3127
+ fi
3128
+ if printf '%s' "$stripped" | grep -qE '\.(md|sh|yml|ts|bats)([^A-Za-z0-9]|$)'; then
3129
+ echo "file-suffix"
3130
+ fi
3131
+ if printf '%s' "$bullet" | grep -qE '(Phase|Step)[[:space:]]+[0-9]+|Helper|Schema|Fixture|Refactor'; then
3132
+ echo "internal-word"
3133
+ fi
3134
+ local len
3135
+ len=$(printf '%s' "$bullet" | LC_ALL=C.UTF-8 wc -m | tr -d ' ')
3136
+ if [ "${len:-0}" -gt 50 ]; then
3137
+ echo "over-length"
3138
+ fi
3139
+ if printf '%s' "$stripped" | grep -qE '(^|[^A-Za-z0-9_])(docs|bin|tests|scripts)/'; then
3140
+ echo "path-fragment"
3141
+ fi
3142
+ return 0
3143
+ }
3144
+
3145
+ # US-CL-004: changelog few-shot style anchors — extract bullets from the
3146
+ # most recent 3 published `## v...` sections of CHANGELOG.md (skipping
3147
+ # `## Unreleased`). Cap at ~1500 chars so the agent's context stays lean.
3148
+ #
3149
+ # _changelog_style_anchors [changelog-path]
3150
+ # Stdout: concatenated bullet lines from the last 3 released versions.
3151
+ # Exit: 0 (empty output when no CHANGELOG.md or no released sections).
3152
+ _changelog_style_anchors() {
3153
+ local changelog="${1:-CHANGELOG.md}"
3154
+ [ -f "$changelog" ] || return 0
3155
+ awk '
3156
+ /^## v/ { ver++; if (ver > 3) exit; printing = 1; next }
3157
+ /^## / { printing = 0 }
3158
+ printing && /^- / { print }
3159
+ ' "$changelog" | head -c 1500
3160
+ }
3161
+
3162
+ # US-CL-005: changelog 风格守门 Phase 2 — self-audit gate.
3163
+ #
3164
+ # _changelog_audit_bullet <bullet>
3165
+ # Stricter than _changelog_lint_bullet: 5 boolean rules, 30-char cap.
3166
+ # Stdout: one failed-rule tag per line; empty = bullet passes.
3167
+ # Exit: 0 always.
3168
+ #
3169
+ # Rules:
3170
+ # over-length-30 visible chars > 30 AND no backtick (user-cmd escape hatch)
3171
+ # internal-id backtick content contains `_` or `()`
3172
+ # path-or-suffix .md/.sh/.yml/.ts/.bats or docs/bin/tests/scripts/ outside backticks
3173
+ # phase-step `Phase N` / `Step N` workflow vocabulary
3174
+ # bad-shape no `—` (em dash) AND no `不再` AND no `现在` keyword
3175
+ _changelog_audit_bullet() {
3176
+ local bullet="$1"
3177
+ local stripped
3178
+ stripped=$(printf '%s' "$bullet" | sed -E 's/`[^`]*`//g')
3179
+
3180
+ # Rule 1: length cap 30 (user-command in backticks bypasses this rule).
3181
+ if ! printf '%s' "$bullet" | grep -q '`'; then
3182
+ local len
3183
+ len=$(LC_ALL=en_US.UTF-8 printf '%s' "$bullet" | LC_ALL=en_US.UTF-8 wc -m | tr -d ' ')
3184
+ if [ "${len:-0}" -gt 30 ]; then
3185
+ echo "over-length-30"
3186
+ fi
3187
+ fi
3188
+
3189
+ # Rule 2: internal identifier inside backticks.
3190
+ if printf '%s' "$bullet" | grep -qE '`[^`]*(_|\(\))[^`]*`'; then
3191
+ echo "internal-id"
3192
+ fi
3193
+
3194
+ # Rule 3: file suffix / path fragment outside backticks.
3195
+ if printf '%s' "$stripped" | grep -qE '\.(md|sh|yml|ts|bats)([^A-Za-z0-9]|$)' \
3196
+ || printf '%s' "$stripped" | grep -qE '(^|[^A-Za-z0-9_])(docs|bin|tests|scripts)/'; then
3197
+ echo "path-or-suffix"
3198
+ fi
3199
+
3200
+ # Rule 4: workflow vocabulary.
3201
+ if printf '%s' "$bullet" | grep -qE '(Phase|Step)[[:space:]]+[0-9]+'; then
3202
+ echo "phase-step"
3203
+ fi
3204
+
3205
+ # Rule 5: required shape — em dash, 不再, or 现在.
3206
+ if ! printf '%s' "$bullet" | grep -qE '—|不再|现在'; then
3207
+ echo "bad-shape"
3208
+ fi
3209
+
3210
+ return 0
3211
+ }
3212
+
3213
+ # _changelog_audit_log <verdict> <round> <bullet> [<reason>...]
3214
+ # Append a JSONL record to the audit log. Path overridable via
3215
+ # ROLL_CHANGELOG_AUDIT_LOG (tests use this to stay out of $HOME).
3216
+ _changelog_audit_log() {
3217
+ local verdict="$1" round="$2" bullet="$3"
3218
+ shift 3
3219
+ local log="${ROLL_CHANGELOG_AUDIT_LOG:-${_SHARED_ROOT}/loop/changelog-audit.jsonl}"
3220
+ mkdir -p "$(dirname "$log")"
3221
+ local ts; ts=$(date -u +%Y-%m-%dT%H:%M:%SZ)
3222
+ local reasons_json='[]'
3223
+ if [ "$#" -gt 0 ]; then
3224
+ reasons_json=$(printf '%s\n' "$@" | jq -R . | jq -sc .)
3225
+ fi
3226
+ jq -nc \
3227
+ --arg ts "$ts" \
3228
+ --arg verdict "$verdict" \
3229
+ --argjson round "$round" \
3230
+ --arg bullet "$bullet" \
3231
+ --argjson reasons "$reasons_json" \
3232
+ '{ts:$ts, verdict:$verdict, round:$round, bullet:$bullet, reasons:$reasons}' \
3233
+ >> "$log"
3234
+ }
3235
+
3236
+ # _changelog_audit_gate <round1> [<round2> <round3>]
3237
+ # Run up to 3 candidate bullets through _changelog_audit_bullet.
3238
+ # First clean candidate wins: print bullet to stdout, exit 0.
3239
+ # All 3 failed: print ⚠️-prefixed last candidate, append ALERT, exit 1.
3240
+ # Each round writes a _changelog_audit_log record.
3241
+ _changelog_audit_gate() {
3242
+ local i=0 last=""
3243
+ for candidate in "$@"; do
3244
+ i=$((i + 1))
3245
+ last="$candidate"
3246
+ local viols
3247
+ # shellcheck disable=SC2207
3248
+ viols=( $(_changelog_audit_bullet "$candidate") )
3249
+ if [ "${#viols[@]}" -eq 0 ]; then
3250
+ _changelog_audit_log pass "$i" "$candidate"
3251
+ printf '%s\n' "$candidate"
3252
+ return 0
3253
+ fi
3254
+ _changelog_audit_log fail "$i" "$candidate" "${viols[@]}"
3255
+ [ "$i" -ge 3 ] && break
3256
+ done
3257
+ # All 3 rounds failed (or fewer if caller passed < 3).
3258
+ mkdir -p "$(dirname "$_LOOP_ALERT")" 2>/dev/null
3259
+ {
3260
+ echo ""
3261
+ echo "# ALERT — changelog audit failed after $i rounds"
3262
+ echo "**Time**: $(date -u +%Y-%m-%dT%H:%M:%SZ)"
3263
+ echo "**Bullet**: $last"
3264
+ echo "**Action**: kept under \`## Unreleased\` with ⚠️ prefix; human review recommended."
3265
+ } >> "$_LOOP_ALERT"
3266
+ printf '⚠️ %s\n' "$last"
3267
+ return 1
3268
+ }
3269
+
3058
3270
  # US-AUTO-036: worktree helpers (loop-safe pure additions).
3059
3271
  #
3060
3272
  # Phase 1 of worktree isolation — these helpers are NOT yet called by
@@ -3150,6 +3362,107 @@ _worktree_merge_back() {
3150
3362
  return 0
3151
3363
  }
3152
3364
 
3365
+ # US-AUTO-033: publish a loop cycle branch as a GitHub PR with auto-merge.
3366
+ #
3367
+ # _loop_publish_pr <branch> [title]
3368
+ # Caller's cwd: a tree where <branch> exists locally.
3369
+ # Steps:
3370
+ # 1. git push origin <branch>
3371
+ # 2. gh pr view <branch> → reuse if a PR is already open
3372
+ # 3. gh pr create --base main --head <branch> ...
3373
+ # 4. gh pr merge <branch> --auto --squash --delete-branch
3374
+ # Stdout: PR URL (always, even on idempotent reuse).
3375
+ # Exit 0 on success / idempotent reuse; non-zero on push or create failure.
3376
+ # On auto-merge failure: still returns 0 (PR exists; human can take over).
3377
+ # When `gh` is not installed: returns 2 — runner script's fallback path.
3378
+ _loop_publish_pr() {
3379
+ local branch="$1"
3380
+ local title="${2:-loop cycle ${branch#loop/}}"
3381
+ if ! command -v gh >/dev/null 2>&1; then
3382
+ _worktree_alert "_loop_publish_pr: gh not installed; cannot publish PR for ${branch}"
3383
+ return 2
3384
+ fi
3385
+ local slug; slug=$(_gh_repo_slug 2>/dev/null) || slug=""
3386
+ if [ -z "$slug" ]; then
3387
+ _worktree_alert "_loop_publish_pr: origin remote is not a github repo; cannot publish PR for ${branch}"
3388
+ return 2
3389
+ fi
3390
+ local _push_err
3391
+ _push_err=$(git push origin "$branch" 2>&1) || {
3392
+ _worktree_alert "_loop_publish_pr: push origin ${branch} failed: ${_push_err}"
3393
+ return 1
3394
+ }
3395
+ local pr_url
3396
+ pr_url=$(gh -R "$slug" pr view "$branch" --json url -q .url 2>/dev/null) || pr_url=""
3397
+ if [ -z "$pr_url" ]; then
3398
+ local body
3399
+ body=$(printf 'Auto-opened by roll-loop cycle.\n\n- Branch: %s\n- TCR micro-commits: %s\n\nThis PR will auto-merge once required checks pass.' \
3400
+ "$branch" "$(git rev-list --count origin/main.."$branch" 2>/dev/null || echo '?')")
3401
+ pr_url=$(gh -R "$slug" pr create --base main --head "$branch" \
3402
+ --title "$title" --body "$body" 2>/dev/null) || pr_url=""
3403
+ if [ -z "$pr_url" ]; then
3404
+ _worktree_alert "_loop_publish_pr: gh pr create failed for ${branch}"
3405
+ return 1
3406
+ fi
3407
+ fi
3408
+ gh -R "$slug" pr merge "$branch" --auto --squash --delete-branch >/dev/null 2>&1 \
3409
+ || _worktree_alert "_loop_publish_pr: gh pr merge --auto failed for ${branch} (PR ${pr_url} left open)"
3410
+ echo "$pr_url"
3411
+ return 0
3412
+ }
3413
+
3414
+ # _loop_is_doc_only_change
3415
+ # Returns 0 if every file changed since origin/main is doc-only
3416
+ # (BACKLOG.md, CHANGELOG.md, PROPOSALS.md, docs/, .claude/).
3417
+ # Returns 1 if any code file changed or there are no changes.
3418
+ _loop_is_doc_only_change() {
3419
+ local changed
3420
+ changed=$(git diff --name-only origin/main HEAD 2>/dev/null) || return 1
3421
+ [ -z "$changed" ] && return 1
3422
+ echo "$changed" | grep -qvE '^(BACKLOG\.md|CHANGELOG\.md|PROPOSALS\.md|docs/|\.claude/)' && return 1
3423
+ return 0
3424
+ }
3425
+
3426
+ # _loop_publish_doc_pr <branch> [title]
3427
+ # Like _loop_publish_pr but merges immediately with --admin (no CI wait).
3428
+ # For doc-only changes where CI is not meaningful.
3429
+ _loop_publish_doc_pr() {
3430
+ local branch="$1"
3431
+ local title="${2:-doc update ${branch#loop/}}"
3432
+ if ! command -v gh >/dev/null 2>&1; then
3433
+ _worktree_alert "_loop_publish_doc_pr: gh not installed; cannot publish PR for ${branch}"
3434
+ return 2
3435
+ fi
3436
+ local slug; slug=$(_gh_repo_slug 2>/dev/null) || slug=""
3437
+ if [ -z "$slug" ]; then
3438
+ _worktree_alert "_loop_publish_doc_pr: origin remote is not a github repo; cannot publish PR for ${branch}"
3439
+ return 2
3440
+ fi
3441
+ if ! git push origin "$branch" --quiet 2>/dev/null; then
3442
+ _worktree_alert "_loop_publish_doc_pr: push origin ${branch} failed"
3443
+ return 1
3444
+ fi
3445
+ local pr_url
3446
+ pr_url=$(gh -R "$slug" pr view "$branch" --json url -q .url 2>/dev/null) || pr_url=""
3447
+ if [ -z "$pr_url" ]; then
3448
+ local body
3449
+ body=$(printf 'Doc-only update by roll-loop cycle.\n\n- Branch: %s\n- Files: BACKLOG / docs only\n\nMerging immediately — no CI gate needed for doc-only changes.' "$branch")
3450
+ pr_url=$(gh -R "$slug" pr create --base main --head "$branch" \
3451
+ --title "$title" --body "$body" 2>/dev/null) || pr_url=""
3452
+ if [ -z "$pr_url" ]; then
3453
+ _worktree_alert "_loop_publish_doc_pr: gh pr create failed for ${branch}"
3454
+ return 1
3455
+ fi
3456
+ fi
3457
+ if ! gh -R "$slug" pr merge "$branch" --admin --squash --delete-branch >/dev/null 2>&1; then
3458
+ _worktree_alert "_loop_publish_doc_pr: gh pr merge --admin failed for ${branch} (PR ${pr_url} left open)"
3459
+ echo "$pr_url"
3460
+ return 1
3461
+ fi
3462
+ echo "$pr_url"
3463
+ return 0
3464
+ }
3465
+
3153
3466
  _loop_monitor() {
3154
3467
  local interval="${1:-3}"
3155
3468
  local project_path; project_path=$(pwd -P)
@@ -3807,8 +4120,20 @@ _dash_proposal_count() {
3807
4120
  grep '^## PROPOSAL' PROPOSALS.md 2>/dev/null | wc -l | tr -d ' '
3808
4121
  }
3809
4122
 
3810
- # ⑤ Release-ready signal — true if latest brief contains 可发版 or "Release ready".
4123
+ # ⑤ Release-ready signal — true iff there are releasable commits since the
4124
+ # latest tag AND the latest brief signals 可发版/Release ready. Releasable =
4125
+ # any commit since the latest tag whose subject does NOT start with the
4126
+ # release-irrelevant prefixes `docs:` or `chore:`. Prevents the flag from
4127
+ # sticking on after a release when only docs rewrites land on top of the tag
4128
+ # (FIX-033 symptom 2).
3811
4129
  _dash_release_ready() {
4130
+ local latest_tag
4131
+ latest_tag=$(git describe --tags --abbrev=0 2>/dev/null) || return 1
4132
+ local commits_with_code
4133
+ commits_with_code=$(git log "${latest_tag}..HEAD" --pretty=format:%s 2>/dev/null \
4134
+ | grep -cvE '^(docs|chore)(\([^)]*\))?:[[:space:]]' 2>/dev/null \
4135
+ || echo 0)
4136
+ [[ "${commits_with_code:-0}" -gt 0 ]] || return 1
3812
4137
  local latest
3813
4138
  latest=$(ls docs/briefs/*.md 2>/dev/null | sort | tail -1 || true)
3814
4139
  [[ -z "$latest" ]] && return 1
@@ -3988,7 +4313,7 @@ _dashboard() {
3988
4313
  printf " ${GREEN}✓ AI 自驱中 — 无需介入${NC}\n"
3989
4314
  else
3990
4315
  (( alerts > 0 )) && printf " ${RED}⚠ %s ALERT${NC} run: roll alert\n" "$alerts"
3991
- (( proposals > 0 )) && printf " ${YELLOW}📋 %s PROPOSAL${NC} run: roll backlog\n" "$proposals"
4316
+ (( proposals > 0 )) && printf " ${YELLOW}📋 %s PROPOSAL${NC} see: PROPOSALS.md\n" "$proposals"
3992
4317
  $release_ready && printf " ${GREEN}✓ Release ready${NC} run: roll release\n"
3993
4318
  fi
3994
4319
  echo ""
@@ -40,6 +40,12 @@
40
40
  - Before claiming completion: verify in the target environment mentioned by
41
41
  user (e.g., specific CLI tool, remote server, hardware platform).
42
42
  - **Workspace**: `BACKLOG.md` index. `docs/features/` for details.
43
+ - **Backlog descriptions** (US, FIX, REFACTOR, IDEA, PROPOSAL): one sentence in plain language.
44
+ Say what changed and why it matters — not how it works internally.
45
+ No file paths, function names, parameter lists, or architecture jargon.
46
+ `depends-on:` and `manual-only:` functional tags are allowed; `Domain:` annotation tags are not.
47
+ Technical details and AC go in `docs/features/`.
48
+ A well-written BACKLOG description can be used directly as a CHANGELOG entry.
43
49
  - **Done**: Push + CI passes + deployed. Local-only is not done.
44
50
  - **Commit message format**:
45
51
  - Format: `<type>: <description>` (Git Hook may auto-prepend type prefix)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seanyao/roll",
3
- "version": "2026.513.1",
3
+ "version": "2026.514.2",
4
4
  "description": "Roll — Roll out features with AI agents",
5
5
  "scripts": {
6
6
  "test": "bash tests/run.sh"
@@ -47,6 +47,14 @@ Create mode:
47
47
 
48
48
  CHANGELOG 是给**使用者**看的,不是给维护者看的。一句话讲清"用户能做什么 / 不再被什么坑",能不写就不写。
49
49
 
50
+ **BACKLOG 描述写好了,CHANGELOG 就是复制 + 过滤,不是重写。**
51
+ 如果 BACKLOG 描述已经是人话、一句话、说用户价值,直接用它(去掉 `depends-on:` / `manual-only:` 等功能性标签)。
52
+ 只有 BACKLOG 描述包含实现细节或技术黑话时,才需要改写。
53
+
54
+ **FIX 条目的 filter 规则**:BACKLOG 的 FIX 描述通常是 `<用户症状> — <修复手段>` 结构。
55
+ CHANGELOG 只取破折号前的用户症状,丢弃破折号后的修复手段。
56
+ 例:`roll update 后 loop 状态误报 off — reload 改用 bootout+bootstrap` → CHANGELOG 只写 `roll update 后 loop 状态不再误报 off`。
57
+
50
58
  **完全跳过(不写入 CHANGELOG):**
51
59
  - 测试基建(teardown 清理、test isolation、bats helper、CI 时序)
52
60
  - prompt / SKILL.md 内部契约(schema 锁定、enum 强制、contract test)
@@ -62,9 +70,9 @@ CHANGELOG 是给**使用者**看的,不是给维护者看的。一句话讲清
62
70
  - 看得见的体验变化(布局、文案、速度、可见性)
63
71
  - 影响安装、升级、配置的改动
64
72
 
65
- **写法约束:**
73
+ **写法约束(BACKLOG 描述不符合时才介入改写):**
66
74
 
67
- 1. **一行,30 字以内**。超了就是太啰嗦。
75
+ 1. **一行**。超了就是太啰嗦。
68
76
  2. **不写实现细节**:禁止文件路径、函数名、字段列表、命令参数、配置键名。
69
77
  3. **不写数字细节**:"3 个服务"、"60+ ghost" 这种内部状态不写。
70
78
  4. **说人话**:避免 "幂等"、"trap"、"epoch" 等技术黑话;说"做两次效果一样"、"异常退出也会清理"、"启动时间"。
@@ -133,6 +141,94 @@ assigned.
133
141
  Do NOT read `package.json` version, do NOT call `git describe`, do NOT invent
134
142
  version numbers like `v2026.511.8`. Just write to `## Unreleased`.
135
143
 
144
+ ### 5.3 Style Anchors — In-Context Few-Shot
145
+
146
+ Before drafting bullets, pull the most recent 3 published versions' bullets as
147
+ in-context examples so the agent doesn't write from a blank slate (the blank
148
+ slate is where the technical-jargon habit comes from — the agent is fresh
149
+ from writing function names and just copies them over).
150
+
151
+ ```bash
152
+ _changelog_style_anchors CHANGELOG.md
153
+ ```
154
+
155
+ Output is the concatenated bullet lines from the last 3 `## v...` sections,
156
+ capped at 1500 characters. Use them as a style reference when drafting — pay
157
+ attention to length, voice ("不再被…坑" / "现在 …"), and absence of internal
158
+ names.
159
+
160
+ ### 5.4 Mechanical Lint — Hard Gate
161
+
162
+ After drafting bullets, run each through the mechanical linter. Any non-empty
163
+ output means the bullet violates at least one blacklist rule and must be
164
+ rewritten.
165
+
166
+ ```bash
167
+ for bullet in "${draft_bullets[@]}"; do
168
+ violations=$(_changelog_lint_bullet "$bullet")
169
+ [ -n "$violations" ] && needs_rewrite+=("$bullet :: $violations")
170
+ done
171
+ ```
172
+
173
+ Violation tags emitted by `_changelog_lint_bullet`:
174
+
175
+ | Tag | Trigger | Why it's noise to users |
176
+ |---|---|---|
177
+ | `backtick-identifier` | `…` contains `_` or `()` | Internal symbol names mean nothing to users |
178
+ | `file-suffix` | `.md/.sh/.yml/.ts/.bats` outside backticks | File paths are maintainer concern |
179
+ | `internal-word` | Phase N / Step N / Helper / Schema / Fixture / Refactor | Workflow/design vocabulary, not user-facing |
180
+ | `over-length` | > 50 visible chars | Too long = probably explaining implementation |
181
+ | `path-fragment` | `docs/` / `bin/` / `tests/` / `scripts/` outside backticks | Source-tree layout is maintainer concern |
182
+
183
+ **Rewrite loop (max 2 rounds)**:
184
+ 1. Show the agent the violation list + original bullet → request rewrite
185
+ 2. Re-lint the rewrite
186
+ 3. If still violating after 2 rewrites → **keep the bullet but prefix `⚠️ `** so
187
+ the human reviewer can spot it, AND append a line to
188
+ `~/.shared/roll/loop/ALERT.md` describing what couldn't be fixed.
189
+ Never block the whole release on style — let the human take the wheel.
190
+
191
+ ### 5.5 Self-Audit — Stage Gate
192
+
193
+ The mechanical linter in Step 5.4 catches **blacklist** patterns; the audit
194
+ gate in this step is a **whitelist** of 5 boolean checks that the bullet must
195
+ satisfy before it can be staged. Stricter (30-char cap vs 50) and shape-aware
196
+ (requires the user-facing `—` or `不再`/`现在` sentence pattern).
197
+
198
+ ```bash
199
+ accepted=$(_changelog_audit_gate "$draft1" "$rewrite1" "$rewrite2")
200
+ # Exit 0: stdout = first clean candidate
201
+ # Exit 1: stdout = ⚠️-prefixed last candidate; ALERT was written
202
+ ```
203
+
204
+ 5 boolean checks evaluated by `_changelog_audit_bullet`:
205
+
206
+ | Tag | Trigger |
207
+ |---|---|
208
+ | `over-length-30` | visible chars > 30 (bypassed if bullet has a backticked user command) |
209
+ | `internal-id` | backtick content contains `_` or `()` |
210
+ | `path-or-suffix` | `.md/.sh/.yml/.ts/.bats` or `docs/bin/tests/scripts/` outside backticks |
211
+ | `phase-step` | `Phase N` or `Step N` |
212
+ | `bad-shape` | none of `—`, `不再`, `现在` present |
213
+
214
+ **3-round retry envelope**:
215
+ 1. Round 1 = original draft; if pass → stage immediately
216
+ 2. Round 2 = rewrite based on violations from round 1
217
+ 3. Round 3 = second rewrite if round 2 also failed
218
+ 4. All 3 failed → keep last candidate prefixed with `⚠️ `, append ALERT to
219
+ `~/.shared/roll/loop/ALERT.md`. **Never block the stage** — let the human
220
+ reviewer take the wheel. The loop must keep moving.
221
+
222
+ **Audit log**: every round (pass or fail) appends one JSONL line to
223
+ `~/.shared/roll/loop/changelog-audit.jsonl`:
224
+
225
+ ```json
226
+ {"ts":"2026-05-13T13:50:00Z","verdict":"fail","round":1,"bullet":"...","reasons":["over-length-30","bad-shape"]}
227
+ ```
228
+
229
+ Useful when reviewing whether the agent actually iterated or just rubber-stamped
230
+ its own first draft. Set `ROLL_CHANGELOG_AUDIT_LOG` to redirect (tests only).
231
+
136
232
  ### 5. Generate CHANGELOG.md
137
233
 
138
234
  **Create mode** (first time, no CHANGELOG.md yet):
@@ -190,3 +286,74 @@ After successful deploy in `$roll-build` / `$roll-fix`:
190
286
  **Post-Deploy:**
191
287
  - `$roll-.changelog` - Sync external changelog
192
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**` 前缀,分组标题已经承担了语义分类的职责。
@@ -222,6 +222,8 @@ section of BACKLOG.md:
222
222
  | REFACTOR-XXX | {one-line description} — flagged by dream {YYYY-MM-DD} | 📋 Todo |
223
223
  ```
224
224
 
225
+ `{one-line description}` 写法:一句人话说清楚"什么地方需要改"以及"不改会怎样"。不写函数名、文件路径、技术方案。例:`loop 状态读取逻辑分散在多处,修一处容易遗漏另一处`。
226
+
225
227
  **Threshold**: only flag items where the fix would meaningfully reduce
226
228
  complexity or prevent future bugs. Ignore cosmetic issues.
227
229
 
@@ -284,9 +284,11 @@ When any signal appears, **do not stop — flag it**:
284
284
  **REFACTOR entry format in BACKLOG.md:**
285
285
 
286
286
  ```markdown
287
- | REFACTOR-001 | Extract payment boundary — leaking into Order module during US-AUTH-003 | 📋 Todo |
287
+ | REFACTOR-001 | {one-line plain-language description} | 📋 Todo |
288
288
  ```
289
289
 
290
+ 描述写法:参见 AGENTS.md "Backlog descriptions" 规则。说清楚"什么需要改"以及"不改会怎样",技术细节写在 `docs/features/refactor-log.md`。
291
+
290
292
  **refactor-log.md entry format:**
291
293
 
292
294
  ```markdown
@@ -625,6 +625,8 @@ DOMAIN_DIR="docs/domain/"
625
625
  | [US-{DOMAIN}-{N}](docs/features/<feature>.md#us-{domain}-{n}) | {one-line description} | 📋 Todo |
626
626
  ```
627
627
 
628
+ `{one-line description}` 写法:用户能读懂的一句话,说清楚"能做什么"或"解决了什么麻烦"。不写实现细节、文件路径、函数名。细节和 AC 写在 `docs/features/` 里。写好了可以直接当 CHANGELOG 条目用。
629
+
628
630
  Note: `{DOMAIN}` maps to the Bounded Context name identified in DDD analysis.
629
631
 
630
632
  **US section in docs/features/\<feature\>.md (full details):**
@@ -180,6 +180,33 @@ If serve mode is available, prefer HTTP transport over direct CLI invocation.
180
180
 
181
181
  **CLI vs. API Key**: `claude`, `deepseek`, `kimi`, `codex` CLIs authenticate via existing subscription accounts — no separate API key required. This is the primary advantage of CLI transport over the MCP/HTTP approach.
182
182
 
183
+ ## Inline Display Mode (Manual Triggers)
184
+
185
+ When peer review is manually triggered by a human (via `/peer`, "叫上 peer", etc.), the executing agent **must display each round inline in the current conversation**. This applies regardless of which agent is executing — Claude, DeepSeek, Kimi, PI, or any other.
186
+
187
+ **Per-round display format:**
188
+
189
+ ```
190
+ ─── Peer Review · Round N ───────────────────────────────
191
+ → Sending to [peer]:
192
+ {full message sent to peer}
193
+
194
+ ← [peer] responds:
195
+ {peer's full response, verbatim}
196
+
197
+ ◆ My analysis: {Claude/executing agent's reaction and position for this round}
198
+ ─────────────────────────────────────────────────────────
199
+ ```
200
+
201
+ **Rules:**
202
+ - Peer CLI calls must be **synchronous** (do NOT use background/async execution).
203
+ - Show the outgoing message **before** calling the peer, so the user sees what's being asked.
204
+ - Relay the peer's response **verbatim** before adding your own analysis.
205
+ - If a peer call fails or times out, report it immediately inline and either retry or ESCALATE.
206
+ - Negotiation log is still written to `~/.shared/roll/peer/logs/` as usual.
207
+
208
+ **Why inline, not tmux:** When a human manually triggers peer review inside an agent's interactive session, the conversation IS the visible interface. tmux auto-attach is only relevant for CLI-launched background sessions (`bin/roll peer`), not for skill invocations.
209
+
183
210
  ## Workflow Integration
184
211
 
185
212
  ### `roll-build` Plan Mode
@@ -74,7 +74,11 @@ Generate between 1 and 3 proposals. For each:
74
74
 
75
75
  ```
76
76
  ## PROPOSAL: {Short title}
77
+ ```
78
+
79
+ `{Short title}` 写法:用户看得懂的一句话,说清楚"加什么"或"解决什么问题"。不用技术术语,不提内部实现。这句话批准后会直接成为 BACKLOG 里的描述。
77
80
 
81
+ ```
78
82
  **Motivation (why):**
79
83
  One to two sentences from the user's perspective explaining the pain or opportunity.
80
84