@seanyao/roll 4.630.2 → 4.702.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 (108) hide show
  1. package/CHANGELOG.md +64 -0
  2. package/README.md +65 -56
  3. package/conventions/global/AGENTS.md +8 -7
  4. package/dist/roll.mjs +12909 -8532
  5. package/docs/INDEX.md +32 -0
  6. package/docs/architecture.md +444 -0
  7. package/docs/difftest-freeze-paradigm.md +113 -0
  8. package/docs/live-console.md +203 -0
  9. package/docs/manifesto.md +65 -0
  10. package/docs/migration/role-taxonomy-v4.md +60 -0
  11. package/docs/verification.md +83 -0
  12. package/guide/INDEX.md +86 -0
  13. package/guide/assets/layouts/cards-2.png +0 -0
  14. package/guide/assets/layouts/cards-3.png +0 -0
  15. package/guide/assets/layouts/cards-4.png +0 -0
  16. package/guide/assets/layouts/compare.png +0 -0
  17. package/guide/assets/layouts/highlight.png +0 -0
  18. package/guide/assets/layouts/pipeline.png +0 -0
  19. package/guide/assets/layouts/plain.png +0 -0
  20. package/guide/assets/layouts/quote.png +0 -0
  21. package/guide/assets/layouts/timeline.png +0 -0
  22. package/guide/en/acceptance-evidence.md +231 -0
  23. package/guide/en/ai-agents.md +185 -0
  24. package/guide/en/backlog-github-sync.md +108 -0
  25. package/guide/en/changelog.md +66 -0
  26. package/guide/en/configuration.md +112 -0
  27. package/guide/en/consistency.md +58 -0
  28. package/guide/en/conventions.md +113 -0
  29. package/guide/en/dream.md +121 -0
  30. package/guide/en/faq.md +855 -0
  31. package/guide/en/feedback.md +31 -0
  32. package/guide/en/getting-started.md +103 -0
  33. package/guide/en/installation.md +86 -0
  34. package/guide/en/legacy-onboarding.md +195 -0
  35. package/guide/en/loop-data-layout.md +256 -0
  36. package/guide/en/loop-driven-architecture.md +186 -0
  37. package/guide/en/loop.md +1324 -0
  38. package/guide/en/methodology.md +715 -0
  39. package/guide/en/migration-2.0.md +154 -0
  40. package/guide/en/overview.md +190 -0
  41. package/guide/en/pairing.md +151 -0
  42. package/guide/en/patterns/README.md +76 -0
  43. package/guide/en/patterns/graft-pattern.md +110 -0
  44. package/guide/en/patterns/replant-pattern.md +114 -0
  45. package/guide/en/patterns/seed-pattern.md +132 -0
  46. package/guide/en/peer.md +71 -0
  47. package/guide/en/pr-review.md +62 -0
  48. package/guide/en/practices/engineering-common-sense.md +395 -0
  49. package/guide/en/pricing.md +116 -0
  50. package/guide/en/project-setup.md +126 -0
  51. package/guide/en/roll-doc-audit.md +98 -0
  52. package/guide/en/skills.md +206 -0
  53. package/guide/en/test-isolation.md +51 -0
  54. package/guide/en/testing/quality-rubric.md +340 -0
  55. package/guide/en/testing.md +123 -0
  56. package/guide/en/tools.md +173 -0
  57. package/guide/skills.md +30 -0
  58. package/guide/zh/acceptance-evidence.md +194 -0
  59. package/guide/zh/ai-agents.md +170 -0
  60. package/guide/zh/backlog-github-sync.md +105 -0
  61. package/guide/zh/changelog.md +57 -0
  62. package/guide/zh/configuration.md +99 -0
  63. package/guide/zh/consistency.md +48 -0
  64. package/guide/zh/conventions.md +96 -0
  65. package/guide/zh/dream.md +97 -0
  66. package/guide/zh/faq.md +773 -0
  67. package/guide/zh/feedback.md +30 -0
  68. package/guide/zh/getting-started.md +96 -0
  69. package/guide/zh/installation.md +83 -0
  70. package/guide/zh/legacy-onboarding.md +192 -0
  71. package/guide/zh/loop-data-layout.md +236 -0
  72. package/guide/zh/loop-driven-architecture.md +186 -0
  73. package/guide/zh/loop.md +1124 -0
  74. package/guide/zh/methodology.md +702 -0
  75. package/guide/zh/migration-2.0.md +154 -0
  76. package/guide/zh/overview.md +186 -0
  77. package/guide/zh/pairing.md +117 -0
  78. package/guide/zh/patterns/README.md +74 -0
  79. package/guide/zh/patterns/graft-pattern.md +108 -0
  80. package/guide/zh/patterns/replant-pattern.md +112 -0
  81. package/guide/zh/patterns/seed-pattern.md +130 -0
  82. package/guide/zh/peer.md +63 -0
  83. package/guide/zh/pr-review.md +54 -0
  84. package/guide/zh/practices/engineering-common-sense.md +393 -0
  85. package/guide/zh/pricing.md +97 -0
  86. package/guide/zh/project-setup.md +114 -0
  87. package/guide/zh/roll-doc-audit.md +90 -0
  88. package/guide/zh/skills.md +191 -0
  89. package/guide/zh/test-isolation.md +46 -0
  90. package/guide/zh/testing/quality-rubric.md +284 -0
  91. package/guide/zh/testing.md +116 -0
  92. package/guide/zh/tools.md +173 -0
  93. package/package.json +4 -1
  94. package/skills/README.md +1 -0
  95. package/skills/roll-.qa/SKILL.md +1 -1
  96. package/skills/roll-.review/SKILL.md +1 -1
  97. package/skills/roll-build/SKILL.md +1 -1
  98. package/skills/roll-build/references/full-contract.md +16 -13
  99. package/skills/roll-design/SKILL.md +3 -3
  100. package/skills/roll-design/references/full-contract.md +17 -13
  101. package/skills/roll-fix/SKILL.md +1 -1
  102. package/skills/roll-fix/references/full-contract.md +13 -10
  103. package/skills/roll-peer/SKILL.md +1 -1
  104. package/skills/roll-prime/SKILL.md +77 -0
  105. package/skills/roll-prime/references/explorer-annex.md +39 -0
  106. package/skills/roll-prime/references/supervisor-prompt.md +165 -0
  107. package/skills/route-cases/skills.json +10 -0
  108. package/template/AGENTS.md +3 -1
@@ -0,0 +1,340 @@
1
+ # Test Quality Rubric
2
+
3
+ > Scope: Vitest tests in `packages/*/test/`, used by `roll-.dream` Scan 7 to
4
+ > surface anti-patterns as structured REFACTOR entries.
5
+ > Chinese version: [quality-rubric.zh.md](./quality-rubric.zh.md)
6
+
7
+ This rubric publishes eight anti-patterns that make tests give wrong signal —
8
+ either false-positive (red on unrelated change) or false-negative (green
9
+ while production is broken). Each category has the same four parts:
10
+
11
+ - **Definition** — what the anti-pattern is
12
+ - **Signals** — how to spot it from the test file alone
13
+ - **Fix template** — minimal repair pattern (not a full rewrite)
14
+ - **Real example** — an actual occurrence inside this repo
15
+
16
+ Categories are numbered ❶ through ❽. Categories ❶–❻ are advisory (dream
17
+ flags them; the maintainer triages). Categories ❼ and ❽ are **blocking**:
18
+ the loop cycle's test-quality merge gate (US-QA-012) rejects a PR that
19
+ introduces a new ❼ or ❽ violation, even if CI is green.
20
+
21
+ ---
22
+
23
+ ## ❶ Hardcoded business data in assertions
24
+
25
+ ### Definition
26
+
27
+ The test asserts a literal business value (price, version string, product
28
+ copy, model name) instead of importing the value from the module under
29
+ test or from a versioned fixture. When the business value changes, every
30
+ unrelated test that hardcoded the old value turns red even though the
31
+ logic under test still works.
32
+
33
+ ### Signals
34
+
35
+ - Bare numeric or string literals inside `[[ "$output" == *"..."*` /
36
+ `[ "$output" = "..." ]` that match values living in the source module.
37
+ - The same literal appears in ≥2 test files (price tables, version
38
+ numbers, model identifiers).
39
+ - The test file is not the canonical owner of the value (e.g. a runner
40
+ test asserts a price, but pricing lives in `packages/core/src/cost/prices.ts`).
41
+
42
+ ### Fix template
43
+
44
+ ```ts
45
+ // BEFORE
46
+ it("opus rate is 5/25", () => {
47
+ expect(computeListCost("claude-opus-4-7", usage)).toBe("5.0 25.0");
48
+ });
49
+
50
+ // AFTER — feed a fixed fixture rate table; assert the FORMULA, not the live rates
51
+ it("computeListCost multiplies tokens × the injected rate", () => {
52
+ const rates = { "m": { in: 2, out: 4 } };
53
+ expect(computeListCost("m", { input_tokens: 1000, output_tokens: 500 }, rates)).toBe(/* 0.002 + 0.002 */ 0.004);
54
+ });
55
+ ```
56
+
57
+ Or assert only structural invariants on the production rates (e.g.
58
+ `cache_read < input`, `out ≥ in`) so a rate-card move can't redden the suite.
59
+
60
+ ### Real example
61
+
62
+ `packages/core/test/prices.difftest.test.ts` used to read the live opus/sonnet/haiku
63
+ rates directly in the assertions — every rate-card move turned it red without
64
+ revealing any regression in the rate-resolution logic. It now feeds a fixed
65
+ fixture table for the arithmetic and asserts only structural invariants on the
66
+ production rates.
67
+
68
+ ---
69
+
70
+ ## ❷ Over-mocking
71
+
72
+ ### Definition
73
+
74
+ The test mocks a boundary it should be hitting for real — database,
75
+ filesystem, child-process spawn, git command — so the test passes against
76
+ a mock that doesn't behave like the real thing. The test ships green; the
77
+ first integration run breaks.
78
+
79
+ ### Signals
80
+
81
+ - `function git() { … }` / `function gh() { … }` overrides at the top of
82
+ a unit test that exercises real git or gh behavior.
83
+ - A SQL or filesystem call replaced with an inline stub returning a
84
+ hand-rolled string.
85
+ - Mocks live in the test file itself rather than in a shared test helper
86
+ module, signaling they were added ad-hoc rather than shared.
87
+
88
+ ### Fix template
89
+
90
+ ```ts
91
+ // Use a real ephemeral substrate (tmp git repo, tmpdir). Clean it up in
92
+ // afterEach/afterAll. The mock disappears entirely.
93
+ let dir: string;
94
+ beforeEach(() => { dir = realpathSync(mkdtempSync(join(tmpdir(), "t-"))); execSync("git init -q", { cwd: dir }); });
95
+ afterEach(() => { rmSync(dir, { recursive: true, force: true }); });
96
+ ```
97
+
98
+ If a true boundary cannot be exercised cheaply (network, gh, launchctl),
99
+ inject it as a port/dependency and pass a fake in the test — the same pattern
100
+ `runner-executor.test.ts` uses with `fakePorts()`.
101
+
102
+ ### Real example
103
+
104
+ Watch for ad-hoc `const gh = () => ({...})` overrides scattered across tests
105
+ that exercise loop-PR routing. The fix is an injected `GithubPort` (as in
106
+ `packages/cli/test/runner-executor.test.ts`) so the same fake is shared,
107
+ deliberate, and discoverable.
108
+
109
+ ---
110
+
111
+ ## ❸ Asserting on implementation details
112
+
113
+ ### Definition
114
+
115
+ The test asserts on the *shape* of internal state (private function
116
+ names, intermediate variable contents, file path of an internal cache)
117
+ rather than the observable behavior. A refactor that preserves behavior
118
+ breaks the test, blocking the refactor for no good reason.
119
+
120
+ ### Signals
121
+
122
+ - `grep -q '_internal_helper' "$output"` — asserting a private symbol
123
+ name is leaked to output.
124
+ - `[[ "$(cat .roll/internal/_cache.tmp)" == ... ]]` — asserting on an
125
+ internal cache file path that the public API never promised.
126
+ - The assertion would still pass after a meaningful behavioral
127
+ regression because it checks the wrong layer.
128
+
129
+ ### Fix template
130
+
131
+ Re-anchor the assertion to the **public effect**: exit code, observable
132
+ output, state visible to a caller. If the internal detail is the only
133
+ thing visible, the production code probably needs a thin public API to
134
+ expose the behavior intentionally.
135
+
136
+ ### Real example
137
+
138
+ Tests asserting `grep -q 'some_internal_helper' <output>` would break
139
+ the moment the helper is renamed, even though the gating logic is
140
+ unchanged. The right assertion is "story X is skipped because dep Y
141
+ is unsatisfied," verified through the public side effect (story stays
142
+ 📋 Todo, log line emitted).
143
+
144
+ ---
145
+
146
+ ## ❹ Fixture order coupling
147
+
148
+ ### Definition
149
+
150
+ Tests share mutable state (a file, an env var, a temp dir) and rely on
151
+ running in a specific order. Parallel runs, `--filter`, or reshuffling
152
+ cause sporadic failures that look like flakes.
153
+
154
+ ### Signals
155
+
156
+ - A test reads state another test in the same file wrote.
157
+ - A module-level (or `beforeAll`) fixture mutates shared state that nothing
158
+ resets, and a later test depends on it.
159
+ - The test passes alone but fails inside the suite (or vice versa).
160
+
161
+ ### Fix template
162
+
163
+ Create per-test state in `beforeEach` (its own tmpdir / env / fixture), assert,
164
+ and clean up in `afterEach`. Cross-test dependencies become explicit only when
165
+ truly necessary, and even then they go through a named helper, not implicit
166
+ ordering or a shared mutable.
167
+
168
+ ### Real example
169
+
170
+ Any test file where a `beforeAll` mutates `$HOME` or writes a shared
171
+ state-`<slug>.yaml` and a later test in the same file reads it without
172
+ re-initializing is a candidate. The fix is per-test isolation through a
173
+ fresh `mkdtempSync` + an injected home/root.
174
+
175
+ ---
176
+
177
+ ## ❺ Testing private functions / bypassing the public API
178
+
179
+ ### Definition
180
+
181
+ The test reaches inside a module to call a private helper directly,
182
+ asserting its return value. The helper can be renamed, inlined, or
183
+ removed without changing behavior — but the test claims regression.
184
+
185
+ ### Signals
186
+
187
+ - A test deep-imports an unexported helper from a module's internals and
188
+ calls it by name.
189
+ - The function name starts with `_` (project convention for private),
190
+ yet a test depends on its signature.
191
+ - The public API isn't exercised at all in the file; the test is
192
+ effectively testing the internal decomposition.
193
+
194
+ ### Fix template
195
+
196
+ Route the call through the public entry point (`roll <cmd>` /
197
+ `my-tool foo`). If the public API doesn't cover the case being tested,
198
+ that's a feature gap — either the case is unreachable (delete the
199
+ test) or the public API needs a new flag (add it intentionally).
200
+
201
+ ### Real example
202
+
203
+ A test that reaches into a private helper (importing an unexported
204
+ `_loopCheckDependsOn` via a back door) instead of running the command and
205
+ observing the skip decision in the run log. The first form locks the helper
206
+ name; the second tests the behavior the user cares about.
207
+
208
+ ---
209
+
210
+ ## ❻ Asserting on framework behavior
211
+
212
+ ### Definition
213
+
214
+ The test exercises Vitest itself (or any framework) rather than the
215
+ project code: asserting that `beforeEach` runs before tests, that a mock
216
+ records calls, that `expect` exists. These assertions pass because the
217
+ framework works; they tell us nothing about the project.
218
+
219
+ ### Signals
220
+
221
+ - Assertions on framework internals / the test runner's own behavior.
222
+ - A test whose body is only setup/teardown verification with no call to
223
+ project code.
224
+ - Tests added after a framework upgrade to "make sure Vitest still works."
225
+
226
+ ### Fix template
227
+
228
+ Delete the test. Framework verification belongs upstream. If the project
229
+ relies on a specific framework guarantee, document that contract in a shared
230
+ test helper and run one smoke test, not a category of them.
231
+
232
+ ### Real example
233
+
234
+ A test that asserts the test runner's own bookkeeping (e.g. that a mock was
235
+ constructed, or that `beforeEach` ran) adds noise to every CI run without ever
236
+ surfacing a project regression. Trust the framework; delete the test.
237
+
238
+ ---
239
+
240
+ ## ❼ Test inlines external tool behaviour
241
+
242
+ ### Definition
243
+
244
+ The test body re-implements the behaviour of external tools (`sed`, `grep`,
245
+ `find`, `awk`, `tr`) via inline shell pipelines, instead of calling a
246
+ project function that encapsulates that logic. When the project replaces
247
+ the external tool or changes the internal parsing, every test that copied
248
+ the pipeline breaks — even though the public-API output didn't change.
249
+
250
+ ### Signals
251
+
252
+ - `foo=$(echo "$output" | grep ... | sed ... | awk ...)` chains inside
253
+ the test body.
254
+ - The same pipeline appears in ≥2 test files (duplicated parsing logic).
255
+ - A project function exists (or should exist) that encapsulates this
256
+ parsing, but the test bypasses it and rolls its own inline version.
257
+
258
+ ### Fix template
259
+
260
+ ```ts
261
+ // BEFORE — inline regex replicates what a project function already does
262
+ const label = /<key>Label<\/key>\s*<string>([^<]*)<\/string>/.exec(plist)?.[1];
263
+
264
+ // AFTER — call the project function that owns the parsing
265
+ import { plistString } from "../src/lib/plist.js";
266
+ const label = plistString(plist, "Label");
267
+ ```
268
+
269
+ If no project function exists, extract the parsing into a named test helper
270
+ module so the logic is shared and deliberate.
271
+
272
+ ### Real example
273
+
274
+ An integration test that hand-rolls a regex chain to parse a plist XML file
275
+ for a `<string>` value, instead of importing the project's own plist parser.
276
+ Any schema change then forces the test author to fix the inline pattern in
277
+ multiple test files — the parser is the single owner that should change.
278
+
279
+ ---
280
+
281
+ ## ❽ Test asserts on a file outside this repo
282
+
283
+ ### Definition
284
+
285
+ The test assertion reads or asserts on a file path outside the repository
286
+ root (e.g. `~/.roll/`, `~/.codex/`, `/etc/`, `/tmp/other-project/`). When
287
+ the repo is cloned on a different machine, or the user's home directory
288
+ has different state, the test either fails spuriously or — worse — passes
289
+ by coincidence while testing the wrong thing.
290
+
291
+ ### Signals
292
+
293
+ - `[[ -f ~/.xxx/... ]]`, `[[ -d ~/.xxx/... ]]`, `cat ~/.xxx/...` inside
294
+ an assertion.
295
+ - A tmp path that the test file did not create itself.
296
+ - Paths starting with `~`, `${HOME}`, `/Users/`, or `/home/` that the test
297
+ file did not create itself.
298
+
299
+ ### Fix template
300
+
301
+ ```ts
302
+ // BEFORE — asserts on a file that lives outside the repo
303
+ it("skill file is synced", () => {
304
+ expect(readFileSync(`${homedir()}/.roll/skills/roll-.dream/SKILL.md`, "utf8")).toMatch(/Scan 6/);
305
+ });
306
+
307
+ // AFTER — recreate the fixture inside a tmpdir owned by the test
308
+ it("skill file includes Scan 6", () => {
309
+ const home = mkdtempSync(join(tmpdir(), "h-"));
310
+ mkdirSync(join(home, ".roll/skills/roll-.dream"), { recursive: true });
311
+ writeFileSync(join(home, ".roll/skills/roll-.dream/SKILL.md"), "### Scan 6 — Doc Freshness\n");
312
+ expect(checkSkillHasScan6({ home })).toBe(true);
313
+ });
314
+ ```
315
+
316
+ If the test's purpose is truly to verify interaction with an external file,
317
+ inject the home/root path (a tmpdir) so the test stays deterministic across
318
+ machines.
319
+
320
+ ### Real example
321
+
322
+ A dream-scan test that asserts on `${HOME}/.roll/skills/roll-.dream/SKILL.md`,
323
+ a file outside the repo whose content depends on whether the user ran
324
+ `roll setup`. On a machine without Roll installed, or with an older version,
325
+ the test fails even though the project code under test is correct. Sandbox the
326
+ fixture under a temp `HOME` instead.
327
+
328
+ ---
329
+
330
+ ## How `roll-.dream` consumes this rubric
331
+
332
+ `roll-.dream` Scan 7 scans the test suite for each category's signals,
333
+ emits at most 5 REFACTOR entries per cycle (rate cap to avoid drowning
334
+ the backlog), and tags each entry with the matching marker:
335
+
336
+ ```markdown
337
+ | REFACTOR-XXX | docs: <one-line description> [test-quality:❶] — flagged by dream YYYY-MM-DD | 📋 Todo |
338
+ ```
339
+
340
+ The maintainer triages the REFACTOR queue in the morning brief.
@@ -0,0 +1,123 @@
1
+ # Roll — Testing Workflow
2
+
3
+ Roll enforces a test-first discipline throughout delivery:
4
+
5
+ - **TCR** (Test && Commit || Revert) — every micro-step passes tests before committing.
6
+ - **E2E Deposit** — each completed Story leaves an E2E test covering its golden path.
7
+ - **CI E2E Gate** — the deposited E2E runs on every push, blocking merges on failure.
8
+ - **proof-of-pass** — pre-commit hook physically blocks commits that haven't passed tests.
9
+
10
+ ## E2E Deposit
11
+
12
+ After TCR micro-steps pass, `$roll-build` Phase 5.5 deposits an E2E test:
13
+
14
+ 1. Detects your project's existing E2E infrastructure (framework, directory, naming).
15
+ 2. Writes one E2E test covering the Story's critical user path.
16
+ 3. Runs it — fixes via TCR if red.
17
+ 4. Commits: `tcr: e2e deposit for <story-id>`.
18
+
19
+ The deposited test becomes a durable regression guard that CI replays on every
20
+ push, blocking merges on failure.
21
+
22
+ ## Pre-commit Hook (proof-of-pass)
23
+
24
+ Roll's pre-commit hook blocks commits unless tests passed within the last 60 seconds
25
+ **on the exact tree being committed**:
26
+
27
+ ```bash
28
+ # Proof written by test runner:
29
+ # .roll/last-test-pass ← timestamp + tree hash
30
+
31
+ # Hook checks at commit time:
32
+ # - elapsed < 60 s
33
+ # - tree hash matches current staged tree
34
+ ```
35
+
36
+ To satisfy the hook, run your tests immediately before committing. If you use TCR
37
+ (which roll-build does), this is automatic.
38
+
39
+ ## CI E2E Gate
40
+
41
+ The template CI workflow (`.github/workflows/ci.yml`) runs E2E tests as a
42
+ separate job that must pass before merge. If E2E fails:
43
+
44
+ 1. Check the failing test name — it corresponds to a Story ID.
45
+ 2. Run the test locally to reproduce.
46
+ 3. Open a `FIX-XXX` entry in `BACKLOG.md`, or use `$roll-fix` to fix immediately.
47
+
48
+ ## Failure Triage
49
+
50
+ `$roll-.qa` provides structured guidance for diagnosing test failures at each
51
+ layer of the test pyramid:
52
+
53
+ | Layer | Run command | Triage starting point |
54
+ |-------|-------------|-----------------------|
55
+ | Unit | `pnpm --filter @roll/<pkg> test` | Failing test file → function name |
56
+ | Integration | `pnpm --filter @roll/cli test` | Captured stdout/exit, fixture cwd |
57
+ | E2E | `<project E2E command>` | User path, environment |
58
+ | Smoke | `roll doctor` | Toolchain health |
59
+
60
+ ## TCR Test Strategy (Phase 3.0)
61
+
62
+ Each TCR micro-step needs second-level feedback. The suite is **Vitest** across
63
+ the pnpm workspace; the gate runs only what the diff touched.
64
+
65
+ ### `roll test` — run only the tests touched by the diff
66
+
67
+ ```bash
68
+ roll test # affected-only (the TCR micro-step gate); writes the test-pass proof
69
+ pnpm --filter @roll/cli exec vitest run test/<file>.test.ts # one file
70
+ pnpm -r test # the full suite (pre-push / CI / release)
71
+ pnpm test:cov # full suite with v8 coverage
72
+ ```
73
+
74
+ `roll test` maps the diff to affected Vitest files, runs them, and writes the
75
+ proof the commit gate checks (see below). A doc-only change has no affected
76
+ tests and exits 0. Pre-push / CI / release always run the full `pnpm -r test`.
77
+
78
+ ## Test Quality Rubric
79
+
80
+ `guide/en/testing/quality-rubric.md` (referenced from `$roll-.dream` Scan 7)
81
+ catalogs eight recurring antipatterns the dream nightly scan flags as
82
+ `REFACTOR-XXX [test-quality:❶|❷|...|❽]`:
83
+
84
+ | # | Antipattern | Fix |
85
+ |---|-------------|-----|
86
+ | ❶ | Hard-coded business data (prices, version strings, product copy) | Inject fixture data via monkey-patch or constructor; assert behaviour, not the data table itself |
87
+ | ❷ | Over-mocking (database, filesystem, real boundary) | Use real subsystems behind small adapter mocks; prefer in-memory test doubles |
88
+ | ❸ | Asserting implementation details (private symbol names, internal data shape) | Assert observable behaviour through the public API |
89
+ | ❹ | Fixture order coupling (shared mutable state between tests) | Setup/teardown each test independently; use immutable fixtures |
90
+ | ❺ | Testing private functions / bypassing the public API | Re-route through the public entry point; if it's hard to reach, the API is wrong |
91
+ | ❻ | Asserting framework behaviour (testing Vitest itself) | Delete the test; trust the framework |
92
+ | ❼ | Inlining external-tool behaviour (`sed`/`grep`/`awk` pipelines duplicated in test bodies) | Call the project helper that owns the parsing, or extract into a test helper module |
93
+ | ❽ | Asserting on a file outside this repo (`~/.codex`, `~/.kimi`, `~/.roll`, system paths) | Sandbox in a temp dir (`mkdtempSync`), redirect env vars there, never touch live config |
94
+
95
+ The dream skill emits at most 5 REFACTOR entries per scan, so the backlog
96
+ doesn't drown in noise. Refactor them in priority order.
97
+
98
+ ### Test-quality merge gate (US-QA-012 / 013)
99
+
100
+ Categories ❼ and ❽ are **blocking**: loop runs
101
+ `roll loop test-quality-check <changed-test-files>` between CI green and
102
+ auto-merge. Violations write `ALERT-<slug>.md` and hold the PR until either
103
+ the test is reshaped or the PR description carries `[skip-test-quality]`
104
+ (case-insensitive). Use the bypass sparingly — the violation still gets
105
+ reported through dream as a REFACTOR row, so it doesn't quietly accumulate.
106
+
107
+ Categories ❶..❻ remain advisory: dream flags them as REFACTOR entries but
108
+ the gate doesn't block on them. Triage them in your usual queue.
109
+
110
+ Lines with `# test-quality:allow` are skipped by the scanner — reserved for
111
+ doc-validation tests that legitimately inline `awk` to parse markdown
112
+ without ever touching production code.
113
+
114
+ `packages/core/test/prices.difftest.test.ts` is the canonical ❶ exemplar —
115
+ assertions that read live production rates broke every time the rate card moved,
116
+ even when the arithmetic was unchanged. It now feeds a fixed fixture price table
117
+ for the arithmetic and asserts only structural invariants (cache_read < input,
118
+ etc.) on the production rates.
119
+
120
+ ## See Also
121
+
122
+ - [loop.md](loop.md) — how loop enforces TCR discipline per story
123
+ - [skills.md](skills.md) — `$roll-build` (delivers + deposits E2E)
@@ -0,0 +1,173 @@
1
+ # Roll — Tools & Policy
2
+
3
+ Roll's tools layer is the governed path for side effects that a delivery cycle performs: shell commands, browser inspection, filesystem access, git, GitHub, network fetches, and MCP calls.
4
+
5
+ Tools are not a replacement for an AI client's own tool allowlist. Client-level `allowed-tools` controls what the inner agent may ask for. Roll's tool layer controls what the outer harness registers, how project policy is resolved, what events are recorded, and how costs appear in cycle evidence.
6
+
7
+ See the tools-layer design plan in [../../.roll/features/tools-layer/plan.md](../../.roll/features/tools-layer/plan.md).
8
+
9
+ ## Concepts
10
+
11
+ | Concept | Meaning |
12
+ |---------|---------|
13
+ | Tool declaration | The shared contract for a tool: id, kind, title, defaults, requirements, and input/output schema. |
14
+ | Registry | The core path that registers tools, resolves policy, invokes adapters, emits events, retries, and snapshots costs. |
15
+ | Adapter | The infra implementation for one tool family, such as `bash`, `browser.screenshot`, `git.push`, or `network.fetch`. |
16
+ | Policy | The effective settings from declaration defaults plus `.roll/policy.yaml` overrides. |
17
+ | Evidence | `tool:invoke`, `tool:result`, cycle cost rows, CLI output, attest reports, and dashboard timeline rows. |
18
+
19
+ Registered tool families today:
20
+
21
+ | Family | Tool ids |
22
+ |--------|----------|
23
+ | Bash | `bash` |
24
+ | Browser | `browser.screenshot`, `browser.console`, `browser.dom-query` |
25
+ | Filesystem | `filesystem.stat`, `filesystem.read`, `filesystem.write` |
26
+ | Git | `git.status`, `git.commit`, `git.push`, `git.merge` |
27
+ | GitHub | `github.pr`, `github.ci` |
28
+ | MCP | `mcp.call` |
29
+ | Network | `network.fetch` |
30
+
31
+ Browse the full built-in tool catalog — every tool with its capability, input/output contract, default guardrails, and requirements — on the machine-global **Tools** page (`tools.html`), one of the `MACHINE › …` breadcrumb pages alongside Agents and Skills.
32
+
33
+ ## Project Policy
34
+
35
+ Tool policy lives under the `tools:` section of `.roll/policy.yaml`.
36
+
37
+ ```yaml
38
+ tools:
39
+ bash:
40
+ enabled: true
41
+ timeoutMs: 30000
42
+ maxInvocationsPerCycle: 20
43
+ sandbox:
44
+ allowedPaths: [.]
45
+ blockedCommands: [sudo]
46
+ maxOutputBytes: 65536
47
+
48
+ browser.screenshot:
49
+ timeoutMs: 60000
50
+ sandbox:
51
+ headlessOnly: true
52
+ allowedOrigins: [http://localhost:4173]
53
+
54
+ network.fetch:
55
+ retry:
56
+ attempts: 2
57
+ backoffMs: 250
58
+ sandbox:
59
+ network: restricted
60
+ allowedOrigins: [https://api.example.com]
61
+ ```
62
+
63
+ Supported fields:
64
+
65
+ | Field | Scope | Meaning |
66
+ |-------|-------|---------|
67
+ | `enabled` | tool | `false` blocks invocation through policy. |
68
+ | `timeoutMs` | tool | Soft timeout used by the adapter unless the input has a narrower limit. |
69
+ | `retry.attempts` | tool | Maximum attempts for adapters that support retry. |
70
+ | `retry.backoffMs` | tool | Delay between retry attempts. |
71
+ | `maxInvocationsPerCycle` | tool | Per-cycle budget limit enforced by the registry. |
72
+ | `sandbox.allowedPaths` | sandbox | Path allowlist for filesystem-like adapters. |
73
+ | `sandbox.blockedCommands` | sandbox | Advisory command blocklist for bash. |
74
+ | `sandbox.hardTimeoutSec` | sandbox | Hard execution limit for adapters that support it. |
75
+ | `sandbox.maxOutputBytes` | sandbox | Output truncation limit. |
76
+ | `sandbox.allowedOrigins` | sandbox | Network or browser origin allowlist. |
77
+ | `sandbox.headlessOnly` | sandbox | Browser lane must stay headless. |
78
+ | `sandbox.network` | sandbox | `inherit`, `restricted`, or `blocked`. |
79
+
80
+ Unknown fields warn but do not reject the policy file, so newer Roll versions can add fields without breaking older project configs.
81
+
82
+ ## CLI
83
+
84
+ Use `roll doctor tools status` to inspect the registered tools, input contracts, requirement readiness, and the effective policy state for the current project.
85
+
86
+ ```bash
87
+ roll doctor tools status
88
+ ```
89
+
90
+ Example output:
91
+
92
+ ```text
93
+ tool kind enabled readiness timeout limit contract sandbox
94
+ bash bash yes available 30000 - args?, command, cwd?, env? maxOutputBytes=65536
95
+ browser.screenshot browser yes available 60000 - screenshotPath?, url, viewport?, waitFor? headlessOnly=true,maxOutputBytes=2097152
96
+ network.fetch network yes available 30000 - body?, headers?, method?, timeoutMs?, url network=restricted
97
+ ```
98
+
99
+ Use it after editing `.roll/policy.yaml` to confirm Roll sees the intended state.
100
+
101
+ ## Complete Example
102
+
103
+ This example locks browser screenshots to a local dev server, confirms the effective policy, and then verifies where the tool evidence appears after a delivery.
104
+
105
+ 1. Configure browser and network policy for the project:
106
+
107
+ ```yaml
108
+ tools:
109
+ browser.screenshot:
110
+ timeoutMs: 60000
111
+ sandbox:
112
+ headlessOnly: true
113
+ allowedOrigins: [http://localhost:4173]
114
+
115
+ network.fetch:
116
+ timeoutMs: 10000
117
+ retry:
118
+ attempts: 2
119
+ backoffMs: 250
120
+ sandbox:
121
+ network: restricted
122
+ allowedOrigins: [http://localhost:4173]
123
+ ```
124
+
125
+ 2. Confirm Roll resolved the policy:
126
+
127
+ ```bash
128
+ roll doctor tools status
129
+ ```
130
+
131
+ Expected rows:
132
+
133
+ ```text
134
+ browser.screenshot browser yes 60000 - allowedOrigins=http://localhost:4173,headlessOnly=true,maxOutputBytes=2097152
135
+ network.fetch network yes 10000 - allowedOrigins=http://localhost:4173,network=restricted
136
+ ```
137
+
138
+ 3. Run a delivery that has a visible web surface. The story spec should declare the page it wants captured:
139
+
140
+ ```yaml
141
+ ---
142
+ deliverable_url: http://localhost:4173
143
+ ---
144
+ ```
145
+
146
+ During attest, Roll captures the page through `browser.screenshot`. The exposed Roll tool id is `browser.screenshot`; Playwright or Chrome may be used underneath, but `playwright` is not a policy key or tool id.
147
+
148
+ 4. Inspect the evidence:
149
+
150
+ ```bash
151
+ roll loop cycle <cycle-id>
152
+ ```
153
+
154
+ Look for rows like:
155
+
156
+ ```text
157
+ tools browser.screenshot×1(2.0s) network.fetch×1(0.4s)
158
+ ```
159
+
160
+ In the story attest report and cycle views, the same event stream appears as:
161
+
162
+ | Evidence | What to check |
163
+ |----------|---------------|
164
+ | `tool:invoke` | Tool id, caller cycle, resolved policy. |
165
+ | `tool:result` | Success or error code, duration, output path. |
166
+ | Screenshot link | A `.roll/tool-dumps/...png` or attached card screenshot path. |
167
+ | Cost row | Currency is preserved as USD, CNY/RMB, or `¥`; mixed currencies are not collapsed into one fake total. |
168
+
169
+ ## Evidence And Cost
170
+
171
+ `roll loop status`, `roll loop cycle`, and attest reports show tool summaries from the event stream. Failed tool calls keep their error codes, and screenshot tools can link directly to captured images.
172
+
173
+ Tool costs preserve their native currency. USD rows stay USD. CNY/RMB rows stay CNY/RMB or `¥`. Roll does not relabel RMB-denominated tool or model costs as dollars and does not blindly add mixed currencies into one number.
@@ -0,0 +1,30 @@
1
+ # Roll Skill Catalog
2
+
3
+ > GENERATED by `roll setup skills` — do not edit by hand.
4
+ > 由 `roll setup skills` 生成 — 请勿手工编辑。
5
+ >
6
+ > Source of truth: each skill's `skills/<name>/SKILL.md` frontmatter.
7
+ > 事实源:各 skill 的 `skills/<name>/SKILL.md` frontmatter。
8
+
9
+ | Skill | Description |
10
+ |-------|-------------|
11
+ | `roll-.changelog` | After build completion, extracts completed Stories from .roll/backlog.md to generate CHANGELOG.md. Auto-triggered after successful deploy, keeping the external changelog in sync with the internal backlog. |
12
+ | `roll-.clarify` | Passive scope-clarification skill. Auto-triggers when roll-build receives vague or under-specified input in Fly mode. Summarizes intent and asks 3–5 targeted questions to establish boundaries before planning or coding. This is a passive skill. Never announce "I'm using roll-.clarify." Just do it naturally: summarize, ask, wait. |
13
+ | `roll-.dream` | Nightly code and architecture health scan. Passively triggered by scheduler (cron or GitHub Actions), not invoked by users directly. Detects dead code, architectural drift from domain model, pruning candidates, emerging patterns, doc coverage gaps, and doc staleness (文档新鲜度). Outputs REFACTOR entries to .roll/backlog.md and a daily log to .roll/dream/. A Loop C observability/maintenance surface: it reviews code structure and architectural health and feeds corrections back into the backlog. |
14
+ | `roll-.echo` | Passive intent clarification skill. Automatically activates when user input is vague, rambling, contradictory, or unclear. Restates the user's intent in structured, concise form and confirms before proceeding. Does NOT activate when intent is already clear — in that case, just execute directly. This is a passive skill. Never announce "I'm using roll-.echo." Just do it naturally: restate, confirm, proceed. |
15
+ | `roll-.qa` | QA coverage reference for build skills. Defines test pyramid (unit/E2E/visual/smoke), coverage requirements, and CI gates. Ensures quality assurance across all testing layers. |
16
+ | `roll-.review` | Self code review step in the TCR workflow. Runs after each micro-step is completed and before commit, checking code quality, security, and design issues. |
17
+ | `roll-build` | Universal delivery skill. Handles any input: a US-XXX ID executes from BACKLOG via TCR; a FIX-XXX redirects to roll-fix; any other text auto-clarifies, designs, and ships as a new Story. |
18
+ | `roll-debug` | Universal web debugger. Mounts a Black Box (BB) diagnostic probe on any page, collects rich diagnostics, analyzes root causes, and auto-fixes when the root cause is in project source. Cleans up after itself. |
19
+ | `roll-design` | Unified entry for discussion, design and planning. Explores options when uncertain, designs solutions with DDD modeling, splits into INVEST-compliant user stories, and writes to .roll/backlog.md. Use when user wants to discuss approaches, design solutions, plan features, or create stories. |
20
+ | `roll-doc-audit` | Documentation/product consistency audit. Checks README, guides, site pages, CLI help and docs against implemented behavior; can also index docs and draft fills from code evidence. |
21
+ | `roll-doctor` | Diagnose Roll toolchain health. Checks skill files, YAML frontmatter, symlinks, conventions sync, template integrity, and config validity. |
22
+ | `roll-fix` | 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. |
23
+ | `roll-idea` | Fast backlog capture. Analyzes a short description, classifies it as bug or idea, and appends it to .roll/backlog.md with an auto-incremented ID. |
24
+ | `roll-loop` | Autonomous BACKLOG executor. Runs on a schedule (hourly via cron or GitHub Actions), scans .roll/backlog.md for 📋 Todo items, and routes each to the appropriate skill: US-XXX → $roll-build, FIX-XXX → $roll-fix, REFACTOR-XXX → $roll-build. Retries the primary agent up to 3 times on transient failure; pauses with ALERT on persistent failure. Never cuts a release autonomously — release is always a human decision. The human reads delivery state from CLI-first surfaces such as roll status, roll cycle, loop watch, alerts, and story reports. |
25
+ | `roll-notes` | Project diary skill. Records development moments — successes, failures, discoveries — appended chronologically to a daily notes file. |
26
+ | `roll-onboard` | Interactive onboarding for legacy projects. Reads existing code, understands the project, asks 9 questions in 3 groups (cognition / scope / privacy), and writes .roll/onboard-plan.yaml as the contract for `roll init --apply` to execute. |
27
+ | `roll-peer` | Cross-agent peer review skill. When a task enters a decision phase (planning, high-risk, ambiguous, destructive), triggers a bidirectional negotiation with another AI agent via a unified protocol. Up to 3 rounds. If consensus is not reached, escalates to the human user. Includes adaptive peer routing based on task type and historical success rate. Trigger: /peer, "叫上 peer", "peer review", or auto-triggered at workflow gates. |
28
+ | `roll-propose` | Human-triggered product proposal generator. Reads project context (BACKLOG, recent commits, existing skills) and generates 1–3 structured US drafts from a user-facing perspective. Writes to .roll/proposals.md for human review — never directly to BACKLOG. Distinct from roll-.dream (which surfaces technical debt from execution experience); roll-propose thinks in user scenarios and feature gaps. |
29
+ | `roll-review-pr` | Agent-agnostic PR review skill. Reviews a pull request diff and emits a structured 3-state verdict (APPROVE / REQUEST_CHANGES / UNCERTAIN). Dispatched by `roll loop pr-inbox` for eligible external PRs. |
30
+ | `roll-spar` | Adversarial TDD mode with Attacker/Defender agents. Attacker writes tests to break the system, Defender writes minimal code to pass. Use for high-risk logic like auth, payments, data integrity, or complex state machines. |