@seanyao/roll 2026.424.4 → 2026.504.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/roll CHANGED
@@ -4,7 +4,7 @@ set -euo pipefail
4
4
  # Roll — AI Agent Convention Manager
5
5
  # Single source of truth for how all AI coding agents behave.
6
6
 
7
- VERSION="2026.424.4"
7
+ VERSION="2026.504.1"
8
8
  ROLL_HOME="${ROLL_HOME:-${HOME}/.roll}"
9
9
  ROLL_CONFIG="${ROLL_HOME}/config.yaml"
10
10
  ROLL_GLOBAL="${ROLL_HOME}/conventions/global"
@@ -6,6 +6,15 @@
6
6
  - User's language. Code/Git/Comments: English. UI: Chinese.
7
7
  - Concise. No summaries/code-walking. Implementation invisible.
8
8
  - Strategy (Why) OK; Tactics (How) NO. Outcomes only.
9
+ - **Ambiguity resolution**: When user says "explicit" in automation contexts,
10
+ interpret as "logged/observable with clear output", NOT "requiring manual
11
+ intervention". Confirm with one question if uncertain.
12
+ - **Bilingual output**: EN + ZH on separate lines, never inline.
13
+ ```
14
+ Processing...
15
+ 处理中...
16
+ ```
17
+ Not: `Processing... 处理中...`
9
18
 
10
19
  ## 2. Code
11
20
  - **TS**: Strict, no `any`. Functional hooks. Early returns.
@@ -20,5 +29,23 @@
20
29
 
21
30
  ## 4. Workflow
22
31
  - **TCR**: Test -> Green = Commit / Red = Revert. No WIP commits.
32
+ - Before implementing: confirm exact files, test strategy, and commit message
33
+ draft with user. Do not write code until approved.
34
+ - Before claiming completion: verify in the target environment mentioned by
35
+ user (e.g., specific CLI tool, remote server, hardware platform).
23
36
  - **Workspace**: `BACKLOG.md` index. `docs/features/` for details.
24
37
  - **Done**: Push + CI passes + deployed. Local-only is not done.
38
+
39
+ ## 5. Refactoring & Renames
40
+
41
+ Project-wide renames require systematic checking. Never assume find/replace
42
+ is sufficient. Execute in order:
43
+
44
+ 1. **Code references** — imports, function names, string literals
45
+ 2. **Documentation** — README, methodology, API docs
46
+ 3. **Config files** — YAML frontmatter, package names, manifests
47
+ 4. **Symlinks** — verify all resolve after rename
48
+ 5. **Installer scripts** — update paths and references
49
+ 6. **Shell environment** — remind user to reload or restart sessions
50
+
51
+ Confirm each phase clean before proceeding to the next.
@@ -17,12 +17,33 @@
17
17
 
18
18
  ## Claude Code-Specific
19
19
 
20
- - When a project has Roll skills, use them (`$roll-design`, `$roll-story`, etc.).
20
+ - When a project has Roll skills, use them (`$roll-design`, `$roll-build`, `$roll-fix`, etc.).
21
21
  - Use plan mode for complex multi-step tasks before executing.
22
22
  - Prefer Edit tool over Bash for file modifications.
23
23
  - Use Agent tool with worktree isolation for parallel independent subtasks.
24
24
  - When I say "帮我看下" or "处理下", go ahead and do it, not just analyze it.
25
25
 
26
+ ## Verification and Testing
27
+
28
+ Before claiming any fix is complete, verify it works in the target environment
29
+ mentioned by the user. If they said a specific CLI tool, remote server, or
30
+ hardware platform, test there explicitly. Do not claim completion until verified.
31
+
32
+ ## Configuration File Editing
33
+
34
+ When editing config files (YAML, TOML, JSON with schema):
35
+ 1. Find official documentation or a verified working example first
36
+ 2. Do not guess syntax
37
+ 3. If no docs found after 2 searches, ask user for a reference config
38
+ 4. Maximum 2 syntax attempts before escalating to research mode
39
+
40
+ ## External Service Integration
41
+
42
+ For npm publishing, proxy configurations, or auth-dependent deployment:
43
+ - Stop after 2 failed attempts and ask user for preferred fallback
44
+ - Do not continue iterating on auth/proxy debugging without explicit direction
45
+ - If OIDC/token issues persist, immediately fallback to manual with explanation
46
+
26
47
  ## Frontend Default Stack
27
48
 
28
49
  - React + shadcn/ui + Tailwind CSS is the default.
@@ -13,5 +13,5 @@
13
13
 
14
14
  - Use `$roll-design` to plan features that span frontend and backend.
15
15
  - When modifying API contracts, update both `api/types.ts` and `src/shared/types/` in the same commit.
16
- - Use worktree isolation for parallel frontend/backend Actions in `$roll-story-build`.
16
+ - Use worktree isolation for parallel frontend/backend Actions in `$roll-build`.
17
17
  - Run `npm run build` to verify both frontend and backend compile before pushing.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seanyao/roll",
3
- "version": "2026.424.4",
3
+ "version": "2026.504.1",
4
4
  "description": "Roll — Roll out features with AI agents",
5
5
  "scripts": {
6
6
  "test": "find tests/unit tests/integration -name '*.bats' | sort | xargs ./tests/helpers/bats-core/bin/bats"
@@ -208,4 +208,4 @@ If project lacks Playwright setup:
208
208
 
209
209
  - [Playwright Docs](https://playwright.dev/)
210
210
  - [Visual Regression Guide](https://playwright.dev/docs/test-snapshots)
211
- - Example implementation: `seanyao/kids-game/e2e/`
211
+ - Example implementation: `<owner>/<repo>/e2e/`
@@ -1,16 +1,19 @@
1
1
  ---
2
2
  name: roll-debug
3
3
  license: MIT
4
- description: Universal web debugger. Collects diagnostics (console/network/DOM) via Playwright, analyzes root causes, and suggests fixes. Works with or without Black Box (BB) integration.
4
+ description: Universal web debugger. Mounts a Black Box (BB) diagnostic probe on any page, collects rich diagnostics, analyzes root causes, and suggests fixes. Cleans up after itself.
5
5
  ---
6
6
 
7
7
  # Roll Debug
8
8
 
9
- Universal web debugging tool that combines diagnostic collection and analysis into a single workflow: **Diagnose → Analyze → Suggest Fix**.
9
+ Web debugging tool that treats the **Black Box (BB) as a diagnostic probe** — mounted when needed, unmounted when done. Combines diagnostic collection and analysis into a single workflow: **MountCollect → Analyze → Unmount**.
10
10
 
11
- Two collection modes:
12
- - **Native BB Mode**: Page has Black Box integrated; click button to collect rich diagnostic data
13
- - **Universal Diagnostic Mode**: Page has no BB; use Playwright to directly collect console/network/DOM/screenshot data
11
+ ## Philosophy
12
+
13
+ - BB is a **diagnostic probe**, not a product feature. Pages do not need to integrate BB natively.
14
+ - For any web diagnosis, **mount BB first** (unless already present).
15
+ - The entire lifecycle is **explicit and visible**: you see when BB mounts, when it collects, and when it unmounts.
16
+ - A visible **BB button** appears on the page during diagnosis so you always know the probe is active.
14
17
 
15
18
  ## When to Use
16
19
 
@@ -31,15 +34,18 @@ Two collection modes:
31
34
  ## Quick Start
32
35
 
33
36
  ```bash
34
- # Full workflow: collect + analyze + suggest fix (recommended)
37
+ # Full workflow: mount + collect + analyze + unmount (recommended)
35
38
  $roll-debug https://example.com/page
36
39
 
37
40
  # Collect data only, skip analysis
38
41
  $roll-debug https://example.com/page --no-analyze
39
42
 
40
- # Force universal diagnostic mode (no BB)
43
+ # Skip BB mount, use built-in universal collector
41
44
  $roll-debug https://example.com/page --universal
42
45
 
46
+ # Use a custom BB SDK instead of the built-in stub
47
+ $roll-debug https://example.com/page --bb-sdk-url https://cdn.example.com/bb.js
48
+
43
49
  # Collect + analyze + auto-fix
44
50
  $roll-debug https://example.com/page --fix
45
51
 
@@ -51,44 +57,21 @@ $roll-debug https://site.com/page1,https://site.com/page2
51
57
  $roll-debug --file urls.txt
52
58
  ```
53
59
 
54
- ## Two Collection Modes
55
-
56
- ### Mode 1: Native BB (Page has Black Box integrated)
57
-
58
- ```
59
- Page with BB → Playwright clicks BB button → Download diagnostic JSON
60
- ```
61
-
62
- Requirements:
63
- - Page has `[data-testid="bb-toggle"]` button
64
- - Or exposes `window.__BB_DATA__`
65
- - Or stores data in `localStorage.bb_diagnostic`
66
-
67
- ### Mode 2: Universal Diagnostic (No BB required)
68
-
69
- ```
70
- Any page → Playwright injects collector → Gather console/network/DOM/screenshot
71
- ```
72
-
73
- Collects:
74
- - Console logs (error/warn/info)
75
- - Network requests (failed XHR/fetch, slow requests)
76
- - DOM state (key elements visibility, HTML length)
77
- - Screenshot (full page + viewport)
78
- - Performance metrics (load time, FCP, LCP, render blocking)
79
- - JavaScript errors with stack traces
80
-
81
- ## Full Workflow
60
+ ## BB Probe Lifecycle
82
61
 
83
62
  ```
84
63
  User: "Debug the page"
85
64
 
86
65
 
87
66
  ┌─────────────────────────────────────┐
88
- │ 1. Auto-detect collection mode
89
- │ ├── Try BB button found?
90
- │ ├── Try window.__BB_DATA__?
91
- │ └── Fallback to Universal
67
+ │ 1. Mount BB Probe
68
+ │ ├── Check: page already has BB?
69
+ ├── Yes → reuse existing
70
+ └── No → inject BB
71
+ │ │ ├── Built-in stub (default)
72
+ │ │ └── Custom SDK (--bb-sdk-url)
73
+ │ ├── Wait for initialization │
74
+ │ └── BB button appears on page │
92
75
  └──────────────────┬──────────────────┘
93
76
 
94
77
 
@@ -97,6 +80,7 @@ User: "Debug the page"
97
80
  │ ├── Console logs │
98
81
  │ ├── Network requests │
99
82
  │ ├── DOM state │
83
+ │ ├── Performance metrics │
100
84
  │ └── Screenshot │
101
85
  └──────────────────┬──────────────────┘
102
86
 
@@ -111,40 +95,82 @@ User: "Debug the page"
111
95
 
112
96
 
113
97
  ┌─────────────────────────────────────┐
114
- │ 4. Suggest (or apply) fix
115
- │ ├── Actionable fix suggestions
116
- │ ├── Auto-fix via TCR (--fix)
117
- └── Deploy & verify
118
- └─────────────────────────────────────┘
98
+ │ 4. Unmount BB Probe
99
+ │ ├── Restore console/fetch/XHR
100
+ │ ├── Remove BB button from DOM
101
+ ├── Delete window.__BB_DATA__
102
+ │ └── Page state fully restored │
103
+ └──────────────────┬──────────────────┘
104
+
105
+
106
+ Fix suggestions (or --fix to apply)
119
107
  ```
120
108
 
109
+ ## Collection Modes
110
+
111
+ ### Mode 1: Mounted BB (Default)
112
+
113
+ BB probe is mounted on the page — either reused from an existing BB or freshly injected.
114
+
115
+ **Visual indicator**: a red circular **BB** button appears at the bottom-right of the page.
116
+
117
+ **Data collected via BB interface**:
118
+ - Console logs (error/warn/info)
119
+ - Network requests (failed XHR/fetch, slow requests)
120
+ - DOM state (key elements visibility, HTML length)
121
+ - Performance metrics (load time, FCP, LCP)
122
+ - JavaScript errors with stack traces
123
+
124
+ **BB Sources**:
125
+
126
+ | Source | When Used | Capability |
127
+ |--------|-----------|------------|
128
+ | Existing native BB | Page already has `[data-testid="bb-toggle"]` or `window.__BB_DATA__` | Full app-specific metrics (contentState, audioState, etc.) |
129
+ | Built-in stub | Default when no BB present | Generic metrics (console, network, DOM, performance, errors) |
130
+ | Custom SDK | `--bb-sdk-url` provided | Determined by the SDK implementation |
131
+
132
+ ### Mode 2: Universal Diagnostic (No BB)
133
+
134
+ When `--universal` is passed, skip BB mount entirely. Use Playwright's built-in event listeners directly.
135
+
136
+ ```bash
137
+ $roll-debug https://example.com/page --universal
138
+ ```
139
+
140
+ Useful when:
141
+ - You explicitly do not want to modify the page state
142
+ - The page has strict CSP that blocks script injection
143
+ - You need a quick check without probe overhead
144
+
121
145
  ## Usage Examples
122
146
 
123
- ### Example 1: Full auto-detect + analyze (default)
147
+ ### Example 1: Full auto-mount + analyze (default)
124
148
 
125
149
  ```bash
126
150
  $roll-debug https://yyy.up.railway.app/story/cars/chapter/1
127
151
 
128
- Detecting diagnostic capability...
129
- ├── BB found: [data-testid="bb-toggle"]
130
- └── Using: Native BB mode
131
-
132
- Collecting data...
133
- ├── Console: 3 errors, 5 warnings
134
- ├── Network: 2 failed requests
135
- ├── DOM: #app rendered, .content empty
136
- └── Screenshot: saved to /tmp/bb-screenshot.png
152
+ 🔍 Diagnosing https://yyy.up.railway.app/story/cars/chapter/1
153
+ 📡 Mounting BB probe...
154
+ ├── Source: built-in stub
155
+ └── Status: ready (320ms)
156
+ └── BB button visible on page ✓
157
+ 📊 Collecting data via BB...
158
+ ├── Console: 3 errors, 5 warnings
159
+ ├── Network: 2 failed requests
160
+ ├── DOM: #app rendered, .content empty
161
+ └── Screenshot: saved to /tmp/bb-screenshot.png
162
+ 🔬 Analyzing...
163
+ 🧹 Unmounting BB probe... done
164
+ └── Page state restored ✓
137
165
 
138
166
  Report: /tmp/bb-report.json
139
167
 
140
- Analyzing...
141
-
142
168
  ## Diagnostic Analysis Report
143
169
 
144
170
  ### Basic Info
145
171
  | Field | Value |
146
172
  |-------|-------|
147
- | Diagnostic Mode | native-bb |
173
+ | Diagnostic Mode | mounted-bb (stub) |
148
174
  | Page URL | https://yyy.up.railway.app/story/cars/chapter/1 |
149
175
 
150
176
  ### Key Findings
@@ -165,58 +191,58 @@ Modify Player.tsx line 45, change useEffect dependency
165
191
  from `[chapter?.id]` to `[chapter?.number]`
166
192
  ```
167
193
 
168
- ### Example 2: Universal mode (no BB)
194
+ ### Example 2: Reuse existing native BB
195
+
196
+ ```bash
197
+ $roll-debug https://example.com/page
198
+
199
+ 🔍 Diagnosing https://example.com/page
200
+ 📡 Mounting BB probe...
201
+ ├── Native BB detected
202
+ └── Reusing existing probe
203
+ 📊 Collecting data via BB...
204
+ ├── Console: 0 errors
205
+ ├── Network: 0 failed
206
+ └── DOM: fully rendered
207
+ 🔬 Analyzing...
208
+ 🧹 Unmounting BB probe... skipped
209
+ └── Native BB left intact
210
+
211
+ No issues found. Page is healthy.
212
+ ```
213
+
214
+ ### Example 3: Universal mode (no BB mount)
169
215
 
170
216
  ```bash
171
217
  $roll-debug https://example.com --universal
172
218
 
173
- Universal diagnostic mode (no BB required)
174
-
175
- Collected:
176
- ├── Console Errors: 2
177
- │ ├── TypeError: Cannot read property 'id' of undefined
178
- │ │ at Player.tsx:45
179
- │ └── ReferenceError: AudioContext is not defined
180
- ├── Failed Network: 1
181
- │ └── GET https://api.example.com/data 404
182
- ├── DOM State:
183
- │ ├── body.innerHTML length: 2340
184
- │ ├── #root: rendered
185
- │ ├── .loading: still visible (timeout?)
186
- │ └── .error-message: visible
187
- ├── Performance:
188
- │ ├── DOMContentLoaded: 1.2s
189
- │ ├── First Contentful Paint: 2.3s
190
- │ └── Largest Contentful Paint: 4.5s
191
- └── Screenshot: /tmp/bb-screenshot.png
219
+ 🔍 Diagnosing https://example.com (universal mode)
220
+ 📡 BB mount skipped (--universal)
221
+ 📊 Collecting data via Playwright events...
222
+ ├── Console Errors: 2
223
+ │ ├── TypeError: Cannot read property 'id' of undefined
224
+ │ │ at Player.tsx:45
225
+ │ └── ReferenceError: AudioContext is not defined
226
+ ├── Failed Network: 1
227
+ │ └── GET https://api.example.com/data 404
228
+ └── Screenshot: /tmp/bb-screenshot.png
229
+ 🔬 Analyzing...
192
230
 
193
231
  Report: /tmp/bb-report.json
194
232
 
195
- Analyzing...
196
-
197
233
  ### Key Findings
198
234
  | Metric | Value | Status |
199
235
  |--------|-------|--------|
200
236
  | Console Errors | 2 | Critical |
201
237
  | Network Failed | 1 | Critical |
202
- | DOM Rendering | Partial | Warning |
203
- | Load Time (LCP) | 4.5s | Slow |
204
-
205
- ### Diagnosis Conclusion
206
- API endpoint returning 404 causes content not to load.
207
- AudioContext undefined suggests missing polyfill or browser incompatibility.
208
-
209
- ### Suggested Fix
210
- 1. Fix API route for /data endpoint (404)
211
- 2. Add AudioContext polyfill or guard with `typeof AudioContext !== 'undefined'`
212
238
  ```
213
239
 
214
- ### Example 3: Analyze existing report file
240
+ ### Example 4: Analyze existing report file
215
241
 
216
242
  ```bash
217
243
  $roll-debug --report /tmp/bb-report.json
218
244
 
219
- Reading report: /tmp/bb-report.json (mode: universal)
245
+ Reading report: /tmp/bb-report.json (mode: mounted-bb)
220
246
 
221
247
  ### Key Findings
222
248
  ...
@@ -226,11 +252,13 @@ Reading report: /tmp/bb-report.json (mode: universal)
226
252
 
227
253
  | Format | Source | Description |
228
254
  |--------|--------|-------------|
229
- | Native BB | Page with Black Box | `window.__BB_DATA__` or `localStorage.bb_diagnostic` |
230
- | Universal | Playwright collector | Injected collector data |
255
+ | Mounted BB (stub) | Injected built-in stub | `window.__BB_DATA__` via injectable-bb.js |
256
+ | Mounted BB (native) | Page with existing Black Box | `window.__BB_DATA__` or `localStorage.bb_diagnostic` |
257
+ | Mounted BB (custom) | Custom SDK via `--bb-sdk-url` | Determined by SDK |
258
+ | Universal | Playwright native events | Direct event listener data |
231
259
  | Legacy | Old diagnostic files | Backward compatible |
232
260
 
233
- ### Native BB Mode Fields
261
+ ### Mounted BB Mode Fields
234
262
 
235
263
  ```javascript
236
264
  const bbData = report.diagnostic.bbData;
@@ -240,6 +268,11 @@ bbData.audioState?.src
240
268
  bbData.audioState?.error
241
269
  bbData.hasAudio
242
270
  bbData.errors
271
+ bbData.console.errors
272
+ bbData.console.warnings
273
+ bbData.network.failed
274
+ bbData.dom.keyElements
275
+ bbData.performance.loadComplete
243
276
  ```
244
277
 
245
278
  ### Universal Mode Fields
@@ -255,7 +288,6 @@ d.dom['#root']
255
288
  d.dom.htmlLength
256
289
  d.performance.loadComplete
257
290
  d.performance.domContentLoaded
258
- d.errors
259
291
  ```
260
292
 
261
293
  ## Analysis Report Template
@@ -266,7 +298,8 @@ d.errors
266
298
  ### Basic Info
267
299
  | Field | Value |
268
300
  |-------|-------|
269
- | Diagnostic Mode | {native-bb / universal} |
301
+ | Diagnostic Mode | {mounted-bb / universal} |
302
+ | BB Source | {native / stub / custom-sdk} |
270
303
  | Page URL | {url} |
271
304
  | Collected At | {timestamp} |
272
305
 
@@ -309,62 +342,123 @@ d.errors
309
342
 
310
343
  ## Implementation Notes
311
344
 
312
- ### Universal Mode Injection
313
-
314
- When page has no BB, inject a lightweight collector via `page.evaluate()`:
345
+ ### BB Mount Flow (Playwright)
315
346
 
316
347
  ```javascript
317
- window.__ROLL_DEBUG_COLLECTOR__ = {
318
- console: [],
319
- network: [],
320
- errors: [],
321
-
322
- init() {
323
- // Hook console methods
324
- ['error', 'warn', 'log', 'info'].forEach(method => {
325
- const original = console[method];
326
- console[method] = (...args) => {
327
- this.console.push({method, args, timestamp: Date.now()});
328
- original.apply(console, args);
329
- };
330
- });
331
-
332
- // Hook fetch/XHR for network interception
333
-
334
- // Listen for JS errors
335
- window.addEventListener('error', e => {
336
- this.errors.push({message: e.message, stack: e.error?.stack});
337
- });
338
- },
348
+ // Pseudocode for AI agent execution
349
+ async function diagnose(page, url, args) {
350
+ log(`🔍 Diagnosing ${url}`);
351
+
352
+ // Step 1: Mount
353
+ const bbState = await mountBB(page, args);
354
+ log(`📡 Mounting BB probe...`);
355
+ log(` ├── Source: ${bbState.source}`); // native / stub / custom
356
+ log(` └── Status: ${bbState.ready ? 'ready' : 'failed'}`);
357
+
358
+ if (bbState.ready && bbState.source !== 'native') {
359
+ log(` └── BB button visible on page ✓`);
360
+ }
361
+
362
+ // Step 2: Collect
363
+ log(`📊 Collecting data via BB...`);
364
+ const data = await collectViaBB(page);
365
+
366
+ // Step 3: Analyze
367
+ log(`🔬 Analyzing...`);
368
+ const analysis = await analyze(data);
369
+
370
+ // Step 4: Unmount (unless native BB)
371
+ if (bbState.source !== 'native') {
372
+ log(`🧹 Unmounting BB probe...`);
373
+ const ok = await page.evaluate(() => window.__BB_UNMOUNT__?.());
374
+ log(` └── ${ok ? 'done' : 'failed'}`);
375
+ log(` └── Page state restored ✓`);
376
+ } else {
377
+ log(`🧹 Unmounting BB probe... skipped`);
378
+ log(` └── Native BB left intact`);
379
+ }
380
+
381
+ return analysis;
382
+ }
383
+
384
+ async function mountBB(page, args) {
385
+ // Check for existing BB
386
+ const hasNative = await page.evaluate(() =>
387
+ !!document.querySelector('[data-testid="bb-toggle"]') || !!window.__BB_DATA__
388
+ );
389
+ if (hasNative) {
390
+ return { source: 'native', ready: true };
391
+ }
392
+
393
+ if (args.universal) {
394
+ return { source: 'universal', ready: false };
395
+ }
339
396
 
340
- getData() {
341
- return {
342
- console: this.console,
343
- network: this.network,
344
- errors: this.errors,
345
- dom: this.captureDOM(),
346
- performance: this.capturePerformance()
347
- };
397
+ // Inject BB
398
+ try {
399
+ if (args.bbSdkUrl) {
400
+ await page.addScriptTag({ url: args.bbSdkUrl });
401
+ } else {
402
+ const stubPath = path.join(__dirname, 'injectable-bb.js');
403
+ await page.addScriptTag({ path: stubPath });
404
+ }
405
+
406
+ // Poll for readiness
407
+ const ready = await poll(
408
+ () => page.evaluate(() => !!window.__BB_DATA__),
409
+ { timeout: 5000, interval: 200 }
410
+ );
411
+
412
+ return { source: args.bbSdkUrl ? 'custom' : 'stub', ready };
413
+ } catch (e) {
414
+ return { source: 'stub', ready: false, error: e.message };
348
415
  }
349
- };
416
+ }
350
417
  ```
351
418
 
352
- The injected collector only exists in the Playwright browser context — no cleanup needed.
419
+ ### Built-in Stub (`injectable-bb.js`)
420
+
421
+ The stub is injected via `page.addScriptTag({ path })` when no native BB exists.
422
+
423
+ **Capabilities**:
424
+ - Hooks `console.*` with internal error firewall (stub bugs never leak to page)
425
+ - Hooks `fetch` and `XMLHttpRequest` transparently — original behavior fully preserved
426
+ - Listens for `error` and `unhandledrejection`
427
+ - Captures Performance Navigation Timing + FCP + LCP
428
+ - Captures DOM state (title, HTML length, key element visibility)
429
+ - Renders a visible **BB** button on the page
430
+
431
+ **Cleanup**:
432
+ - `window.__BB_UNMOUNT__()` restores all modified globals to their original references
433
+ - Removes the BB button from DOM
434
+ - Deletes `window.__BB_DATA__` and `window.__BB_UNMOUNT__`
435
+
436
+ ### Universal Mode (No BB)
437
+
438
+ When `--universal` is used, collect via Playwright native events:
439
+
440
+ ```javascript
441
+ page.on('console', msg => ...);
442
+ page.on('requestfailed', req => ...);
443
+ page.on('response', res => ...);
444
+ page.on('pageerror', err => ...);
445
+ ```
446
+
447
+ No page state is modified.
353
448
 
354
449
  ## Data Output Formats
355
450
 
356
- ### Native BB Mode
451
+ ### Mounted BB Mode
357
452
 
358
453
  ```json
359
454
  {
360
- "mode": "native-bb",
455
+ "mode": "mounted-bb",
456
+ "bbSource": "stub",
361
457
  "timestamp": "2024-01-15T10:30:00Z",
362
458
  "url": "https://example.com/page",
363
459
  "bbData": {},
364
- "collected": {
365
- "console": [],
366
- "network": []
367
- }
460
+ "mountedAt": 1705315800000,
461
+ "unmountedAt": 1705315805000
368
462
  }
369
463
  ```
370
464
 
@@ -389,7 +483,7 @@ The injected collector only exists in the Playwright browser context — no clea
389
483
  "title": "Page Title",
390
484
  "htmlLength": 2340,
391
485
  "keyElements": {
392
- "#root": {"exists": true, "visible": true, "children": 5},
486
+ "#root": {"exists": true, "visible": true, "text": "..."},
393
487
  ".error": {"exists": false}
394
488
  }
395
489
  },
@@ -409,16 +503,26 @@ The injected collector only exists in the Playwright browser context — no clea
409
503
 
410
504
  ## Capability Comparison
411
505
 
412
- | Feature | Native BB Mode | Universal Mode |
413
- |---------|---------------|----------------|
414
- | Requires BB integration | Yes | No |
415
- | Console logs | Yes | Yes |
416
- | Network data | Yes | Yes |
417
- | DOM state | Detailed | Key elements |
418
- | App-specific metrics | Yes | No |
419
- | Screenshot | Yes | Yes |
420
- | Performance metrics | Yes | Yes |
421
- | Works on any site | No | Yes |
506
+ | Feature | Mounted BB (stub) | Mounted BB (native) | Universal |
507
+ |---------|-------------------|---------------------|-----------|
508
+ | Page modification | Yes (mount/unmount) | No (already there) | No |
509
+ | Visible BB button | Yes | If native has one | No |
510
+ | Console logs | Yes | Yes | Yes |
511
+ | Network data | Yes | Yes | Yes |
512
+ | DOM state | Detailed | Detailed | Key elements |
513
+ | App-specific metrics | No | Yes | No |
514
+ | Screenshot | Yes | Yes | Yes |
515
+ | Performance metrics | Yes | Yes | Yes |
516
+ | Works offline | Yes | Yes | Yes |
517
+ | Cleanup on exit | Yes (full restore) | N/A | N/A |
518
+
519
+ ## Safety & Cleanup Guarantees
520
+
521
+ 1. **Stub errors are firewalled** — every hook wraps its internal logic in try/catch. A bug in the stub cannot crash the page.
522
+ 2. **Original behavior preserved** — fetch/XHR wrappers return the exact same values/throw the exact same errors as the originals.
523
+ 3. **Full unmount** — `__BB_UNMOUNT__()` restores console, fetch, XHR, removes listeners, removes DOM element, and deletes globals.
524
+ 4. **Native BB untouched** — if a page already has BB, it is reused but never unmounted.
525
+ 5. **CSP fallback** — if script injection fails (CSP), automatically falls back to Universal mode.
422
526
 
423
527
  ## Integration with Build Skills
424
528
 
@@ -0,0 +1,263 @@
1
+ // Roll Debug — Injectable BB Diagnostic Stub
2
+ // Injected into page context by Playwright when native BB is absent.
3
+ // Exposes window.__BB_DATA__ and [data-testid="bb-toggle"] for unified collection.
4
+ // Fully unmountable via window.__BB_UNMOUNT__().
5
+
6
+ (function () {
7
+ 'use strict';
8
+
9
+ if (window.__BB_DATA__) return; // Already mounted
10
+
11
+ // ─── Backup originals ───
12
+ const _orig = {
13
+ console: {},
14
+ fetch: window.fetch,
15
+ XHR_open: XMLHttpRequest.prototype.open,
16
+ XHR_send: XMLHttpRequest.prototype.send,
17
+ };
18
+ ['error', 'warn', 'log', 'info'].forEach((m) => {
19
+ _orig.console[m] = console[m];
20
+ });
21
+
22
+ // ─── BB State ───
23
+ const BB = {
24
+ version: 'stub-1.0',
25
+ mountedAt: Date.now(),
26
+ console: { errors: [], warnings: [], logs: [] },
27
+ network: { failed: [], slow: [], all: [] },
28
+ errors: [],
29
+ dom: {},
30
+ performance: {},
31
+ };
32
+
33
+ // ─── Console hooks (with internal error firewall) ───
34
+ ['error', 'warn', 'log', 'info'].forEach((m) => {
35
+ const key = m === 'error' ? 'errors' : m === 'warn' ? 'warnings' : 'logs';
36
+ const orig = _orig.console[m];
37
+ console[m] = function bbHookedConsole(...args) {
38
+ try {
39
+ BB.console[key].push({
40
+ message: args
41
+ .map((a) => {
42
+ try {
43
+ return typeof a === 'object' ? JSON.stringify(a) : String(a);
44
+ } catch (e) {
45
+ return '[unstringifiable]';
46
+ }
47
+ })
48
+ .join(' '),
49
+ timestamp: Date.now(),
50
+ });
51
+ } catch (e) {
52
+ /* swallow stub internal error */
53
+ }
54
+ return orig.apply(this, args);
55
+ };
56
+ });
57
+
58
+ // ─── Fetch hook (transparent wrapper) ───
59
+ const origFetch = _orig.fetch;
60
+ window.fetch = function bbHookedFetch(...args) {
61
+ const start = Date.now();
62
+ const url =
63
+ typeof args[0] === 'string'
64
+ ? args[0]
65
+ : args[0]?.url || '[unknown]';
66
+ const method = args[1]?.method || 'GET';
67
+
68
+ return origFetch.apply(this, args).then(
69
+ (res) => {
70
+ try {
71
+ const duration = Date.now() - start;
72
+ const entry = { url, status: res.status, duration, method };
73
+ BB.network.all.push(entry);
74
+ if (!res.ok) BB.network.failed.push(entry);
75
+ if (duration > 3000) BB.network.slow.push(entry);
76
+ } catch (e) {
77
+ /* swallow */
78
+ }
79
+ return res;
80
+ },
81
+ (err) => {
82
+ try {
83
+ BB.network.failed.push({
84
+ url,
85
+ error: err.message,
86
+ duration: Date.now() - start,
87
+ method,
88
+ });
89
+ } catch (e) {
90
+ /* swallow */
91
+ }
92
+ throw err;
93
+ }
94
+ );
95
+ };
96
+ try {
97
+ window.fetch.toString = () => 'function fetch() { [native code] }';
98
+ } catch (e) {
99
+ /* ignore */
100
+ }
101
+
102
+ // ─── XHR hook ───
103
+ XMLHttpRequest.prototype.open = function bbHookedOpen(method, url, ...rest) {
104
+ this._bb = { method, url: String(url), start: null };
105
+ return _orig.XHR_open.call(this, method, url, ...rest);
106
+ };
107
+
108
+ XMLHttpRequest.prototype.send = function bbHookedSend(...args) {
109
+ if (this._bb) this._bb.start = Date.now();
110
+ const handler = () => {
111
+ if (!this._bb) return;
112
+ try {
113
+ const duration = Date.now() - this._bb.start;
114
+ const entry = {
115
+ url: this._bb.url,
116
+ status: this.status,
117
+ duration,
118
+ method: this._bb.method,
119
+ };
120
+ BB.network.all.push(entry);
121
+ if (this.status >= 400 || this.status === 0)
122
+ BB.network.failed.push(entry);
123
+ if (duration > 3000) BB.network.slow.push(entry);
124
+ } catch (e) {
125
+ /* swallow */
126
+ }
127
+ };
128
+ this.addEventListener('loadend', handler, { once: true });
129
+ return _orig.XHR_send.apply(this, args);
130
+ };
131
+
132
+ // ─── JS Error listeners ───
133
+ const onError = (e) => {
134
+ try {
135
+ BB.errors.push({
136
+ message: e.message,
137
+ stack: e.error?.stack,
138
+ timestamp: Date.now(),
139
+ });
140
+ } catch (e) {
141
+ /* swallow */
142
+ }
143
+ };
144
+ const onRejection = (e) => {
145
+ try {
146
+ BB.errors.push({
147
+ message: e.reason?.message || String(e.reason),
148
+ stack: e.reason?.stack,
149
+ timestamp: Date.now(),
150
+ });
151
+ } catch (e) {
152
+ /* swallow */
153
+ }
154
+ };
155
+ window.addEventListener('error', onError);
156
+ window.addEventListener('unhandledrejection', onRejection);
157
+
158
+ // ─── Performance ───
159
+ const capturePerf = () => {
160
+ try {
161
+ const nav = performance.getEntriesByType('navigation')[0];
162
+ BB.performance = {
163
+ domContentLoaded: nav?.domContentLoadedEventEnd,
164
+ loadComplete: nav?.loadEventEnd,
165
+ firstContentfulPaint:
166
+ performance.getEntriesByName('first-contentful-paint')[0]?.startTime,
167
+ largestContentfulPaint: performance
168
+ .getEntriesByType('largest-contentful-paint')
169
+ .pop()?.startTime,
170
+ };
171
+ } catch (e) {
172
+ /* swallow */
173
+ }
174
+ };
175
+ if (document.readyState === 'complete') {
176
+ capturePerf();
177
+ } else {
178
+ window.addEventListener('load', capturePerf, { once: true });
179
+ }
180
+
181
+ // ─── DOM Capture ───
182
+ function captureDOM() {
183
+ try {
184
+ const info = (sel) => {
185
+ const el = document.querySelector(sel);
186
+ return el
187
+ ? {
188
+ exists: true,
189
+ visible: el.offsetParent !== null,
190
+ text: el.textContent?.slice(0, 200),
191
+ }
192
+ : { exists: false };
193
+ };
194
+ return {
195
+ title: document.title,
196
+ url: location.href,
197
+ htmlLength: document.documentElement.innerHTML.length,
198
+ keyElements: {
199
+ '#root': info('#root'),
200
+ '#app': info('#app'),
201
+ '[data-testid="error"]': info('[data-testid="error"]'),
202
+ '.error': info('.error'),
203
+ '.loading': info('.loading'),
204
+ },
205
+ };
206
+ } catch (e) {
207
+ return { error: 'DOM capture failed', url: location.href };
208
+ }
209
+ }
210
+
211
+ // ─── Public API ───
212
+ BB.getData = () => ({ ...BB, dom: captureDOM(), collectedAt: Date.now() });
213
+ window.__BB_DATA__ = BB;
214
+
215
+ // ─── Visible BB toggle button ───
216
+ let btn;
217
+ if (document.body) {
218
+ btn = document.createElement('button');
219
+ btn.dataset.testid = 'bb-toggle';
220
+ btn.textContent = 'BB';
221
+ btn.title = 'Black Box Diagnostic Probe — click to download report';
222
+ btn.style.cssText =
223
+ 'position:fixed;bottom:12px;right:12px;z-index:99999;' +
224
+ 'width:36px;height:36px;border-radius:50%;border:none;' +
225
+ 'background:#ff4444;color:#fff;font-size:11px;font-weight:bold;' +
226
+ 'font-family:sans-serif;cursor:pointer;box-shadow:0 2px 8px rgba(0,0,0,0.3);' +
227
+ 'display:flex;align-items:center;justify-content:center;' +
228
+ 'opacity:0.85;transition:opacity 0.2s;';
229
+ btn.onmouseenter = () => (btn.style.opacity = '1');
230
+ btn.onmouseleave = () => (btn.style.opacity = '0.85');
231
+ btn.onclick = () => {
232
+ const data = BB.getData();
233
+ const blob = new Blob([JSON.stringify(data, null, 2)], {
234
+ type: 'application/json',
235
+ });
236
+ const a = document.createElement('a');
237
+ a.href = URL.createObjectURL(blob);
238
+ a.download = `bb-diagnostic-${Date.now()}.json`;
239
+ a.click();
240
+ };
241
+ document.body.appendChild(btn);
242
+ }
243
+
244
+ // ─── Unmount (restore page to original state) ───
245
+ window.__BB_UNMOUNT__ = function () {
246
+ try {
247
+ ['error', 'warn', 'log', 'info'].forEach(
248
+ (m) => (console[m] = _orig.console[m])
249
+ );
250
+ window.fetch = _orig.fetch;
251
+ XMLHttpRequest.prototype.open = _orig.XHR_open;
252
+ XMLHttpRequest.prototype.send = _orig.XHR_send;
253
+ window.removeEventListener('error', onError);
254
+ window.removeEventListener('unhandledrejection', onRejection);
255
+ if (btn) btn.remove();
256
+ delete window.__BB_DATA__;
257
+ delete window.__BB_UNMOUNT__;
258
+ return true;
259
+ } catch (e) {
260
+ return false;
261
+ }
262
+ };
263
+ })();
@@ -0,0 +1,166 @@
1
+ ---
2
+ name: roll-doctor
3
+ license: MIT
4
+ description: "Diagnose Roll toolchain health. Checks skill files, YAML frontmatter, symlinks, conventions sync, template integrity, and config validity."
5
+ ---
6
+
7
+ # Roll Doctor
8
+
9
+ 诊断 Roll 工具链健康状态。快速定位 skill 不工作、convention 不同步、symlink 断裂等问题。
10
+
11
+ ## When to Use
12
+
13
+ - "roll 怎么不工作了"
14
+ - "skill 找不到了"
15
+ - "convention 没有同步"
16
+ - "$roll-debug 不响应"
17
+ - 任何 Roll 相关功能异常
18
+
19
+ ## Checks
20
+
21
+ ### 1. Roll Installation
22
+
23
+ ```bash
24
+ # Verify ROLL_HOME structure
25
+ ls -la ~/.roll/
26
+ ls -la ~/.roll/skills/
27
+ ls -la ~/.roll/conventions/
28
+ ```
29
+
30
+ Expected:
31
+ - `~/.roll/` exists
32
+ - `~/.roll/skills/` has roll-* directories
33
+ - `~/.roll/conventions/global/` has AGENTS.md, CLAUDE.md, etc.
34
+ - `~/.roll/conventions/templates/` has backend-service, cli, frontend-only, fullstack
35
+
36
+ ### 2. Skill Health
37
+
38
+ ```bash
39
+ # List all skills and check YAML frontmatter
40
+ for f in ~/.roll/skills/*/SKILL.md; do
41
+ echo "=== $(basename $(dirname $f)) ==="
42
+ head -5 "$f"
43
+ done
44
+ ```
45
+
46
+ Check:
47
+ - Each skill directory has `SKILL.md`
48
+ - YAML frontmatter has `name:` and `description:`
49
+ - No duplicate `name` values across skills
50
+ - File is readable markdown
51
+
52
+ ### 3. Symlinks
53
+
54
+ ```bash
55
+ # Claude Code
56
+ ls -la ~/.claude/skills/roll-*
57
+
58
+ # Gemini (if exists)
59
+ ls -la ~/.gemini/skills/roll-* 2>/dev/null
60
+
61
+ # Trae (if exists)
62
+ ls -la ~/.trae/skills/roll-* 2>/dev/null
63
+ ```
64
+
65
+ Check:
66
+ - Each symlink exists
67
+ - Each symlink points to valid target in `~/.roll/skills/`
68
+ - No broken symlinks (red in ls output)
69
+
70
+ ### 4. Conventions Sync
71
+
72
+ ```bash
73
+ # Check roll.md sync
74
+ diff ~/.roll/conventions/global/CLAUDE.md ~/.claude/roll.md
75
+
76
+ # Check CLAUDE.md includes @roll.md
77
+ grep "@roll.md" ~/.claude/CLAUDE.md
78
+ ```
79
+
80
+ Check:
81
+ - `~/.claude/roll.md` exists and matches `~/.roll/conventions/global/CLAUDE.md`
82
+ - `~/.claude/CLAUDE.md` contains `@roll.md` reference
83
+ - `~/.claude/CLAUDE.md` is not missing or stale
84
+
85
+ ### 5. Template Integrity
86
+
87
+ ```bash
88
+ # Global conventions
89
+ ls ~/.roll/conventions/global/
90
+
91
+ # Project type templates
92
+ for t in backend-service cli frontend-only fullstack; do
93
+ ls ~/.roll/conventions/templates/$t/
94
+ done
95
+
96
+ # New project template
97
+ ls ~/.roll/template/
98
+ ```
99
+
100
+ Check:
101
+ - All expected files present in each directory
102
+ - No missing AGENTS.md, CLAUDE.md, or GEMINI.md
103
+
104
+ ### 6. Config
105
+
106
+ ```bash
107
+ cat ~/.roll/config.yaml
108
+ ```
109
+
110
+ Check:
111
+ - File exists and is valid YAML
112
+ - Has required `ai_tool_name` or `ai_claude` entries
113
+
114
+ ## Report Format
115
+
116
+ ```
117
+ 🔬 Roll Doctor Report
118
+
119
+ [✓/✗] Roll installation
120
+ [✓/✗] Skills (N skills checked)
121
+ [✓/✗] Symlinks (N tools checked)
122
+ [✓/✗] Conventions sync
123
+ [✓/✗] Templates integrity
124
+ [✓/✗] Config
125
+
126
+ ---
127
+ Issues found: N
128
+
129
+ 1. [severity] description → fix command
130
+ 2. [severity] description → fix command
131
+
132
+ Recommendation: ...
133
+ ```
134
+
135
+ ## Severity Levels
136
+
137
+ | Level | Meaning | Example |
138
+ |-------|---------|---------|
139
+ | 🔴 Critical | Skill completely broken | Broken symlink, missing SKILL.md |
140
+ | 🟡 Warning | Partial functionality | Stale convention, outdated roll.md |
141
+ | 🟢 Info | Recommendations | Missing optional tool symlink |
142
+
143
+ ## Auto-Fix
144
+
145
+ If user confirms, automatically fix safe issues:
146
+
147
+ - **Broken/missing symlinks** → `roll setup` (recreates all)
148
+ - **Stale conventions** → `roll sync conventions`
149
+ - **Missing template files** → warn user to re-install
150
+
151
+ Never auto-fix without user confirmation for:
152
+ - Overwriting `~/.claude/CLAUDE.md` (may have user customizations)
153
+ - Deleting files
154
+
155
+ ## Quick Fix Commands
156
+
157
+ ```bash
158
+ # Recreate all symlinks and sync conventions
159
+ roll setup
160
+
161
+ # Sync only conventions
162
+ roll sync conventions
163
+
164
+ # Reinstall (nuclear option)
165
+ curl -fsSL https://raw.githubusercontent.com/seanyao/roll/main/install.sh | bash
166
+ ```
@@ -30,9 +30,13 @@ $roll-notes 今天的 code review 给了很好的反馈
30
30
 
31
31
  1. **Determine file path**: `notes/YYYY-MM-DD.md` relative to project root
32
32
  2. **Get current time**: Use `Asia/Shanghai` timezone (`TZ=Asia/Shanghai date`)
33
- 3. **Append**: Add new entry at end of file never overwrite existing content
34
- 4. **Create if missing**: If file doesn't exist, create with a `# YYYY-MM-DD — <one-line summary>` header
35
- 5. **Free format**: Paragraph, list, code block whatever fits the moment
33
+ 3. **Read existing entries for style**: Before writing, read the last 2–3 entries
34
+ in the same file. Analyze their style: heading format, voice/tone,
35
+ paragraph structure, emoji usage, code block conventions, sign-off pattern.
36
+ New entries must continue this style — do not invent new structural patterns.
37
+ 4. **Append**: Add new entry at end of file — never overwrite existing content
38
+ 5. **Create if missing**: If file doesn't exist, create with a `# YYYY-MM-DD — <one-line summary>` header
39
+ 6. **Free format**: Paragraph, list, code block — whatever fits the moment
36
40
 
37
41
  ## File format
38
42
 
@@ -54,8 +58,34 @@ $roll-notes 今天的 code review 给了很好的反馈
54
58
  ...
55
59
  ```
56
60
 
61
+ ## 写作风格(强制)
62
+
63
+ **禁止干巴巴的 checklist。禁止只有结论没有过程。必须有人的声音。**
64
+
65
+ ### 叙事驱动
66
+ 以时间线和事件推进为主线,像讲故事一样记录。不是 `补了功能,pytest 绿`,而是 `"电池剩余电量在哪里看?"我一愣,US-PLAT-003 的 AC 明明写了...`
67
+
68
+ ### 技术细节嵌入叙事
69
+ 代码、数据、命令行是故事的一部分,自然插入,不是附录。
70
+
71
+ ### 对话与互动
72
+ 记录用户的反馈、提问、情绪。日记是对话记录,不是独白。
73
+
74
+ ### 诚实记录错误
75
+ 失败、困惑、教训比成功更值得记录。
76
+
77
+ ### 标题有画面感
78
+ 不是功能清单。如 `每秒 20 行的噪音`、`22 个文件挤在一个抽屉里`。
79
+
80
+ ### 结尾有收束
81
+ 最后一段回到人的状态。如 `"值了"。"睡了"。push。`
82
+
57
83
  ## Rules
58
84
 
85
+ - **Style continuity**: Match the EXACT style, tone, and formatting of previous
86
+ entries in the same file. Do not invent new structural patterns or section
87
+ headers. If the file already has entries, analyze them first and confirm your
88
+ understanding of their style before writing.
59
89
  - **No planning**: Never write "明日待办" or "下一步"
60
90
  - **No summaries**: Never write "今日收获" or "经验教训"
61
91
  - **Pure record**: What happened, as-is — honest, immediate, rough is fine
@@ -10,7 +10,7 @@ One-command publish flow for roll maintainers.
10
10
 
11
11
  ## When Not to Use
12
12
 
13
- - Non-maintainer users (this skill publishes `@seanyao/roll` to npm under seanyao)
13
+ - Non-maintainer users (this skill publishes the package defined in `package.json` confirm scope before running)
14
14
  - Internal project releases — only for the `roll` CLI package itself
15
15
  - Hotfixing code without a version bump (use `$roll-fix`)
16
16
  - Generating user-facing release notes (use `$roll-.changelog`)
@@ -92,8 +92,8 @@ After publish, show:
92
92
  ```
93
93
  ✅ Released v{version}
94
94
  🏷 Tag: v{version} pushed to origin
95
- 📦 npm published: @seanyao/roll@{version}
96
- 🔗 https://www.npmjs.com/package/@seanyao/roll
95
+ 📦 npm published: {package_name}@{version} # package name read from package.json
96
+ 🔗 https://www.npmjs.com/package/{package_name}
97
97
  ```
98
98
 
99
99
  ## Abort Conditions