@seanyao/roll 2026.522.2 → 2026.523.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,5 +1,51 @@
1
1
  # Changelog
2
2
 
3
+ ## v2026.523.2
4
+
5
+ ### Added
6
+
7
+ - **每轮 cycle 实时打出 7 阶段进入退出与耗时** — `startup` / `preflight` / `worktree_setup` / `claude_invoke` / `publish_push` / `publish_wait_merge` / `cleanup`,长静默阶段 30–60s 一次心跳,再也不用盯着空屏猜 loop 卡在哪 `[loop]`
8
+ - **cycle 结束打一份按耗时降序的阶段面板** — 同时把每阶段秒数固化到 `runs.jsonl` 的 `phases` 字段,跨轮对比"今天哪一步最慢"有完整数据 `[loop]`
9
+ - **`roll loop runs` 每行末尾追加 `slowest=<阶段> <占比>%`,新子命令 `roll loop runs --detail <cycle_id>` 打开完整阶段面板** — 不必翻 ndjson 就能定位单轮瓶颈 `[dashboard]`
10
+ - **`tests/run.sh --affected [base-ref]`** — TCR micro-step 只跑被本次改动覆盖的测试文件,反馈秒级;改动 `tests/helpers/*` 等保守降级到全套 `[testing]`
11
+ - **`tests/run.sh --tier=fast|slow|all`** — 本地 TCR 默认 `fast`,CI 跑 `--tier=all`;`ROLL_TEST_TIME_CAP=1` 时 `fast` 全跑 60s 硬上限,性能漂移立刻打红 `[testing]`
12
+ - **测试质量评分卷(6 类反模式 rubric)** — `roll-.dream` Scan 7 按 rubric 输出 `REFACTOR-XXX [test-quality:❶|❷|...]`,单轮上限 5 条,存量 ❶ 类反模式可结构化治理 `[dream]`
13
+ - **`roll lang [zh|en|--list]`** — i18n 地基命令:查看 / 切换 / 列出可用语言,按本机 locale 自动选默认;`ROLL_LANG` 环境变量临时覆盖 `[i18n]`
14
+ - **`roll prices show / refresh`** — 价格表挪进带版本号的 snapshot,能一键拉官方文档对比;调价不再要改源码、提 PR,每个 version 的价格都查得到 `[pricing]`
15
+ - **cycle 结束时按当时价格固化成本** — 以后调价或升级都不会回头改写历史轮次的数字;老事件继续可读,渲染时打 `[legacy]` 标记 `[loop]`
16
+
17
+ ### Fixed
18
+
19
+ - **dashboard 把已合并 PR 错显示成"开放"** — `roll loop status` 兜底走 git log 找 squash merge subject,原正则只认 `loop/cycle-LABEL` 旧分支名,对新的 `loop cycle LABEL (#N)` 默认 subject 匹配不上;现在两种都认 `[dashboard]`
20
+ - **`model_prices.bats` 改用 fixture 价格表注入** — 之前对生产 PRICES 算术断言把调价误伤成测试红,重写后只断结构不变量(cache_read < input、out ≥ in 等)`[testing]`
21
+ - **已合入临时分支堆在 origin 不被清理** — `_loop_cleanup_stale_cycle_branches` 漏过 agent / abort 两条路径,现在 cycle 入口先扫一遍再走主流程 `[loop]`
22
+ - **临时目录复现 bug 时留下幽灵 launchd 服务** — 自动检测临时路径并把所有 `launchctl` 调用走 sandbox 注册表,主用户 launchd domain 不再被污染 `[launchd]`
23
+ - **dashboard 把 story 列错显示成上一轮的 ID** — `_STORY_ID_PAT` 老正则每段只认纯字母,`US-I18N-001` 这种字母+数字混合 segment 匹配不上 → 新 `pick_todo` 被静默丢掉,仍展示前一轮的旧 story 名;正则更新为 `[A-Z][A-Z0-9]*(?:-[A-Z][A-Z0-9]*)*-\d+` 后 I18N / K8S / D2 / S3 等命名都能识别 `[dashboard]`
24
+
25
+ ### Docs
26
+
27
+ - **新增 `guide/{en,zh}/loop.md` "Cycle phases" 小节** — 七阶段表 + 触发时机 + 典型耗时 + 收尾面板样例;FAQ 增 C6 "某阶段慢怎么定位"
28
+ - **新增 `guide/{en,zh}/testing.md` "TCR Test Strategy" 小节** — `--affected` 与 `--tier` 两个杠杆的语义、匹配规则、CI/本地默认组合;新增 "测试质量评分卷" 小节列六类反模式与修复方向
29
+
30
+ ## v2026.523.1
31
+
32
+ ### Added
33
+
34
+ - **`roll loop branches`** — 一眼看见本机残留的 loop 分支;每轮入口先 GC 一次,半途中止的 cycle 也会被收掉 `[loop]`
35
+
36
+ ### Changed
37
+
38
+ - **dashboard token 列拆成 input / output / cache 写 / cache 读** — cache 是真花钱的,账单终于解释得清 `[loop]`
39
+
40
+ ### Fixed
41
+
42
+ - **每日 dream / brief 在 macOS 26.4 上从来没真跑过** — 换成 interval 触发,从今天起稳定每天产出 `[loop]`
43
+ - **dashboard 上 tcr 次数、built 列表、ALERT 文案不再显示假零或别故事的旧标签** `[loop]`
44
+ - **选一个故事不再把别的依赖它的故事也标成"在做"** — dashboard 不再骗你说有人在干活 `[loop]`
45
+ - **`roll setup` / `roll update` 不再在隐藏的覆盖提示上无声卡死**
46
+ - **`$roll-notes` 现在写到 `.roll/notes/`** — 和 dream / brief 一致,不再扔到项目根目录 `[loop]`
47
+ - **loop CI 网关不再把"排队中 / 进行中"误判成失败** `[loop]`
48
+
3
49
  ## v2026.522.2
4
50
 
5
51
  ### Changed
@@ -0,0 +1,110 @@
1
+ #!/usr/bin/env bash
2
+ # dream-test-quality-scan — ad-hoc helper for roll-.dream Scan 7.
3
+ #
4
+ # Walks bats files and flags ❶-class anti-patterns (hardcoded business data
5
+ # in assertion bodies). Emits structured REFACTOR-shaped lines so the
6
+ # maintainer can sanity-check the rubric against the current suite without
7
+ # waiting for the nightly dream cycle.
8
+ #
9
+ # Usage:
10
+ # dream-test-quality-scan [--category N] [--path PATH] [--max N]
11
+ # dream-test-quality-scan --help
12
+ #
13
+ # Only category 1 (❶ hardcoded business data) is implemented as a deterministic
14
+ # heuristic; categories ❷..❻ stay with the dream skill (AI agent applies the
15
+ # rubric). The helper exists so a smoke test and a maintainer dry-run can
16
+ # confirm ❶ detection keeps working as the suite evolves.
17
+
18
+ set -euo pipefail
19
+
20
+ CATEGORY=1
21
+ TARGET=""
22
+ MAX=5
23
+
24
+ usage() {
25
+ cat <<'EOF'
26
+ dream-test-quality-scan — Scan 7 ❶ dry-run helper
27
+
28
+ Usage:
29
+ dream-test-quality-scan [--category N] [--path PATH] [--max N]
30
+ dream-test-quality-scan --help
31
+
32
+ Options:
33
+ --category N Anti-pattern category (only 1 is implemented; default 1)
34
+ --path PATH File or directory to scan (default: tests/)
35
+ --max N Maximum entries to emit (default: 5; matches dream skill cap)
36
+ --help Show this message
37
+
38
+ Output:
39
+ One line per finding:
40
+ [test-quality:❶] <file>:<line> — <one-line description>
41
+ Exit code is 0 even when nothing is found (dry-run is informational).
42
+ EOF
43
+ }
44
+
45
+ while [[ $# -gt 0 ]]; do
46
+ case "$1" in
47
+ --category) CATEGORY="${2:-1}"; shift 2 ;;
48
+ --path) TARGET="${2:-}"; shift 2 ;;
49
+ --max) MAX="${2:-5}"; shift 2 ;;
50
+ --help|-h) usage; exit 0 ;;
51
+ *) echo "unknown flag: $1" >&2; usage >&2; exit 2 ;;
52
+ esac
53
+ done
54
+
55
+ if [[ "$CATEGORY" -ne 1 ]]; then
56
+ echo "category $CATEGORY not yet implemented — only ❶ is mechanical" >&2
57
+ exit 0
58
+ fi
59
+
60
+ # Default scan root.
61
+ if [[ -z "$TARGET" ]]; then
62
+ repo_root=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
63
+ TARGET="${repo_root}/tests"
64
+ fi
65
+
66
+ if [[ ! -e "$TARGET" ]]; then
67
+ echo "path not found: $TARGET" >&2
68
+ exit 2
69
+ fi
70
+
71
+ scan_file() {
72
+ local file="$1"
73
+ # ❶ heuristic — assertion lines whose RHS contains a numeric literal:
74
+ # - lines containing `[[` or `[ ` (bats assertion syntax)
75
+ # - AND containing `==` or `=` (equality)
76
+ # - AND containing a decimal/integer literal of length ≥ 1 inside quotes
77
+ # Emits one entry per file (not per line) to stay under the rate cap.
78
+ local first_hit
79
+ first_hit=$(grep -nE '\[\[.*"[^"]*[0-9]+(\.[0-9]+)?[^"]*"' "$file" 2>/dev/null \
80
+ | head -1 || true)
81
+ [[ -z "$first_hit" ]] && return 1
82
+
83
+ local lineno
84
+ lineno=$(echo "$first_hit" | cut -d: -f1)
85
+ local rel
86
+ rel=$(python3 -c "import os,sys; print(os.path.relpath(sys.argv[1]))" "$file" 2>/dev/null || echo "$file")
87
+ printf '[test-quality:❶] %s:%s — assertion body hardcodes a numeric literal that likely owns its value elsewhere\n' \
88
+ "$rel" "$lineno"
89
+ return 0
90
+ }
91
+
92
+ emitted=0
93
+ if [[ -d "$TARGET" ]]; then
94
+ # Iterate over .bats files under TARGET; stop after MAX hits.
95
+ # Exclude vendored bats-core helpers — those are framework tests, not ours.
96
+ while IFS= read -r f; do
97
+ case "$f" in
98
+ */tests/helpers/bats-*/*) continue ;;
99
+ esac
100
+ if scan_file "$f"; then
101
+ emitted=$((emitted + 1))
102
+ [[ "$emitted" -ge "$MAX" ]] && break
103
+ fi
104
+ done < <(find "$TARGET" -type f -name '*.bats' | sort)
105
+ else
106
+ scan_file "$TARGET" && emitted=1 || true
107
+ fi
108
+
109
+ # Always succeed — dry-run is informational, the dream cycle decides what to do.
110
+ exit 0