@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.
- package/CHANGELOG.md +64 -0
- package/README.md +65 -56
- package/conventions/global/AGENTS.md +8 -7
- package/dist/roll.mjs +12909 -8532
- package/docs/INDEX.md +32 -0
- package/docs/architecture.md +444 -0
- package/docs/difftest-freeze-paradigm.md +113 -0
- package/docs/live-console.md +203 -0
- package/docs/manifesto.md +65 -0
- package/docs/migration/role-taxonomy-v4.md +60 -0
- package/docs/verification.md +83 -0
- package/guide/INDEX.md +86 -0
- package/guide/assets/layouts/cards-2.png +0 -0
- package/guide/assets/layouts/cards-3.png +0 -0
- package/guide/assets/layouts/cards-4.png +0 -0
- package/guide/assets/layouts/compare.png +0 -0
- package/guide/assets/layouts/highlight.png +0 -0
- package/guide/assets/layouts/pipeline.png +0 -0
- package/guide/assets/layouts/plain.png +0 -0
- package/guide/assets/layouts/quote.png +0 -0
- package/guide/assets/layouts/timeline.png +0 -0
- package/guide/en/acceptance-evidence.md +231 -0
- package/guide/en/ai-agents.md +185 -0
- package/guide/en/backlog-github-sync.md +108 -0
- package/guide/en/changelog.md +66 -0
- package/guide/en/configuration.md +112 -0
- package/guide/en/consistency.md +58 -0
- package/guide/en/conventions.md +113 -0
- package/guide/en/dream.md +121 -0
- package/guide/en/faq.md +855 -0
- package/guide/en/feedback.md +31 -0
- package/guide/en/getting-started.md +103 -0
- package/guide/en/installation.md +86 -0
- package/guide/en/legacy-onboarding.md +195 -0
- package/guide/en/loop-data-layout.md +256 -0
- package/guide/en/loop-driven-architecture.md +186 -0
- package/guide/en/loop.md +1324 -0
- package/guide/en/methodology.md +715 -0
- package/guide/en/migration-2.0.md +154 -0
- package/guide/en/overview.md +190 -0
- package/guide/en/pairing.md +151 -0
- package/guide/en/patterns/README.md +76 -0
- package/guide/en/patterns/graft-pattern.md +110 -0
- package/guide/en/patterns/replant-pattern.md +114 -0
- package/guide/en/patterns/seed-pattern.md +132 -0
- package/guide/en/peer.md +71 -0
- package/guide/en/pr-review.md +62 -0
- package/guide/en/practices/engineering-common-sense.md +395 -0
- package/guide/en/pricing.md +116 -0
- package/guide/en/project-setup.md +126 -0
- package/guide/en/roll-doc-audit.md +98 -0
- package/guide/en/skills.md +206 -0
- package/guide/en/test-isolation.md +51 -0
- package/guide/en/testing/quality-rubric.md +340 -0
- package/guide/en/testing.md +123 -0
- package/guide/en/tools.md +173 -0
- package/guide/skills.md +30 -0
- package/guide/zh/acceptance-evidence.md +194 -0
- package/guide/zh/ai-agents.md +170 -0
- package/guide/zh/backlog-github-sync.md +105 -0
- package/guide/zh/changelog.md +57 -0
- package/guide/zh/configuration.md +99 -0
- package/guide/zh/consistency.md +48 -0
- package/guide/zh/conventions.md +96 -0
- package/guide/zh/dream.md +97 -0
- package/guide/zh/faq.md +773 -0
- package/guide/zh/feedback.md +30 -0
- package/guide/zh/getting-started.md +96 -0
- package/guide/zh/installation.md +83 -0
- package/guide/zh/legacy-onboarding.md +192 -0
- package/guide/zh/loop-data-layout.md +236 -0
- package/guide/zh/loop-driven-architecture.md +186 -0
- package/guide/zh/loop.md +1124 -0
- package/guide/zh/methodology.md +702 -0
- package/guide/zh/migration-2.0.md +154 -0
- package/guide/zh/overview.md +186 -0
- package/guide/zh/pairing.md +117 -0
- package/guide/zh/patterns/README.md +74 -0
- package/guide/zh/patterns/graft-pattern.md +108 -0
- package/guide/zh/patterns/replant-pattern.md +112 -0
- package/guide/zh/patterns/seed-pattern.md +130 -0
- package/guide/zh/peer.md +63 -0
- package/guide/zh/pr-review.md +54 -0
- package/guide/zh/practices/engineering-common-sense.md +393 -0
- package/guide/zh/pricing.md +97 -0
- package/guide/zh/project-setup.md +114 -0
- package/guide/zh/roll-doc-audit.md +90 -0
- package/guide/zh/skills.md +191 -0
- package/guide/zh/test-isolation.md +46 -0
- package/guide/zh/testing/quality-rubric.md +284 -0
- package/guide/zh/testing.md +116 -0
- package/guide/zh/tools.md +173 -0
- package/package.json +4 -1
- package/skills/README.md +1 -0
- package/skills/roll-.qa/SKILL.md +1 -1
- package/skills/roll-.review/SKILL.md +1 -1
- package/skills/roll-build/SKILL.md +1 -1
- package/skills/roll-build/references/full-contract.md +16 -13
- package/skills/roll-design/SKILL.md +3 -3
- package/skills/roll-design/references/full-contract.md +17 -13
- package/skills/roll-fix/SKILL.md +1 -1
- package/skills/roll-fix/references/full-contract.md +13 -10
- package/skills/roll-peer/SKILL.md +1 -1
- package/skills/roll-prime/SKILL.md +77 -0
- package/skills/roll-prime/references/explorer-annex.md +39 -0
- package/skills/roll-prime/references/supervisor-prompt.md +165 -0
- package/skills/route-cases/skills.json +10 -0
- 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.
|
package/guide/skills.md
ADDED
|
@@ -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. |
|