@seanyao/roll 4.630.1 → 4.702.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.
Files changed (108) hide show
  1. package/CHANGELOG.md +74 -0
  2. package/README.md +65 -55
  3. package/conventions/global/AGENTS.md +8 -7
  4. package/dist/roll.mjs +13277 -8746
  5. package/docs/INDEX.md +32 -0
  6. package/docs/architecture.md +444 -0
  7. package/docs/difftest-freeze-paradigm.md +113 -0
  8. package/docs/live-console.md +203 -0
  9. package/docs/manifesto.md +65 -0
  10. package/docs/migration/role-taxonomy-v4.md +60 -0
  11. package/docs/verification.md +83 -0
  12. package/guide/INDEX.md +86 -0
  13. package/guide/assets/layouts/cards-2.png +0 -0
  14. package/guide/assets/layouts/cards-3.png +0 -0
  15. package/guide/assets/layouts/cards-4.png +0 -0
  16. package/guide/assets/layouts/compare.png +0 -0
  17. package/guide/assets/layouts/highlight.png +0 -0
  18. package/guide/assets/layouts/pipeline.png +0 -0
  19. package/guide/assets/layouts/plain.png +0 -0
  20. package/guide/assets/layouts/quote.png +0 -0
  21. package/guide/assets/layouts/timeline.png +0 -0
  22. package/guide/en/acceptance-evidence.md +231 -0
  23. package/guide/en/ai-agents.md +185 -0
  24. package/guide/en/backlog-github-sync.md +108 -0
  25. package/guide/en/changelog.md +66 -0
  26. package/guide/en/configuration.md +112 -0
  27. package/guide/en/consistency.md +58 -0
  28. package/guide/en/conventions.md +113 -0
  29. package/guide/en/dream.md +121 -0
  30. package/guide/en/faq.md +855 -0
  31. package/guide/en/feedback.md +31 -0
  32. package/guide/en/getting-started.md +103 -0
  33. package/guide/en/installation.md +86 -0
  34. package/guide/en/legacy-onboarding.md +195 -0
  35. package/guide/en/loop-data-layout.md +256 -0
  36. package/guide/en/loop-driven-architecture.md +186 -0
  37. package/guide/en/loop.md +1324 -0
  38. package/guide/en/methodology.md +715 -0
  39. package/guide/en/migration-2.0.md +154 -0
  40. package/guide/en/overview.md +190 -0
  41. package/guide/en/pairing.md +151 -0
  42. package/guide/en/patterns/README.md +76 -0
  43. package/guide/en/patterns/graft-pattern.md +110 -0
  44. package/guide/en/patterns/replant-pattern.md +114 -0
  45. package/guide/en/patterns/seed-pattern.md +132 -0
  46. package/guide/en/peer.md +71 -0
  47. package/guide/en/pr-review.md +62 -0
  48. package/guide/en/practices/engineering-common-sense.md +395 -0
  49. package/guide/en/pricing.md +116 -0
  50. package/guide/en/project-setup.md +126 -0
  51. package/guide/en/roll-doc-audit.md +98 -0
  52. package/guide/en/skills.md +206 -0
  53. package/guide/en/test-isolation.md +51 -0
  54. package/guide/en/testing/quality-rubric.md +340 -0
  55. package/guide/en/testing.md +123 -0
  56. package/guide/en/tools.md +173 -0
  57. package/guide/skills.md +30 -0
  58. package/guide/zh/acceptance-evidence.md +194 -0
  59. package/guide/zh/ai-agents.md +170 -0
  60. package/guide/zh/backlog-github-sync.md +105 -0
  61. package/guide/zh/changelog.md +57 -0
  62. package/guide/zh/configuration.md +99 -0
  63. package/guide/zh/consistency.md +48 -0
  64. package/guide/zh/conventions.md +96 -0
  65. package/guide/zh/dream.md +97 -0
  66. package/guide/zh/faq.md +773 -0
  67. package/guide/zh/feedback.md +30 -0
  68. package/guide/zh/getting-started.md +96 -0
  69. package/guide/zh/installation.md +83 -0
  70. package/guide/zh/legacy-onboarding.md +192 -0
  71. package/guide/zh/loop-data-layout.md +236 -0
  72. package/guide/zh/loop-driven-architecture.md +186 -0
  73. package/guide/zh/loop.md +1124 -0
  74. package/guide/zh/methodology.md +702 -0
  75. package/guide/zh/migration-2.0.md +154 -0
  76. package/guide/zh/overview.md +186 -0
  77. package/guide/zh/pairing.md +117 -0
  78. package/guide/zh/patterns/README.md +74 -0
  79. package/guide/zh/patterns/graft-pattern.md +108 -0
  80. package/guide/zh/patterns/replant-pattern.md +112 -0
  81. package/guide/zh/patterns/seed-pattern.md +130 -0
  82. package/guide/zh/peer.md +63 -0
  83. package/guide/zh/pr-review.md +54 -0
  84. package/guide/zh/practices/engineering-common-sense.md +393 -0
  85. package/guide/zh/pricing.md +97 -0
  86. package/guide/zh/project-setup.md +114 -0
  87. package/guide/zh/roll-doc-audit.md +90 -0
  88. package/guide/zh/skills.md +191 -0
  89. package/guide/zh/test-isolation.md +46 -0
  90. package/guide/zh/testing/quality-rubric.md +284 -0
  91. package/guide/zh/testing.md +116 -0
  92. package/guide/zh/tools.md +173 -0
  93. package/package.json +4 -1
  94. package/skills/README.md +1 -0
  95. package/skills/roll-.qa/SKILL.md +1 -1
  96. package/skills/roll-.review/SKILL.md +1 -1
  97. package/skills/roll-build/SKILL.md +1 -1
  98. package/skills/roll-build/references/full-contract.md +16 -13
  99. package/skills/roll-design/SKILL.md +3 -3
  100. package/skills/roll-design/references/full-contract.md +17 -13
  101. package/skills/roll-fix/SKILL.md +1 -1
  102. package/skills/roll-fix/references/full-contract.md +13 -10
  103. package/skills/roll-peer/SKILL.md +1 -1
  104. package/skills/roll-prime/SKILL.md +77 -0
  105. package/skills/roll-prime/references/explorer-annex.md +39 -0
  106. package/skills/roll-prime/references/supervisor-prompt.md +165 -0
  107. package/skills/route-cases/skills.json +10 -0
  108. package/template/AGENTS.md +3 -1
@@ -0,0 +1,1124 @@
1
+ # roll loop — 自主 BACKLOG 执行器
2
+
3
+ `roll loop` 负责调度和管理 BACKLOG 故事的自主执行。
4
+ 开启后,loop 按可配置的频次(在活跃窗口内)醒来,摘取最高优先级的待办故事,
5
+ 通过 TCR 微提交完成代码交付。
6
+
7
+ `roll loop on` 是显式切入 **autonomous** 模式;`roll loop off` 或
8
+ `roll loop pause` 回到 **guided**,此时 owner 通过 `roll supervisor next/why`
9
+ 判断下一步,并显式启动任何长时间 Story 工作。`roll loop resume` 会切回
10
+ autonomous,不改变 agent binding。
11
+
12
+ ## 工作原理
13
+
14
+ 1. 读取 `BACKLOG.md`,摘取优先级最高的 `📋 Todo` 条目。
15
+ 2. 将其标记为 `🔨 In Progress` 并提交。
16
+ 3. 调用 `$roll-build <story-id>` 或 `$roll-fix <bug-id>`。
17
+ 4. 成功后:标记为 `✅ Done`,提交,追加一条记录到 `runs.jsonl`。
18
+ 5. 失败后:回退为 `📋 Todo`,写入 `ALERT.md` 告警。
19
+
20
+ Loop 在名为 **`roll-loop-<project-slug>`** 的 **tmux session** 里运行。
21
+ 未静音时,终端窗口会自动弹出,你可以实时旁观 AI 干活。
22
+
23
+ ## 调度配置
24
+
25
+ Loop 通过 **launchd**(macOS)调度。默认每小时在一个根据项目路径推导出的分钟触发
26
+ (不同项目自动错开,避免碰撞)。
27
+
28
+ ```
29
+ 活跃窗口:0–24(默认全天开启;用 `roll config loop-window 10-18` 收窄到上午 10 点 — 下午 6 点)
30
+ ```
31
+
32
+ 活跃窗口之外,loop 静默退出,不执行任何操作。默认 `0-24` 意味着任何时刻触发都会执行;只有显式收窄窗口后,窗口外的触发才会静默退出。
33
+
34
+ ## 配置调度(Configuring the schedule)
35
+
36
+ 不用再手工编辑 `~/.roll/config.yaml` 和 `.roll/local.yaml` 再祈祷 launchd plist 能 reload 上。改用 `roll config` 命令族:每次写入都落到正确的 yaml 文件,**并且**自动重生 runner、重 bootstrap launchd plist、立刻在 `roll loop status` 反映——没有手工 reload 这一步。
37
+
38
+ | 命令 | 设置的内容 |
39
+ |------|-----------|
40
+ | `roll config loop-window <start>-<end>` | loop 活跃窗口小时(`loop_active_start` + `loop_active_end`) |
41
+ | `roll config loop-schedule <period>[/<offset>]` | 触发间隔(`loop_schedule.period_minutes` + `offset_minute`) |
42
+ | `roll config dream-time <HH:MM>` | dream 每日触发时刻(`loop_dream_hour` + `loop_dream_minute`) |
43
+
44
+ ```bash
45
+ roll config loop-window 9-18 # 活跃 9 点 – 18 点;start < end,均在 [0,24]
46
+ roll config loop-schedule 30 # 每 30 分钟触发(period 在 [1,1440])
47
+ roll config loop-schedule 30/7 # 每 30 分钟,偏移 :07(offset 在 [0, period-1])
48
+ roll config dream-time 03:20 # dream 精确 03:20 触发;HH 在 [0,23],MM 在 [0,59]
49
+ ```
50
+
51
+ **读当前值(Reading the current value)。** 任何 facade 不带值跑一次,就打印当前生效组合和来源:
52
+
53
+ ```bash
54
+ roll config loop-window # loop-window: 0-24 (from default)
55
+ roll config dream-time # dream-time: 03:20 (from ~/.roll/config.yaml)
56
+ ```
57
+
58
+ **范围校验。** 超界或非数字输入会被拒绝,并按当前语言返回错误,退出码为 2。
59
+ 例如 `ROLL_LANG=zh roll config loop-window 9-25` 会打印
60
+ `loop-window 结束时间必须 ≤ 24`。
61
+
62
+ **`--global` vs `--project`。** 写入默认 `--project`(`.roll/local.yaml`,仅当前项目)。加 `--global` 写 `~/.roll/config.yaml`,作为所有没有项目级覆盖的项目的默认值。
63
+
64
+ ```bash
65
+ roll config dream-time 03:20 # 当前项目(.roll/local.yaml)
66
+ roll config dream-time 03:20 --global # 所有项目(~/.roll/config.yaml)
67
+ ```
68
+
69
+ **自动 reload(Auto-reload)。** 写完调度 key 后,`roll config` 自动重装 loop / pr / dream 的 launchd plist,下个窗口即生效。reload 失败时(如沙箱里)yaml 仍是真相——跑 `roll loop on` 手工补刀。完整 key 列表与范围见 `roll config --help`。
70
+
71
+ ### 项目级触发频次
72
+
73
+ 一行命令设触发间隔:
74
+
75
+ ```bash
76
+ roll config loop-schedule 30 # 每 30 分钟(period 1–1440,任意间隔)
77
+ roll config loop-schedule 45 # 每 45 分钟(不再限制为 60 的约数)
78
+ ```
79
+
80
+ 这会向 `.roll/local.yaml` 写入 `loop_schedule` 块:
81
+
82
+ ```yaml
83
+ loop_schedule:
84
+ period_minutes: 30 # 1-1440(任意分钟间隔)
85
+ offset_minute: 7 # 0–(period-1)(已废弃,仅向后兼容)
86
+ ```
87
+
88
+ - `period_minutes` — loop 触发间隔。任意值 1–1440。
89
+ - `offset_minute` — (US-LOOP-032 起已废弃)不再影响触发时刻,保留用于向后兼容。
90
+
91
+ 如果没有 `.roll/local.yaml` 或没有 `loop_schedule` 配置,Roll 回退到全局值
92
+ (用 `roll config loop-schedule … --global` 设置),或者根据项目路径哈希推导默认值。
93
+
94
+ `roll loop status` 和 `roll loop on` 直接显示实际频次,一眼可确认生效。
95
+ 非法值(如 `period_minutes: 0` 或 `1441`)在写入时即被拒绝,退出码 2。
96
+
97
+ ### 全局默认(向后兼容)
98
+
99
+ 若所有项目使用同一个全局默认,用 `--global` 写入:
100
+
101
+ ```bash
102
+ roll config loop-window 10-18 --global # 所有项目的活跃窗口
103
+ roll config loop-schedule 60 --global # 所有项目的默认间隔
104
+ ```
105
+
106
+ (agent 选择不再是全局配置项,而是从 Machine Scope 与 Project Scope 的 agent 文件解析。
107
+ 见 [自主角色解析](#自主角色解析)。)
108
+
109
+ 项目级 `.roll/local.yaml` 始终优先于全局默认。
110
+
111
+ ## 子命令参考
112
+
113
+ ```bash
114
+ roll loop on # 安装 launchd 调度器(loop + pr + dream 三个服务)
115
+ roll loop off # 卸载 launchd 调度器
116
+
117
+ roll loop now # 立即执行一次循环(与 launchd 触发的流程完全一致)
118
+ roll loop test # 快速冒烟测试:验证 tmux/弹窗/流式输出链路是否正常
119
+
120
+ roll loop status # 显示调度器状态和当前 loop 状态
121
+ roll loop watch # 默认 owner 视图:phase、quiet 时间、TCR 数、last signal + 实时活动
122
+ roll loop watch -n 50 # 跟随前回看 50 行(默认 200;'all' = 整份日志)
123
+ roll loop watch --events # 从 .roll/loop/events.ndjson 渲染 compact 事件流
124
+ roll loop watch --raw-events # 原样打印 JSON 事件流,仅用于审计/排障
125
+ roll loop watch --verbose # 同时显示原始 agent 转写(默认折叠)
126
+ roll loop watch --attach # 以只读方式 attach 到 loop 的 tmux 观测窗(tmux attach -r)
127
+ roll loop go # 手动运行 goal mode,默认覆盖全部 backlog,直到完成/暂停/触发护栏
128
+ roll loop go --epic <name> # 将 goal 限定到一个 epic
129
+ roll loop go --cards US-1,FIX-2 # 将 goal 限定到指定卡片
130
+ roll loop go --budget 10 # goal 成本达到 $10 后保守停止
131
+ roll loop go --usage-threshold 0.85 # 5 小时或 7 天用量达到该比例后暂停
132
+ roll loop go --no-wait # 触发用量闸后直接暂停返回,不等窗口恢复
133
+ roll loop go --for 5h # 到时间盒后等当前 cycle 收尾再停
134
+ roll loop go --max-cycles 3 # 跑满指定 cycle 数后停止
135
+ roll loop go --review <auto|hetero|self|off> # 设置完成前终审策略
136
+ roll loop goal # 显示持久化 goal 状态、范围、终审模式、用量、限制和安全闸
137
+
138
+ roll loop runs # 显示最近 10 次运行摘要(故事 ID、tcr 提交数、耗时、最慢阶段)
139
+ roll loop runs 20 # 显示最近 20 次
140
+ roll loop runs --all # 显示本机所有项目的运行历史
141
+ roll loop runs --detail <cycle_id> # 打印单个 cycle 的阶段耗时面板
142
+
143
+ roll loop story <ID> # 按故事汇总:所有 cycle 数、耗时、token、费用、PR
144
+ roll loop story <ID> --json # JSON 输出,方便脚本和仪表盘消费
145
+
146
+ roll loop eval # 近 14 轮已评分 cycle 的结果评分趋势(客观)
147
+ roll loop eval 30 # 窗口放大到近 30 轮已评分 cycle
148
+ roll loop signals # 把反复出现的低分模式暴露成改善信号
149
+ roll loop signals --streak 4 # 连续 4 轮低分才触发信号
150
+
151
+ roll loop watch # 推荐的日常实时视图
152
+ tmux attach -t roll-loop-<project-slug> # 只读 tmux 观测窗;需要 pane 时使用
153
+ roll loop mute # 关闭自动弹窗(loop 继续在 tmux 里跑)
154
+ roll loop unmute # 重新开启自动弹窗
155
+
156
+ roll loop pause # 暂停调度(保留 plist,跳过执行)
157
+ roll loop resume # 暂停后恢复调度
158
+
159
+ roll loop reset # 清除 loop 状态(下次触发时重新开始)
160
+
161
+ roll loop gc # 清理孤儿 slug、临时文件、过期备份(默认保留 30 天)
162
+ roll loop gc --dry-run # 预览将被清理的内容,不实际删除
163
+ roll loop gc --keep-days 14 # 覆盖保留天数(也可用 .roll/local.yaml 中的 loop_gc.retention_days)
164
+ # 完整 gc 手册见 guide/zh/loop-data-layout.md
165
+
166
+ # loop 相关分支:`git ls-remote --heads origin 'loop/*'`(branches 子命令已退役)
167
+
168
+ roll loop events # 显示最近 20 条 cycle 事件
169
+ roll loop events 50 # 显示最近 50 条
170
+
171
+ roll agent # 查看 scope、role、agent pool 与 legacy 输入
172
+ roll agent list # 查看本机已装的 agent
173
+ ```
174
+
175
+ ### Goal Mode 与定时模式
176
+
177
+ `roll loop go` 是手动 goal session,不是 launchd 定时 tick。运行期间 Roll 会持有
178
+ `.roll/loop/go.lock`;定时 tick 看到该锁就让路,记录 `goal:tick_skipped`,不会再启动
179
+ 另一个 `roll loop run-once`。
180
+
181
+ goal mode 在 scheduler off 时也能运行,因为它自己启动会话,不依赖 launchd。loop 处于
182
+ paused 状态时不建议直接启动:`PAUSE-<slug>` 标记仍会在 cycle 边界生效,所以应先执行
183
+ `roll loop resume`,再启动 `roll loop go`。
184
+
185
+ ### Goal Mode 安全闸
186
+
187
+ 预算与运行上限每次 `roll loop go` 都是显式的。`--budget`、`--max-cycles`、`--for`
188
+ 只对本次调用生效;省略某项即代表本轮不设该限制——Roll 绝不从上一次会话持久化的
189
+ goal 静默沿用预算或上限,因此一条不带 flag 的 `roll loop go` 既不会被几天前设的
190
+ 上限封顶,也不会被它卡死。范围(`--epic`/`--cards`)与 `--review` 在省略时仍沿用,
191
+ 因为它们是 goal 的身份,而不是每次运行的安全旋钮。
192
+
193
+ `roll loop go` 的安全闸只在 cycle 边界生效。`--budget <usd>` 使用有效成本账本;
194
+ 达到预算时 goal 进入 `budget_limited`。未执行 agent 的 idle 或 aborted 周期记为
195
+ 已知 $0,不算 unknown cost 行;只有真正执行了 agent 却测不到可解析用量的行才记为
196
+ unknown,这类行仍按保守侧停止,不当作 0。用量闸检查 5 小时与 7 天窗口;默认 85%
197
+ 暂停并等待窗口恢复,`--no-wait` 则停下等 owner。恢复等待是有界的——卡死的用量 API
198
+ 不会让会话无限停摆;超时后 Roll 记录 `usage_wait_timeout` 审计事件并让 goal 保持暂停。
199
+ `--for <duration>` 是墙钟时间盒:当前 cycle 收尾后,goal 以 `timebox` 原因暂停。
200
+
201
+ 每次安全闸触发都会记录 `goal:gate_tripped`,`roll loop goal` 会显示最近一次安全闸读数。
202
+
203
+ ### `roll loop goal` 字段含义
204
+
205
+ `roll loop goal` 是 `.roll/loop/goal.yaml` 与最新 goal 事件的读取面。关键字段:
206
+
207
+ | 字段 | 含义 |
208
+ |------|------|
209
+ | `Status` | `active`、`paused`、`budget_limited` 或 `complete`。 |
210
+ | `Scope` | 全 backlog、单个 epic 或显式卡片列表。 |
211
+ | `Review` | 完成前终审策略:`auto`、`hetero`、`self` 或 `off`。 |
212
+ | `Usage` | goal 已跑 cycle 数、有效成本、unknown cost 行数。 |
213
+ | `Limits` | 显式传入的 `--budget`、`--max-cycles`、`--for` 限制。 |
214
+ | `Safety gate` | 最近一次预算、用量或时间盒闸及其读数。 |
215
+ | `Last decision` | goal 继续、暂停、预算限停或完成的原因。 |
216
+
217
+ `auto` 终审降级为同 provider review 时,状态视图会显示 `goal:review_degraded`
218
+ 记录的降级原因。goal 暂不能完成时,`Last decision` 会带上未达成的真相裁定原因
219
+ 或终审拒绝原因。
220
+
221
+ ### Goal Mode 终审
222
+
223
+ `roll loop go` 将 goal 状态持久化在 `.roll/loop/goal.yaml`,并在 goal 进入
224
+ `complete` 前执行终审 gate。默认策略是 `--review auto`:Roll 按排序依次尝试与工
225
+ 作 agent 不同 provider 家族的 reviewer;当所有异构候选都失败、或本机只有同
226
+ provider 可用时,会降级为 self review,并记录 `goal:review_degraded` 事件。
227
+
228
+ 终审使用与 `-peer` skill 相同的结构化 adapter。`goal:final_review` 事件会记录
229
+ reviewer agent、provider、command family、verdict、findings、timeout/error 状态、
230
+ 耗时,以及可用时的 transcript/evidence 路径。瞬时崩溃会轮换到下一个排序候选;全
231
+ 部候选都失败时 Roll 会在 `ERROR` verdict 上记录真实错误原因并抛出 ALERT,而不是
232
+ 塌成无原因的通用 error。
233
+
234
+ 当 completion 必须在缺少异构 reviewer 时 fail-closed,用 `--review hetero`。
235
+ 允许同 provider 终审时,用 `--review self`。`--review off` 只应作为显式人工豁免:
236
+ Roll 会跳过终审 gate,但仍记录 `verdict: SKIPPED` 的 `goal:final_review` 事件。
237
+
238
+ ## 自主角色解析
239
+
240
+ Loop 通过 scoped Agent 模型解析 agent:
241
+
242
+ ```text
243
+ Scope -> Role -> Binding -> Agent -> optional Model
244
+ ```
245
+
246
+ Machine Scope 在 `~/.roll/agents.yaml`;Project Scope 在 `.roll/agents.yaml`。
247
+ 每个 cycle 会解析 `story.execute` 给 Builder,解析 `story.evaluate` 给评审/打分。
248
+ Project 可通过 `inherits: machine` 继承本机 agent pool。
249
+
250
+ ```yaml
251
+ schema: roll-agents/v1
252
+ scope: project
253
+ inherits: machine
254
+ defaults:
255
+ story:
256
+ roles:
257
+ execute:
258
+ kind: select
259
+ from: [kimi, codex, pi]
260
+ require: [execute]
261
+ strategy: first-available
262
+ evaluate:
263
+ kind: select
264
+ from: [claude, codex, kimi, pi, agy, reasonix]
265
+ require: [evaluate]
266
+ strategy: health-aware
267
+ ```
268
+
269
+ `roll agent` 会显示 Machine Scope、Project Scope、已解析角色、候选池、运行时健康说明,
270
+ 以及 legacy compatibility 输入。用 `roll agent migrate --dry-run` 预览旧 agent
271
+ 文件迁移。
272
+
273
+ 运行时健康不是静态策略。auth、网络、VPN、账号或 binary 缺失只会让候选在本次
274
+ resolution 中被跳过,并记录为运行时事实。若没有候选可用,loop 会 PAUSE + ALERT,
275
+ 而不是悄悄改写静态 pool。
276
+
277
+ ### Agent 工具链健康检查(US-V4-022)
278
+
279
+ 调度前,Supervisor 还会从持久事件流中归类 agent 工具链健康信号:auth block、
280
+ network block、setup/skill-root 污染、worktree 权限失败。污染类信号会被作为 FIX
281
+ 路由给 delta team,而不会被误标为 auth 失败。可用 `roll supervisor health` 查看
282
+ 专用面板,或从 `roll supervisor next` / `roll supervisor why` 读取摘要。
283
+
284
+ ### Agent 自降级(too_big 判定)
285
+
286
+ 选定的 agent 在 `roll-build` / `roll-fix` SKILL 的 **Pre-flight self-check**
287
+ 阶段自评。判定 too_big 时输出:
288
+
289
+ ```yaml
290
+ verdict: too_big
291
+ reason: est_min=20 > pi.max=8
292
+ ```
293
+
294
+ The self-downgrade flow then: invoke `roll-design --from-story <id>` to
295
+ re-split with `chain_depth + 1`, flip the parent story to 🚫 Hold, exit the
296
+ cycle cleanly. Next cycle picks up the first smaller sub-story.
297
+
298
+ 链路最多自动拆 **2 次**。第三次会被 `StorySplitCapHit` ALERT 拦下,翻 🚫 Hold
299
+ 等人工介入,避免无限套娃。
300
+
301
+ ## 执行剖面(standard / verified / designed)
302
+
303
+ 上面的路由为某个槽选 **Rig**(`agent × model`)。另一件独立的事是:Roll 为每张
304
+ Story 选一个**执行剖面**——按这张 Story 的风险与 ROI 选**最便宜够用**的角色流水线。
305
+ 你不必声明它;它在 cycle 开始时按 story 的风险信号选一次,并记入 `execution:profile`
306
+ 事件。它们**不是用户面的"团队形状"**,而是风险/ROI 档:
307
+
308
+ - **`standard` = 仅 execute** —— 低风险、范围局部、AC 清晰、证据风险低(改文案、小
309
+ parser bug、内部重命名)。
310
+ - **`verified` = execute → evaluate** —— 用户可见行为、需截图/视觉证据,或历史证据
311
+ 薄弱。独立 `evaluate` 角色(fresh session)裁定交付;blocking review、score、attest 是
312
+ 三个分开的维度。
313
+ - **`designed` = Designer -> Builder -> Evaluator** —— 风险是"做错事":需求不清、跨模块、
314
+ 或触及 truth/release/路由/状态语义。Designer contract 先写契约再进入 execute;evaluate 做
315
+ design-contract-vs-delivered 映射。evaluate → execute 的修复回合受硬熔断约束(最大轮数、
316
+ 重复 finding 签名、预算、超时),触界即升级。
317
+
318
+ 角色之间只通过 artifact 交接(Designer contract、builder 证据、eval-report),绝不共享原始
319
+ 会话。跨 Story 的项目级协调(排序、冲突、预算、发布就绪)归 **Supervisor**
320
+ (`roll supervisor`),不属于任何单张 Story 的执行。
321
+
322
+ 当 owner 要求清空 backlog 时,Supervisor 使用 backlog-clearing standard,而不是只看
323
+ 缺陷队列。默认 scope 是所有 live 且非 Hold 的 `FIX-*`、`US-*`、`REFACTOR-*` 行。启动
324
+ 下一张卡之前,它先对账 backlog truth、open PR、CI/evaluator gate、近期 cycle 结尾、
325
+ manual-merge PR 和 `.roll` meta 状态。每张卡单独选择 fresh Builder;执行剖面需要时,
326
+ 再从当前 Agent roster 中选择独立 Evaluator/Scorer。重复失败、zero TCR、缺少证据、
327
+ 解析失败、auth/permission block 或 `[roll:manual-merge]` PR 都会停止继续调度,直到
328
+ owner 处理 `roll supervisor status/next/why` 给出的行动。
329
+
330
+ ## Cycle 角色可观测
331
+
332
+ 一个 v4 cycle 是多 agent 协作:Builder 写代码,一个或多个 Peer Reviewer 复检
333
+ 有风险的 diff,Evaluator/Scorer 给交付打分,Attest Gate 决定结果是否可被采纳。
334
+ 底层真相在 `events.ndjson` 和 peer/证据产物里,但你不该靠手工 grep 才能回答首跑
335
+ 最关心的那个问题:**谁是 Builder,谁是 Evaluator?**
336
+
337
+ 有三个面回答这个问题。
338
+
339
+ ### `roll loop cycle <id> --roles`
340
+
341
+ ```bash
342
+ roll loop cycle <id> --roles # 单个 cycle 的人类可读执行阵容
343
+ roll loop cycle <id> --roles --json # 同样的事实,输出 cycle-role-summary.v1 JSON
344
+ ```
345
+
346
+ roles 视图渲染单个 cycle 的完整角色链——Builder、Peer Review、
347
+ Evaluator / Score、Gates:
348
+
349
+ ```text
350
+ # Cycle Role Summary — 20260629-112437-39253
351
+
352
+ Story: US-TASK-001
353
+ Execution profile: standard
354
+
355
+ ## Builder
356
+ - pi / deepseek-v4-pro
357
+ - log: .roll/loop/cycle-logs/20260629-112437-39253.agent.log
358
+
359
+ ## Peer Review
360
+ - reasonix: accepted verdict=refine findings=0
361
+ - kimi: returned reviewed, no structured verdict accepted
362
+ - codex: returned reviewed, no structured verdict accepted
363
+
364
+ ## Evaluator / Score
365
+ - reasonix: accepted score=10 verdict=good
366
+ - agy: failed unparseable (control characters before SCORE)
367
+ - kimi: selected, no accepted score
368
+
369
+ ## Gates
370
+ - peer: consulted
371
+ - attest: produced
372
+ ```
373
+
374
+ 命令先读缓存的 summary 产物,缺失或损坏时从 `events.ndjson` 重建,所以对新 cycle
375
+ 和归档 cycle 都能用。
376
+
377
+ ### `summary.md` / `summary.json` 产物
378
+
379
+ 每个 cycle 都把同一份角色阵容写到磁盘,让交付报告或同事不必重跑 CLI 就能读:
380
+
381
+ ```text
382
+ .roll/loop/cycle-logs/<cycle-id>/summary.md # 上面那份 markdown
383
+ .roll/loop/cycle-logs/<cycle-id>/summary.json # cycle-role-summary.v1,机器可读
384
+ ```
385
+
386
+ `summary.json` 为每次 agent 参与记一条 `CycleRoleAttempt`,含角色、agent、model、
387
+ session id、stage、state,以及(相关时)verdict、score、findings、解析失败原因和
388
+ 产物路径。
389
+
390
+ ### Execution Cast 报告块
391
+
392
+ 故事的 attest 报告内嵌一个 **Execution Cast**(执行阵容,🎭)块,把同一份 summary
393
+ 投影进交付视图,让角色链随证据一起流动。没有角色 summary 时该块优雅降级为
394
+ "角色摘要不可用"。被采纳的产物会直接链接——例如 `accepted evaluator artifact`
395
+ 链接指向 gate 实际采用的那份 scorer 输出。
396
+
397
+ ### selected vs returned vs accepted
398
+
399
+ 每个 agent 的 `state` 是正确读阵容的关键。这些状态形成一条阶梯,
400
+ **被选中或返回了输出,并不等于被 gate 采纳**:
401
+
402
+ | 状态 | 含义 |
403
+ |------|------|
404
+ | `selected` | agent 被选中参与该 stage,但没有产出被采纳的结果。 |
405
+ | `started` | agent 开始了该 stage。 |
406
+ | `returned` | reviewer 返回了输出,但没有结构化 verdict 被采纳。 |
407
+ | `parsed` | 结构化输出被解析。 |
408
+ | `accepted` | gate 采纳了这次尝试——这才是算数的 verdict/score。 |
409
+ | `rejected` / `failed` | 被拒、出错或输出无法解析。 |
410
+ | `not_required` / `not_available` | 该角色不需要(如 `standard` 剖面)或无候选。 |
411
+
412
+ **即便咨询了多个 agent,也只有一位 evaluator/scorer 会被 gate 采纳。** 一个 cycle
413
+ 可能为评审选了 reasonix、kimi、codex,让 reasonix 和 agy 打分,但恰好只有一个 score
414
+ 是 `accepted` 并盖进 Attest Gate。其余显示为 `returned`、`selected` 或 `failed`——
415
+ 它们为透明而记录,并非都对交付把关。要找真正决定该 cycle 的 verdict,读 `accepted`
416
+ 那一行(以及 `accepted evaluator` 产物链接),而不是恰好排在最前的那个 agent。
417
+
418
+ 当某个 reviewer 或 scorer 输出无法解析时,它那一行是 `failed`,带一个 `cause`
419
+ (如 `unparseable`)和一个 `raw artifact:` 指针,指向 `.roll/loop/peer/` 下捕获的
420
+ 那次尝试——见
421
+ [排障:无法解析的 score/review](../../docs/live-console.md#故障排查)。
422
+
423
+ ## 协同视图
424
+
425
+ US-OBS-032 写角色阵容(`summary.md` / `summary.json`),US-OBS-033 用
426
+ `roll loop cycle <id> --roles` 把阵容直接展示出来;协同视图是 CycleRoleSummary 的上层。
427
+ 它复用同一份事实,把它渲染成协议接力:谁设计、谁构建、谁评审、谁打分、接力棒最后
428
+ 停在哪里。
429
+
430
+ 入口如下:
431
+
432
+ ```bash
433
+ roll loop cycle <id> --collab # 单个 cycle 的协议接力视图
434
+ roll loop cycle <id> --collab --json # collab-view.v1 JSON
435
+ roll supervisor live --collab # 多 cycle 实时协同流
436
+ roll supervisor live --collab --once
437
+ roll loop cycle --legend # Layer A 协同协议图例
438
+ ```
439
+
440
+ 协议读法是:
441
+
442
+ ```text
443
+ Supervisor/Designer -> Builder -> independent Peer Reviewer/Evaluator -> Gate
444
+ ```
445
+
446
+ Supervisor 介入分三层。`旁观/建议` 表示 Supervisor 只观察、追问证据或把 owner 注意力
447
+ 路由到正确位置,不改 Builder 的工作。`设计/拆分` 表示 Supervisor 把不清楚的范围转成
448
+ 设计产物或更小的后续 action。`Builder override` 是显式且例外的介入:Supervisor 选择
449
+ 或替换 Builder binding,这个决定必须留在执行阵容里。
450
+
451
+ 角色独立是按 session 独立,不是按品牌独立。同一个 agent brand 可以承担多个角色,
452
+ 前提是每个角色都用单独的 fresh session,并且只通过 artifact handoff 交接:plan、diff、
453
+ review、score、AC map 或 report。共享同一段 transcript 不算独立评审。agent 多样性是有用
454
+ 证据和排序信号,特别是当能力/短板画像出现在角色摘要里时;但它不是默认硬排除规则,
455
+ 最终仍由能力、可用性和角色契约决定。
456
+
457
+ `handoff` 和 `escalation` 不是一回事。handoff 是角色之间的正常交接,例如 Builder 带着
458
+ diff 和证据交给 Peer Reviewer。escalation 是正常路径被打断后的显式提醒:Supervisor、
459
+ Gate 或 owner 需要介入,因为普通接力没有干净完成。`terminus` 只说明接力棒最后停在哪里,
460
+ 不表示 pass/fail:`walked_full`、`escalated`、`split`、`supervisor_fix` 描述的是接力终点;
461
+ 是否通过要看 Gate 和 attest 结果。
462
+
463
+ ## Status Dashboard(状态仪表盘)
464
+
465
+ `roll loop status` 输出一个紧凑的仪表盘,包含每个 cycle 的行记录和每日汇总。
466
+
467
+ ### Token 列
468
+
469
+ 每条 cycle 行的 token 用量以 4 分量格式显示:
470
+
471
+ ```
472
+ · 19:18 13m 164/498.2K↑ 12.7M↓/63.3K opus-4-7 $11.07 US-VIEW-012
473
+ ↑ in cw↑ cr↓ out
474
+ ```
475
+
476
+ | 分量 | 含义 |
477
+ |------|------|
478
+ | `164`(第一个 `/` 之前) | Base input tokens(基础输入) |
479
+ | `498.2K↑` | Cache write tokens(缓存写入,按写入费率计费) |
480
+ | `12.7M↓` | Cache read tokens(缓存读取,费率远低于写入) |
481
+ | `63.3K`(最后一个 `/` 之后) | Output tokens(输出) |
482
+
483
+ 没有 cache 数据的旧 cycle 或非 Opus 模型,列退化为两段式 `in/out` 格式。
484
+
485
+ **按 agent 覆盖情况。** token/cost 抓取取决于按 agent 的 usage 插件。
486
+
487
+ | Agent | dashboard token/cost |
488
+ |-------|----------------------|
489
+ | Claude | ✅ 支持 |
490
+ | pi(DeepSeek) | ✅ 支持 |
491
+ | OpenAI(codex) | ✅ 支持 |
492
+ | Gemini | ✅ 支持 |
493
+ | Kimi | ✅ 支持 |
494
+
495
+ 没有插件的 agent 退回 `—/—` 占位符。新增 agent 是一个小的按 agent 插件
496
+ (`lib/agent_usage/<agent>.py`),不会自动出现。五步走 howto 见
497
+ `lib/agent_usage/README.md`。
498
+
499
+ ### 汇总行
500
+
501
+ cycle 列表下方是每日四分量总计:
502
+
503
+ ```
504
+ input tokens 164
505
+ cache writes 498.2K
506
+ cache reads 12.7M
507
+ output tokens 63.3K
508
+ ```
509
+
510
+ 通过这四行,你可以验证 cycle 行显示的费用(如 `$11.07`)与 Anthropic 账单是否吻合——
511
+ 上面这个例子里,86% 的费用来自 cache。
512
+
513
+ ## 按故事汇总(Per-Story Rollup)
514
+
515
+ `roll loop status` 只显示滚动窗口(默认 3 天)。如果你想看**一个故事的全部生命周期**——
516
+ 它跑过的所有 cycle,包括已经滚出 status 窗口的——用 `roll loop story`。
517
+
518
+ ```bash
519
+ roll loop story US-LOOP-004 # 单故事紧凑面板
520
+ roll loop story us-loop-004 # 大小写不敏感
521
+ roll loop story US-LOOP-004 --days 90 # 扩大事件流回溯窗口
522
+ roll loop story US-LOOP-004 --json # JSON 输出,给脚本/仪表盘消费
523
+ ```
524
+
525
+ 面板把你本来要跨多次 `status` 手动累加的总数一次给出:
526
+
527
+ ```
528
+ ── US-LOOP-004 · 把每轮 cycle 成本/token/耗时写进事件流 ──
529
+ cycles 3 (✓ 2 ✗ 1 ⏵ 0)
530
+ span 2026-05-18 14:22 → 2026-05-19 09:11
531
+ duration 1h 47m tokens in 412k out 18.3k cache w 1.2M r 7.8M
532
+ cost $4.92 model claude-opus-4-7
533
+ PRs #128 ✓ #131 ✓ #134 ✗
534
+ recent 20260518-142233-91 ✓ $2.10
535
+ 20260518-203045-12 ✗ $1.71
536
+ 20260519-091112-44 ✓ $1.11
537
+ ```
538
+
539
+ **历史是怎么留下来的:** loop runner 在 `events-<slug>.ndjson` 超过 10 MB 时轮转,
540
+ 保留 `.1` … `.4` 四份归档。`roll loop status` 和 `roll loop story` 都会读 head
541
+ 加全部轮转文件,cycle 一旦落盘就不会从汇总里消失。
542
+
543
+ **退出码:** 找到至少一个 cycle 返回 `0`;窗口内没有匹配的故事 ID 返回 `2`。
544
+ `--json` 形式遵守同样的退出码契约,脚本可以靠它判断"数据是否缺失"。
545
+
546
+ ## Cycle 结果评分(Result Eval)
547
+
548
+ 每轮 cycle 收尾时都会按一套固定的多维 rubric 给结果**客观打分**,且**不花额外
549
+ token**——分数完全从 loop 已有的 facts 算出(是否 merge、CI 结果、TCR 提交数、
550
+ 耗时、ALERT、孤儿)。结果写进该轮 `runs.jsonl` 记录的 `result_eval` 块:
551
+
552
+ ```json
553
+ { "version": 1, "score": 8, "dims": { "outcome": 1.0, "correctness": 1.0,
554
+ "scope_fidelity": 1.0, "quality": 1.0, "efficiency": 0.6, "cleanliness": 1.0 } }
555
+ ```
556
+
557
+ > **结果评分不是 Review Score。** Review Score 是全新独立会话的同行 Reviewer 对
558
+ > 交付质量的复盘(绝非作者自评),写在 `.roll/notes/*.md`;结果评分是这套从 facts
559
+ > 算出的**客观**每轮结果分。两者是不同信号,在 dashboard 上分两行各自显示,绝不混为一谈。
560
+
561
+ ### Rubric(六个维度)
562
+
563
+ 每个维度打 `0.0`–`1.0`,facts 缺失时记 `unknown`。unknown 维度不计入汇总,剩余维度的
564
+ 权重重新归一——所以缺一个 fact 绝不会被悄悄算成 `0`。
565
+
566
+ | 维度 | 权重 | 含义 | 何时为 1.0 |
567
+ |------|------|------|-----------|
568
+ | `outcome` | 3 | 这轮有没有 merge 进 `main`? | 已 merge · `0.0` 未 merge |
569
+ | `correctness` | 2 | 产出 PR 的 CI 是不是绿? | 绿 · `0.0` 红 |
570
+ | `scope_fidelity` | 2 | 有没有完成被路由到的那个故事? | 完成 · `0.0` idle / 跑偏 |
571
+ | `quality` | 1 | 加了测试、没立刻返工? | TCR ≥ 1 且无返工 FIX · `0.5` 有返工 · `0.0` 没测试 |
572
+ | `efficiency` | 1 | 耗时 vs 故事的 `est_min` 预算 | 在预算内 · 超出后逐档降分 |
573
+ | `cleanliness` | 1 | 无孤儿 worktree/分支、无 ALERT | 干净 · `0.0` 有孤儿 / 有 ALERT |
574
+
575
+ 各维度汇总成一个 **1–10 的 cycle 分**:
576
+
577
+ ```
578
+ weighted = Σ(score_i × weight_i 取已知维度) / Σ(weight_i 取已知维度)
579
+ cycle_score = round(1 + weighted × 9) # 0.0 → 1, 1.0 → 10
580
+ ```
581
+
582
+ 权重集中成 `lib/loop_result_eval.py` 里的常量——可调,但刻意不做成用户高频改的旋钮。
583
+
584
+ ### 看趋势——`roll loop eval [N]`
585
+
586
+ `roll loop eval` 聚合近 `N` 轮已评分 cycle(默认 14)的 `result_eval`,输出均分 /
587
+ 最低分 / 各维度命中率 / 趋势箭头。无 `result_eval` 的旧记录跳过;样本不足 3 个时提示
588
+ `(n/a) need 3`。
589
+
590
+ ```
591
+ $ roll loop eval
592
+ Loop result-eval — last 14 cycles
593
+ 循环结果评分 — 最近 14 轮
594
+
595
+ mean 6.8 / 10 ↓
596
+ min 4 / 10
597
+ n 4
598
+
599
+ dimension hit-rate / 各维度命中率
600
+ outcome 75%
601
+ correctness 67%
602
+ scope_fidelity 75%
603
+ quality 75%
604
+ efficiency 50%
605
+ cleanliness 100%
606
+ ```
607
+
608
+ `roll loop status` dashboard 上也有一行结果评分小结,**与 Review Score 那行分开**显示,
609
+ 两者绝不混淆:
610
+
611
+ ```
612
+ result-eval: mean 6.8↓ / min 4 / out 75% ci 67% scope 75% qual 75% eff 50% clean 100% (last 14)
613
+ ```
614
+
615
+ ### 自进化信号——`roll loop signals`
616
+
617
+ 当某个维度连续 `N` 轮(默认 3,`--streak` 可调)都是低分(`0.0`),loop 把它暴露成
618
+ 一条**改善信号**:向 `.roll/signals/candidates.md` 追加一条**候选** backlog 草稿
619
+ (`IDEA` 或 `FIX`,标 `📋 待人确认`),并由 `roll loop signals`(以及
620
+ `roll loop status` 仪表盘)报出来。信号按模式去重,同一个长期问题只提一次,不每轮重复刷。
621
+
622
+ 信号只是提示。它绝不改真实 backlog、绝不激活故事、绝不改代码——只把"哪里在反复出问题"
623
+ 推到面前,让人来决定。cycle 收尾钩子每轮跑一次检测,`roll loop signals` 则按需手动跑。
624
+
625
+ | 维度持续低分 | 暴露为 | 读法 |
626
+ |-------------|--------|------|
627
+ | `outcome` | FIX | cycle 反复 merge 不进 main |
628
+ | `correctness` | FIX | 产出 PR 反复挂 CI |
629
+ | `scope_fidelity` | IDEA | cycle 反复 idle 或跑偏 |
630
+ | `quality` | FIX | cycle 反复没有测试活动就落地 |
631
+ | `efficiency` | IDEA | cycle 反复超出 `est_min` 预算 |
632
+ | `cleanliness` | FIX | cycle 反复留孤儿 / 触发 ALERT |
633
+
634
+ ## TerminalOutcome 词汇表
635
+
636
+ 面向用户的 cycle 投影使用 TerminalOutcome,不再使用旧摘要文本。稳定词汇为:
637
+
638
+ `delivered`, `published_pending_merge`, `failed`, `blocked`,
639
+ `aborted_no_delivery`, `aborted_with_delivery`, `orphan_timeout`,
640
+ `idle_no_work`, `unknown`。
641
+
642
+ 早期 `runs.jsonl` 可能含自由文本结果。dashboard、archive、summary 渲染前
643
+ 都先经 truth adapter 转换。
644
+
645
+ ## 可见性(tmux + 弹窗)
646
+
647
+ 每次 loop 运行都在一个独立的 tmux session 里。
648
+ 未静音时,终端窗口自动弹出,你可以全程旁观。
649
+
650
+ 日常先用 `roll loop watch`。它把 live agent 输出和结构化事件流合成精炼状态层。
651
+ 排查 phase/TCR/event 顺序时用 `roll loop watch --events`。只有需要原始审计 JSON
652
+ 时才用 `roll loop watch --raw-events`。所有 watch 模式都是只读;Ctrl-C 只停止视图。
653
+
654
+ ```bash
655
+ roll loop watch # 默认状态层
656
+ roll loop watch --events # compact 事件流
657
+ roll loop watch --raw-events # 原始审计流
658
+ tmux attach -t roll-loop-<project-slug> # 随时接入运行中的观测 pane
659
+ # Ctrl-B D # 离开(loop 继续运行,不受影响)
660
+
661
+ roll loop mute # 🔇 关闭弹窗(静音文件:~/.shared/roll/mute)
662
+ roll loop unmute # 🔔 重新开启弹窗
663
+ ```
664
+
665
+ `mute` 文件对所有项目、所有自主活动(loop + peer review)共享生效。
666
+ 一个开关控制全部。
667
+
668
+ ### 环境漂移与 session 生命周期
669
+
670
+ tmux session 是长寿命的,但 cycle 的**网络环境永远跟随调用方**,不吃 session 的
671
+ 记忆:每次开 cycle 窗口时,代理族变量(`HTTP_PROXY`/`HTTPS_PROXY`/`ALL_PROXY`/
672
+ `NO_PROXY` 及小写)都从调用方重新注入。runner profile 声明的 agent secret env 名
673
+ 也会按名透传,例如 Reasonix 的 `DEEPSEEK_API_KEY`;`~/.reasonix/.env` 这类文件凭据
674
+ 则由 spawn 层加载。所以两次 cycle 之间本机代理开了又关,照常工作——不会再出现
675
+ "session 在代理时代创建、代理关了之后每个 agent 都 ~45 秒超时 `Connection error`"
676
+ (FIX-230),env-only 的 agent 密钥也不再依赖陈旧 tmux session。每个 cycle 还会把
677
+ 生效的代理变量记成一行 `env:` 进 `.roll/loop/cron.log`,环境型故障从日志直读。其它
678
+ 变量仍来自 `roll loop on` 时创建的 session;若你轮换了别的关键变量,
679
+ `roll loop off && roll loop on` 可重建一个干净 session。
680
+
681
+ ### Edit 折叠
682
+
683
+ 当实时 tmux 流里出现 agent 连续 Edit 同一个文件时,Roll 不再把那条一模一样的
684
+ 长路径在 N 行里复读(以前看起来像卡死)。现在它把相邻的同文件改动折叠成一行,
685
+ 并原地刷新:
686
+
687
+ ```text
688
+ ✏ <basename> | <hint> ×N
689
+ ```
690
+
691
+ - **触发条件** —— 相邻 ≥2 次针对同一 `file_path` 的 `Edit` / `Write`。路径只显示
692
+ `os.path.basename(file_path)`,绝不显示全路径;单次 Edit 不带 `×N` 计数。
693
+ - **`<hint>`** —— 从改动输入里抽出的 ≤20 字特征,让你看到“在改什么”而不只是计数:
694
+ - `replace_all=true` → 字面输出 `replace-all`。
695
+ - 否则取 `new_string` 首行的首个非空 token,去掉前导空白与注释符
696
+ (`#`、`//`、`/*`、`*`、`--`、`;`)。
697
+ - 超过 20 字符的 token 截断为 `token[:20] + "…"`(按 unicode 字符计,中文 /
698
+ emoji 不会被按字节截断)。
699
+ - `new_string` 为空 / 全空白时不产生 hint,整段 ` | <hint>` 一并省略。
700
+ - **跨文件 flush** —— 切到另一个文件(或任意其它事件:`Bash`、`Skill`、错误、cycle
701
+ 结束)会先 flush 前一个文件的最终 streak 行(保留在 scrollback 里),再为新文件
702
+ 起一行。折叠绝不会跨越非 Edit 行。
703
+
704
+ 三个示例(已去除 ANSI 转义):
705
+
706
+ ```text
707
+ # 单次 Edit
708
+ ✏ auth.ts | export
709
+
710
+ # 折叠 ×N(同文件改了 7 次)
711
+ ✏ auth.ts | export ×7
712
+
713
+ # 跨文件切换 —— 两行,第一行在第二行开始前被 flush
714
+ ✏ auth.ts | export ×3
715
+ ✏ router.ts | replace-all
716
+ ```
717
+
718
+ ## Cycle 退出摘要(Cycle exit summary)
719
+
720
+ When a cycle ends and the tmux session detaches, the macOS `.command` window no longer leaves you on a bare `press enter to close` line.
721
+
722
+ cycle 结束、tmux 会话退出后,macOS `.command` 窗口不再只剩一行 `press enter to close`。
723
+
724
+ Just before that prompt, the window renders a compact recap so you can review the cycle without scrolling back or opening the cron log:
725
+
726
+ 就在那行提示之前,窗口会渲染一段紧凑的复盘块,让你不必回滚 tmux scrollback 或翻 cron 日志就能复盘本轮:
727
+
728
+ ```text
729
+ ─── Cycle 20260530-2301-94839 Summary ───
730
+ outcome: delivered · story: US-LOOP-040 · tcr commits: 4
731
+ ci: green
732
+ todo remaining: 7
733
+ phases (top 5 by time):
734
+ build 612s
735
+ ci 94s
736
+ pr 31s
737
+ press enter to close.
738
+ ```
739
+
740
+ The summary covers five signals:
741
+
742
+ 摘要覆盖五类信号:
743
+
744
+ 1. Result — the cycle outcome from `runs.jsonl`, rendered as TerminalOutcome.
745
+
746
+ 本轮处理结果——来自 `runs.jsonl`,经 truth adapter 渲染为 `delivered`、
747
+ `published_pending_merge`、`failed`、`blocked`、`aborted_no_delivery`、
748
+ `aborted_with_delivery`、`orphan_timeout`、`idle_no_work` 或 `unknown`。
749
+ 2. CI / build status — the latest `ci` event outcome: `green` / `red` / `heal-attempting` / `ci: n/a`.
750
+
751
+ 测试 / 构建状态——最新 `ci` 事件结果:`green` / `red` / `heal-attempting`,无 ci 事件时 `ci: n/a`。
752
+ 3. Todo remaining — count of `📋 Todo` lines in `.roll/backlog.md`.
753
+
754
+ Todo 剩余——扫 `.roll/backlog.md` 里 `📋 Todo` 行的总数。
755
+ 4. Phase breakdown — the top 5 cycle phases by elapsed time.
756
+
757
+ 阶段耗时——按耗时降序的前 5 个阶段。
758
+ 5. Failure / alert highlights — failed/aborted runs, red CI, active alerts and suspected zero-diff cycles get a `✗` / `⚠` prefix and (on a colour terminal) red / yellow highlighting; a fully green cycle prints in the default colour with no prefix.
759
+
760
+ 失败 / 告警高亮——failed/aborted、CI red、有 alert、疑似 zero-diff 会带 `✗`(失败)/ `⚠`(告警)前缀,并在彩色终端里红 / 黄高亮;全绿状态以默认色输出、不加前缀。
761
+
762
+ The `press enter to close` prompt is preserved — the summary prints above it, the close interaction is unchanged.
763
+
764
+ `press enter to close` 提示保留——摘要打印在它上方,关闭交互完全不变。
765
+
766
+ ### 关闭颜色(Turning off colour)
767
+
768
+ ANSI colour is only emitted on a real terminal; pipes, redirects and captured output stay plain text. Force colour off with `NO_COLOR=1` (per [no-color.org](https://no-color.org)):
769
+
770
+ ANSI 颜色仅在真实终端启用;管道 / 重定向 / capture 时输出纯文本。在 TTY 上强制关闭颜色用 `NO_COLOR=1`(遵循 [no-color.org](https://no-color.org)):
771
+
772
+ ```bash
773
+ NO_COLOR=1 roll loop now
774
+ ```
775
+
776
+ ### 排障:没有摘要出现(no summary appears)
777
+
778
+ If the cycle exited early (aborted/idle) or `runs.jsonl` had not yet flushed, the window prints a single placeholder line instead, and `press enter` still works:
779
+
780
+ 如果 cycle 早退(aborted/idle)或 `runs.jsonl` 还没写盘,窗口改为打印一行占位文案,`press enter` 仍可用:
781
+
782
+ ```text
783
+ (summary unavailable — see log: ~/.shared/roll/loop/cron-<slug>.log)
784
+ ```
785
+
786
+ Summary rendering is always silent best-effort: if `python3` is missing or the data is corrupt, the cycle skips the recap and falls through to `press enter to close` — it never changes the `.command` exit code or blocks the window.
787
+
788
+ 摘要渲染始终是 silent best-effort:python3 缺失或数据损坏时,跳过摘要直接走 `press enter to close`——绝不改变 `.command` 退出码,也不阻塞窗口关闭。
789
+
790
+ ## 并发安全
791
+
792
+ Loop 有两层保护:
793
+
794
+ - **LOCK 文件**(`<project>/.roll/loop/.LOCK-<slug>`):同一个项目同一时间只有一个 loop 实例运行。
795
+ 如果 loop 已在运行,新的触发直接退出,不重复执行。
796
+ - **🔨 In Progress 状态**:正在被人工或其他 Agent 执行的故事,loop 会跳过,不抢占。
797
+
798
+ 你随时可以运行 `$roll-build US-XXX` 手动接管某个故事;
799
+ loop 看到 `🔨 In Progress` 标记就会自动跳过。
800
+
801
+ ## 失败处理
802
+
803
+ | 场景 | 处理方式 |
804
+ |------|---------|
805
+ | API 错误 | 最多重试 3 次,每次等待 30 秒 |
806
+ | 主 Agent 失败 | 切换到备用 Agent |
807
+ | 两个 Agent 都失败 | 暂停 loop,写 ALERT.md |
808
+ | TCR 提交数为 0 | 故事回退为 📋 Todo,写 ALERT.md |
809
+ | HEAD CI 红 | 尝试自动热修(见下),用完次数后才写 ALERT |
810
+
811
+ ALERT 条目会在 `roll loop status`、`roll loop alert` 和 cycle/story 证据视图中显示。
812
+
813
+ ## CI 自愈(US-LOOP-046..050)
814
+
815
+ 当 loop 检测到 HEAD CI 红时,不再立即写 ALERT 停工。
816
+ 它会先尝试自己把 CI 修好再继续推进 backlog。
817
+
818
+ **工作流程:**
819
+
820
+ 1. 每轮 cycle 扫 backlog 之前先执行 `roll loop precheck-ci`。
821
+ 2. CI 绿 → 正常推进。
822
+ 3. CI 红且允许热修:通过 `roll loop hotfix-head-context` 抓 CI 失败日志和最近 commit diff,调 `roll-fix` 修复,等 CI 变绿。超过 `ROLL_LOOP_HEAL_MAX`(默认 2)次还没修好则写 ALERT 停工。
823
+ 4. CI 红且已用完热修次数或 `ROLL_LOOP_NO_HEAL=1`:写 ALERT(保留原有行为)。
824
+
825
+ 自家 PR(`loop/*` 分支)在 cycle 结束后才转红(US-LOOP-049)会被**后台自愈**(US-LOOP-062a):分类为 `loop_self_ci_red`,PR Loop 路由到 `roll loop pr-heal-run`——checkout 该 PR 分支、把失败 CI 上下文交给项目 agent(`_project_agent`)修,受每 PR 自愈预算(`ROLL_LOOP_HEAL_MAX`,默认 2)和每 PR 锁(防重复并发)约束,自愈在后台跑、PR tick 不阻塞。自愈关闭(`ROLL_LOOP_NO_HEAL=1`)或预算用尽时,写去重 `[TYPE:loop-pr-ci-red]` ALERT,绝不静默跳过。
826
+
827
+ human 已批准、CI 绿、可合并的 PR 会被**主动合并**(US-LOOP-062b):`runner 的 approved-PR merge` 直接 `gh pr merge --squash`,不再依赖仓库级 auto-merge(可能关着);合并失败非致命,PR 留开,下一轮重试。
828
+
829
+ **环境变量:**
830
+
831
+ | 变量 | 默认值 | 作用 |
832
+ |------|--------|------|
833
+ | `ROLL_LOOP_NO_HEAL=1` | 未设置 | 关闭所有 CI 热修,恢复快速失败 |
834
+ | `ROLL_LOOP_HEAL_MAX` | `2` | 连续热修最大尝试次数,超过后写 ALERT |
835
+
836
+ ## PR 收件箱与评审
837
+
838
+ 每轮 loop 会在领取新故事前先处理未合入的 PR。
839
+
840
+ **评审技能:**
841
+
842
+ PR 评审通过 `roll-review-pr` skill 派发。它通过 `gh` 获取 PR 标题、正文和
843
+ diff,渲染评审 prompt,路由到项目配置的 agent(Claude、Kimi、DeepSeek 等)。
844
+ agent 输出结构化结论:
845
+
846
+ | 结论 | 动作 |
847
+ |------|------|
848
+ | `APPROVE` | `gh pr review --approve` |
849
+ | `REQUEST_CHANGES` | `gh pr review --request-changes` 附带原因 |
850
+ | `UNCERTAIN` | 写 ALERT — 人工决定 |
851
+
852
+ **跳过评审:** 在 PR body 中任意位置加入 `[skip-ai-review]` 即可自动批准,
853
+ 不调用 agent。
854
+
855
+ **loop 如何使用:** `roll loop pr-inbox` 对每个 open PR 分类,将 `eligible`
856
+ PR 路由到 `roll-review-pr` skill。loop 自身的 PR(`loop/*` 分支)被跳过,
857
+ 避免 same-source bias。
858
+
859
+ **Stale PR 自动 rebase:** 被分类为 `stale`(CI 失败或分支落后/冲突)的 PR
860
+ 会由 runner 的 stale-PR rebase 自动 rebase 到 `origin/main`。断路器限制
861
+ 24 小时内最多 rebase 3 次,超过后写 ALERT。Fork PR 因无写权限直接跳过并写 ALERT。
862
+
863
+ **Bot 评审检测:** 如果 GitHub Actions bot 已经评审过 PR
864
+ (例如通过可选的 GHA 工作流),`roll loop pr-inbox` 会让步:
865
+ - Bot `APPROVED` → 跳过,让 auto-merge 自行推进
866
+ - Bot `CHANGES_REQUESTED` → 写 ALERT(loop PR 被 GHA reviewer 打回)
867
+
868
+ ### 可选:事件驱动 PR 评审(GHA)
869
+
870
+ 默认情况下,`roll loop pr-inbox` 在每轮 loop 中评审 eligible PR(最多延迟约 1 小时)。
871
+ 如果希望 GitHub 仓库的 PR 秒级得到反馈,安装事件驱动工作流:
872
+
873
+ ```bash
874
+ cp templates/workflows/pr-review-event.yml .github/workflows/
875
+ ```
876
+
877
+ 此工作流在 PR 打开/更新时自动触发 `roll-review-pr` skill。Fork PR 和
878
+ body 中包含 `[skip-ai-review]` 的 PR 会被自动跳过。模板只需要一个
879
+ API key secret — 你配置的 agent 对应的那个。
880
+
881
+ 两种模式共存:GHA 工作流提供即时反馈,`roll loop pr-inbox` 作为安全网兜底。
882
+
883
+ ## Session 清理
884
+
885
+ 每轮 loop 结束时,会自动清理本地残留的 worktree:
886
+
887
+ - `.claude/worktrees/` 下,分支已完全合入 `main` 的目录会被删除
888
+ (`git worktree remove --force` + `git branch -D`)。
889
+ - 随后执行 `git worktree prune` 清理元数据。
890
+
891
+ 这样可以保持 `git worktree list` 干净,防止 `.claude/worktrees/` 随时间积累。
892
+ 分支仍领先于 `main` 的活跃 worktree 不受影响。
893
+
894
+ ## 阶段计时(Cycle phases)
895
+
896
+ 每轮 cycle 在内部切成七个命名阶段。每个阶段进入时 emit `phase_start`,
897
+ 退出时 emit `phase_end` 携带耗时和 ok/fail。耗时较长的阶段(claude、
898
+ PR 等合并)每 30–60s 还会 emit 一次 `phase_tick` 心跳,tmux 不再像卡死。
899
+
900
+ | # | 阶段 | 触发时机 | 典型耗时 |
901
+ |---|------|---------|---------|
902
+ | 1 | `startup` | env / lock / 心跳启动 | < 1 秒 |
903
+ | 2 | `preflight` | 同步 `.roll/` 元数据 + 清理已合并的临时分支 + 找回上轮孤儿 worktree | 0 – 30 秒 |
904
+ | 3 | `worktree_setup` | fetch origin + 建 worktree + 同步 meta | 2 – 10 秒 |
905
+ | 4 | `agent_invoke` | 调起 agent(最多三次重试) | 5 – 45 分钟 |
906
+ | 5 | `publish_push` | push 分支 + 建 PR(doc-only 直接合) | 5 – 30 秒 |
907
+ | 6 | `cleanup` | 落 PR 终态 + 拆 worktree | < 1 秒 |
908
+
909
+ > **US-AUTO-044**:主 loop 开完 PR 即退,**不再等合并**。合并 / rebase / 关 PR 交给专职 PR Loop(`com.roll.pr.<slug>`,每 5 分钟)异步处理;有 open PR 的 story 由资格闸跳过,不会重复开,也不会假 Done。
910
+
911
+ Idle / failed / aborted cycle 只 emit 实际进入过的阶段。
912
+ cycle 收尾时 inner runner 在 stdout 打一份按耗时降序的面板:
913
+
914
+ ```
915
+ ─── Cycle 20260523-114502-12345 Phase Breakdown ───
916
+ agent_invoke 723s ( 96.2%) ████████████████████
917
+ worktree_setup 4s ( 0.5%)
918
+ publish_push 2s ( 0.3%)
919
+ preflight 2s ( 0.3%)
920
+ cleanup 1s ( 0.1%)
921
+ startup 1s ( 0.1%)
922
+ ──────────────────────────────────────
923
+ Total 752s
924
+ ```
925
+
926
+ 各阶段耗时同步固化到 `runs.jsonl` 的顶层 `phases` 字段(详见
927
+ [状态文件](#状态文件))。`roll loop runs` 在每条 built 行尾追加
928
+ `slowest=<阶段名> <占比>%`,跨多轮对比哪一步拖后腿一眼可见。
929
+ 看完整面板:
930
+
931
+ ```bash
932
+ roll loop runs --detail 20260523-114502-12345
933
+ ```
934
+
935
+ ## Cycle 日志存档
936
+
937
+ 每轮 cycle 的完整 agent 输出都会归档到 `.roll/cycle-logs/<cycle-id>.log`,
938
+ ANSI 颜色码已剥离,可用 `less`、`cat` 或任何编辑器直接阅读。
939
+
940
+ - **按 cycle 归档**:每轮一个 `.log` 文件,保存在 `.roll/cycle-logs/`
941
+ - **ANSI 已剥离**:颜色码和控制字符已清除,干净纯文本
942
+ - **保留策略**:保留最近 50 轮,超出的自动轮转删除
943
+ - **静音模式也照存**:即使 `roll loop mute` 开启,日志仍然保存
944
+
945
+ ```bash
946
+ roll loop log # 查看最近一轮 cycle 的完整日志
947
+ roll loop log <cycle-id> # 查看指定 cycle(如 20260525-231803-39799)
948
+ roll loop log <前缀> # 前缀匹配(如 20260525 匹配 5 月 25 日所有 cycle)
949
+ ```
950
+
951
+ Cycle 日志存放在 `.roll/`(项目元数据目录)内,且已被 gitignore,
952
+ 不会污染你的代码仓库。
953
+
954
+ ## 跨机器同步
955
+
956
+ 如果你在多台机器上为同一个项目开启了 loop,Roll 可以把每台机器的 cycle
957
+ 记录同步到一个共享的 git 仓库。每台机器只写自己的事件文件、互不冲突,dashboard
958
+ 读取所有机器的记录合并显示——任一台机器都能看到完整的运行历史。
959
+
960
+ ### 配置
961
+
962
+ 在 `~/.roll/config.yaml` 中添加 `roll_records_remote` 字段:
963
+
964
+ ```yaml
965
+ roll_records_remote: "git@github.com:you/roll-loop-records.git"
966
+ ```
967
+
968
+ **强烈建议使用私有仓库。** Cycle 记录包含 prompt 文本、文件路径等可能敏感的
969
+ 信息。请将 records 仓库按日志级别对待——私有、访问受控、不公开。
970
+
971
+ 如果未配置 `roll_records_remote`,跨机器同步完全跳过——不会有任何记录离开
972
+ 你的本机。
973
+
974
+ ### 工作原理
975
+
976
+ - 每台机器首次运行时生成唯一的 machine-id(UUID v4),缓存在
977
+ `~/.shared/roll/machine-id`。
978
+ - 每轮 cycle 完成后,向 records 仓库推送一个只追加的 `.ndjson` 文件:
979
+ `<slug>/events/<machine-id>.ndjson`。每台机器只写自己的文件——不会产生
980
+ merge 冲突。
981
+ - Dashboard 渲染前,Roll 在本地 clone(`~/.shared/roll/sync/`)执行
982
+ `git pull --ff-only`,读取所有 `*.ndjson` 文件,按时间戳排序并
983
+ 以 `run_id` 去重后合并显示。
984
+ - Push 和 pull 都是后台 best-effort 操作——如果远端不可达,cycle 照常执行,
985
+ dashboard 只显示本地数据。
986
+
987
+ ### Dashboard 同步状态指示器
988
+
989
+ Dashboard 底部显示三种状态之一:
990
+
991
+ | 指示器 | 含义 |
992
+ |--------|------|
993
+ | `sync: ok (2m ago)` | 远端可达,记录已成功合并 |
994
+ | `sync: offline` | 远端不可达(网络问题、认证过期)——仅显示本地数据 |
995
+ | `sync: not configured` | 未设置 `roll_records_remote`——同步已关闭,此状态为预期 |
996
+
997
+ ### Fork 注意事项
998
+
999
+ Roll 根据 `git remote get-url origin` 推导项目 slug。如果你将 `origin` 改为指向
1000
+ fork,slug 会随之变化——原仓库和 fork 的记录会落到 records 仓库的不同目录中。
1001
+ 这是有意为之(不同仓库 = 不同身份),但如果你临时从 fork 工作,请注意 dashboard
1002
+ 不会显示上游仓库的 cycle 历史。
1003
+
1004
+ ## Loop 元数据同步
1005
+
1006
+ 每轮 cycle 启动时,roll 会自动从 `.roll/` 的 git 远端拉取最新的项目元数据
1007
+ (backlog、约定、skill),再去扫描待办故事。
1008
+
1009
+ **工作机制**
1010
+
1011
+ 1. 检测 `.roll/` 是否配置了 `origin` 远端。
1012
+ 没有则静默跳过(对标准 roll 安装没有任何影响)。
1013
+ 2. 执行 `git fetch && git reset --hard origin/main`,超时 15 秒。
1014
+ 3. 成功:emit `meta_sync ok` 事件;cycle 用最新 backlog 继续。
1015
+ 4. 失败:emit `meta_sync stale` 事件;cycle 用本地现有 `.roll/` 兜底继续运行。
1016
+
1017
+ **连续 3 次失败**后 loop 会写 ALERT,提示检查 SSH key 或网络。
1018
+
1019
+ **手动同步**
1020
+
1021
+ ```bash
1022
+ git -C .roll fetch && git -C .roll reset --hard origin/main
1023
+ ```
1024
+
1025
+ **FAQ:loop 跑了一轮但 dashboard 显示 backlog 为空**
1026
+
1027
+ 通常是 `.roll/` 没同步上:
1028
+ - 换机器或重装系统后:需要手动把 roll-meta 克隆到 `.roll/` 并配置 origin 远端。
1029
+ - 确认方法:`git -C .roll remote get-url origin` — 如果为空则不会触发同步。
1030
+ - SSH Key 可能需要重新授权(`ssh -T git@github.com` 测试连通性)。
1031
+
1032
+ ## 远程监控(Remote Monitoring)
1033
+
1034
+ Remote Monitoring — watch the loop from anywhere.
1035
+
1036
+ 不在本机时,依然可以从手机或任意浏览器查看 loop —— backlog 进度、Dream 健康、CI 状
1037
+ 态 —— 无需本地 `roll` 命令。它分两层:**数据层**(本机把状态快照 push 到 roll-meta 仓
1038
+ 库)和 **prompt 层**(把巡检 prompt 粘贴进 Claude Code,读 roll-meta + GitHub API)。
1039
+
1040
+ ### 配置 `roll_meta_dir`
1041
+
1042
+ 在 `~/.roll/config.yaml` 里告诉 roll 你的 roll-meta 检出在哪:
1043
+
1044
+ ```yaml
1045
+ # ~/.roll/config.yaml
1046
+ roll_meta_dir: ~/projects/roll-meta
1047
+ ```
1048
+
1049
+ `~` 会被展开。这个键是可选的——不配就什么都不变,也不会推快照。路径不存在时,roll 向
1050
+ cron 日志打一条 WARNING 并跳过推送(绝不影响 cycle)。
1051
+
1052
+ ### 自动 push 的工作原理
1053
+
1054
+ 配好 `roll_meta_dir` 后,loop 在**每一次** cycle 结束后推一份新快照——包括没跑故事的
1055
+ idle cycle,所以快照同时充当心跳。cycle runner 在 `cycle_end` 事件之后,于后台调用
1056
+ `${roll_meta_dir}/ops/push-loop-status.sh`。脚本写出 `status/loop.md` 并提交 + push 到
1057
+ roll-meta。输出写到 `~/.shared/roll/push-status.log`(1MB 轮转,保留 2 份)。
1058
+
1059
+ 因为 loop 按固定节奏运行,`status/loop.md` 始终保持 **≤35min 新鲜**——巡检 prompt 总能
1060
+ 看到近期数据。推送是 best-effort:网络错误、git 冲突或 >60s 超时都记进
1061
+ push-status.log,进程卡住会被 kill,cycle 继续。不设 ALERT,不重试。
1062
+
1063
+ ### 手动 push
1064
+
1065
+ 随时可以手动推一份快照:
1066
+
1067
+ ```bash
1068
+ bash .roll/ops/push-loop-status.sh .roll
1069
+ ```
1070
+
1071
+ (`.roll` 是你项目的 roll-meta 检出。)这也是在依赖自动 hook 前确认推送链路是否正常的
1072
+ 方法。
1073
+
1074
+ ### 在手机或浏览器上巡检
1075
+
1076
+ 打开 `.roll/prompts/remote-watch.md`,复制全文,粘贴进 Claude Code(网页、手机或远端
1077
+ IDE)。该 prompt 首次执行做一次全量体检,之后每 15min 轮询一次,遇到「CI 连续两次失
1078
+ 败」或「`status/loop.md` 超过 60min 未更新」等条件立即告警。它只读——绝不修改
1079
+ `seanyao/roll`。
1080
+
1081
+ ### 排障:`status/loop.md` 不更新
1082
+
1083
+ 若快照时间戳远早于 35 分钟:
1084
+
1085
+ 1. 看 `~/.shared/roll/push-status.log`——它记录每次推送尝试以及任何超时或 git 错误。
1086
+ 2. 确认 `roll_meta_dir` 已配置且路径存在(`roll config get roll_meta_dir`)。
1087
+ 3. 确认 `${roll_meta_dir}/ops/push-loop-status.sh` 存在且可执行。
1088
+ 4. 跑一次上面的手动 push,观察是否报错。
1089
+
1090
+ ## 状态文件
1091
+
1092
+ Since Phase 2.0, loop state lives inside the project at `<project>/.roll/loop/`.
1093
+
1094
+ 自 Phase 2.0 起,项目的 loop 状态搬进了**项目目录** `<project>/.roll/loop/`。只有机
1095
+ 器级绑定文件(launchd runner、attach 脚本)和全局静音开关留在 `~/.shared/roll/`。完
1096
+ 整布局、迁移与 `roll loop gc` 见 [Loop 数据布局](loop-data-layout.md)。
1097
+
1098
+ | 文件 | 内容 |
1099
+ |------|------|
1100
+ | `<project>/.roll/loop/state-<slug>.yaml` | 当前/最近一次运行:状态、故事 ID、Agent、run_id |
1101
+ | `<project>/.roll/loop/runs.jsonl` | 只追加的运行历史(每次循环一行 JSON);每条记录带 `result_eval` 块(见 [Cycle 结果评分](#cycle-结果评分result-eval)) |
1102
+ | `<project>/.roll/loop/events.ndjson` | 逐 cycle 事件流(phase_start/phase_end…) |
1103
+ | `.roll/signals/candidates.md` | 自进化信号产出的候选 backlog 草稿(`📋 待人确认`,绝不自动激活) |
1104
+ | `<project>/.roll/loop/ALERT-<slug>.md` | 累积的告警(失败、TCR 违规)|
1105
+ | `<project>/.roll/loop/PAUSE-<slug>` | 暂停标记(由 `roll loop pause` 创建)|
1106
+ | `~/.shared/roll/mute` | 全局静音标记(跨项目共享)|
1107
+
1108
+ ## 降级与观察
1109
+
1110
+ - **断网**:周期在网络不可达时失败,loop 降级为**本地交付**——TCR 提交与
1111
+ 绿测试留在分支上,按当前语言打印提示,连败计数**不**累加(断网永远不该累计触发
1112
+ 自动暂停),调度照常呼吸。下次联网的周期 push/PR 自然补上。
1113
+ - **每个 agent 都有实时观察窗**:非 claude agent(pi、kimi、codex 等)在
1114
+ macOS 上套伪终端运行,输出逐行流入观察窗,不再憋到进程退出;claude 走
1115
+ 自己的流式协议,行为不变。
1116
+
1117
+ ## Launchd lanes(任务清单)
1118
+
1119
+ 每个项目 slug 下 Roll 只拥有三个 launchd 任务:`com.roll.loop.<slug>`(周期调度)、
1120
+ `com.roll.dream.<slug>`(夜间扫描)、`com.roll.pr.<slug>`(PR 收件)。`roll loop on`
1121
+ 安装它们;`roll loop off` 卸载它们**并清扫**发现的任何其它 `com.roll.*.<slug>`
1122
+ plist——旧版本退役的形态(ci/alert/brief)曾以僵尸身份指着已删除的引擎存活数周。
1123
+ `roll doctor` 列出本机全部 `com.roll.*` 任务及其目标目录与加载状态;目标目录
1124
+ 已不存在的 lane 标红 STALE。