@seanyao/roll 2026.513.1 → 2026.514.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 +27 -18
- package/bin/roll +333 -8
- package/conventions/global/AGENTS.md +6 -0
- package/package.json +1 -1
- package/skills/roll-.changelog/SKILL.md +98 -2
- package/skills/roll-.dream/SKILL.md +2 -0
- package/skills/roll-build/SKILL.md +3 -1
- package/skills/roll-design/SKILL.md +2 -0
- package/skills/roll-peer/SKILL.md +27 -0
- package/skills/roll-propose/SKILL.md +4 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,21 +1,30 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## Unreleased
|
|
4
|
+
- **Fixed**: dashboard 不再在已发版后还显示 "✓ Release ready"——只有 tag 之后存在非 docs/chore 的可发版提交时才亮,光是文档改写不会再让它误报
|
|
5
|
+
- **Fixed**: dashboard 上 "📋 N PROPOSAL" 的动作提示改成 `see: PROPOSALS.md`,原先指向的 `roll backlog` 只看 BACKLOG.md,点过去看不到 PROPOSAL,逻辑断头
|
|
6
|
+
- **Added**: changelog 生成时自动挡掉技术黑话
|
|
7
|
+
- **Added**: 写 changelog 时会先参考最近几次发布的风格
|
|
8
|
+
- **Added**: 故事跑完自动开 PR + auto-merge,CI 绿就合
|
|
9
|
+
- **Added**: 完成的故事不再直接 push main,留下 PR 作为审计入口
|
|
10
|
+
- **Added**: AI 代码评审可以批准 / 打回 PR — 配合 CI 形成合并双门;紧急 hotfix 在 PR body 加 `[skip-ai-review]` 即可绕开
|
|
11
|
+
- **Added**: changelog 现在自审挡黑话
|
|
12
|
+
|
|
3
13
|
## v2026.513.1
|
|
4
|
-
- **Added**: loop
|
|
5
|
-
- **Added**:
|
|
6
|
-
- **
|
|
7
|
-
- **
|
|
8
|
-
- **Changed**:
|
|
9
|
-
- **Changed**:
|
|
10
|
-
- **Changed**: CI
|
|
11
|
-
- **
|
|
12
|
-
- **Fixed**: `roll
|
|
13
|
-
- **Fixed**: `roll update` 后 `roll loop status` 误报 off — `_install_launchd_plists` reload 路径改用 `launchctl bootout` + `bootstrap`(不动 overrides db),消除 macOS Sonoma+ 上 no-`-w` unload/load 把 label `enabled` 标记吞掉的副作用;不再需要手动 `roll loop on` 恢复
|
|
14
|
+
- **Added**: loop 现在每轮跑在独立的 worktree 里,结束自动合回 main 并清理;跑挂时保留现场目录方便排查 — 再也不会吞掉 main 上你正在改的代码
|
|
15
|
+
- **Added**: BACKLOG 上的 `depends-on:` 和 `manual-only:true` 标签从此有强制力,loop 选 story 前会过一遍,依赖没满足或标 manual-only 的直接跳过,不用人盯
|
|
16
|
+
- **Fixed**: loop 多了一道并发保护 — 偶发的同一个 runner 下两个 claude 同时改状态的情况彻底堵住
|
|
17
|
+
- **Changed**: CI 把 unit 和 integration 测试拆到两个 runner 并行跑,墙钟时间显著下降
|
|
18
|
+
- **Changed**: `roll`(不带参数)的 dashboard 重新设计 — AI 在自动跑什么放第一眼,自治三层 + 四道防线 + Pipeline 全景 + 当前焦点 + 介入区一屏看完,不用再去 `roll loop status` 子命令翻
|
|
19
|
+
- **Changed**: 测试运行器自动按机器核数并行,不再写死 4 个 job
|
|
20
|
+
- **Changed**: 纯文档改动不再触发 CI 跑全套测试,文档 PR 合并节奏更快
|
|
21
|
+
- **Fixed**: `roll peer` 在 REFINE / OBJECT 收尾时偶发的 "unbound variable" 崩溃
|
|
22
|
+
- **Fixed**: `roll update` 后 `roll loop status` 不再误报 off,不用手动 `roll loop on` 恢复
|
|
14
23
|
|
|
15
24
|
## v2026.512.8
|
|
16
|
-
- **Added**: `$roll-doc` —
|
|
17
|
-
- **Added**: `roll-.dream` Scan 6 —
|
|
18
|
-
- **Fixed**:
|
|
25
|
+
- **Added**: `$roll-doc` — 扫描任意项目的文档现状,找出缺口并生成草稿,支持 `--dry-run`
|
|
26
|
+
- **Added**: `roll-.dream` Scan 6 — 每晚检测文档是否跟代码脱节,发现问题自动写进重构待办
|
|
27
|
+
- **Fixed**: 某些 SSH 配置下 loop 把"CI 查询失败"误判为"CI 工具未安装",导致测试没过就标任务完成
|
|
19
28
|
|
|
20
29
|
## v2026.512.7
|
|
21
30
|
- **Added**: `roll alert` — 查看、确认、清除 loop 告警,不用再去翻 loop status
|
|
@@ -23,10 +32,10 @@
|
|
|
23
32
|
- **Added**: `roll ci [--wait]` — 查看当前提交的 CI 状态,或等待 CI 跑完再继续
|
|
24
33
|
- **Fixed**: loop 现在会等 CI 通过后才标记故事完成,CI 失败则保持进行中并发出提醒
|
|
25
34
|
- **Fixed**: changelog 更新不再产生独立 commit,并入故事完成提交,git log 更干净
|
|
26
|
-
- **Added**: `docs/domain/` — Roll
|
|
35
|
+
- **Added**: `docs/domain/` — Roll 架构领域模型文档
|
|
27
36
|
- **Fixed**: `roll loop runs` 不再报"当前项目尚无运行记录",历史记录正常显示
|
|
28
|
-
- **Added**: 文档目录重组 —
|
|
29
|
-
- **Added**: README
|
|
37
|
+
- **Added**: 文档目录重组 — 散落文档统一迁移至 `docs/guide/` 和 `docs/practices/`
|
|
38
|
+
- **Added**: README 精简,新增文档导航索引
|
|
30
39
|
- **Added**: dream 每晚自动检测文档缺口,brief 新增文档覆盖率数字
|
|
31
40
|
|
|
32
41
|
## v2026.512.6
|
|
@@ -91,8 +100,8 @@
|
|
|
91
100
|
- **Fixed**: release.yml 加 fetch-depth: 0,确保历史 tag 在 workflow 中可见
|
|
92
101
|
|
|
93
102
|
## v2026.510.8
|
|
94
|
-
- **Fixed**: release.sh 自洽 —
|
|
95
|
-
- **Fixed**: roll release
|
|
103
|
+
- **Fixed**: release.sh 自洽 — 发版流程不依赖外部调用
|
|
104
|
+
- **Fixed**: `roll release` 改为独立调用 roll-release skill,与 release.sh 解耦
|
|
96
105
|
- **Fixed**: GitHub Release workflow 加 fetch-depth: 0,确保历史 tag 可见
|
|
97
106
|
|
|
98
107
|
## v2026.510.7
|
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.
|
|
7
|
+
VERSION="2026.514.1"
|
|
8
8
|
ROLL_HOME="${ROLL_HOME:-${HOME}/.roll}"
|
|
9
9
|
ROLL_CONFIG="${ROLL_HOME}/config.yaml"
|
|
10
10
|
ROLL_GLOBAL="${ROLL_HOME}/conventions/global"
|
|
@@ -609,6 +609,36 @@ cmd_setup() {
|
|
|
609
609
|
|
|
610
610
|
echo ""
|
|
611
611
|
info "Next: run ${BOLD}roll init${NC} inside a project to initialize it. 下一步:在项目目录运行 roll init"
|
|
612
|
+
|
|
613
|
+
echo ""
|
|
614
|
+
_print_pr_pipeline_hint
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
# ─── PR pipeline hint ────────────────────────────────────────────────────────
|
|
618
|
+
# US-AUTO-035: print the one-time branch-protection command that flips repo
|
|
619
|
+
# from path A (CI gate only) to path C (CI + AI review double gate). Reading
|
|
620
|
+
# this hint is opt-in; the command is destructive (changes branch protection)
|
|
621
|
+
# so it is never run automatically.
|
|
622
|
+
_print_pr_pipeline_hint() {
|
|
623
|
+
cat <<'HINT'
|
|
624
|
+
|
|
625
|
+
Optional — enable AI review as a hard merge gate (path C).
|
|
626
|
+
可选 —— 启用 AI 评审作为合并双门(路径 C)。
|
|
627
|
+
|
|
628
|
+
Run once per repo (requires admin token), then claude-code-review.yml
|
|
629
|
+
approvals become a required merge gate alongside CI:
|
|
630
|
+
每个仓库执行一次(需要管理员 token),之后 claude-code-review.yml 的
|
|
631
|
+
approve 将与 CI 一起成为合并必经的双门:
|
|
632
|
+
|
|
633
|
+
gh api -X PATCH repos/<owner>/<repo>/branches/main/protection \
|
|
634
|
+
-f required_pull_request_reviews.required_approving_review_count=1
|
|
635
|
+
|
|
636
|
+
Escape hatch: add [skip-ai-review] to a PR body, or include
|
|
637
|
+
SKIP_AI_REVIEW in any commit message, to bypass AI review for that PR.
|
|
638
|
+
紧急通道:在 PR body 加 [skip-ai-review],或在任一 commit message
|
|
639
|
+
里包含 SKIP_AI_REVIEW,可对该 PR 绕过 AI 评审。
|
|
640
|
+
|
|
641
|
+
HINT
|
|
612
642
|
}
|
|
613
643
|
|
|
614
644
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
@@ -2118,15 +2148,31 @@ for _attempt in 1 2 3; do
|
|
|
2118
2148
|
fi
|
|
2119
2149
|
done
|
|
2120
2150
|
|
|
2121
|
-
# Post-claude:
|
|
2151
|
+
# Post-claude: publish cycle branch. Doc-only changes (BACKLOG/docs) merge
|
|
2152
|
+
# immediately via --admin; code changes use auto-merge (CI gate required).
|
|
2153
|
+
# When \`gh\` is unavailable, fall back to the legacy ff-merge path.
|
|
2122
2154
|
if [ "\$_USE_WORKTREE" = "1" ]; then
|
|
2123
2155
|
if [ "\$_exit" -eq 0 ]; then
|
|
2124
|
-
if ( cd "
|
|
2156
|
+
if ( cd "\$WT" && _loop_is_doc_only_change ); then
|
|
2157
|
+
( cd "\$WT" && _loop_publish_doc_pr "\$BRANCH" "doc: loop cycle \${CYCLE_ID}" )
|
|
2158
|
+
else
|
|
2159
|
+
( cd "\$WT" && _loop_publish_pr "\$BRANCH" "loop cycle \${CYCLE_ID}" )
|
|
2160
|
+
fi
|
|
2161
|
+
_publish_status=\$?
|
|
2162
|
+
if [ "\$_publish_status" -eq 0 ]; then
|
|
2125
2163
|
_worktree_cleanup "\$WT" "\$BRANCH"
|
|
2126
|
-
echo "[loop] cycle \${CYCLE_ID}:
|
|
2164
|
+
echo "[loop] cycle \${CYCLE_ID}: published; worktree cleaned"
|
|
2165
|
+
elif [ "\$_publish_status" -eq 2 ]; then
|
|
2166
|
+
if ( cd "${project_path}" && _worktree_merge_back "\$BRANCH" ); then
|
|
2167
|
+
_worktree_cleanup "\$WT" "\$BRANCH"
|
|
2168
|
+
echo "[loop] cycle \${CYCLE_ID}: gh unavailable; merged via ff and cleaned up"
|
|
2169
|
+
else
|
|
2170
|
+
_worktree_alert "cycle \${CYCLE_ID}: gh unavailable AND merge_back failed; worktree preserved at \$WT"
|
|
2171
|
+
echo "[loop] cycle \${CYCLE_ID}: gh+merge_back both failed; worktree preserved at \$WT"
|
|
2172
|
+
fi
|
|
2127
2173
|
else
|
|
2128
|
-
_worktree_alert "cycle \${CYCLE_ID}:
|
|
2129
|
-
echo "[loop] cycle \${CYCLE_ID}:
|
|
2174
|
+
_worktree_alert "cycle \${CYCLE_ID}: PR publish failed; worktree preserved at \$WT (branch \$BRANCH)"
|
|
2175
|
+
echo "[loop] cycle \${CYCLE_ID}: PR publish failed; worktree preserved at \$WT"
|
|
2130
2176
|
fi
|
|
2131
2177
|
else
|
|
2132
2178
|
_worktree_alert "cycle \${CYCLE_ID}: claude exited \$_exit; worktree preserved at \$WT (branch \$BRANCH)"
|
|
@@ -3055,6 +3101,172 @@ _loop_is_manual_only() {
|
|
|
3055
3101
|
echo "$row" | grep -qE 'manual-only:true'
|
|
3056
3102
|
}
|
|
3057
3103
|
|
|
3104
|
+
# US-CL-004: changelog 风格守门 Phase 1 — mechanical linter.
|
|
3105
|
+
#
|
|
3106
|
+
# _changelog_lint_bullet <bullet-text>
|
|
3107
|
+
# Stdout: one violation tag per line; empty = bullet passes.
|
|
3108
|
+
# Exit: 0 always (callers read the output stream, not the exit code).
|
|
3109
|
+
#
|
|
3110
|
+
# Violation tags:
|
|
3111
|
+
# backtick-identifier `…` contains `_` or `()` (e.g. `_foo`, `bar()`)
|
|
3112
|
+
# file-suffix `.md`/`.sh`/`.yml`/`.ts`/`.bats` outside backticks
|
|
3113
|
+
# internal-word Phase N / Step N / Helper / Schema / Fixture / Refactor
|
|
3114
|
+
# over-length > 50 visible chars (UTF-8 codepoints; 中文按字符计)
|
|
3115
|
+
# path-fragment docs/ / bin/ / tests/ / scripts/ outside backticks
|
|
3116
|
+
#
|
|
3117
|
+
# Backticks are treated as the "user-quoted" zone — content there is assumed
|
|
3118
|
+
# to be a real user command (e.g. `roll edit notes.md`) and is excluded from
|
|
3119
|
+
# the file-suffix / path-fragment checks.
|
|
3120
|
+
_changelog_lint_bullet() {
|
|
3121
|
+
local bullet="$1"
|
|
3122
|
+
local stripped
|
|
3123
|
+
stripped=$(printf '%s' "$bullet" | sed -E 's/`[^`]*`//g')
|
|
3124
|
+
|
|
3125
|
+
if printf '%s' "$bullet" | grep -qE '`[^`]*(_|\(\))[^`]*`'; then
|
|
3126
|
+
echo "backtick-identifier"
|
|
3127
|
+
fi
|
|
3128
|
+
if printf '%s' "$stripped" | grep -qE '\.(md|sh|yml|ts|bats)([^A-Za-z0-9]|$)'; then
|
|
3129
|
+
echo "file-suffix"
|
|
3130
|
+
fi
|
|
3131
|
+
if printf '%s' "$bullet" | grep -qE '(Phase|Step)[[:space:]]+[0-9]+|Helper|Schema|Fixture|Refactor'; then
|
|
3132
|
+
echo "internal-word"
|
|
3133
|
+
fi
|
|
3134
|
+
local len
|
|
3135
|
+
len=$(printf '%s' "$bullet" | LC_ALL=C.UTF-8 wc -m | tr -d ' ')
|
|
3136
|
+
if [ "${len:-0}" -gt 50 ]; then
|
|
3137
|
+
echo "over-length"
|
|
3138
|
+
fi
|
|
3139
|
+
if printf '%s' "$stripped" | grep -qE '(^|[^A-Za-z0-9_])(docs|bin|tests|scripts)/'; then
|
|
3140
|
+
echo "path-fragment"
|
|
3141
|
+
fi
|
|
3142
|
+
return 0
|
|
3143
|
+
}
|
|
3144
|
+
|
|
3145
|
+
# US-CL-004: changelog few-shot style anchors — extract bullets from the
|
|
3146
|
+
# most recent 3 published `## v...` sections of CHANGELOG.md (skipping
|
|
3147
|
+
# `## Unreleased`). Cap at ~1500 chars so the agent's context stays lean.
|
|
3148
|
+
#
|
|
3149
|
+
# _changelog_style_anchors [changelog-path]
|
|
3150
|
+
# Stdout: concatenated bullet lines from the last 3 released versions.
|
|
3151
|
+
# Exit: 0 (empty output when no CHANGELOG.md or no released sections).
|
|
3152
|
+
_changelog_style_anchors() {
|
|
3153
|
+
local changelog="${1:-CHANGELOG.md}"
|
|
3154
|
+
[ -f "$changelog" ] || return 0
|
|
3155
|
+
awk '
|
|
3156
|
+
/^## v/ { ver++; if (ver > 3) exit; printing = 1; next }
|
|
3157
|
+
/^## / { printing = 0 }
|
|
3158
|
+
printing && /^- / { print }
|
|
3159
|
+
' "$changelog" | head -c 1500
|
|
3160
|
+
}
|
|
3161
|
+
|
|
3162
|
+
# US-CL-005: changelog 风格守门 Phase 2 — self-audit gate.
|
|
3163
|
+
#
|
|
3164
|
+
# _changelog_audit_bullet <bullet>
|
|
3165
|
+
# Stricter than _changelog_lint_bullet: 5 boolean rules, 30-char cap.
|
|
3166
|
+
# Stdout: one failed-rule tag per line; empty = bullet passes.
|
|
3167
|
+
# Exit: 0 always.
|
|
3168
|
+
#
|
|
3169
|
+
# Rules:
|
|
3170
|
+
# over-length-30 visible chars > 30 AND no backtick (user-cmd escape hatch)
|
|
3171
|
+
# internal-id backtick content contains `_` or `()`
|
|
3172
|
+
# path-or-suffix .md/.sh/.yml/.ts/.bats or docs/bin/tests/scripts/ outside backticks
|
|
3173
|
+
# phase-step `Phase N` / `Step N` workflow vocabulary
|
|
3174
|
+
# bad-shape no `—` (em dash) AND no `不再` AND no `现在` keyword
|
|
3175
|
+
_changelog_audit_bullet() {
|
|
3176
|
+
local bullet="$1"
|
|
3177
|
+
local stripped
|
|
3178
|
+
stripped=$(printf '%s' "$bullet" | sed -E 's/`[^`]*`//g')
|
|
3179
|
+
|
|
3180
|
+
# Rule 1: length cap 30 (user-command in backticks bypasses this rule).
|
|
3181
|
+
if ! printf '%s' "$bullet" | grep -q '`'; then
|
|
3182
|
+
local len
|
|
3183
|
+
len=$(LC_ALL=en_US.UTF-8 printf '%s' "$bullet" | LC_ALL=en_US.UTF-8 wc -m | tr -d ' ')
|
|
3184
|
+
if [ "${len:-0}" -gt 30 ]; then
|
|
3185
|
+
echo "over-length-30"
|
|
3186
|
+
fi
|
|
3187
|
+
fi
|
|
3188
|
+
|
|
3189
|
+
# Rule 2: internal identifier inside backticks.
|
|
3190
|
+
if printf '%s' "$bullet" | grep -qE '`[^`]*(_|\(\))[^`]*`'; then
|
|
3191
|
+
echo "internal-id"
|
|
3192
|
+
fi
|
|
3193
|
+
|
|
3194
|
+
# Rule 3: file suffix / path fragment outside backticks.
|
|
3195
|
+
if printf '%s' "$stripped" | grep -qE '\.(md|sh|yml|ts|bats)([^A-Za-z0-9]|$)' \
|
|
3196
|
+
|| printf '%s' "$stripped" | grep -qE '(^|[^A-Za-z0-9_])(docs|bin|tests|scripts)/'; then
|
|
3197
|
+
echo "path-or-suffix"
|
|
3198
|
+
fi
|
|
3199
|
+
|
|
3200
|
+
# Rule 4: workflow vocabulary.
|
|
3201
|
+
if printf '%s' "$bullet" | grep -qE '(Phase|Step)[[:space:]]+[0-9]+'; then
|
|
3202
|
+
echo "phase-step"
|
|
3203
|
+
fi
|
|
3204
|
+
|
|
3205
|
+
# Rule 5: required shape — em dash, 不再, or 现在.
|
|
3206
|
+
if ! printf '%s' "$bullet" | grep -qE '—|不再|现在'; then
|
|
3207
|
+
echo "bad-shape"
|
|
3208
|
+
fi
|
|
3209
|
+
|
|
3210
|
+
return 0
|
|
3211
|
+
}
|
|
3212
|
+
|
|
3213
|
+
# _changelog_audit_log <verdict> <round> <bullet> [<reason>...]
|
|
3214
|
+
# Append a JSONL record to the audit log. Path overridable via
|
|
3215
|
+
# ROLL_CHANGELOG_AUDIT_LOG (tests use this to stay out of $HOME).
|
|
3216
|
+
_changelog_audit_log() {
|
|
3217
|
+
local verdict="$1" round="$2" bullet="$3"
|
|
3218
|
+
shift 3
|
|
3219
|
+
local log="${ROLL_CHANGELOG_AUDIT_LOG:-${_SHARED_ROOT}/loop/changelog-audit.jsonl}"
|
|
3220
|
+
mkdir -p "$(dirname "$log")"
|
|
3221
|
+
local ts; ts=$(date -u +%Y-%m-%dT%H:%M:%SZ)
|
|
3222
|
+
local reasons_json='[]'
|
|
3223
|
+
if [ "$#" -gt 0 ]; then
|
|
3224
|
+
reasons_json=$(printf '%s\n' "$@" | jq -R . | jq -sc .)
|
|
3225
|
+
fi
|
|
3226
|
+
jq -nc \
|
|
3227
|
+
--arg ts "$ts" \
|
|
3228
|
+
--arg verdict "$verdict" \
|
|
3229
|
+
--argjson round "$round" \
|
|
3230
|
+
--arg bullet "$bullet" \
|
|
3231
|
+
--argjson reasons "$reasons_json" \
|
|
3232
|
+
'{ts:$ts, verdict:$verdict, round:$round, bullet:$bullet, reasons:$reasons}' \
|
|
3233
|
+
>> "$log"
|
|
3234
|
+
}
|
|
3235
|
+
|
|
3236
|
+
# _changelog_audit_gate <round1> [<round2> <round3>]
|
|
3237
|
+
# Run up to 3 candidate bullets through _changelog_audit_bullet.
|
|
3238
|
+
# First clean candidate wins: print bullet to stdout, exit 0.
|
|
3239
|
+
# All 3 failed: print ⚠️-prefixed last candidate, append ALERT, exit 1.
|
|
3240
|
+
# Each round writes a _changelog_audit_log record.
|
|
3241
|
+
_changelog_audit_gate() {
|
|
3242
|
+
local i=0 last=""
|
|
3243
|
+
for candidate in "$@"; do
|
|
3244
|
+
i=$((i + 1))
|
|
3245
|
+
last="$candidate"
|
|
3246
|
+
local viols
|
|
3247
|
+
# shellcheck disable=SC2207
|
|
3248
|
+
viols=( $(_changelog_audit_bullet "$candidate") )
|
|
3249
|
+
if [ "${#viols[@]}" -eq 0 ]; then
|
|
3250
|
+
_changelog_audit_log pass "$i" "$candidate"
|
|
3251
|
+
printf '%s\n' "$candidate"
|
|
3252
|
+
return 0
|
|
3253
|
+
fi
|
|
3254
|
+
_changelog_audit_log fail "$i" "$candidate" "${viols[@]}"
|
|
3255
|
+
[ "$i" -ge 3 ] && break
|
|
3256
|
+
done
|
|
3257
|
+
# All 3 rounds failed (or fewer if caller passed < 3).
|
|
3258
|
+
mkdir -p "$(dirname "$_LOOP_ALERT")" 2>/dev/null
|
|
3259
|
+
{
|
|
3260
|
+
echo ""
|
|
3261
|
+
echo "# ALERT — changelog audit failed after $i rounds"
|
|
3262
|
+
echo "**Time**: $(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
3263
|
+
echo "**Bullet**: $last"
|
|
3264
|
+
echo "**Action**: kept under \`## Unreleased\` with ⚠️ prefix; human review recommended."
|
|
3265
|
+
} >> "$_LOOP_ALERT"
|
|
3266
|
+
printf '⚠️ %s\n' "$last"
|
|
3267
|
+
return 1
|
|
3268
|
+
}
|
|
3269
|
+
|
|
3058
3270
|
# US-AUTO-036: worktree helpers (loop-safe pure additions).
|
|
3059
3271
|
#
|
|
3060
3272
|
# Phase 1 of worktree isolation — these helpers are NOT yet called by
|
|
@@ -3150,6 +3362,107 @@ _worktree_merge_back() {
|
|
|
3150
3362
|
return 0
|
|
3151
3363
|
}
|
|
3152
3364
|
|
|
3365
|
+
# US-AUTO-033: publish a loop cycle branch as a GitHub PR with auto-merge.
|
|
3366
|
+
#
|
|
3367
|
+
# _loop_publish_pr <branch> [title]
|
|
3368
|
+
# Caller's cwd: a tree where <branch> exists locally.
|
|
3369
|
+
# Steps:
|
|
3370
|
+
# 1. git push origin <branch>
|
|
3371
|
+
# 2. gh pr view <branch> → reuse if a PR is already open
|
|
3372
|
+
# 3. gh pr create --base main --head <branch> ...
|
|
3373
|
+
# 4. gh pr merge <branch> --auto --squash --delete-branch
|
|
3374
|
+
# Stdout: PR URL (always, even on idempotent reuse).
|
|
3375
|
+
# Exit 0 on success / idempotent reuse; non-zero on push or create failure.
|
|
3376
|
+
# On auto-merge failure: still returns 0 (PR exists; human can take over).
|
|
3377
|
+
# When `gh` is not installed: returns 2 — runner script's fallback path.
|
|
3378
|
+
_loop_publish_pr() {
|
|
3379
|
+
local branch="$1"
|
|
3380
|
+
local title="${2:-loop cycle ${branch#loop/}}"
|
|
3381
|
+
if ! command -v gh >/dev/null 2>&1; then
|
|
3382
|
+
_worktree_alert "_loop_publish_pr: gh not installed; cannot publish PR for ${branch}"
|
|
3383
|
+
return 2
|
|
3384
|
+
fi
|
|
3385
|
+
local slug; slug=$(_gh_repo_slug 2>/dev/null) || slug=""
|
|
3386
|
+
if [ -z "$slug" ]; then
|
|
3387
|
+
_worktree_alert "_loop_publish_pr: origin remote is not a github repo; cannot publish PR for ${branch}"
|
|
3388
|
+
return 2
|
|
3389
|
+
fi
|
|
3390
|
+
local _push_err
|
|
3391
|
+
_push_err=$(git push origin "$branch" 2>&1) || {
|
|
3392
|
+
_worktree_alert "_loop_publish_pr: push origin ${branch} failed: ${_push_err}"
|
|
3393
|
+
return 1
|
|
3394
|
+
}
|
|
3395
|
+
local pr_url
|
|
3396
|
+
pr_url=$(gh -R "$slug" pr view "$branch" --json url -q .url 2>/dev/null) || pr_url=""
|
|
3397
|
+
if [ -z "$pr_url" ]; then
|
|
3398
|
+
local body
|
|
3399
|
+
body=$(printf 'Auto-opened by roll-loop cycle.\n\n- Branch: %s\n- TCR micro-commits: %s\n\nThis PR will auto-merge once required checks pass.' \
|
|
3400
|
+
"$branch" "$(git rev-list --count origin/main.."$branch" 2>/dev/null || echo '?')")
|
|
3401
|
+
pr_url=$(gh -R "$slug" pr create --base main --head "$branch" \
|
|
3402
|
+
--title "$title" --body "$body" 2>/dev/null) || pr_url=""
|
|
3403
|
+
if [ -z "$pr_url" ]; then
|
|
3404
|
+
_worktree_alert "_loop_publish_pr: gh pr create failed for ${branch}"
|
|
3405
|
+
return 1
|
|
3406
|
+
fi
|
|
3407
|
+
fi
|
|
3408
|
+
gh -R "$slug" pr merge "$branch" --auto --squash --delete-branch >/dev/null 2>&1 \
|
|
3409
|
+
|| _worktree_alert "_loop_publish_pr: gh pr merge --auto failed for ${branch} (PR ${pr_url} left open)"
|
|
3410
|
+
echo "$pr_url"
|
|
3411
|
+
return 0
|
|
3412
|
+
}
|
|
3413
|
+
|
|
3414
|
+
# _loop_is_doc_only_change
|
|
3415
|
+
# Returns 0 if every file changed since origin/main is doc-only
|
|
3416
|
+
# (BACKLOG.md, CHANGELOG.md, PROPOSALS.md, docs/, .claude/).
|
|
3417
|
+
# Returns 1 if any code file changed or there are no changes.
|
|
3418
|
+
_loop_is_doc_only_change() {
|
|
3419
|
+
local changed
|
|
3420
|
+
changed=$(git diff --name-only origin/main HEAD 2>/dev/null) || return 1
|
|
3421
|
+
[ -z "$changed" ] && return 1
|
|
3422
|
+
echo "$changed" | grep -qvE '^(BACKLOG\.md|CHANGELOG\.md|PROPOSALS\.md|docs/|\.claude/)' && return 1
|
|
3423
|
+
return 0
|
|
3424
|
+
}
|
|
3425
|
+
|
|
3426
|
+
# _loop_publish_doc_pr <branch> [title]
|
|
3427
|
+
# Like _loop_publish_pr but merges immediately with --admin (no CI wait).
|
|
3428
|
+
# For doc-only changes where CI is not meaningful.
|
|
3429
|
+
_loop_publish_doc_pr() {
|
|
3430
|
+
local branch="$1"
|
|
3431
|
+
local title="${2:-doc update ${branch#loop/}}"
|
|
3432
|
+
if ! command -v gh >/dev/null 2>&1; then
|
|
3433
|
+
_worktree_alert "_loop_publish_doc_pr: gh not installed; cannot publish PR for ${branch}"
|
|
3434
|
+
return 2
|
|
3435
|
+
fi
|
|
3436
|
+
local slug; slug=$(_gh_repo_slug 2>/dev/null) || slug=""
|
|
3437
|
+
if [ -z "$slug" ]; then
|
|
3438
|
+
_worktree_alert "_loop_publish_doc_pr: origin remote is not a github repo; cannot publish PR for ${branch}"
|
|
3439
|
+
return 2
|
|
3440
|
+
fi
|
|
3441
|
+
if ! git push origin "$branch" --quiet 2>/dev/null; then
|
|
3442
|
+
_worktree_alert "_loop_publish_doc_pr: push origin ${branch} failed"
|
|
3443
|
+
return 1
|
|
3444
|
+
fi
|
|
3445
|
+
local pr_url
|
|
3446
|
+
pr_url=$(gh -R "$slug" pr view "$branch" --json url -q .url 2>/dev/null) || pr_url=""
|
|
3447
|
+
if [ -z "$pr_url" ]; then
|
|
3448
|
+
local body
|
|
3449
|
+
body=$(printf 'Doc-only update by roll-loop cycle.\n\n- Branch: %s\n- Files: BACKLOG / docs only\n\nMerging immediately — no CI gate needed for doc-only changes.' "$branch")
|
|
3450
|
+
pr_url=$(gh -R "$slug" pr create --base main --head "$branch" \
|
|
3451
|
+
--title "$title" --body "$body" 2>/dev/null) || pr_url=""
|
|
3452
|
+
if [ -z "$pr_url" ]; then
|
|
3453
|
+
_worktree_alert "_loop_publish_doc_pr: gh pr create failed for ${branch}"
|
|
3454
|
+
return 1
|
|
3455
|
+
fi
|
|
3456
|
+
fi
|
|
3457
|
+
if ! gh -R "$slug" pr merge "$branch" --admin --squash --delete-branch >/dev/null 2>&1; then
|
|
3458
|
+
_worktree_alert "_loop_publish_doc_pr: gh pr merge --admin failed for ${branch} (PR ${pr_url} left open)"
|
|
3459
|
+
echo "$pr_url"
|
|
3460
|
+
return 1
|
|
3461
|
+
fi
|
|
3462
|
+
echo "$pr_url"
|
|
3463
|
+
return 0
|
|
3464
|
+
}
|
|
3465
|
+
|
|
3153
3466
|
_loop_monitor() {
|
|
3154
3467
|
local interval="${1:-3}"
|
|
3155
3468
|
local project_path; project_path=$(pwd -P)
|
|
@@ -3807,8 +4120,20 @@ _dash_proposal_count() {
|
|
|
3807
4120
|
grep '^## PROPOSAL' PROPOSALS.md 2>/dev/null | wc -l | tr -d ' '
|
|
3808
4121
|
}
|
|
3809
4122
|
|
|
3810
|
-
# ⑤ Release-ready signal — true
|
|
4123
|
+
# ⑤ Release-ready signal — true iff there are releasable commits since the
|
|
4124
|
+
# latest tag AND the latest brief signals 可发版/Release ready. Releasable =
|
|
4125
|
+
# any commit since the latest tag whose subject does NOT start with the
|
|
4126
|
+
# release-irrelevant prefixes `docs:` or `chore:`. Prevents the flag from
|
|
4127
|
+
# sticking on after a release when only docs rewrites land on top of the tag
|
|
4128
|
+
# (FIX-033 symptom 2).
|
|
3811
4129
|
_dash_release_ready() {
|
|
4130
|
+
local latest_tag
|
|
4131
|
+
latest_tag=$(git describe --tags --abbrev=0 2>/dev/null) || return 1
|
|
4132
|
+
local commits_with_code
|
|
4133
|
+
commits_with_code=$(git log "${latest_tag}..HEAD" --pretty=format:%s 2>/dev/null \
|
|
4134
|
+
| grep -cvE '^(docs|chore)(\([^)]*\))?:[[:space:]]' 2>/dev/null \
|
|
4135
|
+
|| echo 0)
|
|
4136
|
+
[[ "${commits_with_code:-0}" -gt 0 ]] || return 1
|
|
3812
4137
|
local latest
|
|
3813
4138
|
latest=$(ls docs/briefs/*.md 2>/dev/null | sort | tail -1 || true)
|
|
3814
4139
|
[[ -z "$latest" ]] && return 1
|
|
@@ -3988,7 +4313,7 @@ _dashboard() {
|
|
|
3988
4313
|
printf " ${GREEN}✓ AI 自驱中 — 无需介入${NC}\n"
|
|
3989
4314
|
else
|
|
3990
4315
|
(( alerts > 0 )) && printf " ${RED}⚠ %s ALERT${NC} run: roll alert\n" "$alerts"
|
|
3991
|
-
(( proposals > 0 )) && printf " ${YELLOW}📋 %s PROPOSAL${NC}
|
|
4316
|
+
(( proposals > 0 )) && printf " ${YELLOW}📋 %s PROPOSAL${NC} see: PROPOSALS.md\n" "$proposals"
|
|
3992
4317
|
$release_ready && printf " ${GREEN}✓ Release ready${NC} run: roll release\n"
|
|
3993
4318
|
fi
|
|
3994
4319
|
echo ""
|
|
@@ -40,6 +40,12 @@
|
|
|
40
40
|
- Before claiming completion: verify in the target environment mentioned by
|
|
41
41
|
user (e.g., specific CLI tool, remote server, hardware platform).
|
|
42
42
|
- **Workspace**: `BACKLOG.md` index. `docs/features/` for details.
|
|
43
|
+
- **Backlog descriptions** (US, FIX, REFACTOR, IDEA, PROPOSAL): one sentence in plain language.
|
|
44
|
+
Say what changed and why it matters — not how it works internally.
|
|
45
|
+
No file paths, function names, parameter lists, or architecture jargon.
|
|
46
|
+
`depends-on:` and `manual-only:` functional tags are allowed; `Domain:` annotation tags are not.
|
|
47
|
+
Technical details and AC go in `docs/features/`.
|
|
48
|
+
A well-written BACKLOG description can be used directly as a CHANGELOG entry.
|
|
43
49
|
- **Done**: Push + CI passes + deployed. Local-only is not done.
|
|
44
50
|
- **Commit message format**:
|
|
45
51
|
- Format: `<type>: <description>` (Git Hook may auto-prepend type prefix)
|
package/package.json
CHANGED
|
@@ -47,6 +47,14 @@ Create mode:
|
|
|
47
47
|
|
|
48
48
|
CHANGELOG 是给**使用者**看的,不是给维护者看的。一句话讲清"用户能做什么 / 不再被什么坑",能不写就不写。
|
|
49
49
|
|
|
50
|
+
**BACKLOG 描述写好了,CHANGELOG 就是复制 + 过滤,不是重写。**
|
|
51
|
+
如果 BACKLOG 描述已经是人话、一句话、说用户价值,直接用它(去掉 `depends-on:` / `manual-only:` 等功能性标签)。
|
|
52
|
+
只有 BACKLOG 描述包含实现细节或技术黑话时,才需要改写。
|
|
53
|
+
|
|
54
|
+
**FIX 条目的 filter 规则**:BACKLOG 的 FIX 描述通常是 `<用户症状> — <修复手段>` 结构。
|
|
55
|
+
CHANGELOG 只取破折号前的用户症状,丢弃破折号后的修复手段。
|
|
56
|
+
例:`roll update 后 loop 状态误报 off — reload 改用 bootout+bootstrap` → CHANGELOG 只写 `roll update 后 loop 状态不再误报 off`。
|
|
57
|
+
|
|
50
58
|
**完全跳过(不写入 CHANGELOG):**
|
|
51
59
|
- 测试基建(teardown 清理、test isolation、bats helper、CI 时序)
|
|
52
60
|
- prompt / SKILL.md 内部契约(schema 锁定、enum 强制、contract test)
|
|
@@ -62,9 +70,9 @@ CHANGELOG 是给**使用者**看的,不是给维护者看的。一句话讲清
|
|
|
62
70
|
- 看得见的体验变化(布局、文案、速度、可见性)
|
|
63
71
|
- 影响安装、升级、配置的改动
|
|
64
72
|
|
|
65
|
-
|
|
73
|
+
**写法约束(BACKLOG 描述不符合时才介入改写):**
|
|
66
74
|
|
|
67
|
-
1.
|
|
75
|
+
1. **一行**。超了就是太啰嗦。
|
|
68
76
|
2. **不写实现细节**:禁止文件路径、函数名、字段列表、命令参数、配置键名。
|
|
69
77
|
3. **不写数字细节**:"3 个服务"、"60+ ghost" 这种内部状态不写。
|
|
70
78
|
4. **说人话**:避免 "幂等"、"trap"、"epoch" 等技术黑话;说"做两次效果一样"、"异常退出也会清理"、"启动时间"。
|
|
@@ -133,6 +141,94 @@ assigned.
|
|
|
133
141
|
Do NOT read `package.json` version, do NOT call `git describe`, do NOT invent
|
|
134
142
|
version numbers like `v2026.511.8`. Just write to `## Unreleased`.
|
|
135
143
|
|
|
144
|
+
### 5.3 Style Anchors — In-Context Few-Shot
|
|
145
|
+
|
|
146
|
+
Before drafting bullets, pull the most recent 3 published versions' bullets as
|
|
147
|
+
in-context examples so the agent doesn't write from a blank slate (the blank
|
|
148
|
+
slate is where the technical-jargon habit comes from — the agent is fresh
|
|
149
|
+
from writing function names and just copies them over).
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
_changelog_style_anchors CHANGELOG.md
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Output is the concatenated bullet lines from the last 3 `## v...` sections,
|
|
156
|
+
capped at 1500 characters. Use them as a style reference when drafting — pay
|
|
157
|
+
attention to length, voice ("不再被…坑" / "现在 …"), and absence of internal
|
|
158
|
+
names.
|
|
159
|
+
|
|
160
|
+
### 5.4 Mechanical Lint — Hard Gate
|
|
161
|
+
|
|
162
|
+
After drafting bullets, run each through the mechanical linter. Any non-empty
|
|
163
|
+
output means the bullet violates at least one blacklist rule and must be
|
|
164
|
+
rewritten.
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
for bullet in "${draft_bullets[@]}"; do
|
|
168
|
+
violations=$(_changelog_lint_bullet "$bullet")
|
|
169
|
+
[ -n "$violations" ] && needs_rewrite+=("$bullet :: $violations")
|
|
170
|
+
done
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
Violation tags emitted by `_changelog_lint_bullet`:
|
|
174
|
+
|
|
175
|
+
| Tag | Trigger | Why it's noise to users |
|
|
176
|
+
|---|---|---|
|
|
177
|
+
| `backtick-identifier` | `…` contains `_` or `()` | Internal symbol names mean nothing to users |
|
|
178
|
+
| `file-suffix` | `.md/.sh/.yml/.ts/.bats` outside backticks | File paths are maintainer concern |
|
|
179
|
+
| `internal-word` | Phase N / Step N / Helper / Schema / Fixture / Refactor | Workflow/design vocabulary, not user-facing |
|
|
180
|
+
| `over-length` | > 50 visible chars | Too long = probably explaining implementation |
|
|
181
|
+
| `path-fragment` | `docs/` / `bin/` / `tests/` / `scripts/` outside backticks | Source-tree layout is maintainer concern |
|
|
182
|
+
|
|
183
|
+
**Rewrite loop (max 2 rounds)**:
|
|
184
|
+
1. Show the agent the violation list + original bullet → request rewrite
|
|
185
|
+
2. Re-lint the rewrite
|
|
186
|
+
3. If still violating after 2 rewrites → **keep the bullet but prefix `⚠️ `** so
|
|
187
|
+
the human reviewer can spot it, AND append a line to
|
|
188
|
+
`~/.shared/roll/loop/ALERT.md` describing what couldn't be fixed.
|
|
189
|
+
Never block the whole release on style — let the human take the wheel.
|
|
190
|
+
|
|
191
|
+
### 5.5 Self-Audit — Stage Gate
|
|
192
|
+
|
|
193
|
+
The mechanical linter in Step 5.4 catches **blacklist** patterns; the audit
|
|
194
|
+
gate in this step is a **whitelist** of 5 boolean checks that the bullet must
|
|
195
|
+
satisfy before it can be staged. Stricter (30-char cap vs 50) and shape-aware
|
|
196
|
+
(requires the user-facing `—` or `不再`/`现在` sentence pattern).
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
accepted=$(_changelog_audit_gate "$draft1" "$rewrite1" "$rewrite2")
|
|
200
|
+
# Exit 0: stdout = first clean candidate
|
|
201
|
+
# Exit 1: stdout = ⚠️-prefixed last candidate; ALERT was written
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
5 boolean checks evaluated by `_changelog_audit_bullet`:
|
|
205
|
+
|
|
206
|
+
| Tag | Trigger |
|
|
207
|
+
|---|---|
|
|
208
|
+
| `over-length-30` | visible chars > 30 (bypassed if bullet has a backticked user command) |
|
|
209
|
+
| `internal-id` | backtick content contains `_` or `()` |
|
|
210
|
+
| `path-or-suffix` | `.md/.sh/.yml/.ts/.bats` or `docs/bin/tests/scripts/` outside backticks |
|
|
211
|
+
| `phase-step` | `Phase N` or `Step N` |
|
|
212
|
+
| `bad-shape` | none of `—`, `不再`, `现在` present |
|
|
213
|
+
|
|
214
|
+
**3-round retry envelope**:
|
|
215
|
+
1. Round 1 = original draft; if pass → stage immediately
|
|
216
|
+
2. Round 2 = rewrite based on violations from round 1
|
|
217
|
+
3. Round 3 = second rewrite if round 2 also failed
|
|
218
|
+
4. All 3 failed → keep last candidate prefixed with `⚠️ `, append ALERT to
|
|
219
|
+
`~/.shared/roll/loop/ALERT.md`. **Never block the stage** — let the human
|
|
220
|
+
reviewer take the wheel. The loop must keep moving.
|
|
221
|
+
|
|
222
|
+
**Audit log**: every round (pass or fail) appends one JSONL line to
|
|
223
|
+
`~/.shared/roll/loop/changelog-audit.jsonl`:
|
|
224
|
+
|
|
225
|
+
```json
|
|
226
|
+
{"ts":"2026-05-13T13:50:00Z","verdict":"fail","round":1,"bullet":"...","reasons":["over-length-30","bad-shape"]}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
Useful when reviewing whether the agent actually iterated or just rubber-stamped
|
|
230
|
+
its own first draft. Set `ROLL_CHANGELOG_AUDIT_LOG` to redirect (tests only).
|
|
231
|
+
|
|
136
232
|
### 5. Generate CHANGELOG.md
|
|
137
233
|
|
|
138
234
|
**Create mode** (first time, no CHANGELOG.md yet):
|
|
@@ -222,6 +222,8 @@ section of BACKLOG.md:
|
|
|
222
222
|
| REFACTOR-XXX | {one-line description} — flagged by dream {YYYY-MM-DD} | 📋 Todo |
|
|
223
223
|
```
|
|
224
224
|
|
|
225
|
+
`{one-line description}` 写法:一句人话说清楚"什么地方需要改"以及"不改会怎样"。不写函数名、文件路径、技术方案。例:`loop 状态读取逻辑分散在多处,修一处容易遗漏另一处`。
|
|
226
|
+
|
|
225
227
|
**Threshold**: only flag items where the fix would meaningfully reduce
|
|
226
228
|
complexity or prevent future bugs. Ignore cosmetic issues.
|
|
227
229
|
|
|
@@ -284,9 +284,11 @@ When any signal appears, **do not stop — flag it**:
|
|
|
284
284
|
**REFACTOR entry format in BACKLOG.md:**
|
|
285
285
|
|
|
286
286
|
```markdown
|
|
287
|
-
| REFACTOR-001 |
|
|
287
|
+
| REFACTOR-001 | {one-line plain-language description} | 📋 Todo |
|
|
288
288
|
```
|
|
289
289
|
|
|
290
|
+
描述写法:参见 AGENTS.md "Backlog descriptions" 规则。说清楚"什么需要改"以及"不改会怎样",技术细节写在 `docs/features/refactor-log.md`。
|
|
291
|
+
|
|
290
292
|
**refactor-log.md entry format:**
|
|
291
293
|
|
|
292
294
|
```markdown
|
|
@@ -625,6 +625,8 @@ DOMAIN_DIR="docs/domain/"
|
|
|
625
625
|
| [US-{DOMAIN}-{N}](docs/features/<feature>.md#us-{domain}-{n}) | {one-line description} | 📋 Todo |
|
|
626
626
|
```
|
|
627
627
|
|
|
628
|
+
`{one-line description}` 写法:用户能读懂的一句话,说清楚"能做什么"或"解决了什么麻烦"。不写实现细节、文件路径、函数名。细节和 AC 写在 `docs/features/` 里。写好了可以直接当 CHANGELOG 条目用。
|
|
629
|
+
|
|
628
630
|
Note: `{DOMAIN}` maps to the Bounded Context name identified in DDD analysis.
|
|
629
631
|
|
|
630
632
|
**US section in docs/features/\<feature\>.md (full details):**
|
|
@@ -180,6 +180,33 @@ If serve mode is available, prefer HTTP transport over direct CLI invocation.
|
|
|
180
180
|
|
|
181
181
|
**CLI vs. API Key**: `claude`, `deepseek`, `kimi`, `codex` CLIs authenticate via existing subscription accounts — no separate API key required. This is the primary advantage of CLI transport over the MCP/HTTP approach.
|
|
182
182
|
|
|
183
|
+
## Inline Display Mode (Manual Triggers)
|
|
184
|
+
|
|
185
|
+
When peer review is manually triggered by a human (via `/peer`, "叫上 peer", etc.), the executing agent **must display each round inline in the current conversation**. This applies regardless of which agent is executing — Claude, DeepSeek, Kimi, PI, or any other.
|
|
186
|
+
|
|
187
|
+
**Per-round display format:**
|
|
188
|
+
|
|
189
|
+
```
|
|
190
|
+
─── Peer Review · Round N ───────────────────────────────
|
|
191
|
+
→ Sending to [peer]:
|
|
192
|
+
{full message sent to peer}
|
|
193
|
+
|
|
194
|
+
← [peer] responds:
|
|
195
|
+
{peer's full response, verbatim}
|
|
196
|
+
|
|
197
|
+
◆ My analysis: {Claude/executing agent's reaction and position for this round}
|
|
198
|
+
─────────────────────────────────────────────────────────
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
**Rules:**
|
|
202
|
+
- Peer CLI calls must be **synchronous** (do NOT use background/async execution).
|
|
203
|
+
- Show the outgoing message **before** calling the peer, so the user sees what's being asked.
|
|
204
|
+
- Relay the peer's response **verbatim** before adding your own analysis.
|
|
205
|
+
- If a peer call fails or times out, report it immediately inline and either retry or ESCALATE.
|
|
206
|
+
- Negotiation log is still written to `~/.shared/roll/peer/logs/` as usual.
|
|
207
|
+
|
|
208
|
+
**Why inline, not tmux:** When a human manually triggers peer review inside an agent's interactive session, the conversation IS the visible interface. tmux auto-attach is only relevant for CLI-launched background sessions (`bin/roll peer`), not for skill invocations.
|
|
209
|
+
|
|
183
210
|
## Workflow Integration
|
|
184
211
|
|
|
185
212
|
### `roll-build` Plan Mode
|
|
@@ -74,7 +74,11 @@ Generate between 1 and 3 proposals. For each:
|
|
|
74
74
|
|
|
75
75
|
```
|
|
76
76
|
## PROPOSAL: {Short title}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
`{Short title}` 写法:用户看得懂的一句话,说清楚"加什么"或"解决什么问题"。不用技术术语,不提内部实现。这句话批准后会直接成为 BACKLOG 里的描述。
|
|
77
80
|
|
|
81
|
+
```
|
|
78
82
|
**Motivation (why):**
|
|
79
83
|
One to two sentences from the user's perspective explaining the pain or opportunity.
|
|
80
84
|
|