@seanyao/roll 2026.514.2 → 2026.514.3

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,12 +1,20 @@
1
1
  # Changelog
2
2
 
3
- ## v2026.514.2
3
+ ## v2026.514.3
4
4
 
5
- - Changelog 历史版本全部重新整理:按主题分组、合并同类项、用词更贴近用户视角,附 `[loop]` / `[dream]` 归因标记
6
- - Changelog Skill 新增 Release Notes 生成规范:分组规则、条目合并、归因标签、措辞原则
7
- - README 新增 Evolution 章节,梳理 Roll 从工具到自主交付系统的演进脉络
5
+ ### 约定与导航
6
+
7
+ - `$roll-design` 澄清需求前先自己定位产品端和业务域,问你的问题少了
8
+ - `$roll-doc` 为已有项目生成 AGENTS.md 导航骨架 — 新接入 Roll 不再从空白出发
9
+
10
+ ### 自动化流水线
11
+
12
+ - loop 每轮先消化开放 PR 再领新 backlog — 把队列里的 PR 当成首类工作,不是绕开的障碍 `[loop]`
13
+ - 自己开的 `loop/*` 分支不会被自己评审,避免同源 bias `[loop]`
14
+ - 24 小时内 rebase 同一个 PR 超过 3 次自动熔断,workflow 文件出错时不再无限循环 `[loop]`
15
+ - 每次 session 收尾自动删掉自己推上去的 `claude/*` 临时分支,远端不再积压"看起来要发 PR 实际不会发"的孤儿分支 `[loop]`
8
16
 
9
- ## v2026.514.1
17
+ ## v2026.514.2
10
18
 
11
19
  ### 自动化流水线
12
20
 
@@ -18,6 +26,8 @@
18
26
 
19
27
  - 生成时自动过滤技术黑话,并对照历史风格保持表达一致 `[loop]`
20
28
  - 写入前有一道自审:行文不达标就退回重写,不进 changelog `[loop]`
29
+ - 历史版本全部重新整理:按主题分组、合并同类项、附 `[loop]` / `[dream]` 归因标记
30
+ - Release Notes 生成规范写入 Skill:分组规则、条目合并、归因标签、措辞原则
21
31
 
22
32
  ### 可见性
23
33
 
@@ -28,6 +38,7 @@
28
38
  ### 其他
29
39
 
30
40
  - 纯文档改动直接合 main,不等 CI,合并更快
41
+ - README 新增 Evolution 章节,梳理 Roll 从工具到自主交付系统的演进脉络
31
42
 
32
43
  ## v2026.513.1
33
44
 
package/README.md CHANGED
@@ -7,7 +7,7 @@
7
7
  ╚═╝ ╚═╝ ╚═════╝ ╚══════╝╚══════╝
8
8
  ```
9
9
 
10
- > Roll out features with AI agents — _Move fast, no sprints._
10
+ > _Agents, roll out._
11
11
 
12
12
  **[中文版 README](README_CN.md)**
13
13
 
@@ -27,6 +27,16 @@ Roll is an autonomous delivery system for software teams — AI agents pick stor
27
27
 
28
28
  _Works with Claude, Cursor, Codex, or your own agent._
29
29
 
30
+ ## Evolution
31
+
32
+ Roll didn't start as a framework. It started as a question: *what if the AI didn't just write code, but actually shipped it?*
33
+
34
+ Early versions just pushed engineering conventions to whichever AI tool you were running. Then came multi-agent support — Kimi, DeepSeek, Codex, Trae — and `roll-peer`, which let one AI challenge another's decisions before anything landed on `main`.
35
+
36
+ The real shift was `roll loop`: stories running back-to-back without human prompting, `roll-.dream` filing its own refactor tickets after nightly scans, the system generating its own work queue. What followed was building enough trust to leave it running overnight — worktree isolation, CI + AI review double gates, real-time visibility into what the agent was actually doing.
37
+
38
+ The goal from here: full delivery, end to end — with humans on the loop, not in it.
39
+
30
40
  ---
31
41
 
32
42
  ## Quick Start (30 seconds)
@@ -74,22 +84,6 @@ roll loop on # optional: let the agent work unattended
74
84
 
75
85
  ---
76
86
 
77
- ## Evolution
78
-
79
- Roll didn't start as a framework. It started as a question: *what if the AI didn't just write code, but actually shipped it?*
80
-
81
- The first version was almost embarrassingly small — a way to push engineering conventions to whatever AI tool you happened to be running. But it needed to be trustworthy before it could be useful, so the early weeks went into making Roll self-maintaining: one-command releases, self-updating installs, a clean npm presence.
82
-
83
- The next step was making Roll genuinely multi-agent. Kimi, DeepSeek, Codex, Trae — each integrated with its own skill preferences and model bindings. `roll-peer` came from a simple insight: agents shouldn't just review their own work. Have one AI challenge another's design decisions before anything lands on `main`. That turned out to be the first glimpse of what Roll was actually becoming.
84
-
85
- The real shift happened when `roll loop` went live. Stories started running back-to-back without any human prompt. `roll-.dream` began filing its own refactor tickets after nightly scans. The system had started generating its own work queue — not just executing tasks, but surfacing the next ones.
86
-
87
- What followed was learning to trust that autonomy: real-time terminal windows, worktree isolation so loop runs never touch your in-progress work, a CI + AI review double gate so nothing merges until it's actually ready. The kind of loop you can leave running overnight and wake up to something mergeable.
88
-
89
- The goal from here: full delivery, end to end — with humans on the loop, not in it.
90
-
91
- ---
92
-
93
87
  ## Contributing
94
88
 
95
89
  PRs welcome. Keep them focused on one thing. For larger changes, open an issue first.
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.514.2"
7
+ VERSION="2026.514.3"
8
8
  ROLL_HOME="${ROLL_HOME:-${HOME}/.roll}"
9
9
  ROLL_CONFIG="${ROLL_HOME}/config.yaml"
10
10
  ROLL_GLOBAL="${ROLL_HOME}/conventions/global"
@@ -1903,7 +1903,7 @@ _agent_run_skill() {
1903
1903
  [[ -f "$skill_file" ]] || { err "Skill not found: ${skill}"; return 1; }
1904
1904
  local content; content=$(_skill_content "$skill_file")
1905
1905
  case "$agent" in
1906
- claude) claude -p --output-format stream-json "$content" ;;
1906
+ claude) claude -p --output-format text "$content" ;;
1907
1907
  kimi) kimi --quiet -p "$content" ;;
1908
1908
  deepseek) deepseek "$content" ;;
1909
1909
  pi) pi -p "$content" ;;
@@ -2963,15 +2963,73 @@ _loop_precheck_ci() {
2963
2963
  **Reason**: HEAD CI is red — loop refused to build on a broken base HEAD CI 红,loop 拒绝在破损的基础上构建
2964
2964
 
2965
2965
  **Action required**:
2966
- - Investigate and fix CI: \`gh -R $(_gh_repo_slug) run list --commit ${commit}\`
2966
+ - Investigate and fix CI: \`gh -R ${slug} run list --commit ${commit}\`
2967
2967
  - After fixing and pushing green commit: \`roll loop now\`
2968
2968
  EOF
2969
+ _loop_diagnose_open_prs "$slug" >> "$_LOOP_ALERT"
2969
2970
  _notify "roll ⚠ CI red" "loop refused to build on broken base (${short})"
2970
2971
  return 1
2971
2972
  fi
2972
2973
  return 0
2973
2974
  }
2974
2975
 
2976
+ # _loop_diagnose_open_prs <slug>
2977
+ # Appended to ALERT when CI is red on HEAD.
2978
+ # For each open PR targeting main: lists CI failing tests + changed files,
2979
+ # flags whether failures look unrelated to the PR's own changes.
2980
+ _loop_diagnose_open_prs() {
2981
+ local slug="$1"
2982
+ local prs
2983
+ prs=$(gh -R "$slug" pr list --base main --state open --json number,title,headRefName \
2984
+ --jq '.[] | [.number|tostring, .headRefName, .title] | @tsv' 2>/dev/null) || return 0
2985
+ [[ -z "$prs" ]] && return 0
2986
+
2987
+ printf '\n## Open PRs (potential fixes)\n'
2988
+ while IFS=$'\t' read -r pr_num branch pr_title; do
2989
+ printf '\nPR #%s: %s\n' "$pr_num" "$pr_title"
2990
+
2991
+ # Files changed in this PR
2992
+ local changed_files
2993
+ changed_files=$(gh -R "$slug" pr diff "$pr_num" --name-only 2>/dev/null | head -10) || changed_files="(unable to fetch)"
2994
+ printf ' Changed: %s\n' "$(echo "$changed_files" | tr '\n' ' ')"
2995
+
2996
+ # Latest CI run on the PR branch
2997
+ local run_id conclusion
2998
+ run_id=$(gh -R "$slug" run list --branch "$branch" --json databaseId,conclusion \
2999
+ --jq '[.[] | select(.conclusion != null)] | first | .databaseId' 2>/dev/null) || run_id=""
3000
+ conclusion=$(gh -R "$slug" run list --branch "$branch" --json databaseId,conclusion \
3001
+ --jq '[.[] | select(.conclusion != null)] | first | .conclusion' 2>/dev/null) || conclusion="unknown"
3002
+
3003
+ if [[ "$conclusion" == "success" ]]; then
3004
+ printf ' CI: green — blocked only by branch protection (safe to merge)\n'
3005
+ printf ' Suggest: gh pr merge %s --admin\n' "$pr_num"
3006
+ elif [[ -n "$run_id" ]]; then
3007
+ local failing_tests
3008
+ failing_tests=$(gh -R "$slug" run view "$run_id" --log-failed 2>/dev/null \
3009
+ | grep -oP '(?<=not ok \d{1,4} ).*' | head -8) || failing_tests="(unable to fetch)"
3010
+
3011
+ printf ' CI: %s\n' "$conclusion"
3012
+ printf ' Failing tests:\n'
3013
+ while IFS= read -r t; do
3014
+ [[ -n "$t" ]] && printf ' - %s\n' "$t"
3015
+ done <<< "$failing_tests"
3016
+
3017
+ # Heuristic: if no failing test mentions a changed file, flag as likely unrelated
3018
+ local related=0
3019
+ while IFS= read -r f; do
3020
+ local base; base=$(basename "$f")
3021
+ echo "$failing_tests" | grep -qi "$base" && { related=1; break; }
3022
+ done <<< "$changed_files"
3023
+ if [[ "$related" -eq 0 ]]; then
3024
+ printf ' Note: failing tests appear UNRELATED to changed files — consider manual merge\n'
3025
+ printf ' Suggest: gh pr merge %s --admin (verify tests manually first)\n' "$pr_num"
3026
+ else
3027
+ printf ' Note: failing tests may relate to PR changes — investigate before merging\n'
3028
+ fi
3029
+ fi
3030
+ done <<< "$prs"
3031
+ }
3032
+
2975
3033
  # CI gate before marking a story Done.
2976
3034
  # On CI failure: writes ALERT, returns 1 (caller keeps story 🔨 In Progress).
2977
3035
  # When gh unavailable: returns 0 (graceful skip).
@@ -3101,6 +3159,219 @@ _loop_is_manual_only() {
3101
3159
  echo "$row" | grep -qE 'manual-only:true'
3102
3160
  }
3103
3161
 
3162
+ # US-AUTO-034: PR-first inbox — loop processes open PRs before scanning BACKLOG.
3163
+ #
3164
+ # Three building blocks, kept as pure / mockable functions:
3165
+ # _loop_pr_classify pure routing decision (no side effects)
3166
+ # _loop_pr_rebase_circuit 24h sliding-window circuit breaker on rebase retries
3167
+ # _loop_pr_inbox orchestrator that walks `gh pr list` and routes
3168
+ # each open PR to skip / review / rebase
3169
+ #
3170
+ # Design notes:
3171
+ # - gh missing or any gh failure → return 0 (lenient, like FIX-026's pre-check)
3172
+ # - self-authored loop/* PRs are skipped to avoid same-source AI review
3173
+ # - latest human review of CHANGES_REQUESTED or APPROVED blocks AI review
3174
+ # (Human-review-activity guard from US-AUTO-034 AC)
3175
+ # - rebase attempts ≥3 within 24h trip the circuit breaker (writes ALERT)
3176
+
3177
+ # _loop_pr_classify <head_ref> <human_review_state> <ci_state> <mergeable_state>
3178
+ # Prints one of:
3179
+ # loop_self
3180
+ # blocked_human_request_changes
3181
+ # blocked_human_approved
3182
+ # stale
3183
+ # eligible
3184
+ # Exit 0 always — callers parse the printed token.
3185
+ _loop_pr_classify() {
3186
+ local head_ref="${1:-}"
3187
+ local human_review="${2:-}"
3188
+ local ci_state="${3:-}"
3189
+ local mergeable="${4:-}"
3190
+
3191
+ case "$head_ref" in
3192
+ loop/*) echo "loop_self"; return 0 ;;
3193
+ esac
3194
+
3195
+ case "$human_review" in
3196
+ CHANGES_REQUESTED) echo "blocked_human_request_changes"; return 0 ;;
3197
+ APPROVED) echo "blocked_human_approved"; return 0 ;;
3198
+ esac
3199
+
3200
+ if [ "$ci_state" = "failure" ] || [ "$mergeable" = "CONFLICTING" ] || [ "$mergeable" = "BEHIND" ]; then
3201
+ echo "stale"
3202
+ return 0
3203
+ fi
3204
+
3205
+ echo "eligible"
3206
+ }
3207
+
3208
+ # _loop_pr_rebase_circuit <pr_number>
3209
+ # Side effect: appends current timestamp to $_LOOP_STATE under
3210
+ # pr_state.<PR>.attempts_at, pruning entries older than 24h.
3211
+ # Exit 0: attempt allowed and recorded.
3212
+ # Exit 1: ≥3 attempts within 24h → blocked; ALERT written.
3213
+ _loop_pr_rebase_circuit() {
3214
+ local pr="$1"
3215
+ [ -n "$pr" ] || return 1
3216
+
3217
+ local state="${_LOOP_STATE:-${HOME}/.shared/roll/loop/state.yaml}"
3218
+ local now; now=$(date -u +%s)
3219
+ local cutoff=$((now - 86400))
3220
+
3221
+ # Extract existing timestamps for this PR (empty if absent).
3222
+ local existing=""
3223
+ if [ -f "$state" ]; then
3224
+ existing=$(awk -v pr="\"$pr\":" '
3225
+ $0 ~ "pr_state:" {in_pr=1; next}
3226
+ in_pr && $0 ~ pr {in_target=1; next}
3227
+ in_target && $0 ~ /attempts_at:/ {
3228
+ sub(/^[^"]*"/, ""); sub(/".*$/, ""); print; exit
3229
+ }
3230
+ in_target && /^[^[:space:]]/ {in_target=0}
3231
+ ' "$state" 2>/dev/null)
3232
+ fi
3233
+
3234
+ # Prune stale timestamps (>24h ago).
3235
+ local fresh=""
3236
+ local ts
3237
+ for ts in $existing; do
3238
+ case "$ts" in
3239
+ ''|*[!0-9]*) continue ;;
3240
+ esac
3241
+ if [ "$ts" -ge "$cutoff" ]; then
3242
+ fresh="${fresh:+$fresh }$ts"
3243
+ fi
3244
+ done
3245
+
3246
+ # Count attempts within window; ≥3 means this would be the 4th retry blocked.
3247
+ local count=0
3248
+ for ts in $fresh; do count=$((count + 1)); done
3249
+
3250
+ if [ "$count" -ge 3 ]; then
3251
+ mkdir -p "$(dirname "${_LOOP_ALERT:-/dev/null}")" 2>/dev/null || true
3252
+ cat > "${_LOOP_ALERT}" <<EOF
3253
+ # ALERT — PR rebase circuit breaker tripped
3254
+
3255
+ **Time**: $(date '+%Y-%m-%d %H:%M')
3256
+ **PR**: #${pr}
3257
+ **Reason**: PR #${pr} rebased ${count}× within 24h without CI resolution — possible workflow file error PR rebase 多次未解决,可能是 workflow 文件错误
3258
+
3259
+ **Action required**:
3260
+ - Check PR CI logs and workflow files for breakage
3261
+ - Resolve manually, then: \`roll loop now\`
3262
+ EOF
3263
+ return 1
3264
+ fi
3265
+
3266
+ # Record this attempt and persist.
3267
+ fresh="${fresh:+$fresh }$now"
3268
+ _loop_pr_state_write "$pr" "$fresh" "$state"
3269
+ return 0
3270
+ }
3271
+
3272
+ # Internal: rewrite $state with pr_state.<pr>.attempts_at = "<fresh-ts-list>".
3273
+ # Minimal YAML writer — we own the schema and only need this one field family.
3274
+ _loop_pr_state_write() {
3275
+ local pr="$1"
3276
+ local attempts="$2"
3277
+ local state="$3"
3278
+
3279
+ mkdir -p "$(dirname "$state")" 2>/dev/null || true
3280
+ [ -f "$state" ] || : > "$state"
3281
+
3282
+ local tmp; tmp=$(mktemp)
3283
+ awk -v pr="\"$pr\":" -v attempts="$attempts" '
3284
+ BEGIN { in_pr=0; in_target=0; written=0 }
3285
+ /^pr_state:/ { in_pr=1; print; next }
3286
+ in_pr && $0 ~ pr {
3287
+ in_target=1
3288
+ print " " pr
3289
+ print " attempts_at: \"" attempts "\""
3290
+ written=1
3291
+ next
3292
+ }
3293
+ in_target && /attempts_at:/ { next } # skip old value, already written
3294
+ in_target && /^[^[:space:]]/ { in_target=0 }
3295
+ { print }
3296
+ END {
3297
+ if (!in_pr) {
3298
+ print "pr_state:"
3299
+ print " " pr
3300
+ print " attempts_at: \"" attempts "\""
3301
+ } else if (!written) {
3302
+ print " " pr
3303
+ print " attempts_at: \"" attempts "\""
3304
+ }
3305
+ }
3306
+ ' "$state" > "$tmp" && mv "$tmp" "$state"
3307
+ }
3308
+
3309
+ # _loop_pr_inbox
3310
+ # Walks open PRs and routes each by classification.
3311
+ # Lenient on gh unavailability — returns 0 so the loop continues to BACKLOG.
3312
+ _loop_pr_inbox() {
3313
+ command -v gh >/dev/null 2>&1 || return 0
3314
+
3315
+ local slug; slug=$(_gh_repo_slug 2>/dev/null) || return 0
3316
+ local prs_json
3317
+ prs_json=$(gh -R "$slug" pr list --state open \
3318
+ --json number,headRefName,author,title \
3319
+ 2>/dev/null) || return 0
3320
+ [ -n "$prs_json" ] || return 0
3321
+ [ "$prs_json" = "[]" ] && return 0
3322
+
3323
+ local count; count=$(echo "$prs_json" | jq 'length' 2>/dev/null || echo 0)
3324
+ [ "${count:-0}" -gt 0 ] || return 0
3325
+
3326
+ local i=0
3327
+ while [ "$i" -lt "$count" ]; do
3328
+ local num head_ref
3329
+ num=$(echo "$prs_json" | jq -r ".[$i].number")
3330
+ head_ref=$(echo "$prs_json" | jq -r ".[$i].headRefName")
3331
+
3332
+ # Fetch CI + review state for this PR.
3333
+ local view_json
3334
+ view_json=$(gh -R "$slug" pr view "$num" \
3335
+ --json reviews,mergeStateStatus,statusCheckRollup \
3336
+ 2>/dev/null) || { i=$((i + 1)); continue; }
3337
+
3338
+ local human_review ci_state mergeable
3339
+ human_review=$(echo "$view_json" | jq -r '
3340
+ [.reviews[]? | select(.authorAssociation != "BOT" and .authorAssociation != "APP")]
3341
+ | last // {} | .state // ""' 2>/dev/null)
3342
+ mergeable=$(echo "$view_json" | jq -r '.mergeStateStatus // ""' 2>/dev/null)
3343
+ ci_state=$(echo "$view_json" | jq -r '
3344
+ if (.statusCheckRollup | length) == 0 then ""
3345
+ elif any(.statusCheckRollup[]?; .conclusion == "FAILURE") then "failure"
3346
+ elif all(.statusCheckRollup[]?; .conclusion == "SUCCESS" or .conclusion == "SKIPPED") then "success"
3347
+ else "pending" end' 2>/dev/null)
3348
+
3349
+ local verdict
3350
+ verdict=$(_loop_pr_classify "$head_ref" "$human_review" "$ci_state" "$mergeable")
3351
+
3352
+ case "$verdict" in
3353
+ loop_self|blocked_human_request_changes|blocked_human_approved)
3354
+ : # skip — explained by verdict; nothing to do this cycle
3355
+ ;;
3356
+ stale)
3357
+ _loop_pr_rebase_circuit "$num" || true
3358
+ # Actual rebase work delegated to _loop_pr_rebase_stale when present
3359
+ # (kept out of this cycle to avoid touching git remote in tests).
3360
+ command -v _loop_pr_rebase_stale >/dev/null 2>&1 \
3361
+ && _loop_pr_rebase_stale "$num" "$head_ref" || true
3362
+ ;;
3363
+ eligible)
3364
+ # Hand off to review (US-AUTO-035 supplies the actual decision).
3365
+ command -v _loop_pr_review_external >/dev/null 2>&1 \
3366
+ && _loop_pr_review_external "$num" || true
3367
+ ;;
3368
+ esac
3369
+
3370
+ i=$((i + 1))
3371
+ done
3372
+ return 0
3373
+ }
3374
+
3104
3375
  # US-CL-004: changelog 风格守门 Phase 1 — mechanical linter.
3105
3376
  #
3106
3377
  # _changelog_lint_bullet <bullet-text>
@@ -3156,7 +3427,7 @@ _changelog_style_anchors() {
3156
3427
  /^## v/ { ver++; if (ver > 3) exit; printing = 1; next }
3157
3428
  /^## / { printing = 0 }
3158
3429
  printing && /^- / { print }
3159
- ' "$changelog" | head -c 1500
3430
+ ' "$changelog" | head -c 1500 || true
3160
3431
  }
3161
3432
 
3162
3433
  # US-CL-005: changelog 风格守门 Phase 2 — self-audit gate.
@@ -34,6 +34,10 @@
34
34
  - Requests made in conversation are NOT AC — capture with `roll-idea` first.
35
35
  - Any new Story/Fix requires design doc + user confirmation before TCR starts.
36
36
  - Do not commit without user approval unless explicitly told to auto-commit.
37
+ - **Goal First**: Before any implementation, state verifiable success criteria.
38
+ Transform vague tasks: "add validation" → "write test for invalid input, then make it pass".
39
+ Multi-step work: list steps with verify checkpoints (step → verify: how to check).
40
+ Weak criteria ("make it work") require human clarification before starting.
37
41
  - **TCR**: Test -> Green = Commit / Red = Revert. No WIP commits.
38
42
  - Before implementing: confirm exact files, test strategy, and commit message
39
43
  draft with user. Do not write code until approved.
@@ -83,3 +87,9 @@ Confirm each phase clean before proceeding to the next.
83
87
  - `components/ui/` is shadcn-generated — never edit manually.
84
88
  - Tailwind utility classes only. No inline styles, no CSS modules.
85
89
  - Icons: Lucide React.
90
+
91
+ ## 8. Where to Look
92
+ - **Domain model**: `docs/domain/context-map.md` — Bounded Contexts and relationships
93
+ - **Story details**: `docs/features/` — AC, implementation specs, dependencies
94
+ - **Design decisions**: `docs/domain/` — DDD models, architecture records
95
+ - When `docs/domain/` or `docs/features/` don't exist yet, run `$roll-doc` to bootstrap.
@@ -25,3 +25,8 @@ src/
25
25
  - **TCR**: Mandatory.
26
26
  - **Security**: Input validation (zod), Rate limiting, Secrets rotation.
27
27
  - **Workspace**: `BACKLOG.md` + `docs/features/`.
28
+
29
+ ## 5. Where to Look
30
+ - **Domain model**: `docs/domain/context-map.md` — Bounded Contexts and relationships
31
+ - **Story details**: `docs/features/` — AC, implementation specs, dependencies
32
+ - **Design decisions**: `docs/domain/` — DDD models, architecture records
@@ -27,3 +27,8 @@ tests/
27
27
  - **TCR**: Mandatory.
28
28
  - **Distribution**: `bin` in `package.json`, test `npm i -g`.
29
29
  - **Workspace**: `BACKLOG.md` + `docs/features/`.
30
+
31
+ ## 5. Where to Look
32
+ - **Domain model**: `docs/domain/context-map.md` — Bounded Contexts and relationships
33
+ - **Story details**: `docs/features/` — AC, implementation specs, dependencies
34
+ - **Design decisions**: `docs/domain/` — DDD models, architecture records
@@ -24,3 +24,8 @@ src/
24
24
  - **TCR**: Mandatory.
25
25
  - **Testing**: Unit (hooks/logic) >80%, E2E (Playwright).
26
26
  - **Workspace**: `BACKLOG.md` + `docs/features/`.
27
+
28
+ ## 5. Where to Look
29
+ - **Domain model**: `docs/domain/context-map.md` — Bounded Contexts and relationships
30
+ - **Story details**: `docs/features/` — AC, implementation specs, dependencies
31
+ - **Design decisions**: `docs/domain/` — DDD models, architecture records
@@ -27,3 +27,8 @@ api/
27
27
  - **TCR**: Mandatory.
28
28
  - **Testing**: Unit >80%, E2E for critical flows.
29
29
  - **Workspace**: `BACKLOG.md` + `docs/features/`.
30
+
31
+ ## 5. Where to Look
32
+ - **Domain model**: `docs/domain/context-map.md` — Bounded Contexts and relationships
33
+ - **Story details**: `docs/features/` — AC, implementation specs, dependencies
34
+ - **Design decisions**: `docs/domain/` — DDD models, architecture records
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seanyao/roll",
3
- "version": "2026.514.2",
3
+ "version": "2026.514.3",
4
4
  "description": "Roll — Roll out features with AI agents",
5
5
  "scripts": {
6
6
  "test": "bash tests/run.sh"
@@ -491,6 +491,19 @@ OrderPricingService:
491
491
  Domain: Order Context > Order Aggregate > OrderItem Entity
492
492
  ```
493
493
 
494
+ ### AGENTS.md Where to Look — 指针维护
495
+
496
+ After completing any Domain Slice (User Story level), check if the project's `AGENTS.md` has a `## Where to Look` section with a `docs/domain/` pointer. If missing, append one line:
497
+
498
+ ```markdown
499
+ - **Domain model**: `docs/domain/context-map.md` — Bounded Contexts and relationships
500
+ ```
501
+
502
+ Rules:
503
+ - Idempotent: only append if the pointer line is not already present
504
+ - Do not modify any other content in AGENTS.md
505
+ - Skip silently if `docs/domain/` does not yet exist for this project
506
+
494
507
  ---
495
508
 
496
509
  ## Clarify Phase
@@ -505,10 +518,20 @@ Domain: Order Context > Order Aggregate > OrderItem Entity
505
518
  - Contains ambiguous terms like "优化一下", "改一下", "加个东西", "做个设计"
506
519
  - Could be interpreted in multiple ways
507
520
 
521
+ **Pre-Clarify: three-step product localization (always run first, silently)**
522
+
523
+ Before listing questions, internally determine:
524
+ 1. **产品端 (product end)**: web / mobile / API / CLI / other — which surface does this touch?
525
+ 2. **角色 (role)**: who initiates this action? (end user / admin / system / external)
526
+ 3. **业务域 (domain)**: which business domain does this belong to?
527
+
528
+ Already-localized dimensions become context prefix in the output, not open questions.
529
+
508
530
  **Output format:**
509
531
 
510
532
  ```
511
533
  🎯 Clarified Intent: {1-2 sentences}
534
+ 🗺 Context: {product end} · {role} · {domain} ← omit if all three are unknown
512
535
 
513
536
  📏 Complexity: {small|medium|large}
514
537
 
@@ -124,6 +124,29 @@ For each gap:
124
124
  | Module with no README | `<dir>/README.md` |
125
125
  | No `docs/domain/` entries | `docs/domain/context-map.md` |
126
126
  | No conventions doc | `docs/CONVENTIONS.md` |
127
+ | Missing `AGENTS.md` `## Where to Look` section | `AGENTS.md` (append or create) |
128
+
129
+ **AGENTS.md Where to Look bootstrap:**
130
+
131
+ When `AGENTS.md` has no `## Where to Look` section, generate and append one:
132
+
133
+ 1. Scan which doc directories actually exist: `docs/domain/`, `docs/features/`, `docs/practices/`, etc.
134
+ 2. Generate pointer lines **only for directories that actually exist** — never fabricate pointers to missing paths
135
+ 3. If `docs/domain/context-map.md` exists, read it to extract Bounded Context names for a one-line summary
136
+ 4. Append the section to the end of `AGENTS.md` with the standard draft header
137
+ 5. **Idempotency**: if `## Where to Look` already present, do not overwrite or duplicate — skip this gap
138
+
139
+ Draft output format:
140
+
141
+ ```markdown
142
+ > **Draft** — auto-generated by roll-doc on YYYY-MM-DD. Review before treating as authoritative.
143
+
144
+ ## Where to Look
145
+ - **Domain model**: `docs/domain/context-map.md` — Contexts: {list from context-map, or "see file"}
146
+ - **Story details**: `docs/features/` — AC, implementation specs, dependencies
147
+ ```
148
+
149
+ Only include lines for directories that already exist in the project.
127
150
 
128
151
  **Every generated file starts with this exact header line:**
129
152
 
@@ -84,6 +84,33 @@ exact stuck-red state FIX-026 traces to).
84
84
  - `gh` missing or repo unparseable → graceful skip (`_loop_precheck_ci`
85
85
  returns 0); the post-build `_loop_enforce_ci` remains the strict gate.
86
86
 
87
+ ### Step 1.6 — PR Inbox (US-AUTO-034)
88
+
89
+ Before scanning BACKLOG, process open PRs first. PRs are also units of work:
90
+ external contributors and human teammates expect their PRs to be reviewed and
91
+ moved forward, not starved while loop opens new fronts.
92
+
93
+ Call `_loop_pr_inbox` after the pre-run CI check passes. It walks
94
+ `gh pr list --state open` and routes each PR by classification:
95
+
96
+ | Classification | Action |
97
+ |---|---|
98
+ | `loop_self` (head ref starts with `loop/`) | Skip — let GitHub auto-merge handle it; never AI-review your own commit |
99
+ | `blocked_human_request_changes` | Skip — last human review requested changes; wait for the author to push fixes |
100
+ | `blocked_human_approved` | Skip — let GitHub auto-merge after CI is green |
101
+ | `stale` (CI failed or branch behind/conflicting) | Try `_loop_pr_rebase_stale` after the circuit breaker allows it |
102
+ | `eligible` (clean external PR, no blocking review) | Invoke `_loop_pr_review_external` — the actual decision is provided by US-AUTO-035's GitHub Action |
103
+
104
+ **Rebase circuit breaker** — `_loop_pr_rebase_circuit <pr>` records each rebase
105
+ attempt under `pr_state.<PR>.attempts_at` in `state.yaml`, pruning entries older
106
+ than 24 h. Once ≥3 attempts land within 24 h, further rebases are blocked and an
107
+ ALERT is written (typical cause: a broken workflow file makes CI never run,
108
+ which would otherwise drive infinite rebase loops).
109
+
110
+ **Lenient on infrastructure** — `gh` missing, repo unparseable, or any
111
+ `gh` API failure → `_loop_pr_inbox` returns 0 and the loop falls through to
112
+ Step 2 (BACKLOG scan). Same posture as the pre-run CI check.
113
+
87
114
  ### Step 2 — Scan BACKLOG
88
115
 
89
116
  Read `BACKLOG.md`. Collect all rows where Status = `📋 Todo`, in order: