@seanyao/roll 0.5.0 → 2.602.1
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 +717 -0
- package/LICENSE +21 -0
- package/README.md +65 -165
- package/bin/dream-test-quality-scan +110 -0
- package/bin/roll +14897 -815
- package/conventions/config.yaml +17 -1
- package/conventions/global/AGENTS.md +146 -100
- package/conventions/global/CLAUDE.md +1 -21
- package/conventions/global/GEMINI.md +8 -22
- package/conventions/global/project_rules.md +9 -0
- package/conventions/templates/backend-service/AGENTS.md +30 -81
- package/conventions/templates/backend-service/GEMINI.md +3 -3
- package/conventions/templates/backend-service/project_rules.md +16 -0
- package/conventions/templates/cli/AGENTS.md +31 -58
- package/conventions/templates/cli/CLAUDE.md +3 -5
- package/conventions/templates/cli/GEMINI.md +3 -3
- package/conventions/templates/cli/project_rules.md +16 -0
- package/conventions/templates/frontend-only/AGENTS.md +29 -64
- package/conventions/templates/frontend-only/GEMINI.md +3 -3
- package/conventions/templates/frontend-only/project_rules.md +14 -0
- package/conventions/templates/fullstack/AGENTS.md +31 -79
- package/conventions/templates/fullstack/CLAUDE.md +1 -1
- package/conventions/templates/fullstack/GEMINI.md +3 -3
- package/conventions/templates/fullstack/project_rules.md +15 -0
- package/lib/README.md +42 -0
- package/lib/__pycache__/github_sync.cpython-314.pyc +0 -0
- package/lib/__pycache__/loop-fmt.cpython-314.pyc +0 -0
- package/lib/__pycache__/loop_result_eval.cpython-314.pyc +0 -0
- package/lib/__pycache__/loop_unstick.cpython-314.pyc +0 -0
- package/lib/__pycache__/model_prices.cpython-314.pyc +0 -0
- package/lib/__pycache__/prices_fetcher.cpython-314.pyc +0 -0
- package/lib/__pycache__/roll-home.cpython-314.pyc +0 -0
- package/lib/__pycache__/roll-loop-status.cpython-314.pyc +0 -0
- package/lib/__pycache__/roll_git.cpython-314.pyc +0 -0
- package/lib/__pycache__/roll_render.cpython-314.pyc +0 -0
- package/lib/__pycache__/slides-render.cpython-314.pyc +0 -0
- package/lib/agent_usage/README.md +49 -0
- package/lib/agent_usage/__init__.py +108 -0
- package/lib/agent_usage/__pycache__/__init__.cpython-314.pyc +0 -0
- package/lib/agent_usage/__pycache__/gemini.cpython-314.pyc +0 -0
- package/lib/agent_usage/__pycache__/kimi.cpython-314.pyc +0 -0
- package/lib/agent_usage/__pycache__/openai.cpython-314.pyc +0 -0
- package/lib/agent_usage/__pycache__/pi.cpython-314.pyc +0 -0
- package/lib/agent_usage/__pycache__/pi_emit.cpython-314.pyc +0 -0
- package/lib/agent_usage/__pycache__/qwen.cpython-314.pyc +0 -0
- package/lib/agent_usage/gemini.py +127 -0
- package/lib/agent_usage/kimi.py +278 -0
- package/lib/agent_usage/kimi_emit.py +123 -0
- package/lib/agent_usage/openai.py +126 -0
- package/lib/agent_usage/pi.py +200 -0
- package/lib/agent_usage/pi_emit.py +135 -0
- package/lib/agent_usage/qwen.py +128 -0
- package/lib/backfill-pi-usage.py +243 -0
- package/lib/changelog_audit.py +155 -0
- package/lib/changelog_generate.py +263 -0
- package/lib/context_feed_budget.sh +194 -0
- package/lib/github_sync.py +876 -0
- package/lib/i18n/README.md +54 -0
- package/lib/i18n/agent.sh +75 -0
- package/lib/i18n/alert.sh +20 -0
- package/lib/i18n/backlog.sh +96 -0
- package/lib/i18n/brief.sh +5 -0
- package/lib/i18n/changelog.sh +5 -0
- package/lib/i18n/ci.sh +15 -0
- package/lib/i18n/debug.sh +0 -0
- package/lib/i18n/doctor.sh +44 -0
- package/lib/i18n/dream.sh +0 -0
- package/lib/i18n/init.sh +91 -0
- package/lib/i18n/lang.sh +10 -0
- package/lib/i18n/loop.sh +140 -0
- package/lib/i18n/migrate.sh +74 -0
- package/lib/i18n/offboard.sh +31 -0
- package/lib/i18n/onboard.sh +0 -0
- package/lib/i18n/peer.sh +41 -0
- package/lib/i18n/peer_help.sh +25 -0
- package/lib/i18n/peer_reset.sh +7 -0
- package/lib/i18n/peer_status.sh +5 -0
- package/lib/i18n/prices.sh +3 -0
- package/lib/i18n/prices_refresh.sh +17 -0
- package/lib/i18n/prices_show.sh +7 -0
- package/lib/i18n/propose.sh +0 -0
- package/lib/i18n/release.sh +0 -0
- package/lib/i18n/research.sh +0 -0
- package/lib/i18n/review_pr.sh +0 -0
- package/lib/i18n/sentinel.sh +0 -0
- package/lib/i18n/setup.sh +3 -0
- package/lib/i18n/shared.sh +157 -0
- package/lib/i18n/skills/roll-brief.sh +47 -0
- package/lib/i18n/skills/roll-build.sh +97 -0
- package/lib/i18n/skills/roll-design.sh +18 -0
- package/lib/i18n/skills/roll-fix.sh +53 -0
- package/lib/i18n/skills/roll-loop.sh +28 -0
- package/lib/i18n/skills/roll-onboard.sh +33 -0
- package/lib/i18n/skills_catalog.sh +30 -0
- package/lib/i18n/slides.sh +3 -0
- package/lib/i18n/slides_build.sh +38 -0
- package/lib/i18n/slides_delete.sh +19 -0
- package/lib/i18n/slides_list.sh +14 -0
- package/lib/i18n/slides_logs.sh +12 -0
- package/lib/i18n/slides_new.sh +15 -0
- package/lib/i18n/slides_preview.sh +14 -0
- package/lib/i18n/slides_templates.sh +7 -0
- package/lib/i18n/status.sh +21 -0
- package/lib/i18n/update.sh +24 -0
- package/lib/i18n.sh +211 -0
- package/lib/loop-exit-summary.py +393 -0
- package/lib/loop-fmt.py +589 -0
- package/lib/loop_pick_agent.py +316 -0
- package/lib/loop_result_eval.py +469 -0
- package/lib/loop_unstick.py +180 -0
- package/lib/model_prices.py +186 -0
- package/lib/prices/README.md +35 -0
- package/lib/prices/snapshot-2026-05-22.json +22 -0
- package/lib/prices/snapshot-2026-05-23-deepseek.json +15 -0
- package/lib/prices/snapshot-2026-05-23-kimi.json +14 -0
- package/lib/prices_fetcher.py +285 -0
- package/lib/roll-backlog.py +225 -0
- package/lib/roll-brief.py +286 -0
- package/lib/roll-help.py +158 -0
- package/lib/roll-home.py +556 -0
- package/lib/roll-init.py +156 -0
- package/lib/roll-loop-status.py +1683 -0
- package/lib/roll-loop-story.py +191 -0
- package/lib/roll-onboard-render.py +378 -0
- package/lib/roll-peer.py +252 -0
- package/lib/roll-plan-validate.py +386 -0
- package/lib/roll-setup.py +102 -0
- package/lib/roll-status.py +367 -0
- package/lib/roll_git.py +41 -0
- package/lib/roll_render.py +414 -0
- package/lib/slides/components/README.md +123 -0
- package/lib/slides/components/cards-2.html +9 -0
- package/lib/slides/components/cards-3.html +9 -0
- package/lib/slides/components/cards-4.html +9 -0
- package/lib/slides/components/compare.html +22 -0
- package/lib/slides/components/highlight.html +9 -0
- package/lib/slides/components/pipeline.html +12 -0
- package/lib/slides/components/plain.html +7 -0
- package/lib/slides/components/quote.html +4 -0
- package/lib/slides/components/timeline.html +9 -0
- package/lib/slides/templates/introduction-v3.html +571 -0
- package/lib/slides/templates/pitch.html +0 -0
- package/lib/slides-render.py +778 -0
- package/lib/slides-validate.py +357 -0
- package/lib/test_quality_gate.py +143 -0
- package/package.json +8 -7
- package/skills/roll-.changelog/SKILL.md +406 -33
- package/skills/roll-.clarify/SKILL.md +5 -2
- package/skills/roll-.dream/SKILL.md +374 -0
- package/skills/roll-.echo/SKILL.md +5 -2
- package/skills/roll-.qa/SKILL.md +57 -3
- package/skills/roll-.review/SKILL.md +42 -3
- package/skills/roll-brief/SKILL.md +209 -0
- package/skills/roll-build/SKILL.md +308 -63
- package/skills/roll-debug/SKILL.md +341 -162
- package/skills/roll-debug/injectable-bb.js +263 -0
- package/skills/roll-deck/SKILL.md +296 -0
- package/skills/roll-design/ENGINEERING_CHECKLIST.md +1 -1
- package/skills/roll-design/SKILL.md +727 -94
- package/skills/roll-doc/SKILL.md +595 -0
- package/skills/roll-doctor/SKILL.md +192 -0
- package/skills/roll-fix/SKILL.md +149 -32
- package/skills/{roll-jot → roll-idea}/SKILL.md +18 -10
- package/skills/roll-loop/SKILL.md +578 -0
- package/skills/roll-notes/SKILL.md +103 -0
- package/skills/roll-onboard/SKILL.md +234 -0
- package/skills/roll-peer/SKILL.md +336 -0
- package/skills/roll-propose/SKILL.md +157 -0
- package/skills/roll-review-pr/SKILL.md +58 -0
- package/skills/roll-sentinel/SKILL.md +11 -2
- package/skills/roll-spar/SKILL.md +8 -6
- package/template/.github/workflows/ci.yml +5 -2
- package/template/AGENTS.md +20 -74
- package/skills/roll-research/SKILL.md +0 -307
- package/skills/roll-research/references/schema.json +0 -162
- package/skills/roll-research/scripts/md_to_pdf.py +0 -289
- package/tools/roll-fetch/SKILL.md +0 -182
- package/tools/roll-fetch/package.json +0 -15
- package/tools/roll-fetch/smart-web-fetch.js +0 -558
- package/tools/roll-probe/SKILL.md +0 -84
- /package/template/{BACKLOG.md → .roll/backlog.md} +0 -0
|
@@ -0,0 +1,578 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: roll-loop
|
|
3
|
+
license: MIT
|
|
4
|
+
allowed-tools: "Read, Glob, Grep, Write, Edit, Bash(git:*), Bash(cat:*), Skill"
|
|
5
|
+
description: |
|
|
6
|
+
Autonomous BACKLOG executor. Runs on a schedule (hourly via cron or GitHub
|
|
7
|
+
Actions), scans .roll/backlog.md for 📋 Todo items, and routes each to the
|
|
8
|
+
appropriate skill: US-XXX → $roll-build, FIX-XXX → $roll-fix,
|
|
9
|
+
REFACTOR-XXX → $roll-build. Retries the primary agent up to 3 times on
|
|
10
|
+
transient failure; pauses with ALERT on persistent failure.
|
|
11
|
+
Never cuts a release autonomously — release is always a human decision.
|
|
12
|
+
Triggers roll-brief when a Feature completes.
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# Roll Loop (Autonomous BACKLOG Executor)
|
|
16
|
+
|
|
17
|
+
> Follows the Architecture Constraints, Development Discipline, and Engineering
|
|
18
|
+
> Common Sense defined in the project AGENTS.md.
|
|
19
|
+
|
|
20
|
+
Runs on a schedule. Picks up pending BACKLOG items and executes them without
|
|
21
|
+
human intervention. The human stays informed via `roll-brief` and retains
|
|
22
|
+
sole authority over releases.
|
|
23
|
+
|
|
24
|
+
## Execution Boundary
|
|
25
|
+
|
|
26
|
+
**What roll-loop executes autonomously:**
|
|
27
|
+
- US-XXX (User Stories) → `$roll-build`
|
|
28
|
+
- FIX-XXX (Bug fixes) → `$roll-fix`
|
|
29
|
+
- REFACTOR-XXX (Refactors) → `$roll-build`
|
|
30
|
+
|
|
31
|
+
**What roll-loop never executes:**
|
|
32
|
+
- Releases — production deployment is always a human decision (requires 2FA in real terminal)
|
|
33
|
+
- Any Story marked 🚫 Hold or flagged for human review
|
|
34
|
+
- Destructive operations outside normal skill scope
|
|
35
|
+
|
|
36
|
+
**Human bypass path** — roll-loop 是默认调度器,不垄断执行权。任何时刻人可直接
|
|
37
|
+
`$roll-build US-XXX` 或 `$roll-fix FIX-XXX` 绕过 loop 立即执行(紧急 bug、中断插入、
|
|
38
|
+
故事评审等场景)。loop 通过 LOCK 和 `🔨 In Progress` 状态识别并跳过人正在做的故事,
|
|
39
|
+
人机并行不会撞车(见 Concurrency Safety)。
|
|
40
|
+
|
|
41
|
+
## Environment Constraints (autonomous loop)
|
|
42
|
+
|
|
43
|
+
You are running inside an autonomous cycle. No human is watching this turn.
|
|
44
|
+
Adapt commands to the constraints below — otherwise you will burn turns on
|
|
45
|
+
denied operations and the cycle will idle-exit.
|
|
46
|
+
|
|
47
|
+
- **No `AskUserQuestion`**: no human can answer. If you genuinely cannot
|
|
48
|
+
proceed without a decision, write an entry to `${HOME}/.shared/roll/loop/ALERT-<slug>.md`
|
|
49
|
+
describing what's needed and exit cleanly.
|
|
50
|
+
- **Avoid compound bash**: each `Bash` call must run a single command.
|
|
51
|
+
No `cmd1 && cmd2`, no `cmd1 ; cmd2`, no pipes (`|`), no `$(...)` /
|
|
52
|
+
backtick subshells, no `bash -c '...'` with nested quoting. These are
|
|
53
|
+
rejected by static analysis before they run. Chain operations as
|
|
54
|
+
separate Bash calls and read intermediate output yourself.
|
|
55
|
+
- **Prefer Read/Edit over cat/sed**: use the `Read` tool for any file
|
|
56
|
+
lookup, `Edit` for modifications. They cross sandbox boundaries that
|
|
57
|
+
`cat` / `ls` / `sed` cannot.
|
|
58
|
+
- **CWD-relative paths first**: the cycle's CWD is the per-cycle worktree.
|
|
59
|
+
Files inside it (.roll/backlog.md, bin/roll, tests/, docs/) are always
|
|
60
|
+
accessible. Files at `~/.shared/roll/...` are reachable via the `Read`
|
|
61
|
+
tool but not via shell commands.
|
|
62
|
+
- **Quote every glob**: the `Bash` tool runs commands through the user's
|
|
63
|
+
login shell, which on macOS is typically `zsh`. zsh's default `nomatch`
|
|
64
|
+
aborts unquoted globs that find no match with `(eval):1: no matches
|
|
65
|
+
found: <pattern>` and exit 1, burning a turn on a meaningless error.
|
|
66
|
+
Quote literal globs (`ls 'tests/integration/helpers.*'`) or — better —
|
|
67
|
+
use the `Glob` tool, which is shell-agnostic and never aborts on empty
|
|
68
|
+
matches.
|
|
69
|
+
- **Skill invocation is the work**: route US/REFACTOR via `$roll-build`,
|
|
70
|
+
FIX via `$roll-fix`. Do not try to re-implement those flows inline.
|
|
71
|
+
|
|
72
|
+
## Configuration
|
|
73
|
+
|
|
74
|
+
```yaml
|
|
75
|
+
# ~/.roll/config.yaml
|
|
76
|
+
loop:
|
|
77
|
+
primary_agent: claude # claude | deepseek | kimi | pi | ...
|
|
78
|
+
max_items_per_run: 1 # one story per cycle — atomic delivery, predictable cycle time
|
|
79
|
+
brief_on_feature_complete: true
|
|
80
|
+
retry_backoff: [2, 4, 8, 16] # seconds, exponential
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Workflow
|
|
84
|
+
|
|
85
|
+
> **One story per cycle (强约束)**: 每个 cycle 只 pick 一个 Todo、跑完 Step 4
|
|
86
|
+
> 立刻退出,不再回 Step 2 找下一个。理由:
|
|
87
|
+
> - cycle 时间可预测(不会因贪心一连串 PR 撞 45 分钟 hard timeout)
|
|
88
|
+
> - PR / events / dashboard 每行一个故事,归因清晰
|
|
89
|
+
> - 一个故事一个 PR 一次 review,blast radius 最小
|
|
90
|
+
>
|
|
91
|
+
> 唯一例外是依赖修复(CI self-heal 等)已经内嵌在当前故事的 Step 4 里——
|
|
92
|
+
> 那部分不算"新挑故事"。
|
|
93
|
+
>
|
|
94
|
+
> 实现层:max_items_per_run 默认 1,executor skill 跑完 Step 5 必须 exit。
|
|
95
|
+
> 不要在同一个 cycle 内多次 emit `pick_todo` 事件。
|
|
96
|
+
|
|
97
|
+
### Step 1 — Orphan 🔨 Recovery
|
|
98
|
+
|
|
99
|
+
Process-level crash recovery (LOCK, heartbeat, retry budget) is handled by
|
|
100
|
+
the runner in `bin/roll:_write_loop_runner_script` — the per-project LOCK
|
|
101
|
+
guarantees only one cycle for this slug is alive when you start. So at
|
|
102
|
+
this point, any `🔨 In Progress` row in `.roll/backlog.md` belongs to a
|
|
103
|
+
previous cycle that crashed before flipping it back; reclaim it before
|
|
104
|
+
scanning.
|
|
105
|
+
|
|
106
|
+
**Important — skip `manual-only:*` rows.** A row tagged `manual-only:*`
|
|
107
|
+
means a human (or another non-loop process) has explicitly claimed it;
|
|
108
|
+
it is not loop's to reclaim. Reverting it would silently undo the
|
|
109
|
+
human's claim and cause confusing churn for `roll-brief` / dashboard
|
|
110
|
+
readers. The rule mirrors the gate in Step 2.
|
|
111
|
+
|
|
112
|
+
1. Scan .roll/backlog.md for all rows whose Status column contains `🔨 In Progress`.
|
|
113
|
+
2. For each candidate row, run the manual-only gate before touching it:
|
|
114
|
+
```bash
|
|
115
|
+
bash -c 'source "$(command -v roll)"; _loop_is_manual_only "<story-id>" .roll/backlog.md'
|
|
116
|
+
# exit 0 → row has `manual-only:*` → SKIP (human-claimed; not orphan)
|
|
117
|
+
# exit 1 → reclaimable orphan; continue to step 3
|
|
118
|
+
```
|
|
119
|
+
3. For each row that passes the gate: revert the status back to
|
|
120
|
+
`📋 Todo`, commit `chore: revert orphan 🔨 US-XXX to 📋`, and append
|
|
121
|
+
a line to `~/.shared/roll/loop/ALERT-<slug>.md` recording the orphan
|
|
122
|
+
id and time so the next brief surfaces it.
|
|
123
|
+
4. After orphan sweep, proceed to Step 1.5 (Pre-run CI health check) before scanning.
|
|
124
|
+
|
|
125
|
+
### Step 1.5 — Pre-run CI Health Check
|
|
126
|
+
|
|
127
|
+
Call `roll loop precheck-ci` before scanning BACKLOG. This is a **defensive gate**
|
|
128
|
+
against building on a broken base. Check the **exit code** and route accordingly:
|
|
129
|
+
|
|
130
|
+
| Exit code | Meaning | Action |
|
|
131
|
+
|-----------|---------|--------|
|
|
132
|
+
| `0` | CI green / pending / unknown | Proceed to Step 1.6 (PR Inbox) and Step 2 (BACKLOG scan) |
|
|
133
|
+
| `1` | CI red AND heal exhausted or `ROLL_LOOP_NO_HEAL=1` | ALERT already written; exit cleanly this cycle |
|
|
134
|
+
| `2` | CI red AND heal attempt allowed (US-LOOP-046) | **Hot-fix path** — skip BACKLOG, fix CI instead (see below) |
|
|
135
|
+
|
|
136
|
+
`gh` missing or repo unparseable → `precheck-ci` returns `0`; graceful skip.
|
|
137
|
+
|
|
138
|
+
**Hot-fix path (exit code 2) — US-LOOP-046:**
|
|
139
|
+
|
|
140
|
+
Do NOT pick any BACKLOG stories this cycle. Instead:
|
|
141
|
+
|
|
142
|
+
1. Capture context: `roll loop hotfix-head-context` → prints path to context log
|
|
143
|
+
2. Invoke `Skill("roll-fix")` with brief:
|
|
144
|
+
`"CI red on HEAD. Failing run logs at <context-log-path>. Diagnose root cause, fix via TCR, commit, push. Do NOT change BACKLOG status."`
|
|
145
|
+
3. After `roll-fix` completes, re-run `roll ci --wait` to verify the fix
|
|
146
|
+
4. If CI is still red: run `roll loop precheck-ci` again; if it returns `1` (heal exhausted),
|
|
147
|
+
exit cleanly — ALERT was already written by the precheck
|
|
148
|
+
|
|
149
|
+
### Step 1.6 — PR Inbox (US-AUTO-034)
|
|
150
|
+
|
|
151
|
+
Before scanning BACKLOG, process open PRs first. PRs are also units of work:
|
|
152
|
+
external contributors and human teammates expect their PRs to be reviewed and
|
|
153
|
+
moved forward, not starved while loop opens new fronts.
|
|
154
|
+
|
|
155
|
+
Call `_loop_pr_inbox` after the pre-run CI check passes. It walks
|
|
156
|
+
`gh pr list --state open` and routes each PR by classification:
|
|
157
|
+
|
|
158
|
+
| Classification | Action |
|
|
159
|
+
|---|---|
|
|
160
|
+
| `loop_self` (head ref starts with `loop/`) | Skip — let GitHub auto-merge handle it; never AI-review your own commit |
|
|
161
|
+
| `blocked_human_request_changes` | Skip — last human review requested changes; wait for the author to push fixes |
|
|
162
|
+
| `blocked_human_approved` | Skip — let GitHub auto-merge after CI is green |
|
|
163
|
+
| `stale` (CI failed or branch behind/conflicting) | Try `_loop_pr_rebase_stale` after the circuit breaker allows it |
|
|
164
|
+
| `eligible` (clean external PR, no blocking review) | Invoke `_loop_pr_review_external` — the actual decision is provided by US-AUTO-035's GitHub Action |
|
|
165
|
+
|
|
166
|
+
**Rebase circuit breaker** — `_loop_pr_rebase_circuit <pr>` records each rebase
|
|
167
|
+
attempt under `pr_state.<PR>.attempts_at` in the per-slug state file
|
|
168
|
+
(`~/.shared/roll/loop/state-<slug>.yaml`, FIX-052), pruning entries older
|
|
169
|
+
than 24 h. Once ≥3 attempts land within 24 h, further rebases are blocked and an
|
|
170
|
+
ALERT is written (typical cause: a broken workflow file makes CI never run,
|
|
171
|
+
which would otherwise drive infinite rebase loops).
|
|
172
|
+
|
|
173
|
+
**Lenient on infrastructure** — `gh` missing, repo unparseable, or any
|
|
174
|
+
`gh` API failure → `_loop_pr_inbox` returns 0 and the loop falls through to
|
|
175
|
+
Step 2 (BACKLOG scan). Same posture as the pre-run CI check.
|
|
176
|
+
|
|
177
|
+
### Step 2 — Scan BACKLOG
|
|
178
|
+
|
|
179
|
+
Read `.roll/backlog.md`. Collect all rows where Status = `📋 Todo`, in order:
|
|
180
|
+
|
|
181
|
+
Priority: FIX-XXX first (bugs block progress), then US-XXX, then REFACTOR-XXX.
|
|
182
|
+
|
|
183
|
+
**Skip rows with Status = `🔨 In Progress`**. These are currently being executed by:
|
|
184
|
+
- Another concurrent executor (human via `$roll-build`, peer agent)
|
|
185
|
+
- An earlier loop iteration that hasn't finished yet (rare; should be guarded by LOCK)
|
|
186
|
+
- A previous interrupted run (the resume logic in Step 1 will pick these up)
|
|
187
|
+
|
|
188
|
+
**In-flight PR gate** (FIX-048). Before picking, also exclude stories already
|
|
189
|
+
claimed by an **open `loop/*` PR**. Each cycle's worktree is branched from
|
|
190
|
+
`origin/main`, so a story another cycle has marked 🔨 In Progress is invisible
|
|
191
|
+
locally until that cycle's PR merges. Without this gate, two cycles started
|
|
192
|
+
back-to-back will both pick the same Todo row and produce duplicate PRs.
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
bash -c 'source "$(command -v roll)"; _loop_pr_claimed_stories'
|
|
196
|
+
# stdout: one story ID per line (deduped) — these are claimed by open
|
|
197
|
+
# loop/* PRs on the remote. SKIP any candidate whose ID appears.
|
|
198
|
+
# exit 0 always (lenient: gh missing / API error → empty output).
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
**Dependency gate** (FIX-032). For each `📋 Todo` candidate, before picking:
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
# Source bin/roll once per cycle, then call the helpers per candidate.
|
|
205
|
+
source "$(command -v roll)"
|
|
206
|
+
|
|
207
|
+
bash -c 'source "$(command -v roll)"; _loop_is_manual_only "<story-id>" .roll/backlog.md'
|
|
208
|
+
# exit 0 → row has `manual-only:true` → SKIP this story, log to runs.jsonl
|
|
209
|
+
# `skipped`, append INFO line ("manual-only — requires $roll-build")
|
|
210
|
+
|
|
211
|
+
bash -c 'source "$(command -v roll)"; _loop_check_depends_on "<story-id>" .roll/backlog.md'
|
|
212
|
+
# exit 0 → all `depends-on:US-X,US-Y` are ✅ Done → eligible
|
|
213
|
+
# exit 1 → stdout lists unsatisfied dep IDs; SKIP this story, log to
|
|
214
|
+
# runs.jsonl `skipped` with reason "depends-on: <unsatisfied>"
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
Move to the next candidate when skipping. The two gates are pure functions
|
|
218
|
+
over .roll/backlog.md text — no side effects, no LOCK interaction.
|
|
219
|
+
|
|
220
|
+
Cap at `max_items_per_run` to limit blast radius per cycle.
|
|
221
|
+
|
|
222
|
+
### Concurrency Safety
|
|
223
|
+
|
|
224
|
+
Loop has two layers of concurrency protection:
|
|
225
|
+
|
|
226
|
+
1. **Per-project LOCK** (enforced by runner script, see `bin/roll:_write_loop_runner_script`):
|
|
227
|
+
- LOCK file path: `~/.shared/roll/loop/.LOCK-<project-slug>`
|
|
228
|
+
- On launch: if LOCK exists and the PID inside is alive → exit 0 (previous loop still running)
|
|
229
|
+
- On launch: if LOCK exists but PID is dead → clean up stale LOCK and continue
|
|
230
|
+
- On exit (normal or via trap): LOCK is removed
|
|
231
|
+
- One LOCK per project — different projects' loops run independently
|
|
232
|
+
|
|
233
|
+
2. **🔨 In Progress story status** (enforced here):
|
|
234
|
+
- Before picking a story, check its status is `📋 Todo`
|
|
235
|
+
- Skip any `🔨 In Progress` row (someone else is on it)
|
|
236
|
+
- Mark each story `🔨 In Progress` BEFORE invoking the executor skill (see Step 3)
|
|
237
|
+
- On completion: update to `✅ Done`; on TCR failure: revert to `📋 Todo`
|
|
238
|
+
|
|
239
|
+
Together these mean: only one loop runs at a time per project (LOCK), and within a loop, stories already claimed by humans or peer agents are skipped (status check).
|
|
240
|
+
|
|
241
|
+
### Step 3 — Route and Execute
|
|
242
|
+
|
|
243
|
+
> **US-AGENT-006 — Per-story agent routing (pre-cycle)**
|
|
244
|
+
>
|
|
245
|
+
> Before this skill even starts, the runner inner script has already:
|
|
246
|
+
> 1. Picked the next eligible Todo via `_loop_pick_next_story` (priority FIX > US > REFACTOR, manual-only / depends-on gates respected)
|
|
247
|
+
> 2. Read its Agent profile (est_min / risk_zone) and routed an agent via `_loop_pick_agent_for_story` (hard rules from `.roll/agent-routes.yaml` + soft preference from `runs.jsonl`)
|
|
248
|
+
> 3. Exported `ROLL_LOOP_ROUTED_STORY` / `ROLL_LOOP_ROUTED_AGENT` / `ROLL_LOOP_ROUTED_RULE` and printed `[loop] story <id> routed to <agent> via <rule_kind>` to cron.log
|
|
249
|
+
>
|
|
250
|
+
> When `ROLL_LOOP_ROUTED_STORY` is set, prefer it as `US_ID` for this cycle. The story has already been chosen by hard+soft routing rules — and, per FIX-146, the runner re-validates it against the authoritative backlog right before handing it to you (re-picking the next eligible Todo if it went ✅ Done / In Progress / ineligible between pick and handoff, emitting a `story_stale` event). So treat `ROLL_LOOP_ROUTED_STORY` as already-eligible and just work it. Only if you still find at cycle start that it is no longer 📋 Todo in BACKLOG (a residual concurrent flip), re-pick the next eligible Todo via `_loop_pick_next_story` rather than idling the whole cycle.
|
|
251
|
+
>
|
|
252
|
+
> Old single-agent fallback (`primary_agent` from `~/.roll/config.yaml`) still applies when:
|
|
253
|
+
> - no story is pickable (empty Todo / all manual-only)
|
|
254
|
+
> - the matching agent-routes.yaml has no agent that fits the story profile (then `cold_start_default` is used)
|
|
255
|
+
|
|
256
|
+
For each item, **before invoking the executor skill**, mark the story 🔨 In Progress in the **main repo's** .roll/backlog.md so brief and peer agents can see it being worked on. The cycle worktree is gitignored at .roll/, so editing the worktree's own copy + committing carries no change back to main — write directly via the helper instead:
|
|
257
|
+
|
|
258
|
+
```bash
|
|
259
|
+
bash -c 'source "$(command -v roll)"; _loop_mark_in_progress US-XXX'
|
|
260
|
+
# Updates ${ROLL_MAIN_PROJECT}/.roll/backlog.md in place: flips the row
|
|
261
|
+
# containing US-XXX from "📋 Todo" to "🔨 In Progress". Idempotent.
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
If the executor fails (TCR aborts, CI red, etc.), revert the marker so the next cycle can re-pick the story:
|
|
265
|
+
|
|
266
|
+
```bash
|
|
267
|
+
bash -c 'source "$(command -v roll)"; _loop_mark_todo US-XXX'
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
Status flips happen in main directly — no per-cycle commit needed. `roll-brief` reads main's backlog, so the 🔨 marker is visible the moment the helper returns.
|
|
271
|
+
|
|
272
|
+
选定故事后,调用 `_loop_event` 发出 pick_todo 事件,让 dashboard / monitor / attach 都能把"这个 cycle 选了哪个 story"正确归类:
|
|
273
|
+
|
|
274
|
+
```bash
|
|
275
|
+
# 选定故事后立即 emit(在调用 executor skill 之前)
|
|
276
|
+
# label 必须是 cycle_id(来自 bin/roll 注入的 LOOP_CYCLE_ID 环境变量),
|
|
277
|
+
# 不是 US_ID — dashboard 按 label 聚类,US_ID 当 label 会让事件分到错的桶
|
|
278
|
+
# 里,cycle 看起来"有 token 没 ID"。
|
|
279
|
+
_loop_event pick_todo "$LOOP_CYCLE_ID" "$US_ID" ""
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
Then invoke the executor:
|
|
283
|
+
|
|
284
|
+
```
|
|
285
|
+
Item type → Skill invoked
|
|
286
|
+
─────────────────────────────────
|
|
287
|
+
US-XXX → Skill("roll-build", "US-XXX")
|
|
288
|
+
FIX-XXX → Skill("roll-fix", "FIX-XXX")
|
|
289
|
+
REFACTOR-XXX → Skill("roll-build", "REFACTOR-XXX")
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
The executor will update the row to `✅ Done` on success (it transitions from `🔨 In Progress` → `✅ Done`, same Edit logic as from `📋 Todo`).
|
|
293
|
+
|
|
294
|
+
Before invoking, also write current item to the per-slug state file
|
|
295
|
+
(`~/.shared/roll/loop/state-<slug>.yaml`, FIX-052):
|
|
296
|
+
|
|
297
|
+
```yaml
|
|
298
|
+
status: running
|
|
299
|
+
current_item: US-AUTO-004
|
|
300
|
+
started_at: "2026-05-10T02:00:00+08:00"
|
|
301
|
+
agent: claude
|
|
302
|
+
run_id: loop-20260510-0200
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
### Step 4 — Post-Item Cleanup
|
|
306
|
+
|
|
307
|
+
After each item completes:
|
|
308
|
+
|
|
309
|
+
1. **TCR 硬校验** — call `roll loop enforce-tcr <story_id> <started_at>`:
|
|
310
|
+
- Count `tcr:` prefix commits since `started_at` via `git log --oneline --since=<started_at>`
|
|
311
|
+
- Count == 0 → revert story status in .roll/backlog.md from ✅ Done → 📋 Todo; write ALERT to `~/.shared/roll/loop/ALERT-<slug>.md` with story ID, time, reason "zero tcr: commits since story start", and suggested actions (`roll loop now` / `$roll-build <id>` / `roll loop reset`)
|
|
312
|
+
- Count > 0 → continue normally
|
|
313
|
+
2. **CI Gate** — **MUST** invoke `roll ci --wait` (the `_loop_enforce_ci`
|
|
314
|
+
wrapper). **Do NOT call `gh` directly** (no `gh run list`, no `gh run watch`,
|
|
315
|
+
no ad-hoc shell checks): `roll ci --wait` is the only sanctioned entry —
|
|
316
|
+
it derives `owner/repo` from the git remote and uses `gh -R <slug>`, which
|
|
317
|
+
is required to work through `~/.ssh/config` host rewrites that break gh's
|
|
318
|
+
auto-detection.
|
|
319
|
+
- CI passes → clear any `heal_count:` entry in `~/.shared/roll/loop/state-<slug>.yaml` (idempotent — drop the line if present, no-op otherwise) and continue normally
|
|
320
|
+
- CI fails / times out / `gh` call fails → enter **CI self-heal** (US-AUTO-041)
|
|
321
|
+
- `gh` binary not installed (`command -v gh` fails) → skip gracefully
|
|
322
|
+
(return 0). Any other `gh` error is **not** "gh unavailable" — it is a
|
|
323
|
+
hard failure and must block the gate.
|
|
324
|
+
|
|
325
|
+
**CI self-heal (US-AUTO-041)** — bounded auto-fix before ALERT.
|
|
326
|
+
|
|
327
|
+
Read `heal_count:` from `~/.shared/roll/loop/state-<slug>.yaml`; treat a missing line as `0`. If the count is below `ROLL_LOOP_HEAL_MAX` (default 2) and `ROLL_LOOP_NO_HEAL` is not set, increment it and take Path A. Otherwise take Path B.
|
|
328
|
+
|
|
329
|
+
**Path A — attempt allowed (counter incremented in `state-<slug>.yaml`):**
|
|
330
|
+
|
|
331
|
+
1. Capture failure summary:
|
|
332
|
+
```
|
|
333
|
+
gh run view --log-failed --repo <slug> \
|
|
334
|
+
$(gh run list --commit HEAD --json databaseId,conclusion -L 5 \
|
|
335
|
+
| jq -r '.[] | select(.conclusion=="failure") | .databaseId' | head -1) \
|
|
336
|
+
2>/dev/null | head -200 > /tmp/roll-heal-<story_id>.log
|
|
337
|
+
```
|
|
338
|
+
2. Invoke `Skill("roll-fix")` with brief:
|
|
339
|
+
`"CI red after <story_id>. Failing run logs at /tmp/roll-heal-<story_id>.log.
|
|
340
|
+
Diagnose root cause, fix via TCR, commit, push. Do NOT change <story_id>'s
|
|
341
|
+
BACKLOG status — it stays ✅ Done. The fix is a follow-up."`
|
|
342
|
+
3. After `roll-fix` completes, return to step 2 (CI Gate) — re-run `roll ci --wait`.
|
|
343
|
+
The counter in `state-<slug>.yaml` prevents infinite loops.
|
|
344
|
+
|
|
345
|
+
**Path B — heal exhausted (≥`ROLL_LOOP_HEAL_MAX`, default 2) or disabled (`ROLL_LOOP_NO_HEAL=1`) (exit 1):**
|
|
346
|
+
|
|
347
|
+
1. Do NOT force ✅ Done here. CI red means the PR will not merge. Under
|
|
348
|
+
**US-AUTO-044** the main loop no longer waits for merge — it publishes the
|
|
349
|
+
PR and exits; the dedicated PR Loop (`com.roll.pr.<slug>`, every 5 min)
|
|
350
|
+
merges / rebases / closes it asynchronously. There is no false-Done risk:
|
|
351
|
+
with worktree isolation the ✅ Done lives only in the unmerged PR, never on
|
|
352
|
+
the loop's main checkout, and the story is not re-picked meanwhile via the
|
|
353
|
+
open-PR eligibility gate (`_loop_story_is_eligible`, FIX-146). The story's
|
|
354
|
+
✅ Done lands on main only when the PR Loop actually merges the PR.
|
|
355
|
+
2. Write ALERT to `~/.shared/roll/loop/ALERT-<slug>.md` with:
|
|
356
|
+
- story ID, time, commit SHA
|
|
357
|
+
- heal attempts made (read `heal_count:` from `state-<slug>.yaml`)
|
|
358
|
+
- last failure summary (head of `/tmp/roll-heal-<story_id>.log`)
|
|
359
|
+
- suggested actions: `$roll-fix` manually / inspect CI / `roll loop reset`
|
|
360
|
+
3. Skip to next story.
|
|
361
|
+
|
|
362
|
+
**Bypass for debugging / cost control:** set `ROLL_LOOP_NO_HEAL=1` to restore
|
|
363
|
+
pre-US-AUTO-041 fail-fast behaviour.
|
|
364
|
+
3. Update state file: `status: idle`
|
|
365
|
+
4. Check if a Feature is now fully complete (all its Stories ✅)
|
|
366
|
+
5. If yes and `brief_on_feature_complete: true` → invoke `Skill("roll-brief")`
|
|
367
|
+
6. **EXIT the cycle.** 不要回 Step 2 找下一个故事,不要再 emit `pick_todo`。
|
|
368
|
+
一个 cycle 只交付一个故事;剩下的 Todo 等下一个 launchd tick 起新 cycle 处理。
|
|
369
|
+
|
|
370
|
+
### Step 5 — Write Run Summary
|
|
371
|
+
|
|
372
|
+
> **FIX-044**: The inner runner script (`_write_loop_runner_script` in `bin/roll`)
|
|
373
|
+
> now appends this record deterministically at cycle end. The shell write is the
|
|
374
|
+
> authoritative record; the agent should still emit a run summary in the cycle's
|
|
375
|
+
> final report for `cron.log` visibility.
|
|
376
|
+
|
|
377
|
+
After all items in this cycle:
|
|
378
|
+
|
|
379
|
+
```yaml
|
|
380
|
+
# ~/.shared/roll/loop/state-<slug>.yaml (FIX-052)
|
|
381
|
+
status: idle
|
|
382
|
+
last_run: "2026-05-10T02:15:00+08:00"
|
|
383
|
+
last_run_items: [US-AUTH-003, FIX-007]
|
|
384
|
+
last_run_outcome: success
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
Then append a JSONL record to `~/.shared/roll/loop/runs.jsonl` for per-iteration
|
|
388
|
+
visibility (one line per cycle, append-only — never delete or rewrite earlier lines).
|
|
389
|
+
|
|
390
|
+
**⚠️ Strict schema contract — do NOT deviate.** Every field has exactly one
|
|
391
|
+
canonical form. Synonyms like `"success"`, `"noop"`, `"completed"` are forbidden
|
|
392
|
+
for `status`. Numbers and arrays cannot be interchanged. UTC `Z` suffix only,
|
|
393
|
+
no timezone offsets. **No extra fields** — emit only the keys listed below (plus
|
|
394
|
+
optional `reason` when `status="failed"`); do not add `note`, `comment`,
|
|
395
|
+
`details`, `info`, etc. If you feel the urge to annotate, put it in the cycle's
|
|
396
|
+
final report in `cron.log` instead.
|
|
397
|
+
|
|
398
|
+
**Canonical record (copy this exact shape, fill in real values):**
|
|
399
|
+
|
|
400
|
+
```json
|
|
401
|
+
{"ts":"2026-05-11T11:46:43Z","project":"roll-d9dfa0","run_id":"loop-20260511-1911","status":"built","built":["US-AUTO-024","US-AUTO-025"],"skipped":[],"alerts":[],"tcr_count":5,"duration_sec":2080}
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
**Field contract — types are enforced**:
|
|
405
|
+
|
|
406
|
+
| Field | Type | Format / Enum |
|
|
407
|
+
|---|---|---|
|
|
408
|
+
| `ts` | string | ISO 8601 **UTC** with `Z` suffix. Get via `date -u +%Y-%m-%dT%H:%M:%SZ`. Never use `+08:00` or other offsets. |
|
|
409
|
+
| `project` | string | Project **slug** only (e.g. `roll-d9dfa0`), NOT the absolute path and NOT plain `basename`. Compute via: `p=$(pwd -P); base=$(basename "$p" | tr -cs '[:alnum:]' '-' | sed 's/-*$//'); hash=$(printf '%s' "$p" | md5 | cut -c1-6 2>/dev/null || printf '%s' "$p" | md5sum | cut -c1-6); echo "${base}-${hash}"` |
|
|
410
|
+
| `run_id` | string | Matches `state-<slug>.yaml` `run_id` exactly. Format: `loop-YYYYMMDD-HHMM`. |
|
|
411
|
+
| `status` | enum | Exactly one of: `built` (≥1 story shipped), `idle` (no Todo items found), `failed` (paused/error). **No synonyms.** |
|
|
412
|
+
| `built` | array<string> | Story ids completed this cycle. `[]` when none. **Always array, never null/number.** |
|
|
413
|
+
| `skipped` | array<string> | Story ids skipped because they were `🔨 In Progress`. `[]` when none. **Always array.** |
|
|
414
|
+
| `alerts` | array<string> | Newly raised ALERT identifiers/tags this cycle. `[]` when none. **Always array, never number.** |
|
|
415
|
+
| `tcr_count` | integer | Total `tcr:` prefix commits made this cycle. `0` when none. |
|
|
416
|
+
| `duration_sec` | integer | Seconds from cycle start to completion. Integer only, no decimals. |
|
|
417
|
+
|
|
418
|
+
Optional field, only when `status == "failed"`:
|
|
419
|
+
- `reason` (string): short human-readable explanation.
|
|
420
|
+
|
|
421
|
+
**Write recipe:**
|
|
422
|
+
|
|
423
|
+
```bash
|
|
424
|
+
ts=$(date -u +%Y-%m-%dT%H:%M:%SZ)
|
|
425
|
+
# Compute project slug — inlined equivalent of bin/roll's _project_slug
|
|
426
|
+
# (Claude sessions can't call roll's internal functions, so we inline).
|
|
427
|
+
# Must produce identical output to _project_slug to match `roll loop runs` filter.
|
|
428
|
+
_p=$(pwd -P)
|
|
429
|
+
_base=$(basename "$_p" | tr -cs '[:alnum:]' '-' | sed 's/-*$//')
|
|
430
|
+
_hash=$(printf '%s' "$_p" | md5 | cut -c1-6 2>/dev/null || printf '%s' "$_p" | md5sum | cut -c1-6)
|
|
431
|
+
project="${_base}-${_hash}" # e.g. roll-d9dfa0 — must match roll loop runs filter
|
|
432
|
+
# duration_sec = cycle_end_epoch - cycle_start_epoch (track at Step 1)
|
|
433
|
+
# tcr_count = git log --oneline --since="<cycle_start>" | grep -c '^[a-f0-9]* tcr:'
|
|
434
|
+
|
|
435
|
+
jq -nc \
|
|
436
|
+
--arg ts "$ts" \
|
|
437
|
+
--arg project "$project" \
|
|
438
|
+
--arg run_id "$run_id" \
|
|
439
|
+
--arg status "built" \
|
|
440
|
+
--argjson built '["US-AUTO-024"]' \
|
|
441
|
+
--argjson skipped '[]' \
|
|
442
|
+
--argjson alerts '[]' \
|
|
443
|
+
--argjson tcr_count 14 \
|
|
444
|
+
--argjson duration_sec 1680 \
|
|
445
|
+
'{ts:$ts, project:$project, run_id:$run_id, status:$status,
|
|
446
|
+
built:$built, skipped:$skipped, alerts:$alerts,
|
|
447
|
+
tcr_count:$tcr_count, duration_sec:$duration_sec}' \
|
|
448
|
+
>> ~/.shared/roll/loop/runs.jsonl
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
The companion read-side is `roll loop runs [N] [--all]` — shows the most recent
|
|
452
|
+
N records (default 10) for the current project, or across all projects with `--all`.
|
|
453
|
+
|
|
454
|
+
## Failure Handling
|
|
455
|
+
|
|
456
|
+
### Network Error (transient)
|
|
457
|
+
|
|
458
|
+
```
|
|
459
|
+
Attempt 1 fails
|
|
460
|
+
→ wait 2s → Attempt 2
|
|
461
|
+
→ wait 4s → Attempt 3
|
|
462
|
+
→ wait 8s → Attempt 4
|
|
463
|
+
→ wait 16s → Attempt 5
|
|
464
|
+
→ still failing → escalate to token/agent failure path
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
### Token Exhausted / Agent Unavailable
|
|
468
|
+
|
|
469
|
+
```
|
|
470
|
+
Primary agent fails (non-network error)
|
|
471
|
+
→ 3 attempts at the agent_invoke phase (with 30s back-off between)
|
|
472
|
+
→ still failing → PAUSE
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
### Pause + Alert
|
|
476
|
+
|
|
477
|
+
When the primary agent exhausts its retry budget:
|
|
478
|
+
|
|
479
|
+
1. Write state:
|
|
480
|
+
```yaml
|
|
481
|
+
status: paused
|
|
482
|
+
paused_at: "2026-05-10T02:07:00+08:00"
|
|
483
|
+
paused_on: US-AUTH-003
|
|
484
|
+
reason: "primary agent (claude) unavailable after 3 attempts"
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
2. Write alert:
|
|
488
|
+
```markdown
|
|
489
|
+
# ALERT — roll-loop paused
|
|
490
|
+
|
|
491
|
+
**Time**: 2026-05-10 02:07
|
|
492
|
+
**Paused on**: US-AUTH-003
|
|
493
|
+
**Reason**: claude exited non-zero on 3 consecutive attempts
|
|
494
|
+
|
|
495
|
+
**Action required** (choose one):
|
|
496
|
+
- Top up credits and run: `roll loop resume`
|
|
497
|
+
- Switch agent: edit `~/.roll/config.yaml` → `primary_agent`
|
|
498
|
+
- Take over manually: `$roll-build US-AUTH-003`
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
3. Write alert file to `~/.shared/roll/loop/ALERT-<slug>.md`
|
|
502
|
+
|
|
503
|
+
## Resuming After Pause
|
|
504
|
+
|
|
505
|
+
```bash
|
|
506
|
+
roll loop resume # picks up from state-<slug>.yaml current_item
|
|
507
|
+
roll loop status # show current state without running
|
|
508
|
+
roll loop reset # clear state and start fresh next scheduled run
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
## Scheduler Configuration
|
|
512
|
+
|
|
513
|
+
roll-loop runs **locally** — it needs access to the local codebase, local
|
|
514
|
+
test runner, and local agent CLI. GitHub Actions runs on remote servers and
|
|
515
|
+
cannot fulfill these requirements.
|
|
516
|
+
|
|
517
|
+
### Local cron (default)
|
|
518
|
+
|
|
519
|
+
Install once with `roll loop on` — it reads the configured agent from
|
|
520
|
+
`.roll.yaml` or `~/.roll/config.yaml` and writes the correct cron entry
|
|
521
|
+
automatically. No agent-specific command needed.
|
|
522
|
+
|
|
523
|
+
```bash
|
|
524
|
+
roll loop on # install cron for loop + dream + brief
|
|
525
|
+
roll loop off # remove cron entries
|
|
526
|
+
roll loop status # show current state
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
### Manual run (for testing)
|
|
530
|
+
|
|
531
|
+
```bash
|
|
532
|
+
roll loop now # execute one cycle immediately
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
### Live attach (transparency)
|
|
536
|
+
|
|
537
|
+
Each loop iteration runs inside a detached tmux session named
|
|
538
|
+
`roll-loop-<slug>` (tmux is a required dependency — `roll setup` auto-installs
|
|
539
|
+
it via Homebrew on macOS, or prints the install command elsewhere).
|
|
540
|
+
|
|
541
|
+
**Default — auto-attach popup**: when the loop fires, a background Terminal
|
|
542
|
+
window pops up running `tmux attach -t roll-loop-<slug>`. You can watch the
|
|
543
|
+
agent work in real time without typing anything. The popup is best-effort
|
|
544
|
+
focus-retaining (it captures the previously-active app and restores focus
|
|
545
|
+
after the window appears) and the tmux session keeps running even if you
|
|
546
|
+
close the window.
|
|
547
|
+
|
|
548
|
+
**Manual attach** (any time):
|
|
549
|
+
|
|
550
|
+
```bash
|
|
551
|
+
roll loop attach # exec tmux attach -t roll-loop-<slug>
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
Press `Ctrl-B D` to detach — the loop continues running uninterrupted.
|
|
555
|
+
|
|
556
|
+
**Mute / unmute the popup**:
|
|
557
|
+
|
|
558
|
+
```bash
|
|
559
|
+
roll loop mute # 🔇 — suppress auto-attach popup (loop still runs in tmux)
|
|
560
|
+
roll loop unmute # 🔔 — re-enable the popup
|
|
561
|
+
```
|
|
562
|
+
|
|
563
|
+
Mute state is a single marker file at `~/.shared/roll/mute` and is shared
|
|
564
|
+
across all projects on this machine. Check the current state with
|
|
565
|
+
`roll loop status` — it shows an `Auto-attach: live | muted` line.
|
|
566
|
+
|
|
567
|
+
## Integration Map
|
|
568
|
+
|
|
569
|
+
```
|
|
570
|
+
roll-loop
|
|
571
|
+
├── reads .roll/backlog.md
|
|
572
|
+
├── invokes $roll-build (US-XXX, REFACTOR-XXX)
|
|
573
|
+
├── invokes $roll-fix (FIX-XXX)
|
|
574
|
+
├── invokes $roll-brief (on Feature completion)
|
|
575
|
+
├── reads ~/.roll/config.yaml (agent routing)
|
|
576
|
+
├── writes ~/.shared/roll/loop/state-<slug>.yaml
|
|
577
|
+
└── writes ~/.shared/roll/loop/ALERT-<slug>.md (on failure)
|
|
578
|
+
```
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: roll-notes
|
|
3
|
+
license: MIT
|
|
4
|
+
allowed-tools: "Read, Edit, Write, Bash(date:*)"
|
|
5
|
+
description: "Project diary skill. Records development moments — successes, failures, discoveries — appended chronologically to a daily notes file."
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# roll-notes
|
|
9
|
+
|
|
10
|
+
> 项目日记技能。记录开发过程中的成功、失败、感受和任何值得记住的事情。
|
|
11
|
+
> Append-only, timeline-driven, no fixed format.
|
|
12
|
+
|
|
13
|
+
## Trigger
|
|
14
|
+
|
|
15
|
+
- User says "记一下"、"写进日记"、"append 到日记"
|
|
16
|
+
- User asks to record a feeling, finding, or moment
|
|
17
|
+
- A project milestone is reached and worth capturing
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
$roll-notes 终于搞定了那个 WebSocket 断线重连的 bug
|
|
21
|
+
$roll-notes 今天的 code review 给了很好的反馈
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## When Not to Use
|
|
25
|
+
|
|
26
|
+
- Capturing a bug or feature idea into BACKLOG (use `$roll-idea`)
|
|
27
|
+
- Generating the user-facing changelog (use `$roll-.changelog`)
|
|
28
|
+
- Writing design documents or AC (use `$roll-design`)
|
|
29
|
+
|
|
30
|
+
## Behavior
|
|
31
|
+
|
|
32
|
+
1. **Determine file path**: `.roll/notes/YYYY-MM-DD.md` relative to project root (parallel to `.roll/dream/` and `.roll/briefs/` — notes is project metadata, not source)
|
|
33
|
+
2. **Get current time**: Use `Asia/Shanghai` timezone (`TZ=Asia/Shanghai date`)
|
|
34
|
+
3. **Read existing entries for style**: Before writing, read the last 2–3 entries
|
|
35
|
+
in the same file. Analyze their style: heading format, voice/tone,
|
|
36
|
+
paragraph structure, emoji usage, code block conventions, sign-off pattern.
|
|
37
|
+
New entries must continue this style — do not invent new structural patterns.
|
|
38
|
+
4. **Append**: Add new entry at end of file — never overwrite existing content
|
|
39
|
+
5. **Create if missing**: If file doesn't exist, create with a `# YYYY-MM-DD — <one-line summary>` header
|
|
40
|
+
6. **Free format**: Paragraph, list, code block — whatever fits the moment
|
|
41
|
+
|
|
42
|
+
## File format
|
|
43
|
+
|
|
44
|
+
```markdown
|
|
45
|
+
# YYYY-MM-DD — 一句话概括今天
|
|
46
|
+
|
|
47
|
+
> 时间:北京时间 HH:MM
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## HH:MM — 事件标题
|
|
52
|
+
|
|
53
|
+
发生了什么、怎么解决的、什么感受。
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## HH:MM — 另一个事件
|
|
58
|
+
|
|
59
|
+
...
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## 写作风格(强制)
|
|
63
|
+
|
|
64
|
+
**禁止干巴巴的 checklist。禁止只有结论没有过程。必须有人的声音。**
|
|
65
|
+
|
|
66
|
+
### 叙事驱动
|
|
67
|
+
以时间线和事件推进为主线,像讲故事一样记录。不是 `补了功能,pytest 绿`,而是 `"电池剩余电量在哪里看?"我一愣,US-PLAT-003 的 AC 明明写了...`
|
|
68
|
+
|
|
69
|
+
### 技术细节嵌入叙事
|
|
70
|
+
代码、数据、命令行是故事的一部分,自然插入,不是附录。
|
|
71
|
+
|
|
72
|
+
### 对话与互动
|
|
73
|
+
记录用户的反馈、提问、情绪。日记是对话记录,不是独白。
|
|
74
|
+
|
|
75
|
+
### 诚实记录错误
|
|
76
|
+
失败、困惑、教训比成功更值得记录。
|
|
77
|
+
|
|
78
|
+
### 标题有画面感
|
|
79
|
+
不是功能清单。如 `每秒 20 行的噪音`、`22 个文件挤在一个抽屉里`。
|
|
80
|
+
|
|
81
|
+
### 结尾有收束
|
|
82
|
+
最后一段回到人的状态。如 `"值了"。"睡了"。push。`
|
|
83
|
+
|
|
84
|
+
## Rules
|
|
85
|
+
|
|
86
|
+
- **Style continuity**: Match the EXACT style, tone, and formatting of previous
|
|
87
|
+
entries in the same file. Do not invent new structural patterns or section
|
|
88
|
+
headers. If the file already has entries, analyze them first and confirm your
|
|
89
|
+
understanding of their style before writing.
|
|
90
|
+
- **No planning**: Never write "明日待办" or "下一步"
|
|
91
|
+
- **No summaries**: Never write "今日收获" or "经验教训"
|
|
92
|
+
- **Pure record**: What happened, as-is — honest, immediate, rough is fine
|
|
93
|
+
- **Append freely**: Multiple entries per day is normal
|
|
94
|
+
|
|
95
|
+
## File location
|
|
96
|
+
|
|
97
|
+
```
|
|
98
|
+
.roll/
|
|
99
|
+
└── notes/
|
|
100
|
+
└── YYYY-MM-DD.md
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
注:notes 是项目元数据(与 `.roll/dream/` / `.roll/briefs/` 同级),不入 git;由 dream/brief 等下游 skill 跨日聚合。
|