@seanyao/roll 2026.510.10 → 2026.511.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,7 +1,20 @@
1
1
  # Changelog
2
2
 
3
+ ## v2026.511.2
4
+ - **Added**: roll loop monitor 三服务状态 — 监控台新增 loop/dream/brief 三个 launchd 服务的运行状态、调度时间和实时 log tail,一屏掌握全局执行情况
5
+ - **Fixed**: dashboard 多处展示问题 — 修复 pending_count 算术错误、brief 内联显示 release readiness、移除底部冗余命令列表
6
+ - **Fixed**: loop 异常退出后 state 未重置 — 防止 queue 卡住导致后续任务无法执行
7
+ - **Fixed**: CI 稳定性 — 修复 _notify_update 裸返回和时间断言,消除环境差异引起的随机失败
8
+ - **Improved**: roll-brief 输出格式 — 序号命名、省略空 section、元信息格式精简,减少无效噪音
9
+
10
+ ## v2026.511.1
11
+ - **Changed**: roll loop 调度器切换到 launchd(macOS)— `roll setup` 自动安装 loop/dream/brief 三个 launchd 服务(默认关闭),`roll loop on/off/status` 统一走 launchctl 管理,幂等安装,Linux 保留 crontab 回退路径
12
+ - **Added**: roll-loop TCR 硬校验 — 故事完成后自动统计 `tcr:` 微提交数量,为 0 时将故事状态回退为 📋 Todo 并写 ALERT,防止 agent 跳过 TCR 节奏
13
+ - **Fixed**: CI 测试环境兼容 — 移除依赖本地 state.yaml 的 hello_world.bats,修复 GitHub Actions 持续失败
14
+
3
15
  ## v2026.510.10
4
16
  - **Fixed**: release.sh changelog 同步时序修复 — 修正条件逻辑和执行顺序,确保每次发版时 changelog 正确更新
17
+ - **Added**: roll-loop 22:00 自动执行验证 — 新增 hello_world.bats 作为 loop 定时执行的端到端存档,可回放确认调度器正常工作
5
18
 
6
19
  ## v2026.510.9
7
20
  - **Fixed**: CHANGELOG 改版本号分组 — 每个 release 独立 section,GitHub Release 增量内容提取正确
package/README.md CHANGED
@@ -9,6 +9,8 @@
9
9
 
10
10
  > Roll out features with AI agents — _Move fast, no sprints._
11
11
 
12
+ **[中文版 README](README_CN.md)**
13
+
12
14
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
13
15
  [![npm version](https://img.shields.io/npm/v/@seanyao/roll.svg)](https://www.npmjs.com/package/@seanyao/roll)
14
16
  [![CI](https://github.com/seanyao/roll/actions/workflows/ci.yml/badge.svg)](https://github.com/seanyao/roll/actions/workflows/ci.yml)
@@ -17,20 +19,21 @@
17
19
 
18
20
  ## What is Roll?
19
21
 
20
- Roll solves a specific problem: when developers on the same team use different AI clients (Claude Code, Cursor, Gemini CLI, Codex), each agent operates under different engineering constraints. One developer's Claude runs tests another's Cursor never even considers.
22
+ Roll solves a specific problem: when developers on the same team use different AI clients (Claude Code, Cursor, Gemini CLI, Kimi, Codex, DeepSeek, Pi, OpenCode, Trae), each agent operates under different engineering constraints. One developer's Claude runs tests another's Cursor never even considers.
21
23
 
22
- Roll fixes this through two mechanisms:
24
+ Roll fixes this through three mechanisms:
23
25
 
24
- 1. **Convention CLI** — a single source of engineering conventions distributed to every AI client on the machine (`~/.claude/`, `~/.cursor/`, `~/.gemini/`, etc.)
25
- 2. **Skill System** — proven engineering practices (TDD, TCR, SRE, INVEST) encoded as instructions that any AI agent can follow
26
+ 1. **Convention CLI** — a single source of engineering conventions distributed to every AI client on the machine (`~/.claude/`, `~/.cursor/`, `~/.gemini/`, `~/.kimi/`, `~/.codex/`, `~/.deepseek/`, etc.)
27
+ 2. **Skill System** — 20 proven engineering practices (TDD, TCR, SRE, INVEST, DDD) encoded as instructions that any AI agent can follow
28
+ 3. **Autonomous Evolution** — an optional layer that lets the agent work unattended: `roll-loop` executes BACKLOG items hourly, `roll-.dream` scans code health nightly, and `roll-brief` briefs the human each morning. The human retains sole authority over releases.
26
29
 
27
- The result: any agent, any client, same constraints.
30
+ The result: any agent, any client, same constraints — and optionally, continuous autonomous delivery.
28
31
 
29
32
  ---
30
33
 
31
34
  ## Start Here
32
35
 
33
- Before commands and skills, read the Engineering Methodology — it explains the three-loop architecture (Research → Build → Observe), why each skill exists, and how they connect into a complete delivery system.
36
+ Before commands and skills, read the Engineering Methodology — it explains the three-loop architecture (Research → Build → Observe), the autonomous evolution layer, why each skill exists, and how they connect into a complete delivery system.
34
37
 
35
38
  **[English](docs/methodology-en.md)** · **[中文](docs/methodology.md)**
36
39
 
@@ -57,17 +60,25 @@ roll update
57
60
 
58
61
  ## Convention Management
59
62
 
60
- Unified behavioral conventions for Claude Code / Gemini CLI / Cursor / Codex — all from one source.
63
+ Unified behavioral conventions for Claude Code / Gemini CLI / Cursor / Kimi / Codex / DeepSeek / Pi / OpenCode / Trae — all from one source.
61
64
 
62
65
  ### Commands
63
66
 
67
+ Commands fall into two categories: **bash commands** run pure shell logic; **agent commands** (marked with 🤖) launch a full AI agent session to execute a SKILL.md.
68
+
64
69
  | Command | Description |
65
70
  |---------|-------------|
66
- | `roll setup` | First-time install on this machine, or re-sync after editing `~/.roll/config.yaml` (use `--force` to overwrite local cache) |
67
- | `roll update` | One-step upgrade: `npm install -g @seanyao/roll@latest` + re-sync via `roll setup` |
71
+ | `roll setup [-f]` | First-time install on this machine, or re-sync (use `--force` to overwrite local cache) |
72
+ | `roll update` | One-step upgrade: `npm install -g @seanyao/roll@latest` + re-sync via setup |
68
73
  | `roll init` | New project: create `AGENTS.md` + `BACKLOG.md` + `docs/features/` |
69
-
70
74
  | `roll status` | Show sync state, skill links, and detected AI tools |
75
+ | `roll backlog` | Show all pending tasks from BACKLOG.md |
76
+ | `roll agent [use <name>\|list]` | Per-project agent selection — affects all 🤖 commands |
77
+ | `roll loop <on\|off\|now\|status\|monitor\|resume\|reset>` | 🤖 Manage the autonomous BACKLOG executor (three-service: loop/dream/brief) |
78
+ | `roll brief` | 🤖 Show latest owner brief (regenerate if stale >24h) |
79
+ | `roll peer` | 🤖 Cross-agent code review and negotiation |
80
+ | `roll release` | 🤖 Sync changelog + version bump + tag + npm publish + GitHub Release |
81
+ | `roll` _(no args, in project dir)_ | Dashboard: loop status, pending count, latest brief summary |
71
82
 
72
83
  ### Typical Flow
73
84
 
@@ -83,7 +94,13 @@ roll init
83
94
  # 3. Upgrade to a new release
84
95
  roll update
85
96
 
97
+ # 4. Enable autonomous evolution (optional)
98
+ roll loop on # three services: loop (hourly) + dream (nightly) + brief (daily)
99
+ roll loop monitor # real-time top-like dashboard
100
+ roll brief # read the latest digest
86
101
 
102
+ # 5. Switch agent per project
103
+ roll agent use kimi # all 🤖 commands now use kimi for this project
87
104
  ```
88
105
 
89
106
  ### How Convention Layering Works
@@ -134,32 +151,93 @@ Research → Design → Build → Check → Fix → (loop)
134
151
  | Execute a planned Story | `$roll-build US-001` |
135
152
  | Free-form feature request | `$roll-build "add search to admin"` |
136
153
  | Bug fix | `$roll-fix FIX-001` |
137
- | Fast backlog capture (bug / idea) | `$roll-jot "description"` |
154
+ | Fast backlog capture (bug / idea) | `$roll-idea "description"` |
138
155
  | High-risk logic (payments, auth, state machines) | `$roll-spar "feature"` |
139
156
  | Deep research (product / company / tech) | `$roll-research "subject"` |
157
+ | Cross-agent code review | `$roll-peer` |
140
158
  | Patrol production for regressions | `$roll-sentinel patrol` |
141
159
  | Debug a broken page | `$roll-debug <URL>` |
142
160
  | Record a dev moment / diary entry | `$roll-notes "just fixed that nasty bug"` |
161
+ | Let the agent work overnight | `roll loop on` |
143
162
 
144
163
  ### Full Skill List
145
164
 
146
- | Skill | Phase | Description |
147
- |-------|-------|-------------|
148
- | `$roll-build` | DESIGN+BUILD | Universal entry: Story ID, fix ID, or free-text fly mode |
149
- | `$roll-design` | DESIGN | Multi-turn discuss → [peer review] → DDD model → solution design → [peer review] → write Stories to BACKLOG |
150
- | `$roll-fix` | FIX | Bug fix / hotfix from BACKLOG |
151
- | `$roll-jot` | Support | Fast capture a bug or idea into BACKLOG.md |
152
- | `$roll-spar` | BUILD | Adversarial TDD — Attacker writes tests, Defender writes code |
153
- | `$roll-sentinel` | CHECK | Scheduled production patrol |
154
- | `$roll-debug` | CHECK | Deep page diagnostics + root cause analysis |
155
- | `$roll-research` | RESEARCH | HV Analysis framework, outputs PDF report |
156
- | `$roll-release` | RELEASE | One-command publish: auto version (YYYY.MMDD.N) → tag → npm publish |
157
- | `$roll-.review` | Support | Pre-commit self code review |
158
- | `$roll-.qa` | Support | Test pyramid and coverage standards reference |
159
- | `$roll-.changelog` | Support | Auto-generate CHANGELOG from BACKLOG |
160
- | `$roll-.echo` | Support | Passive intent clarification |
161
- | `$roll-.clarify` | Support | Passive scope clarification for vague build requests |
162
- | `$roll-notes` | Support | Project diary — append dev moments to `notes/YYYY-MM-DD.md` |
165
+ **Loop A Research & Design**
166
+
167
+ | Skill | Description |
168
+ |-------|-------------|
169
+ | `$roll-research` | HV Analysis (Horizontal-Vertical) deep research framework, outputs PDF report |
170
+ | `$roll-design` | Multi-turn discuss [peer review] DDD model solution design → [peer review] → write Stories to BACKLOG |
171
+
172
+ **Loop B Implementation & Iteration**
173
+
174
+ | Skill | Description |
175
+ |-------|-------------|
176
+ | `$roll-build` | Universal entry: Story ID, fix ID, or free-text fly mode. TCR-driven micro-commits |
177
+ | `$roll-spar` | Adversarial TDD Attacker writes tests, Defender writes code |
178
+ | `$roll-fix` | Bug fix / hotfix from BACKLOG |
179
+ | `$roll-release` | One-command publish: auto version (YYYY.MMDD.N) → tag → npm publish → GitHub Release |
180
+ | `$roll-peer` | Cross-agent code review Claude / Kimi / DeepSeek / Codex bilateral negotiation |
181
+
182
+ **Loop C — Observability & Maintenance**
183
+
184
+ | Skill | Description |
185
+ |-------|-------------|
186
+ | `$roll-sentinel` | Randomized production patrol with adaptive hot-spot weighting |
187
+ | `$roll-debug` | Deep page diagnostics + root cause analysis (Black Box probe) |
188
+
189
+ **Autonomous Evolution (optional, via `roll loop on`)**
190
+
191
+ | Skill | Description |
192
+ |-------|-------------|
193
+ | `$roll-loop` | Hourly BACKLOG executor — routes US/FIX/REFACTOR to the right skill, enforces TCR |
194
+ | `$roll-.dream` | Nightly code health scan — dead code, architectural drift → REFACTOR entries |
195
+ | `$roll-brief` | Owner-facing digest — what's done, what's pending, release readiness verdict |
196
+
197
+ **Passive Support**
198
+
199
+ | Skill | Description |
200
+ |-------|-------------|
201
+ | `$roll-.review` | Pre-commit multi-dimensional self code review |
202
+ | `$roll-.qa` | Test pyramid and coverage standards reference |
203
+ | `$roll-.changelog` | Auto-generate CHANGELOG from BACKLOG |
204
+ | `$roll-.echo` | Passive intent clarification |
205
+ | `$roll-.clarify` | Passive scope clarification for vague build requests |
206
+ | `$roll-idea` | Fast capture a bug or idea into BACKLOG.md |
207
+ | `$roll-notes` | Project diary — append dev moments to `notes/YYYY-MM-DD.md` |
208
+ | `$roll-doctor` | One-command dev toolchain health check |
209
+
210
+ ---
211
+
212
+ ## Autonomous Evolution
213
+
214
+ Off by default. Enable with `roll loop on` to let the agent work unattended on your project.
215
+
216
+ ```
217
+ ┌─────────────────────────────────────────────────────────┐
218
+ │ Base layer (always active) │
219
+ │ $roll-design → $roll-build → $roll-fix → $roll-spar │
220
+ │ Human drives every action │
221
+ ├─────────────────────────────────────────────────────────┤
222
+ │ Autonomous layer (opt-in: roll loop on) │
223
+ │ roll-loop — hourly BACKLOG executor │
224
+ │ roll-.dream — nightly code health scan │
225
+ │ roll-brief — daily morning digest + release readiness │
226
+ │ Human reviews briefs; retains sole release authority │
227
+ └─────────────────────────────────────────────────────────┘
228
+ ```
229
+
230
+ Three services are scheduled via macOS launchd (Linux: crontab) and managed as a unit:
231
+
232
+ - **roll-loop** runs hourly, picks up `📋 Todo` items from BACKLOG, routes them (`US-XXX → $roll-build`, `FIX-XXX → $roll-fix`, `REFACTOR-XXX → $roll-build`), and enforces TCR discipline — if a story completes with zero `tcr:` micro-commits, it reverts to Todo with an ALERT.
233
+ - **roll-.dream** runs nightly, scanning for dead code, architectural drift, and refactoring opportunities. Outputs `REFACTOR-XXX` entries to BACKLOG.
234
+ - **roll-brief** runs each morning (or on-demand via `roll brief`), producing an owner-facing digest with a release-readiness verdict.
235
+
236
+ The autonomous layer **never** invokes `roll-release`. Production deployment is always a human decision.
237
+
238
+ Use `roll loop monitor` for a real-time `top`-like view of all three services: launchd status, current execution state, pending queue, alerts, and live log tail.
239
+
240
+ Per-project agent configuration: `roll agent use <name>` writes `.roll.yaml` so each project can use a different AI client (claude / kimi / deepseek / pi / codex / opencode). Lookup order: `.roll.yaml` → `~/.roll/config.yaml` → `claude` (default).
163
241
 
164
242
  ---
165
243
 
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.510.10"
7
+ VERSION="2026.511.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"
@@ -544,6 +544,12 @@ cmd_setup() {
544
544
  _peer_ensure_state_dir
545
545
  ok "Peer state directory ready. Peer 状态目录已就绪。"
546
546
 
547
+ if [[ "$(uname)" == "Darwin" ]]; then
548
+ echo ""
549
+ info "Installing launchd plists... 正在安装 LaunchAgents..."
550
+ _install_launchd_plists "$(pwd -P)"
551
+ fi
552
+
547
553
  echo ""
548
554
  ok "Setup complete. 初始化完成。"
549
555
 
@@ -1729,8 +1735,128 @@ cmd_agent() {
1729
1735
  # ═══════════════════════════════════════════════════════════════════════════════
1730
1736
 
1731
1737
  _LOOP_TAG="# roll-loop"
1738
+ _SHARED_ROOT="${HOME}/.shared/roll"
1732
1739
  _LOOP_STATE="${HOME}/.shared/roll/loop/state.yaml"
1733
1740
  _LOOP_ALERT="${HOME}/.shared/roll/loop/ALERT.md"
1741
+ _LAUNCHD_DIR="${HOME}/Library/LaunchAgents"
1742
+
1743
+ # Returns a filesystem-safe slug combining the project basename and a 6-char
1744
+ # hash of the full path, ensuring uniqueness across sibling dirs with same name.
1745
+ _project_slug() {
1746
+ local path="$1"
1747
+ local base; base=$(basename "$path")
1748
+ local hash
1749
+ if command -v md5 &>/dev/null; then
1750
+ hash=$(printf '%s' "$path" | md5 | cut -c1-6)
1751
+ else
1752
+ hash=$(printf '%s' "$path" | md5sum | cut -c1-6)
1753
+ fi
1754
+ base=$(printf '%s' "$base" | tr -cs '[:alnum:]' '-' | sed 's/-*$//')
1755
+ printf '%s' "${base}-${hash}"
1756
+ }
1757
+
1758
+ _launchd_label() {
1759
+ local service="$1" project_path="$2"
1760
+ printf 'com.roll.%s.%s' "$service" "$(_project_slug "$project_path")"
1761
+ }
1762
+
1763
+ _launchd_plist_path() {
1764
+ local service="$1" project_path="$2"
1765
+ printf '%s/%s.plist' "$_LAUNCHD_DIR" "$(_launchd_label "$service" "$project_path")"
1766
+ }
1767
+
1768
+ _write_launchd_plist() {
1769
+ local plist_path="$1" label="$2" project_path="$3"
1770
+ local minute="$4" hour="$5" runner_script="$6"
1771
+
1772
+ local hour_xml=""
1773
+ [[ -n "$hour" ]] && hour_xml=" <key>Hour</key>
1774
+ <integer>${hour}</integer>
1775
+ "
1776
+
1777
+ local content
1778
+ content="<?xml version=\"1.0\" encoding=\"UTF-8\"?>
1779
+ <!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">
1780
+ <plist version=\"1.0\">
1781
+ <dict>
1782
+ <key>Label</key>
1783
+ <string>${label}</string>
1784
+ <key>ProgramArguments</key>
1785
+ <array>
1786
+ <string>/bin/bash</string>
1787
+ <string>-l</string>
1788
+ <string>${runner_script}</string>
1789
+ </array>
1790
+ <key>StartCalendarInterval</key>
1791
+ <dict>
1792
+ <key>Minute</key>
1793
+ <integer>${minute}</integer>
1794
+ ${hour_xml} </dict>
1795
+ <key>WorkingDirectory</key>
1796
+ <string>${project_path}</string>
1797
+ </dict>
1798
+ </plist>"
1799
+
1800
+ if [[ -f "$plist_path" ]] && [[ "$(cat "$plist_path")" == "$content" ]]; then
1801
+ return 0
1802
+ fi
1803
+ printf '%s\n' "$content" > "$plist_path"
1804
+ }
1805
+
1806
+ _write_runner_script() {
1807
+ local script_path="$1" project_path="$2" cmd="$3" log_path="$4"
1808
+ mkdir -p "$(dirname "$script_path")"
1809
+ printf '#!/bin/bash -l\ncd "%s" && %s >> "%s" 2>&1\n' "$project_path" "$cmd" "$log_path" > "$script_path"
1810
+ chmod +x "$script_path"
1811
+ }
1812
+
1813
+ _launchd_is_loaded() {
1814
+ launchctl list "$1" &>/dev/null 2>&1
1815
+ }
1816
+
1817
+ # Install launchd plist files (disabled by default) and runner scripts for
1818
+ # a given project path. Idempotent — skips unchanged files. Does NOT load.
1819
+ _install_launchd_plists() {
1820
+ local project_path="$1"
1821
+ local sd="${ROLL_HOME}/skills"
1822
+ local shared="${_SHARED_ROOT}"
1823
+
1824
+ mkdir -p "$_LAUNCHD_DIR"
1825
+ mkdir -p "${shared}/loop" "${shared}/dream" "${shared}/brief"
1826
+
1827
+ local services=("loop" "dream" "brief")
1828
+ local skill_names=("roll-loop" "roll-.dream" "roll-brief")
1829
+ local minutes=("0" "0" "0")
1830
+ local hours=("" "3" "9")
1831
+
1832
+ local updated=0
1833
+ for i in "${!services[@]}"; do
1834
+ local svc="${services[$i]}"
1835
+ local skill="${skill_names[$i]}"
1836
+ local minute="${minutes[$i]}"
1837
+ local hour="${hours[$i]}"
1838
+ local label; label=$(_launchd_label "$svc" "$project_path")
1839
+ local plist; plist=$(_launchd_plist_path "$svc" "$project_path")
1840
+ local runner="${shared}/${svc}/run.sh"
1841
+ local log="${shared}/${svc}/cron.log"
1842
+ local cmd; cmd=$(_agent_skill_cmd "${sd}/${skill}/SKILL.md" 2>/dev/null || echo "roll loop now")
1843
+
1844
+ _write_runner_script "$runner" "$project_path" "cd \"${project_path}\" && ${cmd}" "$log"
1845
+
1846
+ local before=""
1847
+ [[ -f "$plist" ]] && before=$(cat "$plist")
1848
+ _write_launchd_plist "$plist" "$label" "$project_path" "$minute" "$hour" "$runner"
1849
+ local after; after=$(cat "$plist")
1850
+ [[ "$before" != "$after" ]] && updated=$((updated + 1))
1851
+ done
1852
+
1853
+ if [[ $updated -gt 0 ]]; then
1854
+ ok "Launchd plists installed (${updated} updated) LaunchAgents 已安装"
1855
+ echo " Run: roll loop on to activate 运行 roll loop on 激活"
1856
+ else
1857
+ ok "Launchd plists up to date LaunchAgents 无需更新"
1858
+ fi
1859
+ }
1734
1860
 
1735
1861
  _agent_skill_cmd() {
1736
1862
  local skill_path="$1"
@@ -1738,7 +1864,7 @@ _agent_skill_cmd() {
1738
1864
  # Strip YAML frontmatter inline for cron commands
1739
1865
  local strip="awk 'NR==1 && /^---$/{skip=1;next} skip && /^---$/{skip=0;next} !skip{print}' '${skill_path}'"
1740
1866
  case "$agent" in
1741
- claude) echo "claude -p \"\$(${strip})\"" ;;
1867
+ claude) local _bin; _bin=$(command -v claude 2>/dev/null || echo "claude"); echo "${_bin} -p \"\$(${strip})\"" ;;
1742
1868
  kimi) echo "kimi --quiet -p \"\$(${strip})\"" ;;
1743
1869
  deepseek) echo "deepseek \"\$(${strip})\"" ;;
1744
1870
  pi) echo "pi -p \"\$(${strip})\"" ;;
@@ -1751,31 +1877,57 @@ _agent_skill_cmd() {
1751
1877
  cmd_loop() {
1752
1878
  local subcmd="${1:-status}"; shift || true
1753
1879
  case "$subcmd" in
1754
- on) _loop_on ;;
1755
- off) _loop_off ;;
1756
- now) _loop_now ;;
1757
- status) _loop_status ;;
1758
- resume) _loop_resume ;;
1759
- reset) _loop_reset ;;
1760
- *) err "Usage: roll loop <on|off|now|status|resume|reset>"; exit 1 ;;
1880
+ on) _loop_on ;;
1881
+ off) _loop_off ;;
1882
+ now) _loop_now ;;
1883
+ status) _loop_status ;;
1884
+ monitor) _loop_monitor "${1:-3}" ;;
1885
+ resume) _loop_resume ;;
1886
+ reset) _loop_reset ;;
1887
+ *) err "Usage: roll loop <on|off|now|status|monitor|resume|reset>"; exit 1 ;;
1761
1888
  esac
1762
1889
  }
1763
1890
 
1764
1891
  _loop_on() {
1765
1892
  local project_path; project_path=$(pwd -P)
1766
1893
  local agent; agent=$(_project_agent)
1767
- local sd="${ROLL_HOME}/skills"
1768
1894
 
1895
+ if [[ "$(uname)" == "Darwin" ]]; then
1896
+ _install_launchd_plists "$project_path" >/dev/null
1897
+
1898
+ local all_loaded=true
1899
+ for svc in loop dream brief; do
1900
+ local label; label=$(_launchd_label "$svc" "$project_path")
1901
+ if ! _launchd_is_loaded "$label"; then
1902
+ all_loaded=false
1903
+ launchctl load "$(_launchd_plist_path "$svc" "$project_path")" 2>/dev/null || true
1904
+ fi
1905
+ done
1906
+
1907
+ if $all_loaded; then
1908
+ warn "Loop already enabled for this project 当前项目 loop 已启用"; return 0
1909
+ fi
1910
+
1911
+ ok "Loop enabled 已启用"
1912
+ echo " • roll-loop every hour :00 每小时整点"
1913
+ echo " • roll-.dream nightly at 03:00 每晚 03:00"
1914
+ echo " • roll-brief daily at 09:00 每天 09:00"
1915
+ echo " • Agent: ${agent} (change: roll agent use <name>)"
1916
+ return 0
1917
+ fi
1918
+
1919
+ # Linux: crontab
1920
+ local sd="${ROLL_HOME}/skills"
1769
1921
  if crontab -l 2>/dev/null | grep -q "${_LOOP_TAG}:${project_path}"; then
1770
1922
  warn "Loop already enabled for this project 当前项目 loop 已启用"; return 0
1771
1923
  fi
1772
1924
 
1773
- mkdir -p ~/.shared/roll/{loop,dream,brief}
1925
+ mkdir -p "${_SHARED_ROOT}/loop" "${_SHARED_ROOT}/dream" "${_SHARED_ROOT}/brief"
1774
1926
 
1775
1927
  local loop_cmd dream_cmd brief_cmd
1776
- loop_cmd="cd \"${project_path}\" && $(_agent_skill_cmd "${sd}/roll-loop/SKILL.md") >> ~/.shared/roll/loop/cron.log 2>&1"
1777
- dream_cmd="cd \"${project_path}\" && $(_agent_skill_cmd "${sd}/roll-.dream/SKILL.md") >> ~/.shared/roll/dream/cron.log 2>&1"
1778
- brief_cmd="cd \"${project_path}\" && $(_agent_skill_cmd "${sd}/roll-brief/SKILL.md") >> ~/.shared/roll/brief/cron.log 2>&1"
1928
+ loop_cmd="cd \"${project_path}\" && $(_agent_skill_cmd "${sd}/roll-loop/SKILL.md") >> ${_SHARED_ROOT}/loop/cron.log 2>&1"
1929
+ dream_cmd="cd \"${project_path}\" && $(_agent_skill_cmd "${sd}/roll-.dream/SKILL.md") >> ${_SHARED_ROOT}/dream/cron.log 2>&1"
1930
+ brief_cmd="cd \"${project_path}\" && $(_agent_skill_cmd "${sd}/roll-brief/SKILL.md") >> ${_SHARED_ROOT}/brief/cron.log 2>&1"
1779
1931
 
1780
1932
  (
1781
1933
  crontab -l 2>/dev/null
@@ -1786,13 +1938,31 @@ _loop_on() {
1786
1938
 
1787
1939
  ok "Loop enabled 已启用"
1788
1940
  echo " • roll-loop every hour :00 每小时整点"
1789
- echo " • roll-.dream nightly at 01:00 每晚 01:00"
1790
- echo " • roll-brief daily at 08:00 每天 08:00"
1941
+ echo " • roll-.dream nightly at 03:00 每晚 03:00"
1942
+ echo " • roll-brief daily at 09:00 每天 09:00"
1791
1943
  echo " • Agent: ${agent} (change: roll agent use <name>)"
1792
1944
  }
1793
1945
 
1794
1946
  _loop_off() {
1795
1947
  local project_path; project_path=$(pwd -P)
1948
+
1949
+ if [[ "$(uname)" == "Darwin" ]]; then
1950
+ local any_loaded=false
1951
+ for svc in loop dream brief; do
1952
+ local label; label=$(_launchd_label "$svc" "$project_path")
1953
+ if _launchd_is_loaded "$label"; then
1954
+ any_loaded=true
1955
+ launchctl unload "$(_launchd_plist_path "$svc" "$project_path")" 2>/dev/null || true
1956
+ fi
1957
+ done
1958
+ if ! $any_loaded; then
1959
+ warn "Loop not enabled for this project 当前项目 loop 未启用"; return 0
1960
+ fi
1961
+ ok "Loop disabled 已停用"
1962
+ return 0
1963
+ fi
1964
+
1965
+ # Linux: crontab
1796
1966
  if ! crontab -l 2>/dev/null | grep -q "${_LOOP_TAG}:${project_path}"; then
1797
1967
  warn "Loop not enabled for this project 当前项目 loop 未启用"; return 0
1798
1968
  fi
@@ -1805,14 +1975,26 @@ _loop_now() {
1805
1975
  warn "Loop already running loop 正在运行中"; return 0
1806
1976
  fi
1807
1977
  info "Triggering loop cycle... 正在触发一个周期..."
1808
- _agent_run_skill "roll-loop"
1978
+ local log_file="${_SHARED_ROOT}/loop/launchd.log"
1979
+ mkdir -p "$(dirname "$log_file")"
1980
+ _agent_run_skill "roll-loop" 2>&1 | tee -a "$log_file" || true
1981
+ # Reset stale running state if skill exited without cleanup (e.g. API error, SIGKILL)
1982
+ if [[ -f "$_LOOP_STATE" ]] && grep -q "^status: running" "$_LOOP_STATE" 2>/dev/null; then
1983
+ printf "status: idle\n" > "$_LOOP_STATE"
1984
+ fi
1809
1985
  }
1810
1986
 
1811
1987
  _loop_status() {
1812
1988
  local project_path; project_path=$(pwd -P)
1813
1989
  local agent; agent=$(_project_agent)
1814
1990
  echo ""
1815
- if crontab -l 2>/dev/null | grep -q "${_LOOP_TAG}:${project_path}"; then
1991
+ local loop_enabled=false
1992
+ if [[ "$(uname)" == "Darwin" ]]; then
1993
+ _launchd_is_loaded "$(_launchd_label "loop" "$project_path")" && loop_enabled=true
1994
+ else
1995
+ crontab -l 2>/dev/null | grep -q "${_LOOP_TAG}:${project_path}" && loop_enabled=true
1996
+ fi
1997
+ if $loop_enabled; then
1816
1998
  echo -e " Scheduler ${GREEN}● enabled${NC} Agent: ${CYAN}${agent}${NC}"
1817
1999
  else
1818
2000
  echo -e " Scheduler ${YELLOW}○ disabled${NC} run: roll loop on"
@@ -1842,6 +2024,176 @@ _loop_reset() {
1842
2024
  fi
1843
2025
  }
1844
2026
 
2027
+ # Count `tcr:` prefixed commits in the current git repo since started_at timestamp.
2028
+ _loop_tcr_count() {
2029
+ local started_at="$1"
2030
+ git log --oneline --since="${started_at}" 2>/dev/null \
2031
+ | awk '/^[a-f0-9]+ tcr:/{n++} END{print n+0}'
2032
+ }
2033
+
2034
+ # Verify TCR rhythm after a story completes. Returns 0 if ok, 1 if no TCR commits.
2035
+ # On failure: reverts story in BACKLOG.md to 📋 Todo and writes ALERT.
2036
+ _loop_enforce_tcr() {
2037
+ local story_id="$1"
2038
+ local started_at="${2:-}"
2039
+
2040
+ [[ -z "$started_at" ]] && return 0
2041
+
2042
+ local count; count=$(_loop_tcr_count "$started_at")
2043
+
2044
+ if [[ "$count" -eq 0 ]]; then
2045
+ # Revert story status
2046
+ if [[ -f "BACKLOG.md" ]]; then
2047
+ local tmp; tmp=$(mktemp)
2048
+ sed "/\[${story_id}\]/s/ | ✅ Done |/ | 📋 Todo |/" BACKLOG.md > "$tmp" \
2049
+ && mv "$tmp" BACKLOG.md
2050
+ fi
2051
+
2052
+ # Write ALERT
2053
+ mkdir -p "$(dirname "$_LOOP_ALERT")"
2054
+ cat > "$_LOOP_ALERT" << EOF
2055
+ # ALERT — TCR check failed
2056
+
2057
+ **Time**: $(date '+%Y-%m-%d %H:%M')
2058
+ **Story**: ${story_id}
2059
+ **Reason**: zero tcr: commits since story start (${started_at})
2060
+
2061
+ **Action required** (choose one):
2062
+ - Add TCR commits and re-run: \`roll loop now\`
2063
+ - Take over manually: \`\$roll-build ${story_id}\`
2064
+ - Reset and retry: \`roll loop reset\` then \`roll loop now\`
2065
+ EOF
2066
+ return 1
2067
+ fi
2068
+
2069
+ return 0
2070
+ }
2071
+
2072
+ _loop_monitor() {
2073
+ local interval="${1:-3}"
2074
+ local project_path; project_path=$(pwd -P)
2075
+ local project_name; project_name=$(basename "$project_path")
2076
+
2077
+ # Determine terminal clear capability
2078
+ local clear_cmd="clear"
2079
+ command -v clear &>/dev/null || clear_cmd="echo ''"
2080
+
2081
+ while true; do
2082
+ $clear_cmd
2083
+ local agent; agent=$(_project_agent)
2084
+ local now; now=$(date '+%Y-%m-%d %H:%M:%S')
2085
+
2086
+ echo -e "\n ${BOLD}${CYAN}roll loop monitor${NC} ${YELLOW}${project_name}${NC} ${now} (Ctrl-C to exit)\n"
2087
+
2088
+ # Services status (three services on macOS, single on Linux)
2089
+ echo -e " ${BOLD}Services 服务状态${NC} Agent: ${CYAN}${agent}${NC}"
2090
+ if [[ "$(uname)" == "Darwin" ]]; then
2091
+ local svc_info svc schedule
2092
+ for svc_info in "loop:hourly" "dream:03:00" "brief:09:00"; do
2093
+ svc="${svc_info%%:*}"
2094
+ schedule="${svc_info#*:}"
2095
+ if _launchd_is_loaded "$(_launchd_label "$svc" "$project_path")"; then
2096
+ printf " ${GREEN}%-8s ● enabled${NC} (%s)\n" "$svc" "$schedule"
2097
+ else
2098
+ printf " ${YELLOW}%-8s ○ disabled${NC} (%s)\n" "$svc" "$schedule"
2099
+ fi
2100
+ done
2101
+ else
2102
+ if crontab -l 2>/dev/null | grep -q "${_LOOP_TAG}:${project_path}"; then
2103
+ echo -e " ${GREEN}loop ● enabled${NC}"
2104
+ else
2105
+ echo -e " ${YELLOW}loop ○ disabled${NC} run: roll loop on"
2106
+ fi
2107
+ fi
2108
+
2109
+ # Current state
2110
+ if [[ -f "$_LOOP_STATE" ]]; then
2111
+ local status current_item started_at run_id
2112
+ status=$(grep '^status:' "$_LOOP_STATE" | awk '{print $2}')
2113
+ current_item=$(grep '^current_item:' "$_LOOP_STATE" | awk '{print $2}')
2114
+ started_at=$(grep '^started_at:' "$_LOOP_STATE" | cut -d' ' -f2- | tr -d '"')
2115
+ run_id=$(grep '^run_id:' "$_LOOP_STATE" | awk '{print $2}')
2116
+ echo ""
2117
+ case "$status" in
2118
+ running) echo -e " State ${GREEN}▶ running${NC} ${CYAN}${current_item}${NC} started: ${started_at} run: ${run_id}" ;;
2119
+ paused) echo -e " State ${RED}‖ paused${NC} on: ${current_item}" ;;
2120
+ idle) echo -e " State ${YELLOW}○ idle${NC}" ;;
2121
+ *) echo -e " State ${status}" ;;
2122
+ esac
2123
+ else
2124
+ echo ""
2125
+ echo -e " State ${YELLOW}○ no state file${NC}"
2126
+ fi
2127
+
2128
+ # Alert
2129
+ if [[ -f "$_LOOP_ALERT" ]]; then
2130
+ echo ""
2131
+ echo -e " ${RED}⚠ ALERT:${NC}"
2132
+ sed 's/^/ /' "$_LOOP_ALERT"
2133
+ fi
2134
+
2135
+ # Queue: pending items
2136
+ echo ""
2137
+ echo -e " ${BOLD}Queue 待处理队列${NC}"
2138
+ local backlog="BACKLOG.md"
2139
+ if [[ -f "$backlog" ]]; then
2140
+ local queue_count=0
2141
+ local fix_pending us_pending refactor_pending
2142
+ fix_pending=$(grep -E '^\| FIX-' "$backlog" | grep -F '| 📋 Todo |' || true)
2143
+ us_pending=$(grep -E '^\| \[US-' "$backlog" | grep -F '| 📋 Todo |' || true)
2144
+ refactor_pending=$(grep -E '^\| REFACTOR-' "$backlog" | grep -F '| 📋 Todo |' || true)
2145
+
2146
+ # FIX first (priority)
2147
+ while IFS= read -r line; do
2148
+ [[ -z "$line" ]] && continue
2149
+ local id desc
2150
+ id=$(echo "$line" | awk -F'|' '{print $2}' | tr -d ' ')
2151
+ desc=$(echo "$line" | awk -F'|' '{print $3}' | sed 's/^ *//;s/ *$//' | cut -c1-60)
2152
+ printf " ${RED}%-14s${NC} %s\n" "$id" "$desc"
2153
+ (( queue_count++ )) || true
2154
+ done <<< "$fix_pending"
2155
+
2156
+ # US stories
2157
+ while IFS= read -r line; do
2158
+ [[ -z "$line" ]] && continue
2159
+ local id desc
2160
+ id=$(echo "$line" | sed 's/.*\[\(US-[^]]*\)\].*/\1/')
2161
+ desc=$(echo "$line" | awk -F'|' '{print $3}' | sed 's/^ *//;s/ *$//' | cut -c1-60)
2162
+ printf " ${CYAN}%-14s${NC} %s\n" "$id" "$desc"
2163
+ (( queue_count++ )) || true
2164
+ done <<< "$us_pending"
2165
+
2166
+ # Refactors
2167
+ while IFS= read -r line; do
2168
+ [[ -z "$line" ]] && continue
2169
+ local id desc
2170
+ id=$(echo "$line" | awk -F'|' '{print $2}' | tr -d ' ')
2171
+ desc=$(echo "$line" | awk -F'|' '{print $3}' | sed 's/^ *//;s/ *$//' | cut -c1-60)
2172
+ printf " ${YELLOW}%-14s${NC} %s\n" "$id" "$desc"
2173
+ (( queue_count++ )) || true
2174
+ done <<< "$refactor_pending"
2175
+
2176
+ [[ $queue_count -eq 0 ]] && echo -e " ${GREEN}✓ empty${NC}"
2177
+ else
2178
+ echo " BACKLOG.md not found"
2179
+ fi
2180
+
2181
+ # Log tail (launchd.log)
2182
+ local log_file="${_SHARED_ROOT}/loop/launchd.log"
2183
+ echo ""
2184
+ echo -e " ─────────────────────────────────────────────────────"
2185
+ echo -e " ${BOLD}Log Tail 实时日志${NC} (~/.shared/roll/loop/launchd.log, last 10 lines)"
2186
+ if [[ -f "$log_file" && -s "$log_file" ]]; then
2187
+ tail -10 "$log_file" | sed 's/^/ /'
2188
+ else
2189
+ echo -e " ${YELLOW}(no log yet)${NC}"
2190
+ fi
2191
+
2192
+ echo ""
2193
+ sleep "$interval"
2194
+ done
2195
+ }
2196
+
1845
2197
  # ═══════════════════════════════════════════════════════════════════════════════
1846
2198
  # BRIEF — owner-facing project digest
1847
2199
  # ═══════════════════════════════════════════════════════════════════════════════
@@ -1872,6 +2224,75 @@ cmd_release() {
1872
2224
  _agent_run_skill "roll-release"
1873
2225
  }
1874
2226
 
2227
+ # ═══════════════════════════════════════════════════════════════════════════════
2228
+ # BACKLOG — show pending tasks
2229
+ # ═══════════════════════════════════════════════════════════════════════════════
2230
+
2231
+ cmd_backlog() {
2232
+ local backlog="BACKLOG.md"
2233
+ if [[ ! -f "$backlog" ]]; then
2234
+ err "BACKLOG.md not found — run 'roll init' first 未找到 BACKLOG.md,请先运行 roll init"
2235
+ return 1
2236
+ fi
2237
+
2238
+ local us_items fix_items refactor_items total=0
2239
+
2240
+ us_items=$(grep -E '^\| \[US-' "$backlog" | grep -F '| 📋 Todo |' || true)
2241
+ fix_items=$(grep -E '^\| FIX-' "$backlog" | grep -F '| 📋 Todo |' || true)
2242
+ refactor_items=$(grep -E '^\| REFACTOR-' "$backlog" | grep -F '| 📋 Todo |' || true)
2243
+
2244
+ local us_count fix_count refactor_count
2245
+ us_count=$(echo "$us_items" | grep -c . || true)
2246
+ fix_count=$(echo "$fix_items" | grep -c . || true)
2247
+ refactor_count=$(echo "$refactor_items" | grep -c . || true)
2248
+ [[ -z "$us_items" ]] && us_count=0
2249
+ [[ -z "$fix_items" ]] && fix_count=0
2250
+ [[ -z "$refactor_items" ]] && refactor_count=0
2251
+ total=$(( us_count + fix_count + refactor_count ))
2252
+
2253
+ echo ""
2254
+ echo -e " ${BOLD}Pending Backlog 待处理任务${NC} (${total} items)"
2255
+ echo ""
2256
+
2257
+ if [[ $fix_count -gt 0 ]]; then
2258
+ echo -e " ${RED}Bug Fixes 缺陷修复 (${fix_count})${NC}"
2259
+ while IFS= read -r line; do
2260
+ local id desc
2261
+ id=$(echo "$line" | awk -F'|' '{print $2}' | tr -d ' ')
2262
+ desc=$(echo "$line" | awk -F'|' '{print $3}' | sed 's/^ *//;s/ *$//')
2263
+ printf " %-12s %s\n" "$id" "$desc"
2264
+ done <<< "$fix_items"
2265
+ echo ""
2266
+ fi
2267
+
2268
+ if [[ $us_count -gt 0 ]]; then
2269
+ echo -e " ${CYAN}User Stories 用户故事 (${us_count})${NC}"
2270
+ while IFS= read -r line; do
2271
+ local id desc
2272
+ id=$(echo "$line" | sed 's/.*\[\(US-[^]]*\)\].*/\1/')
2273
+ desc=$(echo "$line" | awk -F'|' '{print $3}' | sed 's/^ *//;s/ *$//')
2274
+ printf " %-14s %s\n" "$id" "$desc"
2275
+ done <<< "$us_items"
2276
+ echo ""
2277
+ fi
2278
+
2279
+ if [[ $refactor_count -gt 0 ]]; then
2280
+ echo -e " ${YELLOW}Refactors 重构 (${refactor_count})${NC}"
2281
+ while IFS= read -r line; do
2282
+ local id desc
2283
+ id=$(echo "$line" | awk -F'|' '{print $2}' | tr -d ' ')
2284
+ desc=$(echo "$line" | awk -F'|' '{print $3}' | sed 's/^ *//;s/ *$//')
2285
+ printf " %-16s %s\n" "$id" "$desc"
2286
+ done <<< "$refactor_items"
2287
+ echo ""
2288
+ fi
2289
+
2290
+ if [[ $total -eq 0 ]]; then
2291
+ echo -e " ${GREEN}✓ Nothing pending — backlog is clear 暂无待处理任务${NC}"
2292
+ echo ""
2293
+ fi
2294
+ }
2295
+
1875
2296
  # ─────────────────────────────────────────────────────────────────────────────
1876
2297
 
1877
2298
  _dashboard() {
@@ -1881,31 +2302,55 @@ _dashboard() {
1881
2302
 
1882
2303
  echo -e "\n ${BOLD}${CYAN}${project_name}${NC} ${YELLOW}v${VERSION}${NC}\n"
1883
2304
 
1884
- if crontab -l 2>/dev/null | grep -q "${_LOOP_TAG}:${project_path}"; then
2305
+ local _dash_loop_enabled=false
2306
+ if [[ "$(uname)" == "Darwin" ]]; then
2307
+ _launchd_is_loaded "$(_launchd_label "loop" "$project_path")" && _dash_loop_enabled=true
2308
+ else
2309
+ crontab -l 2>/dev/null | grep -q "${_LOOP_TAG}:${project_path}" && _dash_loop_enabled=true
2310
+ fi
2311
+ if $_dash_loop_enabled; then
1885
2312
  echo -e " Loop ${GREEN}● on${NC} Agent: ${CYAN}${agent}${NC}"
2313
+ if [[ "$(uname)" == "Darwin" ]]; then
2314
+ for svc_info in "loop:hourly" "dream:03:00" "brief:09:00"; do
2315
+ local svc="${svc_info%%:*}" schedule="${svc_info#*:}"
2316
+ if _launchd_is_loaded "$(_launchd_label "$svc" "$project_path")"; then
2317
+ printf " ${GREEN}%-8s ● enabled${NC} (%s)\n" "$svc" "$schedule"
2318
+ else
2319
+ printf " ${YELLOW}%-8s ○ disabled${NC} (%s)\n" "$svc" "$schedule"
2320
+ fi
2321
+ done
2322
+ fi
1886
2323
  else
1887
2324
  echo -e " Loop ${YELLOW}○ off${NC} run: roll loop on"
1888
2325
  fi
1889
2326
  [[ -f "$_LOOP_ALERT" ]] && echo -e " ${RED}⚠ ALERT — run: roll loop status${NC}"
1890
2327
 
2328
+ # Backlog summary
2329
+ if [[ -f "BACKLOG.md" ]]; then
2330
+ local pending_count
2331
+ pending_count=$(grep -cF '| 📋 Todo |' "BACKLOG.md" 2>/dev/null) || pending_count=0
2332
+ if (( pending_count > 0 )); then
2333
+ echo -e " Backlog ${YELLOW}${pending_count} pending${NC} run: roll backlog"
2334
+ else
2335
+ echo -e " Backlog ${GREEN}✓ clear${NC}"
2336
+ fi
2337
+ fi
2338
+
1891
2339
  local briefs_dir="docs/briefs"
1892
2340
  local latest; latest=$(ls "${briefs_dir}"/*.md 2>/dev/null | sort | tail -1 || true)
1893
2341
  if [[ -n "$latest" ]]; then
1894
2342
  local mod_time now age
1895
2343
  mod_time=$(stat -c %Y "$latest" 2>/dev/null || stat -f %m "$latest" 2>/dev/null || echo 0)
1896
2344
  now=$(date +%s); age=$(( (now - mod_time) / 3600 ))
1897
- echo -e "\n Brief ${CYAN}$(basename "$latest")${NC} (${age}h ago)"
1898
- grep -A1 "## Release Readiness" "$latest" 2>/dev/null | tail -1 | sed 's/^/ /'
1899
- echo ""
1900
- echo " run: roll brief for full brief"
2345
+ local readiness; readiness=$(awk '/^## Release Readiness/{found=1;next} found && /[^[:space:]]/{gsub(/\*\*/,""); print; exit}' "$latest" 2>/dev/null || true)
2346
+ local done_count; done_count=$(grep -c '| Story\|| Fix\|| Refactor' "$latest" 2>/dev/null) || done_count=0
2347
+ echo -e "\n Brief (${age}h ago) ${readiness:+${CYAN}${readiness}${NC}}"
2348
+ [[ "$done_count" -gt 0 ]] && echo -e " This cycle: ${done_count} items completed"
1901
2349
  else
1902
2350
  echo -e "\n No brief yet — run: roll brief"
1903
2351
  fi
1904
2352
 
1905
2353
  echo ""
1906
- echo " loop <on|off|now|status|resume|reset> · brief · agent use <name> · release"
1907
- echo " setup · update · init · status · peer · help"
1908
- echo ""
1909
2354
  }
1910
2355
 
1911
2356
  # ═══════════════════════════════════════════════════════════════════════════════
@@ -1930,8 +2375,9 @@ usage() {
1930
2375
  echo " init [Project] Create AGENTS.md + BACKLOG.md + docs/ 初始化项目工作流文件"
1931
2376
  echo " status [Diagnostic] Show current state 显示当前状态"
1932
2377
  echo " peer [Peer Review] Cross-agent negotiation 跨 Agent 协商对审"
1933
- echo " loop <on|off|now|status|resume|reset> [Autonomous] Manage scheduled BACKLOG executor 管理自主执行循环"
2378
+ echo " loop <on|off|now|status|monitor|resume|reset> [Autonomous] Manage scheduled BACKLOG executor 管理自主执行循环"
1934
2379
  echo " brief [Digest] Show latest owner brief (regenerate if stale) 展示最新简报"
2380
+ echo " backlog [View] Show all pending tasks from BACKLOG.md 显示待处理任务清单"
1935
2381
  echo " agent [use <name>|list] [Config] Per-project agent selection 切换项目 agent"
1936
2382
  echo " release [Publish] Sync changelog + version bump + npm publish 同步日志并发版"
1937
2383
  echo ""
@@ -1941,6 +2387,7 @@ usage() {
1941
2387
  echo " roll init # New or re-merge project (run in project) 新建或重新合并(项目目录)"
1942
2388
  echo " roll loop on # Enable autonomous loop (cron) 启用自主执行循环"
1943
2389
  echo " roll brief # Show latest brief 查看最新简报"
2390
+ echo " roll backlog # Show pending BACKLOG items 查看待处理任务"
1944
2391
  echo " roll agent use kimi # Switch this project to kimi 切换当前项目到 kimi"
1945
2392
 
1946
2393
  }
@@ -1957,6 +2404,7 @@ main() {
1957
2404
  peer) cmd_peer "$@" ;;
1958
2405
  loop) cmd_loop "$@" ;;
1959
2406
  brief) cmd_brief "$@" ;;
2407
+ backlog) cmd_backlog "$@" ;;
1960
2408
  agent) cmd_agent "$@" ;;
1961
2409
  release) cmd_release "$@" ;;
1962
2410
  version|--version|-v) echo "roll v${VERSION}" ;;
@@ -2012,7 +2460,7 @@ _check_update_async() {
2012
2460
 
2013
2461
  _notify_update() {
2014
2462
  local cache="${ROLL_HOME}/.update-check"
2015
- [[ -f "$cache" ]] || return
2463
+ [[ -f "$cache" ]] || return 0
2016
2464
  local latest; latest=$(awk '{print $2}' "$cache" 2>/dev/null || true)
2017
2465
  [[ -z "$latest" || "$latest" == "$VERSION" ]] && return
2018
2466
  local newer; newer=$(printf '%s\n%s\n' "$VERSION" "$latest" | sort -V | tail -1)
@@ -31,7 +31,7 @@
31
31
 
32
32
  ## 4. Workflow
33
33
  - **Scope Gate**: Only implement what is explicitly listed in the AC. Nothing more.
34
- - Requests made in conversation are NOT AC — capture with `roll-jot` first.
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
37
  - **TCR**: Test -> Green = Commit / Red = Revert. No WIP commits.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seanyao/roll",
3
- "version": "2026.510.10",
3
+ "version": "2026.511.2",
4
4
  "description": "Roll — Roll out features with AI agents",
5
5
  "scripts": {
6
6
  "test": "find tests/unit tests/integration -name '*.bats' | sort | xargs ./tests/helpers/bats-core/bin/bats"
@@ -22,7 +22,7 @@ Auto-invoked by `roll-build` (Fly mode) when the user input is:
22
22
  ## When Not to Use
23
23
 
24
24
  - Intent is already clear and actionable
25
- - User gives a specific command with a skill trigger (e.g. `$roll-jot ...`)
25
+ - User gives a specific command with a skill trigger (e.g. `$roll-idea ...`)
26
26
  - User is answering a clarification question you just asked
27
27
  - The task is simple enough that misinterpretation risk is negligible
28
28
  - User messy thoughts need restatement rather than questioning (use `$roll-.echo`)
@@ -233,10 +233,10 @@ CI failure
233
233
 
234
234
  ```bash
235
235
  # For fixable bugs — create FIX entry
236
- $roll-jot fix "CI: {step} fails — {root cause summary}"
236
+ $roll-idea fix "CI: {step} fails — {root cause summary}"
237
237
 
238
238
  # For flaky/environmental issues — create IDEA entry
239
- $roll-jot idea "CI: investigate flaky {test name}"
239
+ $roll-idea idea "CI: investigate flaky {test name}"
240
240
  ```
241
241
 
242
242
  ### Step 4: Execute Fix
@@ -95,52 +95,72 @@ A simple heuristic — not a gate, just a signal for the human:
95
95
 
96
96
  ### Step 4 — Write Brief
97
97
 
98
- Output to `docs/briefs/YYYY-MM-DD-HH.md`:
98
+ 文件命名:`docs/briefs/YYYY-MM-DD-{NN}.md`,NN 为当日序号(01 起,零填充两位)。
99
+ 计算方式:`ls docs/briefs/YYYY-MM-DD-*.md 2>/dev/null | wc -l`,+1 即为本次序号。
100
+
101
+ 全部用中文输出。**省略空 section**(无内容时连标题一起不输出):
99
102
 
100
103
  ```markdown
101
- # Brief {YYYY-MM-DD HH:mm}
104
+ # 简报 {YYYY-MM-DD HH:mm}
105
+
106
+ > 触发:{触发原因} | 覆盖:{起止时间范围}
102
107
 
103
- ## What's Done
104
- | ID | Description | Type |
108
+ ## 已完成({N} 项)
109
+ | 编号 | 描述 | 类型 |
105
110
  |----|-------------|------|
106
- | US-XXX | {title} | Story |
107
- | FIX-XXX | {title} | Fix |
108
- | REFACTOR-XXX | {title} | Refactor |
111
+ | US-XXX | {标题} | 用户故事 |
112
+ | FIX-XXX | {标题} | 缺陷修复 |
113
+ | REFACTOR-XXX | {标题} | 重构 |
109
114
 
110
- ## In Progress
111
- | ID | Description |
115
+ <!-- 仅当有 🔨 条目时输出 -->
116
+ ## 进行中
117
+ | 编号 | 描述 |
112
118
  |----|-------------|
113
- | US-XXX | {title} — started {date} |
119
+ | US-XXX | {标题} — 开始于 {date} |
114
120
 
115
- ## Queue ({N} items)
116
- | ID | Description | Priority |
121
+ <!-- 仅当有 📋 条目时输出 -->
122
+ ## 待处理队列({N} 项)
123
+ | 编号 | 描述 | 优先级 |
117
124
  |----|-------------|----------|
118
- | US-XXX | {title} | high |
125
+ | US-XXX | {标题} | |
126
+
127
+ <!-- 仅当 roll-.dream 有新发现时输出 -->
128
+ ## 悟见
129
+ {来自 docs/dream/ 的摘要}
130
+
131
+ <!-- 仅当有 ESCALATE 告警时输出 -->
132
+ ## 需人工介入
133
+ {告警内容}
119
134
 
120
- ## Overnight Dream Findings
121
- {summary from docs/dream/ since last brief, or "No new findings."}
135
+ ## 发版就绪
136
+ { 可发版 / ⚠️ 暂缓 原因}
122
137
 
123
- ## Escalations Requiring Human Input
124
- {any alerts, or "None — agent operating normally."}
138
+ **下一版本:** `v{YYYY}.{MMDD}.{序号}`
125
139
 
126
- ## Release Readiness
127
- { Release candidate / ⚠️ Hold — reason}
140
+ - {本轮新增 1}
141
+ - {本轮新增 2}
128
142
 
129
143
  ---
130
- *Next scheduled brief: {datetime}*
144
+ *状态:进行中 {N} · 待处理 {N} · 告警 {N} | 下次简报:{datetime}*
145
+ ```
146
+
147
+ **首份简报或传入 `--full-history`**:在"已完成"表格后追加历史汇总区块:
148
+
149
+ ```markdown
150
+ ### 历史汇总
151
+ **Epic: {Name}** — {done}/{total} ✅
131
152
  ```
132
153
 
133
154
  ### Step 5 — Notify
134
155
 
135
- After writing the file, print the brief path so it's visible in the terminal
136
- or CI log:
156
+ 写完文件后在终端或 CI 日志中打印简报路径:
137
157
 
138
158
  ```
139
- 📋 Brief written: docs/briefs/2026-05-10-08.md
140
- Release readiness: ✅ Release candidate
159
+ 📋 简报已生成:docs/briefs/2026-05-10-01.md
160
+ 发布就绪:✅ 可发版
141
161
  ```
142
162
 
143
- If there are escalations, print them prominently so they're impossible to miss.
163
+ 有升级事项时须显著打印,不得遗漏。
144
164
 
145
165
  ## Scheduler Configuration
146
166
 
@@ -18,7 +18,7 @@ One entry point. Any input. Full delivery.
18
18
  Input received
19
19
  ├── matches "US-[A-Z]+-[0-9]+" → Story mode: read BACKLOG → TCR workflow
20
20
  ├── matches "FIX-[A-Z]+-[0-9]+" → redirect to $roll-fix
21
- ├── matches "IDEA-[0-9]+" → redirect to $roll-jot (lookup and expand)
21
+ ├── matches "IDEA-[0-9]+" → redirect to $roll-idea (lookup and expand)
22
22
  └── anything else → Fly mode: clarify → design → execute
23
23
  ```
24
24
 
@@ -654,7 +654,7 @@ When complex state management is error-prone → consider full reset + re-initia
654
654
  roll-build → ship anything (new idea, US-ID, free-text request)
655
655
  roll-fix → fix a specific known bug (FIX-XXX / BUG-XXX)
656
656
  roll-design → plan and design before building (no code output)
657
- roll-jot → fast capture a bug or idea into BACKLOG.md
657
+ roll-idea → fast capture a bug or idea into BACKLOG.md
658
658
  roll-.clarify → passive scope clarification for vague build requests
659
659
  ```
660
660
 
@@ -29,7 +29,7 @@ Discuss approaches, design architecture, plan requirements, and write to `BACKLO
29
29
 
30
30
  ## When Not to Use
31
31
 
32
- - One-liner capture of ideas or bugs (use `$roll-jot`)
32
+ - One-liner capture of ideas or bugs (use `$roll-idea`)
33
33
  - Executing an already-split US-XXX (use `$roll-build`)
34
34
  - Fixing a well-defined FIX-XXX (use `$roll-fix`)
35
35
 
@@ -1,22 +1,22 @@
1
1
  ---
2
- name: roll-jot
2
+ name: roll-idea
3
3
  license: MIT
4
4
  allowed-tools: "Read, Edit"
5
5
  description: "Fast backlog capture. Analyzes a short description, classifies it as bug or idea, and appends it to BACKLOG.md with an auto-incremented ID."
6
6
  ---
7
7
 
8
- # roll-jot
8
+ # roll-idea
9
9
 
10
10
  > One-liner in, backlog entry out. No questions asked.
11
11
 
12
12
  ## Trigger
13
13
 
14
- User explicitly invokes `roll-jot` with a free-text description.
14
+ User explicitly invokes `roll-idea` with a free-text description.
15
15
 
16
16
  ```
17
- $roll-jot 评价页面的星星也要纳入到 draft 的 scope 里
18
- $roll-jot 手机端星星指标一行一个从上往下排
19
- $roll-jot 给 HOD 加一个批量导出 PDF 的功能
17
+ $roll-idea 评价页面的星星也要纳入到 draft 的 scope 里
18
+ $roll-idea 手机端星星指标一行一个从上往下排
19
+ $roll-idea 给 HOD 加一个批量导出 PDF 的功能
20
20
  ```
21
21
 
22
22
  ## When Not to Use
@@ -92,9 +92,13 @@ run_id: loop-20260510-0200
92
92
 
93
93
  After each item completes:
94
94
 
95
- 1. Update state file: `status: idle`
96
- 2. Check if a Feature is now fully complete (all its Stories ✅)
97
- 3. If yes and `brief_on_feature_complete: true` invoke `Skill("roll-brief")`
95
+ 1. **TCR 硬校验** call `_loop_enforce_tcr <story_id> <started_at>`:
96
+ - Count `tcr:` prefix commits since `started_at` via `git log --oneline --since=<started_at>`
97
+ - Count == 0 → revert story status in BACKLOG.md from Done → 📋 Todo; write ALERT to `~/.shared/roll/loop/ALERT.md` with story ID, time, reason "zero tcr: commits since story start", and suggested actions (`roll loop now` / `$roll-build <id>` / `roll loop reset`)
98
+ - Count > 0 → continue normally
99
+ 2. Update state file: `status: idle`
100
+ 3. Check if a Feature is now fully complete (all its Stories ✅)
101
+ 4. If yes and `brief_on_feature_complete: true` → invoke `Skill("roll-brief")`
98
102
 
99
103
  ### Step 5 — Write Run Summary
100
104
 
@@ -23,7 +23,7 @@ $roll-notes 今天的 code review 给了很好的反馈
23
23
 
24
24
  ## When Not to Use
25
25
 
26
- - Capturing a bug or feature idea into BACKLOG (use `$roll-jot`)
26
+ - Capturing a bug or feature idea into BACKLOG (use `$roll-idea`)
27
27
  - Generating user-facing release notes (use `$roll-.changelog`)
28
28
  - Writing design documents or AC (use `$roll-design`)
29
29