@seanyao/roll 2026.521.1 → 2026.521.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 +9 -0
- package/bin/roll +13 -6
- package/lib/__pycache__/model_prices.cpython-314.pyc +0 -0
- package/lib/__pycache__/roll-loop-status.cpython-314.pyc +0 -0
- package/lib/__pycache__/roll_render.cpython-314.pyc +0 -0
- package/lib/roll-loop-status.py +1 -1
- package/package.json +1 -1
- package/skills/roll-loop/SKILL.md +34 -29
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v2026.521.2
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- **`roll setup` 不再在 macOS 后台活动列表里留下 ghost「bash」条目** — 改由 `roll init` 和 `roll loop on` 按需安装 `[loop]`
|
|
8
|
+
- **`roll setup` 现在能正确显示哪些步骤真的装了** — 之前装好的 skills 和 peer 状态目录会被误标成"跳过" `[loop]`
|
|
9
|
+
- **`roll loop status --days 7` 不再吞掉天数参数** — 之前永远只显示默认 3 天,dashboard 底部那条 "more" 提示也改成能直接复制跑通 `[loop]`
|
|
10
|
+
- **loop 每轮起手不再刷一堆"找不到文件"报错** — 修了内部脚本里的旧路径,并让 agent 在 zsh 下不再被未匹配的通配符卡住 `[loop]`
|
|
11
|
+
|
|
3
12
|
## v2026.521.1
|
|
4
13
|
|
|
5
14
|
### Added
|
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.521.
|
|
7
|
+
VERSION="2026.521.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"
|
|
@@ -577,6 +577,10 @@ _ensure_tmux() {
|
|
|
577
577
|
# a re-copy with identical content is recognised as a no-op even when the inner
|
|
578
578
|
# helper rewrites the file. Watch is a colon-separated list of directories;
|
|
579
579
|
# missing dirs are skipped silently.
|
|
580
|
+
# FIX-079: also track symlinks (`_link_skills` only creates symlinks) and
|
|
581
|
+
# directories (`_peer_ensure_state_dir` only creates dirs). Without these, a
|
|
582
|
+
# step that did real work but produced no regular file would falsely render
|
|
583
|
+
# as ↷ on a brand-new install.
|
|
580
584
|
_setup_snapshot() {
|
|
581
585
|
local watch="$1"
|
|
582
586
|
local -a dirs
|
|
@@ -586,6 +590,10 @@ _setup_snapshot() {
|
|
|
586
590
|
for d in "${dirs[@]}"; do
|
|
587
591
|
[[ -d "$d" ]] || continue
|
|
588
592
|
find "$d" -type f -print0 2>/dev/null | xargs -0 cksum 2>/dev/null
|
|
593
|
+
while IFS= read -r l; do
|
|
594
|
+
printf 'L %s -> %s\n' "$l" "$(readlink "$l")"
|
|
595
|
+
done < <(find "$d" -type l 2>/dev/null)
|
|
596
|
+
find "$d" -type d -print 2>/dev/null
|
|
589
597
|
done
|
|
590
598
|
} | sort
|
|
591
599
|
}
|
|
@@ -660,10 +668,9 @@ cmd_setup() {
|
|
|
660
668
|
fi
|
|
661
669
|
fi
|
|
662
670
|
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
fi
|
|
671
|
+
# FIX-078: launchd plist 安装从 setup 里拿掉——plist 是 per-project 资源,
|
|
672
|
+
# setup 是全局安装阶段,不应该给 cwd 留 disabled 的占位。需要时 cmd_init /
|
|
673
|
+
# _loop_on 各自会调 _install_launchd_plists。
|
|
667
674
|
|
|
668
675
|
_emit_setup_v2_ui "${steps_buf[@]}"
|
|
669
676
|
}
|
|
@@ -3933,7 +3940,7 @@ cmd_loop() {
|
|
|
3933
3940
|
off) _loop_off ;;
|
|
3934
3941
|
now) _loop_now ;;
|
|
3935
3942
|
test) _loop_test ;;
|
|
3936
|
-
status) _loop_status ;;
|
|
3943
|
+
status) _loop_status "$@" ;;
|
|
3937
3944
|
monitor) _loop_monitor "${1:-3}" ;;
|
|
3938
3945
|
runs) _loop_runs "$@" ;;
|
|
3939
3946
|
events) _loop_event_log "${1:-20}" ;;
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/lib/roll-loop-status.py
CHANGED
|
@@ -706,7 +706,7 @@ def render(events, cron, state, backlog, *, days=3, lang="both", now=None,
|
|
|
706
706
|
c("muted", " ") +
|
|
707
707
|
c("dim", "watch ") + c("blue", "roll loop --watch") +
|
|
708
708
|
c("muted", " ") +
|
|
709
|
-
c("dim", "more ") + c("blue", "roll loop --days 7"))
|
|
709
|
+
c("dim", "more ") + c("blue", "roll loop status --days 7"))
|
|
710
710
|
|
|
711
711
|
def _read_plist_loop_minute() -> int:
|
|
712
712
|
"""FIX-063: read actual loop Minute from launchd plist (truth source).
|
package/package.json
CHANGED
|
@@ -44,7 +44,7 @@ Adapt commands to the constraints below — otherwise you will burn turns on
|
|
|
44
44
|
denied operations and the cycle will idle-exit.
|
|
45
45
|
|
|
46
46
|
- **No `AskUserQuestion`**: no human can answer. If you genuinely cannot
|
|
47
|
-
proceed without a decision, write an entry to `${HOME}/.shared/roll/loop/ALERT
|
|
47
|
+
proceed without a decision, write an entry to `${HOME}/.shared/roll/loop/ALERT-<slug>.md`
|
|
48
48
|
describing what's needed and exit cleanly.
|
|
49
49
|
- **Avoid compound bash**: each `Bash` call must run a single command.
|
|
50
50
|
No `cmd1 && cmd2`, no `cmd1 ; cmd2`, no pipes (`|`), no `$(...)` /
|
|
@@ -58,6 +58,13 @@ denied operations and the cycle will idle-exit.
|
|
|
58
58
|
Files inside it (.roll/backlog.md, bin/roll, tests/, docs/) are always
|
|
59
59
|
accessible. Files at `~/.shared/roll/...` are reachable via the `Read`
|
|
60
60
|
tool but not via shell commands.
|
|
61
|
+
- **Quote every glob**: the `Bash` tool runs commands through the user's
|
|
62
|
+
login shell, which on macOS is typically `zsh`. zsh's default `nomatch`
|
|
63
|
+
aborts unquoted globs that find no match with `(eval):1: no matches
|
|
64
|
+
found: <pattern>` and exit 1, burning a turn on a meaningless error.
|
|
65
|
+
Quote literal globs (`ls 'tests/integration/helpers.*'`) or — better —
|
|
66
|
+
use the `Glob` tool, which is shell-agnostic and never aborts on empty
|
|
67
|
+
matches.
|
|
61
68
|
- **Skill invocation is the work**: route US/REFACTOR via `$roll-build`,
|
|
62
69
|
FIX via `$roll-fix`. Do not try to re-implement those flows inline.
|
|
63
70
|
|
|
@@ -75,23 +82,20 @@ loop:
|
|
|
75
82
|
|
|
76
83
|
## Workflow
|
|
77
84
|
|
|
78
|
-
### Step 1 —
|
|
85
|
+
### Step 1 — Orphan 🔨 Recovery
|
|
79
86
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
fi
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
**Orphan 🔨 recovery** — clean up stories left in `🔨 In Progress` by a crashed previous run:
|
|
87
|
+
Process-level crash recovery (LOCK, heartbeat, retry budget) is handled by
|
|
88
|
+
the runner in `bin/roll:_write_loop_runner_script` — the per-project LOCK
|
|
89
|
+
guarantees only one cycle for this slug is alive when you start. So at
|
|
90
|
+
this point, any `🔨 In Progress` row in `.roll/backlog.md` belongs to a
|
|
91
|
+
previous cycle that crashed before flipping it back; reclaim it before
|
|
92
|
+
scanning.
|
|
90
93
|
|
|
91
94
|
1. Scan .roll/backlog.md for all rows whose Status column contains `🔨 In Progress`.
|
|
92
|
-
2. For each
|
|
93
|
-
|
|
94
|
-
|
|
95
|
+
2. For each row found: revert the status back to `📋 Todo`, commit
|
|
96
|
+
`chore: revert orphan 🔨 US-XXX to 📋`, and append a line to
|
|
97
|
+
`~/.shared/roll/loop/ALERT-<slug>.md` recording the orphan id and time
|
|
98
|
+
so the next brief surfaces it.
|
|
95
99
|
3. After orphan sweep, proceed to Step 1.5 (Pre-run CI health check) before scanning.
|
|
96
100
|
|
|
97
101
|
### Step 1.5 — Pre-run CI Health Check
|
|
@@ -126,7 +130,8 @@ Call `_loop_pr_inbox` after the pre-run CI check passes. It walks
|
|
|
126
130
|
| `eligible` (clean external PR, no blocking review) | Invoke `_loop_pr_review_external` — the actual decision is provided by US-AUTO-035's GitHub Action |
|
|
127
131
|
|
|
128
132
|
**Rebase circuit breaker** — `_loop_pr_rebase_circuit <pr>` records each rebase
|
|
129
|
-
attempt under `pr_state.<PR>.attempts_at` in
|
|
133
|
+
attempt under `pr_state.<PR>.attempts_at` in the per-slug state file
|
|
134
|
+
(`~/.shared/roll/loop/state-<slug>.yaml`, FIX-052), pruning entries older
|
|
130
135
|
than 24 h. Once ≥3 attempts land within 24 h, further rebases are blocked and an
|
|
131
136
|
ALERT is written (typical cause: a broken workflow file makes CI never run,
|
|
132
137
|
which would otherwise drive infinite rebase loops).
|
|
@@ -239,10 +244,10 @@ REFACTOR-XXX → Skill("roll-build", "REFACTOR-XXX")
|
|
|
239
244
|
|
|
240
245
|
The executor will update the row to `✅ Done` on success (it transitions from `🔨 In Progress` → `✅ Done`, same Edit logic as from `📋 Todo`).
|
|
241
246
|
|
|
242
|
-
Before invoking, also write current item to state file
|
|
247
|
+
Before invoking, also write current item to the per-slug state file
|
|
248
|
+
(`~/.shared/roll/loop/state-<slug>.yaml`, FIX-052):
|
|
243
249
|
|
|
244
250
|
```yaml
|
|
245
|
-
# ~/.shared/roll/loop/state.yaml
|
|
246
251
|
status: running
|
|
247
252
|
current_item: US-AUTO-004
|
|
248
253
|
started_at: "2026-05-10T02:00:00+08:00"
|
|
@@ -256,7 +261,7 @@ After each item completes:
|
|
|
256
261
|
|
|
257
262
|
1. **TCR 硬校验** — call `roll loop enforce-tcr <story_id> <started_at>`:
|
|
258
263
|
- Count `tcr:` prefix commits since `started_at` via `git log --oneline --since=<started_at>`
|
|
259
|
-
- Count == 0 → revert story status in .roll/backlog.md from ✅ Done → 📋 Todo; write ALERT to `~/.shared/roll/loop/ALERT
|
|
264
|
+
- 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`)
|
|
260
265
|
- Count > 0 → continue normally
|
|
261
266
|
2. **CI Gate** — **MUST** invoke `roll ci --wait` (the `_loop_enforce_ci`
|
|
262
267
|
wrapper). **Do NOT call `gh` directly** (no `gh run list`, no `gh run watch`,
|
|
@@ -274,7 +279,7 @@ After each item completes:
|
|
|
274
279
|
|
|
275
280
|
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.
|
|
276
281
|
|
|
277
|
-
**Path A — attempt allowed (counter incremented in `state
|
|
282
|
+
**Path A — attempt allowed (counter incremented in `state-<slug>.yaml`):**
|
|
278
283
|
|
|
279
284
|
1. Capture failure summary:
|
|
280
285
|
```
|
|
@@ -288,15 +293,15 @@ After each item completes:
|
|
|
288
293
|
Diagnose root cause, fix via TCR, commit, push. Do NOT change <story_id>'s
|
|
289
294
|
BACKLOG status — it stays ✅ Done. The fix is a follow-up."`
|
|
290
295
|
3. After `roll-fix` completes, return to step 2 (CI Gate) — re-run `roll ci --wait`.
|
|
291
|
-
The counter in `state
|
|
296
|
+
The counter in `state-<slug>.yaml` prevents infinite loops.
|
|
292
297
|
|
|
293
298
|
**Path B — heal exhausted (≥`ROLL_LOOP_HEAL_MAX`, default 2) or disabled (`ROLL_LOOP_NO_HEAL=1`) (exit 1):**
|
|
294
299
|
|
|
295
300
|
1. Keep story as ✅ Done — commits are already on main; CI red is a follow-up
|
|
296
301
|
problem, not a story failure.
|
|
297
|
-
2. Write ALERT to `~/.shared/roll/loop/ALERT
|
|
302
|
+
2. Write ALERT to `~/.shared/roll/loop/ALERT-<slug>.md` with:
|
|
298
303
|
- story ID, time, commit SHA
|
|
299
|
-
- heal attempts made (read `heal_count:` from `state
|
|
304
|
+
- heal attempts made (read `heal_count:` from `state-<slug>.yaml`)
|
|
300
305
|
- last failure summary (head of `/tmp/roll-heal-<story_id>.log`)
|
|
301
306
|
- suggested actions: `$roll-fix` manually / inspect CI / `roll loop reset`
|
|
302
307
|
3. Skip to next story.
|
|
@@ -317,7 +322,7 @@ After each item completes:
|
|
|
317
322
|
After all items in this cycle:
|
|
318
323
|
|
|
319
324
|
```yaml
|
|
320
|
-
# ~/.shared/roll/loop/state
|
|
325
|
+
# ~/.shared/roll/loop/state-<slug>.yaml (FIX-052)
|
|
321
326
|
status: idle
|
|
322
327
|
last_run: "2026-05-10T02:15:00+08:00"
|
|
323
328
|
last_run_items: [US-AUTH-003, FIX-007]
|
|
@@ -347,7 +352,7 @@ final report in `cron.log` instead.
|
|
|
347
352
|
|---|---|---|
|
|
348
353
|
| `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. |
|
|
349
354
|
| `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}"` |
|
|
350
|
-
| `run_id` | string | Matches `state
|
|
355
|
+
| `run_id` | string | Matches `state-<slug>.yaml` `run_id` exactly. Format: `loop-YYYYMMDD-HHMM`. |
|
|
351
356
|
| `status` | enum | Exactly one of: `built` (≥1 story shipped), `idle` (no Todo items found), `failed` (paused/error). **No synonyms.** |
|
|
352
357
|
| `built` | array<string> | Story ids completed this cycle. `[]` when none. **Always array, never null/number.** |
|
|
353
358
|
| `skipped` | array<string> | Story ids skipped because they were `🔨 In Progress`. `[]` when none. **Always array.** |
|
|
@@ -439,12 +444,12 @@ reason: "both primary (claude) and fallback (deepseek) unavailable"
|
|
|
439
444
|
- Take over manually: `$roll-build US-AUTH-003`
|
|
440
445
|
```
|
|
441
446
|
|
|
442
|
-
3. Write alert file to `~/.shared/roll/loop/ALERT
|
|
447
|
+
3. Write alert file to `~/.shared/roll/loop/ALERT-<slug>.md`
|
|
443
448
|
|
|
444
449
|
## Resuming After Pause
|
|
445
450
|
|
|
446
451
|
```bash
|
|
447
|
-
roll loop resume # picks up from state
|
|
452
|
+
roll loop resume # picks up from state-<slug>.yaml current_item
|
|
448
453
|
roll loop status # show current state without running
|
|
449
454
|
roll loop reset # clear state and start fresh next scheduled run
|
|
450
455
|
```
|
|
@@ -514,6 +519,6 @@ roll-loop
|
|
|
514
519
|
├── invokes $roll-fix (FIX-XXX)
|
|
515
520
|
├── invokes $roll-brief (on Feature completion)
|
|
516
521
|
├── reads ~/.roll/config.yaml (agent routing)
|
|
517
|
-
├── writes ~/.shared/roll/loop/state
|
|
518
|
-
└── writes ~/.shared/roll/loop/ALERT
|
|
522
|
+
├── writes ~/.shared/roll/loop/state-<slug>.yaml
|
|
523
|
+
└── writes ~/.shared/roll/loop/ALERT-<slug>.md (on failure)
|
|
519
524
|
```
|