@seanyao/roll 2026.529.5 → 2026.601.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.
Files changed (59) hide show
  1. package/CHANGELOG.md +57 -25
  2. package/README.md +10 -7
  3. package/bin/roll +3952 -317
  4. package/conventions/config.yaml +7 -0
  5. package/lib/__pycache__/github_sync.cpython-314.pyc +0 -0
  6. package/lib/__pycache__/loop_result_eval.cpython-314.pyc +0 -0
  7. package/lib/__pycache__/model_prices.cpython-314.pyc +0 -0
  8. package/lib/__pycache__/roll-home.cpython-314.pyc +0 -0
  9. package/lib/__pycache__/roll-loop-status.cpython-314.pyc +0 -0
  10. package/lib/__pycache__/roll_git.cpython-314.pyc +0 -0
  11. package/lib/__pycache__/slides-render.cpython-314.pyc +0 -0
  12. package/lib/agent_usage/__init__.py +4 -0
  13. package/lib/agent_usage/__pycache__/__init__.cpython-314.pyc +0 -0
  14. package/lib/agent_usage/__pycache__/gemini.cpython-314.pyc +0 -0
  15. package/lib/agent_usage/__pycache__/kimi.cpython-314.pyc +0 -0
  16. package/lib/agent_usage/__pycache__/openai.cpython-314.pyc +0 -0
  17. package/lib/agent_usage/__pycache__/qwen.cpython-314.pyc +0 -0
  18. package/lib/agent_usage/gemini.py +127 -0
  19. package/lib/agent_usage/kimi.py +127 -0
  20. package/lib/agent_usage/openai.py +126 -0
  21. package/lib/agent_usage/qwen.py +128 -0
  22. package/lib/context_feed_budget.sh +194 -0
  23. package/lib/github_sync.py +876 -0
  24. package/lib/i18n/agent.sh +54 -0
  25. package/lib/i18n/init.sh +22 -0
  26. package/lib/i18n/peer.sh +7 -0
  27. package/lib/i18n/peer_help.sh +4 -0
  28. package/lib/i18n/skills_catalog.sh +30 -0
  29. package/lib/loop-exit-summary.py +393 -0
  30. package/lib/loop-fmt.py +93 -75
  31. package/lib/loop_pick_agent.py +241 -170
  32. package/lib/loop_result_eval.py +469 -0
  33. package/lib/model_prices.py +0 -10
  34. package/lib/roll-home.py +1 -28
  35. package/lib/roll-loop-status.py +330 -40
  36. package/lib/roll-onboard-render.py +378 -0
  37. package/lib/roll-peer.py +1 -1
  38. package/lib/roll-plan-validate.py +165 -0
  39. package/lib/roll_git.py +41 -0
  40. package/lib/slides/components/README.md +8 -2
  41. package/lib/slides/templates/introduction-v3.html +1 -6
  42. package/lib/slides-render.py +305 -15
  43. package/lib/slides-validate.py +195 -7
  44. package/package.json +1 -1
  45. package/skills/roll-.changelog/SKILL.md +67 -56
  46. package/skills/roll-brief/SKILL.md +1 -1
  47. package/skills/roll-build/SKILL.md +14 -12
  48. package/skills/roll-deck/SKILL.md +152 -0
  49. package/skills/roll-design/SKILL.md +13 -6
  50. package/skills/roll-doc/SKILL.md +269 -6
  51. package/skills/roll-fix/SKILL.md +15 -9
  52. package/skills/roll-loop/SKILL.md +9 -7
  53. package/skills/roll-notes/SKILL.md +1 -1
  54. package/skills/roll-onboard/SKILL.md +85 -0
  55. package/skills/roll-peer/SKILL.md +6 -5
  56. package/lib/agent_routes_lint.py +0 -203
  57. package/skills/roll-research/SKILL.md +0 -316
  58. package/skills/roll-research/references/schema.json +0 -166
  59. package/skills/roll-research/scripts/md_to_pdf.py +0 -289
@@ -95,12 +95,13 @@ Walk every directory (applying Phase 1 exclusions):
95
95
 
96
96
  1. Count non-hidden, non-`.md` source files directly in the directory
97
97
  2. If count ≥ 3 AND no `README.md` in that directory AND no `docs/INDEX.md` entry links to it → **module gap**
98
+ 3. **High fan-in gap**: a directory imported by **≥ 5 other source files** is a gap targeting `<dir>/README.md`, **even if it has < 3 source files** — critical low-volume modules (shared utils, single-file core) must not be skipped by the file-count threshold. The existing count ≥ 3 rule (rule 2) continues to apply independently; high fan-in only widens what qualifies, it never overrides rule 2.
98
99
 
99
100
  **Special gaps (checked once per project):**
100
101
  - No `.roll/domain/` directory or empty → gap: `.roll/domain/context-map.md`
101
102
  - No `CONVENTIONS.md` or `docs/CONVENTIONS.md` exists → gap: `docs/CONVENTIONS.md`
102
103
 
103
- **Threshold**: directories with ≥ 3 source files (default). Tune by editing the skill.
104
+ **Threshold**: a directory qualifies for a module README gap when **file count ≥ 3 OR imported by ≥ 5 other source files** (default). The fan-in count comes from the Phase 3b symbol table `imports` edges; exclude test files (`*.test.*`, `*.spec.*`, `tests/`) when counting referencing files. An existing `<dir>/README.md` is never overwritten unless `--force`. Tune by editing the skill.
104
105
 
105
106
  Record all gaps — they become Phase 3 input.
106
107
 
@@ -177,9 +178,12 @@ Pure documentation-only projects (no source code gaps, no code characteristics)
177
178
 
178
179
  ### Step 1 — Build Symbol Table
179
180
 
180
- Read every source file **in full** (no truncation). The existing Phase 3 "up to 20 source files"
181
- limit does not apply Phase 3b builds a complete project symbol table for downstream topic
182
- detection to reason across files without missing logic.
181
+ Read each source file through the context-feed budget (US-CTX-001), not by hard-stuffing whole
182
+ files unconditionally. The existing Phase 3 "up to 20 source files" count limit does not apply —
183
+ Phase 3b aims for a complete project symbol table — but per-file material still goes through the
184
+ feed budget: files within budget are read in full, and over-budget files are summarized/chunked
185
+ with an explicit notice (never silently truncated) so the inner agent's context window is not
186
+ blown. This replaces the previous "read every source file in full, no truncation" unbounded path.
183
187
 
184
188
  **Exclusion directories** (same as Phase 1):
185
189
 
@@ -272,6 +276,236 @@ If no chain meets the ≥ 3 directory threshold, skip generation entirely (no em
272
276
 
273
277
  **Idempotency:** skip (do not overwrite) if `docs/data-flows.md` already exists, unless `--force`.
274
278
 
279
+ #### State Machine
280
+
281
+ **Detection rule:** enums whose name matches `*State` or `*Status` (case-insensitive)
282
+ AND that are imported/referenced by **≥ 2 distinct source files**. Only count
283
+ imports from source files — exclude test files (`*.test.*`, `*.spec.*`, `tests/`)
284
+ and declaration files (`*.d.ts`).
285
+
286
+ **Threshold:** if no enum meets the ≥ 2 cross-file reference threshold, skip
287
+ generation entirely (no empty `docs/state-machines.md`).
288
+
289
+ **Output document structure** (`docs/state-machines.md`):
290
+
291
+ ```markdown
292
+ > **Draft** — auto-generated by roll-doc on YYYY-MM-DD. Review before treating as authoritative.
293
+
294
+ # State Machines
295
+
296
+ ## State: `{EnumName}`
297
+
298
+ **Defined in:** `{file}:{line}`
299
+
300
+ ### States
301
+ {list of all enum values}
302
+
303
+ ### Referenced By
304
+
305
+ | File:Line | Context |
306
+ |-----------|---------|
307
+ | `path/to/file:12` | `function processOrder(status: OrderState)` |
308
+ | `path/to/file:34` | `if (status === OrderState.Pending)` |
309
+
310
+ ### Inferred Transitions
311
+
312
+ | From | To | Evidence |
313
+ |------|----|----------|
314
+ | `Pending` | `Processing` | `order.ts:45` — `if (status === Pending) { status = Processing }` |
315
+ | `Processing` | `Shipped` | `handler.ts:78` — assignment after `ship()` call |
316
+ ```
317
+
318
+ **Transition inference:** scan the referencing files for patterns where an enum
319
+ value is checked (`if (status === X)`, `case X:`) and the status variable is
320
+ reassigned to another enum value in the same block. Record each distinct
321
+ `{from, to, evidence file:line}` pair. Only include transitions backed by
322
+ explicit code — do not fabricate implied transitions.
323
+
324
+ **Output rules:**
325
+ - One top-level `## State:` section per qualifying enum, sorted alphabetically.
326
+ - Each enum section lists all defined values under `### States`.
327
+ - `file:line` annotations must come from actual symbol table records — do not fabricate.
328
+ - Existing `docs/state-machines.md` → skip unless `--force`.
329
+ - No qualifying enum → skip generation entirely (no empty document).
330
+
331
+ #### External Integrations
332
+
333
+ **Detection rule:** scan `external_urls` from the symbol table —
334
+ `fetch(...)` / `axios` / `http.get` (and `http.*`) calls, constants shaped like
335
+ `API_ENDPOINT` / `*_URL` / `*_HOST`, and hardcoded `https?://` strings. Exclude
336
+ matches inside comments and test fixtures (`*.test.*`, `*.spec.*`, `tests/`,
337
+ `fixtures/`).
338
+
339
+ **Threshold:** if no external integration is detected, skip generation entirely
340
+ (no empty `docs/integrations.md`).
341
+
342
+ **Output document structure** (`docs/integrations.md`):
343
+
344
+ ```markdown
345
+ > **Draft** — auto-generated by roll-doc on YYYY-MM-DD. Review before treating as authoritative.
346
+
347
+ # External Integrations
348
+
349
+ ## Integration: `{endpoint URL}`
350
+
351
+ ### Call Sites
352
+
353
+ | File:Line | Timeout | Error Handling / Fallback |
354
+ |-----------|---------|---------------------------|
355
+ | `path/to/file:12` | `timeout: 5000` | `.catch` retry fallback |
356
+ | `path/to/file:48` | — | try/catch |
357
+ ```
358
+
359
+ **Per-integration fields:**
360
+ - **endpoint URL** — the external URL / endpoint reached.
361
+ - **call file:line** — every source location calling this endpoint.
362
+ - **timeout config** — the value if the calling code carries a `timeout: N` field,
363
+ otherwise `—`.
364
+ - **error handling / fallback** — recorded when the calling code has a `.catch`
365
+ handler or a `try` / `catch` block around the call, otherwise `—`.
366
+
367
+ **Output rules:**
368
+ - The same endpoint reached from multiple sites is merged into **one** integration
369
+ section that lists **all** its call sites — never one section per call.
370
+ - One top-level `## Integration:` section per distinct endpoint, sorted alphabetically.
371
+ - `file:line` annotations must come from actual symbol table records — do not fabricate.
372
+ - Existing `docs/integrations.md` → skip unless `--force`.
373
+ - No external integration detected → skip generation entirely (no empty document).
374
+
375
+ #### Deployment Pipeline
376
+
377
+ **Detection rule:** the project has at least one CI configuration file —
378
+ `.github/workflows/*.yml` (or `*.yaml`) / `.gitlab-ci.yml` / `circle.yml` /
379
+ `.circleci/config.yml` / `Jenkinsfile` — AND a deploy URL pattern appears in the
380
+ code or CI config (vercel / netlify / cloudflare / firebase, or hostnames ending
381
+ in `*.app` / `*.dev`). Pull deploy URLs from the `configs` and `external_urls`
382
+ fields of the symbol table.
383
+
384
+ **Threshold:** if no CI configuration file is detected, skip generation entirely
385
+ (no empty `docs/deployment.md`).
386
+
387
+ **Output document structure** (`docs/deployment.md`):
388
+
389
+ ```markdown
390
+ > **Draft** — auto-generated by roll-doc on YYYY-MM-DD. Review before treating as authoritative.
391
+
392
+ # Deployment
393
+
394
+ ## Pipeline: `{ci config file}`
395
+
396
+ **CI Platform:** GitHub Actions
397
+ **Trigger events:** push (main), pull_request, release
398
+
399
+ ### Key Jobs
400
+
401
+ | Job | File:Line | Purpose |
402
+ |-----|-----------|---------|
403
+ | `test` | `.github/workflows/deploy.yml:18` | run test suite |
404
+ | `build` | `.github/workflows/deploy.yml:25` | compile artifacts |
405
+ | `deploy` | `.github/workflows/deploy.yml:33` | deploy to Vercel |
406
+
407
+ ### Deploy Targets
408
+
409
+ | URL | File:Line |
410
+ |-----|-----------|
411
+ | `https://my-app.vercel.app` | `.github/workflows/deploy.yml:14` |
412
+
413
+ ### Environment Variables
414
+
415
+ | Name | File:Line |
416
+ |------|-----------|
417
+ | `NODE_ENV` | `.github/workflows/deploy.yml:11` |
418
+ | `VERCEL_TOKEN` | `.github/workflows/deploy.yml:12` |
419
+ ```
420
+
421
+ **Per-pipeline fields:**
422
+ - **CI platform** — inferred from which config file is present (GitHub Actions /
423
+ GitLab CI / CircleCI / Jenkins).
424
+ - **trigger events** — the events that start the pipeline (`push` / `pull_request` /
425
+ `tag` / `release`), read from the CI config.
426
+ - **key jobs** — each job / stage name with its `file:line` location.
427
+ - **deploy target URL** — the deploy URL(s) matched by the detection rule.
428
+ - **environment variable names** — every env var name referenced in the CI config,
429
+ listed **without values** (never emit secret values, only names).
430
+
431
+ **Output rules:**
432
+ - One top-level `## Pipeline:` section per CI configuration file, sorted alphabetically.
433
+ - `file:line` annotations must come from actual symbol table / CI config records — do not fabricate.
434
+ - Existing `docs/deployment.md` → skip unless `--force`.
435
+ - No CI configuration detected → skip generation entirely (no empty document).
436
+
437
+ #### Agent Entrypoint (AGENTS.md)
438
+
439
+ This topic handles the **「文件不存在 → 创建」** case: when a project has no agent-facing
440
+ convention file at all, generate a complete baseline `AGENTS.md`. It is complementary to the
441
+ Phase 2 **"AGENTS.md Where to Look bootstrap"** rule, which handles the **「文件存在 → 补章节」**
442
+ case (append a missing `## Where to Look` section to an existing `AGENTS.md`). The two rules
443
+ never overlap — this one only fires when no `AGENTS.md` exists.
444
+
445
+ **Detection rule:** project root has **no** `AGENTS.md` AND the source root (`src/`, or the
446
+ equivalent — `lib/`, `app/`, `pkg/`, etc.) contains **≥ 3 subdirectories**. If `AGENTS.md`
447
+ already exists, skip entirely (do not overwrite unless `--force`). If the source root has
448
+ fewer than 3 subdirectories, skip — the project is too small to warrant a baseline doc.
449
+
450
+ **Output document structure** (`AGENTS.md`) — at minimum three required sections:
451
+
452
+ ```markdown
453
+ > **Draft** — auto-generated by roll-doc on YYYY-MM-DD. Review before treating as authoritative.
454
+
455
+ # {Project Name}
456
+
457
+ {One-sentence positioning: what this project is, inferred from README / package metadata.}
458
+
459
+ ## Where to Look
460
+
461
+ - **Domain model**: `.roll/domain/context-map.md` — Contexts: {list, or "see file"}
462
+ - **Story details**: `.roll/features/` — AC, implementation specs, dependencies
463
+ - **Doc index**: `docs/INDEX.md` — generated documentation map
464
+
465
+ ## Source Layout
466
+
467
+ | Directory | Purpose |
468
+ |-----------|---------|
469
+ | `src/parser/` | tokenize and parse input (≤ 2 lines each) |
470
+ | `src/codegen/` | emit output artifacts |
471
+ | `src/cli/` | command-line entry and option wiring |
472
+ ```
473
+
474
+ **Per-section rules:**
475
+ - **Project positioning** — a single sentence describing what the project does, inferred from
476
+ the README / package metadata; never fabricate beyond what the source supports.
477
+ - **`## Where to Look`** — pointer lines **only for directories that actually exist**
478
+ (`.roll/domain/`, `.roll/features/`, `docs/INDEX.md`, etc.); never fabricate pointers to
479
+ missing paths. If `.roll/domain/context-map.md` exists, read it for Bounded Context names.
480
+ - **Source Layout** — one row per key source subdirectory, each description **≤ 2 lines**,
481
+ inferred from the files actually read in the symbol table.
482
+
483
+ **Output rules:**
484
+ - Existing `AGENTS.md` → skip unless `--force` (never overwrite a human-authored file).
485
+ - Source root with < 3 subdirectories → skip generation entirely (no empty `AGENTS.md`).
486
+ - All three sections must be present in the generated file.
487
+
488
+ #### High Fan-in Directory (README)
489
+
490
+ This topic complements the Phase 2 file-count threshold. A directory may hold only one or two
491
+ source files yet be imported across the whole codebase (shared utils, a single-file core
492
+ module). The file-count rule (≥ 3 files) alone skips such hot paths, so high fan-in widens the
493
+ threshold to **「文件数 ≥ 3 OR 被 ≥ 5 个文件引用」**.
494
+
495
+ **Detection rule:** using the symbol table's `imports` edges, count the **distinct source files
496
+ that import any file in the directory**. If that count is **≥ 5**, the directory qualifies for a
497
+ `<dir>/README.md` gap, **even when it contains < 3 source files**. Only count referencing files
498
+ that are source files — exclude test files (`*.test.*`, `*.spec.*`, `tests/`) and files inside
499
+ the directory itself. This rule operates **in addition to** the Phase 2 count ≥ 3 rule, never
500
+ replacing it: directories meeting either condition get a README.
501
+
502
+ **Output rules:**
503
+ - Target file is `<dir>/README.md`, generated with the same Module README content as Phase 3
504
+ (purpose, key files, dependencies).
505
+ - Existing `<dir>/README.md` → skip unless `--force` (never overwrite).
506
+ - Directory imported by < 5 source files **and** holding < 3 source files → skip (no gap).
507
+ - A directory already qualifying under the count ≥ 3 rule is not double-counted — one README.
508
+
275
509
  ### Step 3 — Source Annotations
276
510
 
277
511
  Every topic document generated in Step 2 must cite `file:line` for each claim (function call,
@@ -279,6 +513,16 @@ endpoint URL, state transition, CI job, import path). Annotations must come from
279
513
  symbol table records — do not fabricate line numbers. Follows the same "Do not fabricate"
280
514
  rule as Phase 3.
281
515
 
516
+ **Mandatory `file:line` column** — each topic document's source-reference tables MUST carry a
517
+ dedicated `file:line` column so the reader can jump straight to the code:
518
+ - 「涉及文件」/ Files Involved table → one `file:line` per row.
519
+ - 「引用文件」/ Referenced By table → one `file:line` per referencing site.
520
+ - 「调用链」/ Complete Call Chain table → one `file:line` per hop.
521
+
522
+ Do **not** fabricate a `file:line` value: every annotation MUST come from an actual symbol
523
+ table record (the same "Do not fabricate" rule as above). If the symbol table has no line for
524
+ a claim, omit the row rather than invent a location.
525
+
282
526
  ---
283
527
 
284
528
  ## Phase 4 — Report
@@ -302,15 +546,34 @@ Phase 3 — Fill
302
546
 
303
547
  Phase 3b — Deep Read
304
548
  Symbol table: exports(N) imports(N) enums(N) external_urls(N) configs(N)
305
- N topic documents generated: [list of paths and types]
549
+ N topic documents generated:
550
+ - docs/dataflow.md (data-flow) source entries: N
551
+ - docs/state-machines.md (state-machine) source entries: N
552
+ - docs/integrations.md (external-integration) source entries: N
306
553
  N topics skipped (no matches or already exist; use --force to regenerate)
307
- (if no topics generated: "no subject-level drafts generated")
308
554
 
309
555
  📋 Review priority (largest / most active modules first):
310
556
  1. src/commands/README.md — 8 source files
311
557
  2. docs/CONVENTIONS.md — 6 patterns detected
312
558
  ```
313
559
 
560
+ Each generated topic document is listed with its **path**, **type**, and **来源条目数 /
561
+ source entries** (how many symbol-table records back the document). When Phase 3b produced no
562
+ subject-level documents, print exactly one line and do **not** error:
563
+
564
+ ```
565
+ Phase 3b: no subject-level drafts generated
566
+ ```
567
+
568
+ If `--dry-run`, the `Phase 3b — Deep Read` section is shown the same way but every generated
569
+ line is tagged `(plan)` — matching Phase 3's dry-run convention (nothing is written):
570
+
571
+ ```
572
+ Phase 3b — Deep Read (plan)
573
+ N topic documents would be generated:
574
+ - docs/dataflow.md (data-flow) source entries: N (plan)
575
+ ```
576
+
314
577
  If no gaps were found:
315
578
 
316
579
  ```
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  name: roll-fix
3
3
  license: MIT
4
+ allowed-tools: "Read, Edit, Write, Glob, Grep, Bash, Skill"
4
5
  description: Execute bugfix/hotfix from backlog. Reads FIX/BUG from .roll/backlog.md, delivers via TCR workflow. Lighter than roll-build, focused on single-issue fixes.
5
6
  ---
6
7
 
@@ -89,24 +90,29 @@ Before creating any file or directory:
89
90
 
90
91
  ### 0. Pre-flight self-check (US-AGENT-007)
91
92
 
92
- Before locking the issue, read the FIX's Agent profile (est_min / risk_zone / chain_depth) from the linked feature md and decide whether this cycle should attempt the fix:
93
+ Before locking the issue, read the FIX's Agent profile (est_min / chain_depth) from the linked feature md and decide whether this cycle should attempt the fix:
93
94
 
94
95
  ```
95
96
  inputs:
96
97
  fix.est_min (from **Agent profile:** block on the FIX row's feature md)
97
- fix.risk_zone (low / medium / high)
98
98
  fix.chain_depth (0 unless already a downgrade product)
99
- agent.max_est_min (from .roll/agent-routes.yaml for the current agent)
100
- history.prefer_threshold + history.hit_rate (FIX history for this agent)
101
99
 
102
100
  verdict:
103
- too_big when ANY:
104
- 1. fix.est_min > agent.max_est_min
105
- 2. fix.risk_zone not in agent.risk
106
- 3. history.hit_rate < prefer_threshold AND fix.chain_depth == 0
101
+ too_big when:
102
+ fix.est_min > 20 (lands in the `hard` complexity tier)
107
103
  ok otherwise
108
104
  ```
109
105
 
106
+ Routing is a single axis now (US-AGENT-022): `est_min` maps to one of three
107
+ complexity tiers — `easy` (≤8), `default` (8<x≤20), `hard` (>20) — and each
108
+ tier binds to a locally-installed agent slot in `.roll/agents.yaml` (model =
109
+ the agent's own default; a `fallback` slot mechanically covers an unavailable
110
+ agent). The retired v1 model (three-dimension type/est/risk_zone matching
111
+ against `agent-routes.yaml`, soft history hit-rate preference, per-agent
112
+ `max_est_min`/`risk`) no longer applies. The pre-flight verdict here is just
113
+ the `hard`-tier boundary: a FIX estimated past it is a sanity signal that the
114
+ "single small fix" assumption may be wrong.
115
+
110
116
  Emit `verdict: ok` or `verdict: too_big` (with `reason:`) as the first cycle output line.
111
117
 
112
118
  - `ok` → continue with step 1 below normally
@@ -369,7 +375,7 @@ Change the Status of the corresponding row from `📋 Todo` or `🔨 In Progress
369
375
 
370
376
  ### 12. Update Changelog
371
377
 
372
- **Mandatory** — release notes depend on this step. Do not skip.
378
+ **Mandatory** — the release (GitHub Release body = this version's changelog section) depends on this step. Do not skip.
373
379
 
374
380
  ```bash
375
381
  $roll-.changelog
@@ -247,7 +247,7 @@ Together these mean: only one loop runs at a time per project (LOCK), and within
247
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
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
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 — do not re-pick a different one unless that story can no longer be found in BACKLOG (e.g. status changed concurrently).
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
251
  >
252
252
  > Old single-agent fallback (`primary_agent` from `~/.roll/config.yaml`) still applies when:
253
253
  > - no story is pickable (empty Todo / all manual-only)
@@ -344,12 +344,14 @@ After each item completes:
344
344
 
345
345
  **Path B — heal exhausted (≥`ROLL_LOOP_HEAL_MAX`, default 2) or disabled (`ROLL_LOOP_NO_HEAL=1`) (exit 1):**
346
346
 
347
- 1. Do NOT force ✅ Done here. CI red means the PR will not merge, and
348
- FIX-140's merge gate (in `bin/roll`, after `publish_wait_merge`) is the
349
- single authority on final status: if the PR actually merges, the story
350
- stays Done; if it does not merge, the gate reverts it ✅ Done → 📋 Todo
351
- so BACKLOG never shows a false Done for code that isn't on main. (FIX-141
352
- then stops the next cycle re-opening a duplicate PR while this one is open.)
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.
353
355
  2. Write ALERT to `~/.shared/roll/loop/ALERT-<slug>.md` with:
354
356
  - story ID, time, commit SHA
355
357
  - heal attempts made (read `heal_count:` from `state-<slug>.yaml`)
@@ -24,7 +24,7 @@ $roll-notes 今天的 code review 给了很好的反馈
24
24
  ## When Not to Use
25
25
 
26
26
  - Capturing a bug or feature idea into BACKLOG (use `$roll-idea`)
27
- - Generating user-facing release notes (use `$roll-.changelog`)
27
+ - Generating the user-facing changelog (use `$roll-.changelog`)
28
28
  - Writing design documents or AC (use `$roll-design`)
29
29
 
30
30
  ## Behavior
@@ -55,6 +55,58 @@ Walk the repo. Identify:
55
55
  - **domains**: top business/technical domains (e.g., "auth", "billing", "search")
56
56
  - **key_modules**: top 3-5 modules that hold most of the logic
57
57
 
58
+ ### Step 1b — Phase 2 analysis: business model, tech, tests (US-ONBOARD-016)
59
+
60
+ A single onboard now produces three structured analysis sections in the plan
61
+ (`domain_model`, `tech_analysis`, `test_assessment`). Build them here so Step 4
62
+ can serialise them.
63
+
64
+ **`domain_model`** — from the code you read, identify the bounded contexts. For
65
+ each: a `name`, its `aggregates` (the entities that own consistency), and its
66
+ `ubiquitous_language` (the domain terms the code/docs actually use). If you
67
+ genuinely cannot infer contexts from the code, emit an empty
68
+ `bounded_contexts: []` — do NOT invent contexts that aren't in the code.
69
+
70
+ **`tech_analysis`** — `stack` (languages/frameworks evidenced by manifests),
71
+ `dependencies` (from `package.json` / `pyproject.toml` / `go.mod` / `Cargo.toml`
72
+ etc.), `architecture_notes` (observed structure, not aspirational), and `risks`
73
+ (each a mapping with a `description`; optionally `severity: LOW|MEDIUM|HIGH` and
74
+ an `evidence: detected|inferred` tag).
75
+
76
+ **`test_assessment`** — this section is under a **hard anti-hallucination
77
+ constraint** (next sub-step). Do NOT write it from intuition.
78
+
79
+ #### The verifiable test scan (ANTI-HALLUCINATION HARD CONSTRAINT)
80
+
81
+ Every `test_assessment` claim must be backed by a real filesystem scan you run
82
+ here — never by "what a project like this usually needs". Run these probes and
83
+ record the raw counts/paths:
84
+
85
+ 1. **Count test files** by the conventional patterns:
86
+ - `*.test.*` / `*.spec.*` (JS/TS), `*_test.go` (Go), `test_*.py` / `*_test.py` (Python), `*_spec.rb` (Ruby), `*Test.java` (Java)
87
+ - e.g. `git ls-files | grep -cE '\.(test|spec)\.[jt]sx?$'` and similar per pattern
88
+ 2. **Probe for runner configs**: `jest.config.*`, `pytest.ini` (or `[tool.pytest]` in `pyproject.toml`), `.mocharc.*`, `vitest.config.*`, `karma.conf.*`, `phpunit.xml`, `go test` (implied by `*_test.go`)
89
+ 3. **Probe for a `coverage/` directory** (and `.coverage` / `coverage.xml` artifacts)
90
+
91
+ Then turn the raw findings into claims, each a **mapping** carrying a `claim`
92
+ string plus an `evidence` tag:
93
+
94
+ - `evidence: detected` — the scan directly found it (e.g. "42 `*.test.ts` files detected", "vitest.config.ts present", "coverage/ directory present").
95
+ - `evidence: inferred` — a judgement you derived FROM the detected facts (e.g. "unit layer present but no E2E config — integration coverage likely thin"). The inference must trace back to something the scan detected.
96
+
97
+ **"none detected" rule**: when a probe finds nothing, you MUST say so explicitly
98
+ with a tagged claim — `{claim: "none detected", evidence: detected}` (a scan that
99
+ ran and returned zero is a genuine `detected` finding). You must NOT silently
100
+ omit the dimension, and you must NOT invent generic filler like "needs more E2E
101
+ tests" / "consider adding integration tests" with no detected basis. The plan
102
+ validator (`lib/roll-plan-validate.py`) rejects any untagged free-text claim, so
103
+ filler will fail `roll init --apply`.
104
+
105
+ Map the findings into the three buckets:
106
+ - `current_layers`: what test layers actually exist (each tagged `detected`)
107
+ - `gaps`: dimensions where the scan found nothing (`none detected`, tagged `detected`) or thin coverage you can justify (`inferred`)
108
+ - `recommended_actions`: actions that trace to a detected gap (tag `inferred`); if nothing is missing, this bucket may be `[]`
109
+
58
110
  ### Step 2 — Get gap report
59
111
 
60
112
  Run `roll-doc --dry-run` (READ-ONLY mode). This reports:
@@ -121,6 +173,39 @@ agent_routes_template: default # user's Q10 — agent routing pre
121
173
  # minimal = single agent (pi), no history
122
174
  # heavy = pi/deepseek/claude/kimi + larger window
123
175
  # skip = don't seed .roll/agent-routes.yaml
176
+
177
+ # ── US-ONBOARD-016: Phase 2 analysis sections (optional, but emit all three) ──
178
+ # All three are validated only when present, so they are backward-compatible,
179
+ # but a normal onboard should produce them from Step 1b.
180
+
181
+ domain_model:
182
+ bounded_contexts: # [] if none can be inferred — never invent
183
+ - name: auth
184
+ aggregates: [User, Session]
185
+ ubiquitous_language: [login, token, refresh]
186
+
187
+ tech_analysis:
188
+ stack: [bash, python3] # evidenced by manifests
189
+ dependencies: [pyyaml] # from package.json / pyproject / go.mod / ...
190
+ architecture_notes: ["single-binary CLI + python helpers in lib/"]
191
+ risks:
192
+ - description: "no automated test run on macOS bash 3.2"
193
+ severity: HIGH # optional: LOW | MEDIUM | HIGH
194
+ evidence: detected # optional: detected | inferred
195
+
196
+ # test_assessment — ANTI-HALLUCINATION: every claim is a mapping with an
197
+ # `evidence` tag (detected | inferred). A zero-result scan is `none detected`
198
+ # tagged `detected`. Untagged free-text is REJECTED by the validator.
199
+ test_assessment:
200
+ current_layers:
201
+ - claim: "112 bats files detected under tests/" # evidence: a real scan count
202
+ evidence: detected
203
+ gaps:
204
+ - claim: "none detected" # e.g. no coverage/ dir found
205
+ evidence: detected
206
+ recommended_actions: # [] if nothing is missing
207
+ - claim: "add a macOS CI runner (inferred from launchd-only test skips)"
208
+ evidence: inferred # an inference traceable to a detected fact
124
209
  ```
125
210
 
126
211
  Then tell the user:
@@ -180,7 +180,7 @@ Running
180
180
 
181
181
  ### Per Peer Pair (e.g., kimi→claude)
182
182
 
183
- Stored in `~/.shared/roll/peer/state.yaml`:
183
+ Stored in `~/.roll/.peer-state/` (flat key files per pair):
184
184
 
185
185
  ```yaml
186
186
  kimi→claude:
@@ -269,7 +269,7 @@ When peer review is manually triggered by a human (via `/peer`, "叫上 peer", e
269
269
  - Relay the peer's response **verbatim** before adding your own analysis.
270
270
  - After the peer's reply, the reviewer's own analysis block must explicitly state whether the peer's root cause and fix direction match the reviewer's own (independent) conclusion — that comparison is what determines the next round's action.
271
271
  - If a peer call fails or times out, report it immediately inline and either retry or ESCALATE.
272
- - Negotiation log is still written to `~/.shared/roll/peer/logs/` as usual.
272
+ - Negotiation log is written to `<project>/.roll/peer/logs/` as usual.
273
273
 
274
274
  **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.
275
275
 
@@ -300,8 +300,9 @@ When Attacker and Defender reach a stalemate (both tests pass but interpretation
300
300
 
301
301
  ## Output Artifacts
302
302
 
303
- - **Negotiation log**: `~/.shared/roll/peer/logs/<timestamp>_<from>_<to>.md`
304
- - **State file**: `~/.shared/roll/peer/state.yaml`
303
+ - **Negotiation log**: `<project>/.roll/peer/logs/<timestamp>_<from>_<to>.md`
304
+ - **Structured record**: `<project>/.roll/peer/runs.jsonl`
305
+ - **State file**: `~/.roll/.peer-state/`
305
306
  - **Decision record**: If AGREE, append summary to `docs/decisions/` or `.roll/backlog.md` (optional)
306
307
 
307
308
  ## Configuration
@@ -327,7 +328,7 @@ peer:
327
328
 
328
329
  ## Limitations
329
330
 
330
- 1. **Reverse link reliability**: Direct CLI calls are preferred. Reliability varies by tool — see Peer Invocation Reference table. If a peer fails consistently, the adaptive streak tracker marks it `abandoned` and falls back to the next candidate. File mailbox (`~/.shared/roll/peer/mailbox/`) is the last-resort fallback.
331
+ 1. **Reverse link reliability**: Direct CLI calls are preferred. Reliability varies by tool — see Peer Invocation Reference table. If a peer fails consistently, the adaptive streak tracker marks it `abandoned` and falls back to the next candidate. File mailbox (`<project>/.roll/peer/mailbox/`) is the last-resort fallback.
331
332
  - `deepseek serve --http` is the most reliable option when available — prefer it over direct `deepseek` CLI invocation.
332
333
  - `codex exec` has known TTY/Ink issues in non-interactive environments; treat as low-priority fallback.
333
334
  2. **Cost**: Every peer review consumes tokens on both sides. Only trigger for tasks where the cost of a wrong decision exceeds the cost of peer review. DeepSeek is the most cost-effective peer for general use.