openspec-playwright 0.2.10 → 0.3.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.
@@ -2,7 +2,7 @@
2
2
  name: openspec-e2e
3
3
  description: Run Playwright E2E verification for an OpenSpec change. Use when the user wants to validate that the implementation works end-to-end by running Playwright tests generated from the specs.
4
4
  license: MIT
5
- compatibility: Requires openspec CLI, Playwright (with browsers installed), and @playwright/mcp (globally installed via `claude mcp add playwright npx @playwright/mcp@latest`).
5
+ compatibility: Requires openspec CLI, Playwright (with browsers installed), /browse (gstack, for exploration), and @playwright/mcp (globally installed via `claude mcp add playwright npx @playwright/mcp@latest`, for test execution + Healer).
6
6
  metadata:
7
7
  author: openspec-playwright
8
8
  version: "2.24"
@@ -129,8 +129,8 @@ expect((await post.json()).likeCount).toBeGreaterThan(0); // verify persistence
129
129
  - Announce: "Mode: full app exploration + Page Object discovery"
130
130
  - **Goal**: Discover new routes, extract selectors, and build `pages/*.ts` Page Objects — accumulated asset for future Change tests
131
131
  - **Route discovery** (in order):
132
- 1. **sitemap.xml**: `browser_navigate(${BASE_URL}/sitemap.xml)` → parse URLs
133
- 2. **Link extraction**: Navigate to `${BASE_URL}/` → `browser_evaluate` extracts all `<a href>`:
132
+ 1. **sitemap.xml**: `$B goto ${BASE_URL}/sitemap.xml` → parse URLs
133
+ 2. **Link extraction**: Navigate to `${BASE_URL}/` → `$B js` extracts all `<a href>`:
134
134
  ```javascript
135
135
  // Extract all internal links from current page
136
136
  () => {
@@ -199,18 +199,31 @@ Explore to collect real DOM data before writing test plan. This eliminates blind
199
199
 
200
200
  #### 4.1. Verify BASE_URL + Read app-knowledge.md
201
201
 
202
- 1. **Verify BASE_URL**: `browser_navigate(BASE_URL)` → if HTTP 5xx → **STOP: backend error. Fix app first.**
202
+ 1. **Verify BASE_URL**: `$B goto <BASE_URL>` → if HTTP 5xx → **STOP: backend error. Fix app first.**
203
203
  2. **Read app-knowledge.md**: known risks, project conventions
204
204
  3. **Routes** (from Step 1): use already-discovered routes — no need to re-extract
205
205
 
206
- #### 4.2. Explore each route via Playwright MCP
206
+ #### 4.2. Explore each route via /browse
207
207
 
208
208
  For each route:
209
209
 
210
210
  ```
211
- browser_navigatebrowser_console_messagesbrowser_snapshotbrowser_take_screenshot
211
+ $B goto <url> $B console $B snapshot $B screenshot
212
212
  ```
213
213
 
214
+ #### Parallel Exploration (via `openspec-pw explore`)
215
+
216
+ For apps with many routes (>=5), use the dedicated CLI command for true parallel exploration:
217
+
218
+ ```bash
219
+ openspec-pw explore --parallel 4 # 4 independent Chromium workers
220
+ openspec-pw explore --dry-run # preview chunk assignment first
221
+ ```
222
+
223
+ **Why**: `$B` uses a single shared Chromium instance with one active page — `Promise.allSettled` on `$B` commands serializes execution and causes navigation state conflicts. `openspec-pw explore` launches N independent browser processes, each with its own Chromium context, for genuine parallelism.
224
+
225
+ **After parallel exploration completes**, read the updated `app-exploration.md` to continue with Step 4.3.
226
+
214
227
  **After navigating, check for app-level errors**:
215
228
 
216
229
  | Signal | Meaning | Action |
@@ -225,43 +238,42 @@ browser_navigate → browser_console_messages → browser_snapshot → browser_t
225
238
 
226
239
  ```
227
240
  // 1. Initial capture
228
- await browser_navigate(`${BASE_URL}/<route>`);
229
- await new Promise(r => setTimeout(r, 1500)); // wait for SPA hydration
230
- const url1 = await browser_evaluate(() => window.location.href);
241
+ $B goto <url>
242
+ $B wait --networkidle // wait for SPA hydration
243
+ $B js "window.location.href" // url1
244
+ $B wait 2000
231
245
 
232
246
  // 2. Observe stability
233
- await new Promise(r => setTimeout(r, 2000));
234
- const url2 = await browser_evaluate(() => window.location.href);
235
- const msgs = await browser_console_messages();
247
+ $B js "window.location.href" // url2
248
+ $B console --errors
236
249
 
237
250
  // 3. Detect
238
251
  if (url1 !== url2) {
239
- → ❌ URL changed — redirect loop (ERR_TOO_MANY_REDIRECTS)
240
- → Is this route protected without valid auth.storageState?
252
+ → ❌ URL changed — redirect loop
253
+ → Is this route protected without valid auth?
241
254
  → Yes → auth.setup.ts is broken → fix auth first
242
255
  → No → App middleware bug → mark route ❌ skip, record as App Bug
243
256
  }
244
- if (msgs.filter(m => m.type === 'warning' || m.type === 'error').length > 10) {
257
+ if (console errors/warnings > 10) {
245
258
  → ❌ Excessive console errors — page refresh / JS crash loop
246
259
  → Mark route ❌ skip, record as App Bug
247
260
  }
248
261
  ```
249
262
 
250
- **Network monitoring**: After navigating, use `browser_network_requests` to check for failed API calls. Failed requests (status ≥ 400) on a route indicate an API/backend issue — record in `app-exploration.md` for reference.
263
+ **Network monitoring**: After navigating, use `$B network` to check for failed API calls. Failed requests (status ≥ 400) on a route indicate an API/backend issue — record in `app-exploration.md` for reference.
251
264
 
252
265
  **For guest routes** (no auth):
253
266
 
254
- ```javascript
255
- // Navigate directly
256
- await browser_navigate(`${BASE_URL}/<route>`);
267
+ ```
268
+ $B goto <url>
257
269
  ```
258
270
 
259
271
  **For protected routes** (auth required):
260
272
 
261
- ```javascript
273
+ ```
262
274
  // Option A: use existing storageState (recommended)
263
275
  // Option B: navigate to /login first, fill form, then navigate to target
264
- // Option C: use browser_run_code to set auth cookies directly
276
+ // Option C: $B cookie to set auth cookies directly
265
277
  ```
266
278
 
267
279
  **If credentials are not yet available**:
@@ -279,7 +291,7 @@ Wait for page stability:
279
291
 
280
292
  #### 4.3. Parse the snapshot
281
293
 
282
- From `browser_snapshot` output, extract **interactive elements** for each route:
294
+ From `$B snapshot` output, extract **interactive elements** for each route:
283
295
 
284
296
  | Element type | What to capture | Selector priority |
285
297
  | -------------------- | ------------------------------------ | ---------------------------------------------------------- |
@@ -293,7 +305,7 @@ From `browser_snapshot` output, extract **interactive elements** for each route:
293
305
 
294
306
  #### 4.3.1. Detect special elements
295
307
 
296
- From `browser_snapshot` + `browser_evaluate`, identify these special elements per route:
308
+ From `$B snapshot` + `$B js`, identify these special elements per route:
297
309
 
298
310
  **Special element detection matrix:**
299
311
 
@@ -310,9 +322,9 @@ From `browser_snapshot` + `browser_evaluate`, identify these special elements pe
310
322
  | Drag-and-drop | drag events in JS | Simulate DnD via coordinate clicks | Low |
311
323
  | Date picker | specific `data-testid` or class patterns | Click triggers → evaluate value | Low (skip unless specs mention) |
312
324
  | Infinite scroll | Dynamic row insertion | Count elements before/after scroll | Low (skip unless specs mention dynamic lists/pagination) |
313
- | WebSocket / SSE | No DOM signal | Check `browser_console_messages` for WS events | Low (check only if app uses real-time features) |
325
+ | WebSocket / SSE | No DOM signal | Check `$B console --errors` for WS events | Low (check only if app uses real-time features) |
314
326
 
315
- **For each detected special element, capture via `browser_evaluate` with targeted DOM queries:**
327
+ **For each detected special element, capture via `$B js` with targeted DOM queries:**
316
328
  - Canvas: `getContext('webgl2'/'webgl'/'2d')`, `width`, `height`
317
329
  - Iframe: `src` attribute → use `frameLocator` in tests
318
330
  - CAPTCHA: `.g-recaptcha`, `.h-captcha`, `[data-sitekey]`, canvas+slider detection
@@ -347,6 +359,24 @@ After exploration, add route-level notes (redirects, dynamic content → see 4.5
347
359
 
348
360
  **Idempotency**: If `app-exploration.md` already exists → read it, verify routes still match the live app, update only new routes or changed pages.
349
361
 
362
+ #### Route Snapshot Hash -- Skip Unchanged Routes
363
+
364
+ Before re-exploring, compute a lightweight hash of the app's current state:
365
+
366
+ 1. **Quick hash** (for re-runs): Navigate to `${BASE_URL}/sitemap.xml` → hash the XML content
367
+ 2. **If hash unchanged** since last exploration: Skip re-exploration entirely -- use cached `app-exploration.md`
368
+ 3. **If hash changed**: Re-explore only changed routes (diff sitemap XML)
369
+ 4. **Store hash** in `app-knowledge.md` → `Exploration State` section
370
+
371
+ ```markdown
372
+ ## Exploration State
373
+ Last explored: 2026-04-12T07:30:00Z
374
+ Sitemap hash: sha256:abc123...
375
+ Routes count: 20
376
+ ```
377
+
378
+ This prevents redundant exploration on every `/opsx:e2e` run.
379
+
350
380
  #### 4.6. Update app-knowledge.md
351
381
 
352
382
  After writing `app-exploration.md`, extract **project-level shared knowledge** and append to `tests/playwright/app-knowledge.md`:
@@ -502,6 +532,28 @@ Is this assertion about a visible UI result?
502
532
  4. Write test code with verified selectors
503
533
  5. If selector unverifiable → note for Healer (Step 9)
504
534
 
535
+ **Selector Caching — reuse Step 4 exploration results:**
536
+
537
+ After Step 4, verified selectors are stored in `app-exploration.md`. Before navigating to verify a selector in Step 6, check if the route already has verified selectors from Step 4.
538
+
539
+ ```javascript
540
+ // Step 6: Before navigating to verify, check cached selectors
541
+ const cachedSelectors = appExploration.routes.find(r => r.path === routePath)?.elements
542
+ if (cachedSelectors && cachedSelectors.length > 0) {
543
+ // Use first-priority selector from cache instead of re-navigating
544
+ const selector = getFirstPrioritySelector(cachedSelectors)
545
+ // Only navigate to verify if selector is missing or marked "Fragile"
546
+ if (selector.stability !== 'Fragile') {
547
+ return selector // use cached, skip verification navigation
548
+ }
549
+ }
550
+ // Fallback: navigate + snapshot + verify
551
+ ```
552
+
553
+ **Benefit**: For a 50-test case suite, this saves 30-50 redundant navigations (~2-5 minutes).
554
+
555
+ **File to read**: The selector caching uses data already stored in Step 4.4's `app-exploration.md` output — no new file needed.
556
+
505
557
  **Test coverage — empty states**: For list/detail pages, explore the empty state. If the app shows a "no data" UI when the list is empty, generate a test to verify it. Empty states are often missing from specs but are real user paths.
506
558
 
507
559
  **Test coverage — special elements**: Check `app-exploration.md` → **Special Elements Detected** table. For each special element, generate tests using the following strategies:
@@ -622,7 +674,7 @@ Auth required. To set up:
622
674
 
623
675
  **Idempotency**: If `auth.setup.ts` already exists → verify format, update only if stale.
624
676
 
625
- **Post-auth re-exploration**: If Step 4 skipped protected routes due to missing auth, re-run exploration for those routes now that auth is configured. Navigate to each protected route with auth context → `browser_snapshot` → update `app-exploration.md`. Selectors verified now are better than guesses used during test generation.
677
+ **Post-auth re-exploration**: If Step 4 skipped protected routes due to missing auth, re-run exploration for those routes now that auth is configured. Navigate to each protected route with auth context → `$B snapshot` → update `app-exploration.md`. Selectors verified now are better than guesses used during test generation.
626
678
 
627
679
  ### 8. Configure playwright.config.ts
628
680
 
@@ -742,41 +794,65 @@ For every App Bug classified in Phase 1, record it in `openspec/reports/app-bug-
742
794
 
743
795
  **Healer — Phase 2: Repair**
744
796
 
797
+ > **Performance note**: Playwright startup (~15-20s) is the dominant cost. Batch diagnosis first, then sequential isolated runs.
798
+
745
799
  After Triage classifies failure as "Test Bug" or "Ambiguous":
746
800
 
747
- 1. Navigate to the failing page
748
- 2. Get page snapshot: `browser_snapshot`
749
- 3. **EXPLICIT COMPARISON** output before fixing:
801
+ **Step 0 Batch diagnosis** (no Playwright, fast):
802
+ For ALL Phase 2 failures in this run, do this simultaneously:
803
+ 1. Read the failing test spec file(s) to understand what each test verifies
804
+ 2. Read `app-knowledge.md` for any previous fixes or selector patterns
805
+ 3. Read `app-knowledge.md` → **Common Selector Patterns** for project conventions
806
+ 4. For each test, output:
807
+ ```
808
+ TEST: <test-name>
809
+ ROUTE: <route>
810
+ ASSERTION: "<what the test expects>"
811
+ EXPECTED_BEHAVIOR: <from spec, one line>
812
+ KNOWN_FIX: <yes/no — from app-knowledge.md Selector Fixes table>
813
+ SELECTOR_CANDIDATES: <list from app-knowledge.md patterns if available>
814
+ ```
815
+ 5. Classify each test:
816
+ - `ready-to-fix` — known fix exists, or selector pattern is clear
817
+ - `needs-assertion-fix` — assertion itself needs changing (typo, wrong expected value)
818
+ - `needs-phase3` — ambiguous, no clear fix path
819
+ - `needs-more-diagnosis` — need to see actual page to determine
820
+
821
+ **Skip remaining steps for `needs-phase3` tests** — go directly to Phase 3.
822
+
823
+ **For `needs-more-diagnosis` tests** — go to Step 1 (navigate + snapshot) for each individually.
824
+
825
+ **Step 1** (for `needs-assertion-fix` or `needs-more-diagnosis`): Navigate to the failing page → `browser_snapshot` → **EXPLICIT COMPARISON**:
750
826
  ```
751
827
  ASSERTION: "<what the test expects>"
752
828
  ACTUAL: "<what the snapshot shows>"
753
829
  MATCH: <yes/no>
754
830
  ```
755
- 4. If MATCH=no:
756
831
 
757
- **⚠️ Assertion modification guard — never skip Phase 3 unless ALL conditions are met:**
832
+ **Assertion modification guard — never skip Phase 3 unless ALL conditions are met:**
833
+ - The test has **never passed with the current assertion** (newly generated test, or this is the first time this assertion fails)
834
+ - The ACTUAL value is **verifiably** from a different spec section (e.g., this test was in the wrong describe block)
835
+ - You can point to the **specific line in the spec** that defines the ACTUAL behavior
758
836
 
759
- - The test has **never passed with the current assertion** (newly generated test, or this is the first time this assertion fails)
760
- - The ACTUAL value is **verifiably** from a different spec section (e.g., this test was in the wrong describe block)
761
- - You can point to the **specific line in the spec** that defines the ACTUAL behavior
837
+ **If ANY condition is uncertain Phase 3 immediately.** Do NOT modify the assertion.
762
838
 
763
- **If ANY condition is uncertain → Phase 3 immediately.** Do NOT modify the assertion.
839
+ **Safe to fix without Phase 3:**
840
+ - Typo in assertion (e.g., "Subm\"it" vs "Submit" in the expected text)
841
+ - Selector was correct but the element was moved to a different location (same text, different selector)
842
+ - Explicit spec drift confirmed by reading the spec (e.g., spec says "button says Submit" but test says "button says Submit Form")
764
843
 
765
- **Safe to fix without Phase 3:**
766
- - Typo in assertion (e.g., "Submmit" vs "Submit" in the expected text)
767
- - Selector was correct but the element was moved to a different location (same text, different selector)
768
- - Explicit spec drift confirmed by reading the spec (e.g., spec says "button says Submit" but test says "button says Submit Form")
844
+ **Never fix without Phase 3:**
845
+ - App behavior changed after an action (e.g., "after clicking submit, balance should decrease" ACTUAL shows no change → **Phase 3**, could be optimistic update bug, backend failure, or spec mismatch)
846
+ - Data values differ (e.g., expected "¥1000" but got "¥999" **Phase 3**, could be rounding, discount, or calculation bug)
847
+ - Missing elements after interaction (e.g., "after creating order, success message should appear" no message **Phase 3**)
769
848
 
770
- **Never fix without Phase 3:**
771
- - App behavior changed after an action (e.g., "after clicking submit, balance should decrease" → ACTUAL shows no change → **Phase 3**, could be optimistic update bug, backend failure, or spec mismatch)
772
- - Data values differ (e.g., expected "¥1000" but got "¥999" → **Phase 3**, could be rounding, discount, or calculation bug)
773
- - Missing elements after interaction (e.g., "after creating order, success message should appear" → no message → **Phase 3**)
849
+ **Steps 2-4 (original)** consolidated into Step 1 (navigate + snapshot + comparison).
774
850
 
775
- 5. If selector issue generate candidate list, then select:
851
+ **Step 5** (for `ready-to-fix` with selector issue): Generate candidate list from snapshot:
776
852
 
777
- **5a. Extract candidates** — identify the target element from the failing test's assertion, then from the snapshot list all selectors for that element:
853
+ **5a. Extract candidates** — identify the target element from the failing test's assertion, then from the snapshot list all selectors for that element.
778
854
 
779
- First check `app-knowledge.md` → **Common Selector Patterns** for project-specific conventions (e.g., if project uses Tailwind and `.btn-primary` is listed as preferred → treat it as Fair or higher, not Fragile).
855
+ First check `app-knowledge.md` → **Common Selector Patterns** for project-specific conventions.
780
856
 
781
857
  ```
782
858
  Target: <element from failing assertion, e.g. "button with text 'Submit'">
@@ -793,8 +869,14 @@ After Triage classifies failure as "Test Bug" or "Ambiguous":
793
869
 
794
870
  **5b. Select top candidate** — pick the highest-stability candidate that matches the target. Output: `SELECTED: <selector> — reason: <why this one>`.
795
871
 
796
- 6. Apply fix re-run **only that test** (attempt 1/3)
797
- 7. If healed append to `app-knowledge.md` **Selector Fixes** table (route, old → new selector, reason, date)
872
+ **Step 6** — **Apply ALL fixes, then run sequential isolated**:
873
+ 1. Apply all prepared fixes to all Phase 2 tests in this run
874
+ 2. Run: `npx playwright test --workers=1 --grep "<test1|test2|test3|...>"` (all Phase 2 tests as a single combined grep)
875
+ 3. `--workers=1` ensures true isolated execution — no data races, no shared state pollution
876
+ 4. Each test result is independent — even if one fails, others proceed cleanly
877
+ 5. Report per-test result (pass/fail/error) and update heal counters accordingly
878
+
879
+ **Step 7** — If healed → append to `app-knowledge.md` → **Selector Fixes** table (route, old → new selector, reason, date)
798
880
 
799
881
  **Element Missing handling (when browser_snapshot shows element not found):**
800
882
 
package/README.md CHANGED
@@ -20,7 +20,7 @@ openspec-pw init # Install Playwright E2E integration
20
20
 
21
21
  ## Supported AI Coding Assistants
22
22
 
23
- Claude Code — E2E workflow is driven by SKILL.md using Playwright MCP tools (`/opsx:e2e <change-name>`).
23
+ Claude Code — E2E workflow is driven by SKILL.md using /browse (exploration) + Playwright MCP (test execution).
24
24
 
25
25
  ## Usage
26
26
 
@@ -52,7 +52,7 @@ openspec-pw uninstall # Remove integration from the project
52
52
 
53
53
  ├── 3. Validate env → run seed.spec.ts
54
54
 
55
- ├── 4. Explore app → Playwright MCP explores real DOM
55
+ ├── 4. Explore app → /browse explores real DOM
56
56
  │ ├─ Read app-knowledge.md (project-level knowledge)
57
57
  │ ├─ Extract routes from specs
58
58
  │ ├─ Navigate each route → snapshot → screenshot
@@ -77,13 +77,10 @@ openspec-pw uninstall # Remove integration from the project
77
77
  ## Prerequisites
78
78
 
79
79
  1. **Node.js >= 20**
80
- 2. **OpenSpec** initialized: `npm install -g @fission-ai/openspec && openspec init`
81
- 3. **Claude Code** with `.claude/` directory
82
-
83
- After prerequisites, install Playwright MCP:
84
- ```bash
85
- claude mcp add playwright npx @playwright/mcp@latest
86
- ```
80
+ 2. **Claude Code** with `.claude/` directory
81
+ 3. **gstack** (for exploration + browser QA): `git clone --single-branch --depth 1 https://github.com/garrytan/gstack.git ~/.claude/skills/gstack && cd ~/.claude/skills/gstack && ./setup`
82
+ 4. **OpenSpec** initialized: `npm install -g @fission-ai/openspec && openspec init`
83
+ 5. **Playwright MCP** (for test execution + Healer): `claude mcp add playwright npx @playwright/mcp@latest`
87
84
 
88
85
  ## What `openspec-pw init` Does
89
86
 
@@ -101,14 +98,15 @@ Run through these steps in order when using the E2E workflow for the first time:
101
98
  | Step | Command | If it fails |
102
99
  |------|---------|-------------|
103
100
  | 1. Install CLI | `npm install -g openspec-playwright` | Check Node.js version `node -v` (needs >= 20) |
104
- | 2. Install OpenSpec | `npm install -g @fission-ai/openspec && openspec init` | `npm cache clean -f && npm install -g @fission-ai/openspec` |
105
- | 3. Initialize E2E | `openspec-pw init` | Run `openspec-pw doctor` to see what's missing |
106
- | 4. Install Playwright MCP | `claude mcp add playwright npx @playwright/mcp@latest` | `claude mcp list` to confirm installation |
107
- | 5. Install browsers | `npx playwright install --with-deps` | macOS may need `xcode-select --install` first |
108
- | 6. Start dev server | `npm run dev` (in a separate terminal) | Confirm port, set `BASE_URL` if non-standard |
109
- | 7. Validate env | `npx playwright test tests/playwright/seed.spec.ts` | Check `webServer` in `playwright.config.ts` |
110
- | 8. Configure auth (if needed) | See "Authentication" below | Debug with `npx playwright test --project=setup` |
111
- | 9. Run first E2E | `/opsx:e2e <change-name>` | Check `openspec/reports/` for the report |
101
+ | 2. Install gstack | `git clone --single-branch --depth 1 https://github.com/garrytan/gstack.git ~/.claude/skills/gstack && cd ~/.claude/skills/gstack && ./setup` | Requires Bun: `curl -fsSL https://bun.sh/install | bash` |
102
+ | 3. Install OpenSpec | `npm install -g @fission-ai/openspec && openspec init` | `npm cache clean -f && npm install -g @fission-ai/openspec` |
103
+ | 4. Initialize E2E | `openspec-pw init` | Run `openspec-pw doctor` to see what's missing |
104
+ | 5. Install Playwright MCP | `claude mcp add playwright npx @playwright/mcp@latest` | `claude mcp list` to confirm installation |
105
+ | 6. Install browsers | `npx playwright install --with-deps` | macOS may need `xcode-select --install` first |
106
+ | 7. Start dev server | `npm run dev` (in a separate terminal) | Confirm port, set `BASE_URL` if non-standard |
107
+ | 8. Validate env | `npx playwright test tests/playwright/seed.spec.ts` | Check `webServer` in `playwright.config.ts` |
108
+ | 9. Configure auth (if needed) | See "Authentication" below | Debug with `npx playwright test --project=setup` |
109
+ | 10. Run first E2E | `/opsx:e2e <change-name>` | Check `openspec/reports/` for the report |
112
110
 
113
111
  ## Authentication
114
112
 
package/README.zh-CN.md CHANGED
@@ -13,13 +13,10 @@ npm install -g openspec-playwright
13
13
  ## 前置条件
14
14
 
15
15
  1. **Node.js >= 20**
16
- 2. **OpenSpec** 已初始化: `npm install -g @fission-ai/openspec && openspec init`
17
- 3. **Claude Code** 且项目中有 `.claude/` 目录
18
-
19
- 安装 Playwright MCP
20
- ```bash
21
- claude mcp add playwright npx @playwright/mcp@latest
22
- ```
16
+ 2. **Claude Code** 且项目中有 `.claude/` 目录
17
+ 3. **gstack**(用于探索 + 浏览器 QA):`git clone --single-branch --depth 1 https://github.com/garrytan/gstack.git ~/.claude/skills/gstack && cd ~/.claude/skills/gstack && ./setup`
18
+ 4. **OpenSpec** 已初始化: `npm install -g @fission-ai/openspec && openspec init`
19
+ 5. **Playwright MCP**(用于测试执行 + Healer):`claude mcp add playwright npx @playwright/mcp@latest`
23
20
 
24
21
  ## 初始化
25
22
 
@@ -33,7 +30,7 @@ openspec-pw init # 安装 Playwright E2E 集成
33
30
 
34
31
  ## 支持的 AI 编码助手
35
32
 
36
- Claude Code — E2E 工作流由 SKILL.md 驱动,使用 Playwright MCP 工具(`/opsx:e2e <change-name>`)。
33
+ Claude Code — E2E 工作流由 SKILL.md 驱动,使用 /browse(探索)+ Playwright MCP(测试执行)。
37
34
 
38
35
  ## 使用
39
36
 
@@ -65,7 +62,7 @@ openspec-pw uninstall # 移除项目中的集成
65
62
 
66
63
  ├── 3. 验证环境 → 运行 seed.spec.ts
67
64
 
68
- ├── 4. 探索应用 → Playwright MCP 探索真实 DOM
65
+ ├── 4. 探索应用 → /browse 探索真实 DOM
69
66
  │ ├─ 读取 app-knowledge.md(项目级知识)
70
67
  │ ├─ 从 specs 提取路由
71
68
  │ ├─ 遍历每个路由 → snapshot → screenshot
@@ -102,14 +99,15 @@ openspec-pw uninstall # 移除项目中的集成
102
99
  | 步骤 | 命令 | 失败时快速修复 |
103
100
  |------|------|----------------|
104
101
  | 1. 安装 CLI | `npm install -g openspec-playwright` | 检查 Node.js 版本 `node -v`(需 >= 20) |
105
- | 2. 安装 OpenSpec | `npm install -g @fission-ai/openspec && openspec init` | `npm cache clean -f && npm install -g @fission-ai/openspec` |
106
- | 3. 初始化 E2E | `openspec-pw init` | 运行 `openspec-pw doctor` 查看具体缺失项 |
107
- | 4. 安装 Playwright MCP | `claude mcp add playwright npx @playwright/mcp@latest` | `claude mcp list` 确认安装成功 |
108
- | 5. 安装浏览器 | `npx playwright install --with-deps` | macOS 可能需先运行 `xcode-select --install` |
109
- | 6. 启动开发服务器 | `npm run dev`(在另一个终端) | 确认端口,配置 `BASE_URL` |
110
- | 7. 验证环境 | `npx playwright test tests/playwright/seed.spec.ts` | 检查 `playwright.config.ts` 中的 `webServer` 配置 |
111
- | 8. 配置认证(如需要) | 见下方"认证配置" | `npx playwright test --project=setup` 调试 |
112
- | 9. 运行第一个 E2E | `/opsx:e2e <change-name>` | 查看 `openspec/reports/` 中的报告 |
102
+ | 2. 安装 gstack | `git clone --single-branch --depth 1 https://github.com/garrytan/gstack.git ~/.claude/skills/gstack && cd ~/.claude/skills/gstack && ./setup` | 需要 Bun:`curl -fsSL https://bun.sh/install \| bash` |
103
+ | 3. 安装 OpenSpec | `npm install -g @fission-ai/openspec && openspec init` | `npm cache clean -f && npm install -g @fission-ai/openspec` |
104
+ | 4. 初始化 E2E | `openspec-pw init` | 运行 `openspec-pw doctor` 查看具体缺失项 |
105
+ | 5. 安装 Playwright MCP | `claude mcp add playwright npx @playwright/mcp@latest` | `claude mcp list` 确认安装成功 |
106
+ | 6. 安装浏览器 | `npx playwright install --with-deps` | macOS 可能需先运行 `xcode-select --install` |
107
+ | 7. 启动开发服务器 | `npm run dev`(在另一个终端) | 确认端口,配置 `BASE_URL` |
108
+ | 8. 验证环境 | `npx playwright test tests/playwright/seed.spec.ts` | 检查 `playwright.config.ts` 中的 `webServer` 配置 |
109
+ | 9. 配置认证(如需要) | 见下方"认证配置" | `npx playwright test --project=setup` 调试 |
110
+ | 10. 运行第一个 E2E | `/opsx:e2e <change-name>` | 查看 `openspec/reports/` 中的报告 |
113
111
 
114
112
  ## 认证配置
115
113
 
@@ -0,0 +1,17 @@
1
+ export interface ExploreOptions {
2
+ parallel?: number;
3
+ dryRun?: boolean;
4
+ }
5
+ export interface RouteResult {
6
+ path: string;
7
+ url: string;
8
+ status: "ok" | "error" | "auth-required" | "skipped";
9
+ errorMessage?: string;
10
+ snapshot: {
11
+ title?: string;
12
+ mainHeading?: string;
13
+ formCount: number;
14
+ linkCount: number;
15
+ };
16
+ }
17
+ export declare function explore(options: ExploreOptions): Promise<void>;
@@ -0,0 +1,366 @@
1
+ import { chromium } from "playwright";
2
+ import chalk from "chalk";
3
+ import { existsSync, cpSync, renameSync, readFileSync, writeFileSync } from "fs";
4
+ import { join } from "path";
5
+ // ── File utilities ────────────────────────────────────────────────────────────────
6
+ /**
7
+ * Atomic write: backup original, write new content, revert on failure.
8
+ * Uses cpSync + rename for atomicity (POSIX rename is atomic on success).
9
+ */
10
+ function atomicWrite(filePath, content) {
11
+ const backupPath = `${filePath}.bak`;
12
+ cpSync(filePath, backupPath, { force: true });
13
+ try {
14
+ writeFileSync(filePath, content, "utf-8");
15
+ }
16
+ catch (err) {
17
+ cpSync(backupPath, filePath, { force: true });
18
+ throw err;
19
+ }
20
+ }
21
+ const LOCK_TTL_MS = 30 * 60 * 1000; // 30 minutes
22
+ /**
23
+ * Acquire a lock file. Stale locks (>30min) are auto-removed.
24
+ * Returns false if already locked by a live process.
25
+ */
26
+ function acquireLock(lockPath) {
27
+ // Check for stale lock from crashed process
28
+ if (existsSync(lockPath)) {
29
+ try {
30
+ const content = readFileSync(lockPath, "utf-8");
31
+ const [pidStr, tsStr] = content.split(":");
32
+ const ts = parseInt(tsStr, 10);
33
+ if (!isNaN(ts) && Date.now() - ts > LOCK_TTL_MS) {
34
+ // Stale: remove and retry
35
+ renameSync(lockPath, `${lockPath}.stale`);
36
+ }
37
+ else {
38
+ // Check if process is still alive
39
+ if (pidStr) {
40
+ try {
41
+ process.kill(parseInt(pidStr, 10), 0);
42
+ return false; // Process alive, lock held
43
+ }
44
+ catch {
45
+ // Process dead, stale lock
46
+ renameSync(lockPath, `${lockPath}.stale`);
47
+ }
48
+ }
49
+ }
50
+ }
51
+ catch {
52
+ // Can't read lock, try to remove it
53
+ try {
54
+ renameSync(lockPath, `${lockPath}.stale`);
55
+ }
56
+ catch { /* ignore */ }
57
+ }
58
+ }
59
+ try {
60
+ writeFileSync(lockPath, `${process.pid}:${Date.now()}`, { flag: "wx" });
61
+ return true;
62
+ }
63
+ catch {
64
+ return false;
65
+ }
66
+ }
67
+ function releaseLock(lockPath) {
68
+ try {
69
+ if (existsSync(lockPath))
70
+ renameSync(lockPath, `${lockPath}.released`);
71
+ }
72
+ catch {
73
+ // ignore — lock release is best-effort
74
+ }
75
+ }
76
+ // ── Parsing ────────────────────────────────────────────────────────────────────
77
+ function parseExplorationFile(content) {
78
+ const baseUrlMatch = content.match(/BASE_URL:\s*(\S+)/);
79
+ const baseUrl = baseUrlMatch ? baseUrlMatch[1].trim() : "http://localhost:3000";
80
+ const routes = [];
81
+ // More permissive regex: match any column count, allow spaces in route path
82
+ const tableRegex = /^\|\s*(\/[^\s|]+)\s*\|\s*([^|]+?)\s*\|\s*([^|]+?)\s*\|\s*(.+?)\s*\|/gm;
83
+ let match;
84
+ while ((match = tableRegex.exec(content)) !== null) {
85
+ routes.push({
86
+ path: match[1],
87
+ auth: match[2].trim(),
88
+ status: match[3].trim(),
89
+ readySignal: match[4].trim(),
90
+ });
91
+ }
92
+ return { baseUrl, routes, rawContent: content };
93
+ }
94
+ function updateExplorationFile(original, results) {
95
+ const lines = original.rawContent.split("\n");
96
+ const updatedLines = [];
97
+ for (const line of lines) {
98
+ // Update the Exploration Summary table with new results
99
+ if (line.match(/^\| \//)) {
100
+ const pathMatch = line.match(/^\| (\/\S+)/);
101
+ if (pathMatch) {
102
+ const path = pathMatch[1];
103
+ const result = results.find((r) => r.path === path);
104
+ if (result) {
105
+ const icon = result.status === "ok"
106
+ ? "explored"
107
+ : result.status === "error"
108
+ ? "error"
109
+ : result.status === "auth-required"
110
+ ? "auth-required"
111
+ : "skipped";
112
+ // Replace status column (3rd column)
113
+ const cols = line.split("|");
114
+ if (cols.length >= 4) {
115
+ cols[3] = ` ${icon} `;
116
+ updatedLines.push(cols.join("|"));
117
+ continue;
118
+ }
119
+ }
120
+ }
121
+ }
122
+ updatedLines.push(line);
123
+ }
124
+ return updatedLines.join("\n");
125
+ }
126
+ function appendFailureSection(content, results) {
127
+ const failures = results.filter((r) => r.status === "error" || r.status === "auth-required");
128
+ if (failures.length === 0)
129
+ return content;
130
+ const failureLines = [
131
+ "",
132
+ "## Exploration Failures",
133
+ "",
134
+ "| Route | Status | Error | Notes |",
135
+ "|-------|--------|-------|-------|",
136
+ ];
137
+ for (const f of failures) {
138
+ const notes = f.status === "auth-required"
139
+ ? "Requires authentication — set up auth.setup.ts"
140
+ : "";
141
+ failureLines.push(`| ${f.path} | ${f.status} | ${f.errorMessage ?? "unknown"} | ${notes} |`);
142
+ }
143
+ failureLines.push("");
144
+ return content + failureLines.join("\n");
145
+ }
146
+ // ── Worker ──────────────────────────────────────────────────────────────────────
147
+ async function exploreRoute(page, route, baseUrl) {
148
+ const url = `${baseUrl}${route}`;
149
+ const loginPatterns = [
150
+ "/login",
151
+ "/signin",
152
+ "/auth",
153
+ "/sign-in",
154
+ "/log-in",
155
+ "/oauth",
156
+ "/sso",
157
+ ];
158
+ try {
159
+ const response = await page.goto(url, {
160
+ waitUntil: "networkidle",
161
+ timeout: 30000,
162
+ });
163
+ const status = response?.status() ?? 0;
164
+ const finalUrl = page.url();
165
+ // Detect redirect to login page (auth-required route)
166
+ if (status < 400) {
167
+ try {
168
+ const expectedOrigin = new URL(baseUrl).origin;
169
+ const finalOrigin = new URL(finalUrl).origin;
170
+ if (finalOrigin === expectedOrigin &&
171
+ finalUrl !== url &&
172
+ loginPatterns.some((p) => finalUrl.toLowerCase().includes(p))) {
173
+ return {
174
+ path: route,
175
+ url,
176
+ status: "auth-required",
177
+ errorMessage: `Redirected to login`,
178
+ snapshot: { formCount: 0, linkCount: 0 },
179
+ };
180
+ }
181
+ }
182
+ catch {
183
+ // URL parsing failed — proceed with status-based result
184
+ }
185
+ }
186
+ if (status >= 400) {
187
+ return {
188
+ path: route,
189
+ url,
190
+ status: "error",
191
+ errorMessage: `HTTP ${status}`,
192
+ snapshot: { formCount: 0, linkCount: 0 },
193
+ };
194
+ }
195
+ // Collect snapshot data
196
+ const snapshot = await page.evaluate(() => {
197
+ const title = document.title;
198
+ const h1 = document.querySelector("h1");
199
+ const mainHeading = h1?.textContent?.trim();
200
+ const formCount = document.querySelectorAll("form").length;
201
+ const linkCount = document.querySelectorAll("a").length;
202
+ return { title, mainHeading, formCount, linkCount };
203
+ });
204
+ return {
205
+ path: route,
206
+ url,
207
+ status: "ok",
208
+ snapshot,
209
+ };
210
+ }
211
+ catch (err) {
212
+ const error = err;
213
+ return {
214
+ path: route,
215
+ url,
216
+ status: "error",
217
+ errorMessage: error.message,
218
+ snapshot: { formCount: 0, linkCount: 0 },
219
+ };
220
+ }
221
+ }
222
+ async function runWorker(workerId, routes, baseUrl) {
223
+ const results = [];
224
+ if (routes.length === 0)
225
+ return results;
226
+ let browser;
227
+ try {
228
+ browser = await chromium.launch({
229
+ headless: true,
230
+ timeout: 60000,
231
+ });
232
+ const context = await browser.newContext();
233
+ const page = await context.newPage();
234
+ for (const route of routes) {
235
+ const result = await exploreRoute(page, route, baseUrl);
236
+ results.push(result);
237
+ const icon = result.status === "ok"
238
+ ? chalk.green("ok")
239
+ : result.status === "auth-required"
240
+ ? chalk.yellow("auth")
241
+ : chalk.red("err");
242
+ const pathDisplay = route.length > 40 ? route.slice(0, 40) + "..." : route;
243
+ console.log(` [worker ${workerId}] ${icon} ${pathDisplay}`);
244
+ }
245
+ await context.close();
246
+ }
247
+ finally {
248
+ if (browser) {
249
+ await browser.close();
250
+ }
251
+ }
252
+ return results;
253
+ }
254
+ function chunkRoutes(routes, numWorkers) {
255
+ const chunks = Array.from({ length: numWorkers }, () => []);
256
+ routes.forEach((route, i) => {
257
+ chunks[i % numWorkers].push(route);
258
+ });
259
+ return chunks;
260
+ }
261
+ // ── Main ───────────────────────────────────────────────────────────────────────
262
+ export async function explore(options) {
263
+ const projectRoot = process.cwd();
264
+ const MAX_WORKERS = 16;
265
+ const numWorkers = Math.min(options.parallel ?? 4, MAX_WORKERS);
266
+ const startTime = Date.now();
267
+ // ── Signal handling: clean up on Ctrl+C / SIGTERM ──
268
+ let interrupted = false;
269
+ const cleanup = () => {
270
+ if (interrupted)
271
+ return;
272
+ interrupted = true;
273
+ console.log(chalk.yellow("\n Interrupted — cleaning up Chromium processes...\n"));
274
+ process.exit(130);
275
+ };
276
+ process.on("SIGINT", cleanup);
277
+ process.on("SIGTERM", cleanup);
278
+ console.log(chalk.blue(`\nApp Exploration — ${numWorkers} workers\n`));
279
+ // 1. Read app-exploration.md
280
+ const explorationPath = join(projectRoot, "app-exploration.md");
281
+ if (!existsSync(explorationPath)) {
282
+ console.log(chalk.red(` app-exploration.md not found in ${projectRoot}\n` +
283
+ ` Run openspec-pw init first to generate it, or create it manually.\n`));
284
+ process.exit(1);
285
+ }
286
+ const fileContent = readFileSync(explorationPath, "utf-8");
287
+ const parsed = parseExplorationFile(fileContent);
288
+ if (parsed.routes.length === 0) {
289
+ console.log(chalk.red(` ERROR: No routes parsed from app-exploration.md.\n` +
290
+ ` Check that the file contains a valid route table with | /path | ... | format.\n`));
291
+ process.exit(1);
292
+ }
293
+ const baseUrl = process.env.BASE_URL ?? parsed.baseUrl;
294
+ const paths = parsed.routes.map((r) => r.path);
295
+ console.log(chalk.blue("─── Dry Run ───"));
296
+ if (options.dryRun) {
297
+ for (let i = 0; i < paths.length; i++) {
298
+ const chunk = i % numWorkers;
299
+ console.log(` worker ${chunk}: ${paths[i]}`);
300
+ }
301
+ console.log(chalk.gray(`\n (dry run — no browsers launched)\n`));
302
+ return;
303
+ }
304
+ // 1b. Acquire lock to prevent concurrent runs from overwriting each other
305
+ const lockPath = join(projectRoot, ".explore.lock");
306
+ if (!acquireLock(lockPath)) {
307
+ console.log(chalk.red(` ERROR: Another explore process is running.\n` +
308
+ ` Remove ${lockPath} if no other process is active.\n`));
309
+ process.exit(1);
310
+ }
311
+ try {
312
+ // 2. Split routes into chunks
313
+ const chunks = chunkRoutes(paths, numWorkers);
314
+ const nonEmptyChunks = chunks
315
+ .map((c, i) => ({ chunk: c, id: i }))
316
+ .filter((x) => x.chunk.length > 0);
317
+ console.log(chalk.blue(`─── Exploring ${paths.length} routes with ${nonEmptyChunks.length} workers ───\n`));
318
+ // 3. Launch workers concurrently
319
+ const workerPromises = nonEmptyChunks.map(({ chunk, id }) => runWorker(id, chunk, baseUrl));
320
+ const settled = await Promise.allSettled(workerPromises);
321
+ // 4. Merge results
322
+ const allResults = [];
323
+ for (const result of settled) {
324
+ if (result.status === "fulfilled") {
325
+ allResults.push(...result.value);
326
+ }
327
+ }
328
+ // 5. Sort results to match original route order
329
+ const pathOrder = new Map(paths.map((p, i) => [p, i]));
330
+ allResults.sort((a, b) => (pathOrder.get(a.path) ?? 0) - (pathOrder.get(b.path) ?? 0));
331
+ // 6. Write updated app-exploration.md (atomic)
332
+ let updatedContent = updateExplorationFile(parsed, allResults);
333
+ updatedContent = appendFailureSection(updatedContent, allResults);
334
+ atomicWrite(explorationPath, updatedContent);
335
+ // 7. Print summary
336
+ const durationMs = Date.now() - startTime;
337
+ const durationSec = (durationMs / 1000).toFixed(1);
338
+ const okCount = allResults.filter((r) => r.status === "ok").length;
339
+ const authCount = allResults.filter((r) => r.status === "auth-required").length;
340
+ const errorCount = allResults.filter((r) => r.status === "error").length;
341
+ const totalCount = allResults.length;
342
+ console.log(chalk.blue("\n─── Summary ───"));
343
+ console.log(` Routes explored: ${totalCount} ` +
344
+ chalk.green(`ok ${okCount}`) +
345
+ (authCount > 0
346
+ ? ` ${chalk.yellow(`auth-required ${authCount}`)}`
347
+ : "") +
348
+ " " +
349
+ (errorCount > 0
350
+ ? chalk.red(`error ${errorCount}`)
351
+ : chalk.gray(`error ${errorCount}`)) +
352
+ ` Duration: ${durationSec}s`);
353
+ console.log(chalk.blue("\n─── Updated ───"));
354
+ console.log(chalk.green(` ${explorationPath}\n`));
355
+ if (errorCount > 0) {
356
+ console.log(chalk.red(`Exploration completed with ${errorCount} error(s).\n`));
357
+ }
358
+ else {
359
+ console.log(chalk.green("Exploration completed successfully.\n"));
360
+ }
361
+ }
362
+ finally {
363
+ releaseLock(lockPath);
364
+ }
365
+ }
366
+ //# sourceMappingURL=explore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"explore.js","sourceRoot":"","sources":["../../src/commands/explore.ts"],"names":[],"mappings":"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"}
@@ -3,6 +3,8 @@ export interface RunOptions {
3
3
  timeout?: number;
4
4
  json?: boolean;
5
5
  grep?: string;
6
+ smoke?: boolean;
7
+ workers?: number;
6
8
  appBugs?: number;
7
9
  healed?: number;
8
10
  raft?: number;
@@ -39,9 +39,24 @@ export async function run(changeName, options) {
39
39
  if (options.project) {
40
40
  args.push("--project=" + options.project);
41
41
  }
42
- if (options.grep) {
42
+ // Escape regex special chars in grep to prevent injection.
43
+ // Note: | is escaped too, so grep "a|b" matches the literal string "a|b",
44
+ // not "a OR b". For OR semantics, run two separate commands.
45
+ const escapeGrep = (s) => s.replace(/[.*+?()[\]{}|\\^$]/g, "\\$&");
46
+ if (options.grep && options.smoke) {
47
+ // Run tests matching BOTH smoke tag AND the grep pattern
48
+ const eg = escapeGrep(options.grep);
49
+ args.push("--grep", `@smoke.*${eg}|${eg}.*@smoke`);
50
+ }
51
+ else if (options.grep) {
43
52
  args.push("--grep=" + options.grep);
44
53
  }
54
+ else if (options.smoke) {
55
+ args.push("--grep", "@smoke");
56
+ }
57
+ if (options.workers) {
58
+ args.push("--workers=" + options.workers);
59
+ }
45
60
  if (options.headed) {
46
61
  args.push("--headed");
47
62
  }
@@ -1 +1 @@
1
- {"version":3,"file":"run.js","sourceRoot":"","sources":["../../src/commands/run.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"run.js","sourceRoot":"","sources":["../../src/commands/run.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,IAAI,CAAC;AACxE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,KAAK,MAAM,OAAO,CAAC;AAiB1B,MAAM,WAAW,GAAG,kBAAkB,CAAC;AAEvC,MAAM,CAAC,KAAK,UAAU,GAAG,CAAC,UAAkB,EAAE,OAAmB;IAC/D,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,iCAAiC,UAAU,IAAI,CAAC,CAAC,CAAC;IAEzE,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAElC,6BAA6B;IAC7B,MAAM,YAAY,GAChB,UAAU,KAAK,KAAK;QAClB,CAAC,CAAC,iBAAiB;QACnB,CAAC,CAAC,GAAG,UAAU,UAAU,CAAC;IAC9B,MAAM,QAAQ,GAAG,IAAI,CACnB,WAAW,EACX,OAAO,EACP,YAAY,EACZ,UAAU,KAAK,KAAK,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,WAAW,UAAU,IAAI,YAAY,EAAE,CAC9E,CAAC;IACF,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC1B,MAAM,mBAAmB,GACvB,UAAU,KAAK,KAAK;YAClB,CAAC,CAAC,oBAAoB,YAAY,EAAE;YACpC,CAAC,CAAC,4BAA4B,UAAU,IAAI,YAAY,EAAE,CAAC;QAC/D,OAAO,CAAC,GAAG,CACT,KAAK,CAAC,GAAG,CAAC,4BAA4B,mBAAmB,EAAE,CAAC,CAC7D,CAAC;QACF,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,4CAA4C,CAAC,CAAC,CAAC;QACtE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,uBAAuB;IACvB,SAAS,CAAC,IAAI,CAAC,WAAW,EAAE,WAAW,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE/D,6BAA6B;IAC7B,MAAM,SAAS,GAAG,IAAI,CACpB,WAAW,EACX,OAAO,EACP,YAAY,EACZ,kBAAkB,CACnB,CAAC;IACF,MAAM,cAAc,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC;IAC7C,IAAI,CAAC,cAAc,EAAE,CAAC;QACpB,OAAO,CAAC,GAAG,CACT,KAAK,CAAC,MAAM,CACV,iEAAiE,CAClE,CACF,CAAC;IACJ,CAAC;IAED,oFAAoF;IACpF,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC,CAAC;IAEjD,MAAM,cAAc,GAAG,IAAI,CAAC,WAAW,EAAE,cAAc,CAAC,CAAC;IACzD,MAAM,cAAc,GAAG,IAAI,CAAC,cAAc,EAAE,cAAc,CAAC,CAAC;IAE5D,MAAM,IAAI,GAAG;QACX,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,QAAQ;QACrC,iBAAiB;QACjB,WAAW,GAAG,cAAc;KAC7B,CAAC;IACF,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QACpB,IAAI,CAAC,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5C,CAAC;IACD,2DAA2D;IAC3D,0EAA0E;IAC1E,6DAA6D;IAC7D,MAAM,UAAU,GAAG,CAAC,CAAS,EAAE,EAAE,CAC/B,CAAC,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;IAC3C,IAAI,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;QAClC,yDAAyD;QACzD,MAAM,EAAE,GAAG,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACpC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,WAAW,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC;IACrD,CAAC;SAAM,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;QACxB,IAAI,CAAC,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,CAAC;SAAM,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAChC,CAAC;IACD,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QACpB,IAAI,CAAC,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5C,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;QACnB,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACxB,CAAC;IACD,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC;QAC5B,IAAI,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;IAClC,CAAC;IAED,IAAI,UAAU,GAAG,EAAE,CAAC;IAEpB,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE;YACtC,GAAG,EAAE,WAAW;YAChB,QAAQ,EAAE,OAAO;YACjB,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;YAC/B,OAAO,EAAE,CAAC,OAAO,CAAC,OAAO,IAAI,GAAG,CAAC,GAAG,IAAI;SACzC,CAAC,CAAC;QACH,UAAU,GAAG,MAAM,CAAC;IACtB,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACtB,MAAM,KAAK,GAAG,GAA2C,CAAC;QAC1D,UAAU,GAAG,CAAC,KAAK,CAAC,MAAM,IAAI,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC;IAC3D,CAAC;IAED,mFAAmF;IACnF,MAAM,OAAO,GAAG,yBAAyB,CAAC,cAAc,CAAC,IAAI,qBAAqB,CAAC,UAAU,CAAC,CAAC;IAE/F,0BAA0B;IAC1B,IACE,UAAU,CAAC,QAAQ,CAAC,6BAA6B,CAAC;QAClD,UAAU,CAAC,QAAQ,CAAC,mBAAmB,CAAC;QACxC,UAAU,CAAC,QAAQ,CAAC,UAAU,CAAC,EAC/B,CAAC;QACD,OAAO,CAAC,GAAG,CACT,KAAK,CAAC,MAAM,CACV,gEAAgE,CACjE,CACF,CAAC;IACJ,CAAC;IAED,8BAA8B;IAC9B,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAC9E,MAAM,UAAU,GAAG,IAAI,CACrB,WAAW,EACX,WAAW,EACX,kBAAkB,UAAU,IAAI,SAAS,KAAK,CAC/C,CAAC;IAEF,MAAM,aAAa,GAAG,cAAc,CAAC,UAAU,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;IAC9E,aAAa,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;IAEzC,aAAa;IACb,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;QACjB,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAC3B;YACE,MAAM,EAAE,UAAU;YAClB,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,MAAM,EAAE,UAAU;YAClB,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,EAAE,EAAE,OAAO,CAAC,MAAM,KAAK,CAAC;SACzB,EACD,IAAI,EACJ,CAAC,CACF,CAAC;QACF,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACpB,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACxC,OAAO;IACT,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC,CAAC;IAC7C,OAAO,CAAC,GAAG,CACT,YAAY,OAAO,CAAC,KAAK,IAAI;QAC3B,KAAK,CAAC,KAAK,CAAC,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC;QAClC,IAAI;QACJ,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC;YACjB,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC;YAClC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;QACtC,eAAe,OAAO,CAAC,QAAQ,EAAE,CACpC,CAAC;IAEF,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC,CAAC;IAC5C,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,UAAU,IAAI,CAAC,CAAC,CAAC;IAEhD,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,OAAO,CAAC,GAAG,CACT,KAAK,CAAC,GAAG,CAAC,8BAA8B,OAAO,CAAC,MAAM,SAAS,CAAC,CACjE,CAAC;QACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,2BAA2B,CAAC,CAAC,CAAC;IACxD,CAAC;AACH,CAAC;AAmDD,SAAS,yBAAyB,CAAC,QAAgB;IACjD,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,OAAO,IAAI,CAAC;IAEvC,IAAI,GAAY,CAAC;IACjB,IAAI,CAAC;QACH,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC;IACpD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,IAAI,GAAG,GAAuB,CAAC;IACrC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM;QAAE,OAAO,IAAI,CAAC;IAEvC,MAAM,OAAO,GAAgB;QAC3B,KAAK,EAAE,CAAC;QACR,MAAM,EAAE,CAAC;QACT,MAAM,EAAE,CAAC;QACT,QAAQ,EAAE,IAAI;QACd,KAAK,EAAE,EAAE;KACV,CAAC;IAEF,qCAAqC;IACrC,IAAI,eAAe,GAAG,CAAC,CAAC;IAExB,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;QAChC,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;YAC/B,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;gBAClC,MAAM,MAAM,GACV,MAAM,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC;gBAEnD,sDAAsD;gBACtD,MAAM,aAAa,GAAG,MAAM,CAAC,WAAW,CAAC,IAAI,CAC3C,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,KAAK,WAAW,IAAI,CAAC,CAAC,IAAI,CAC/C,CAAC;gBAEF,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC;oBACjB,IAAI,EAAE,IAAI,CAAC,KAAK;oBAChB,MAAM;oBACN,UAAU,EAAE,aAAa,EAAE,IAAI,IAAI,SAAS;iBAC7C,CAAC,CAAC;gBAEH,OAAO,CAAC,KAAK,EAAE,CAAC;gBAChB,IAAI,MAAM,KAAK,QAAQ;oBAAE,OAAO,CAAC,MAAM,EAAE,CAAC;;oBACrC,OAAO,CAAC,MAAM,EAAE,CAAC;gBAEtB,eAAe,IAAI,MAAM,CAAC,QAAQ,CAAC;YACrC,CAAC;QACH,CAAC;IACH,CAAC;IAED,IAAI,OAAO,CAAC,KAAK,GAAG,CAAC,IAAI,eAAe,GAAG,CAAC,EAAE,CAAC;QAC7C,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,eAAe,GAAG,IAAI,CAAC,CAAC;QAChD,OAAO,CAAC,QAAQ,GAAG,IAAI,GAAG,EAAE,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,EAAE,CAAC,KAAK,IAAI,GAAG,EAAE,GAAG,CAAC;IACxF,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,kFAAkF;AAClF,8EAA8E;AAE9E,MAAM,UAAU,qBAAqB,CAAC,MAAc;IAClD,MAAM,OAAO,GAAgB;QAC3B,KAAK,EAAE,CAAC;QACR,MAAM,EAAE,CAAC;QACT,MAAM,EAAE,CAAC;QACT,QAAQ,EAAE,IAAI;QACd,KAAK,EAAE,EAAE;KACV,CAAC;IAEF,kDAAkD;IAClD,MAAM,aAAa,GAAG,0CAA0C,CAAC;IACjE,IAAI,KAAK,CAAC;IACV,OAAO,CAAC,KAAK,GAAG,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;QACrD,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC;QACtD,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;QAC/C,OAAO,CAAC,KAAK,EAAE,CAAC;QAChB,IAAI,MAAM,KAAK,QAAQ;YAAE,OAAO,CAAC,MAAM,EAAE,CAAC;;YACrC,OAAO,CAAC,MAAM,EAAE,CAAC;IACxB,CAAC;IAED,8DAA8D;IAC9D,MAAM,aAAa,GAAG,MAAM,CAAC,KAAK,CAAC,wCAAwC,CAAC,CAAC;IAC7E,IAAI,aAAa;QAAE,OAAO,CAAC,QAAQ,GAAG,aAAa,CAAC,CAAC,CAAC,CAAC;IAEvD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAS,cAAc,CACrB,UAAkB,EAClB,SAAiB,EACjB,OAAoB,EACpB,OAAmB;IAEnB,MAAM,QAAQ,GAAG,SAAS,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAC1D,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC;IAE1D,MAAM,KAAK,GAAa;QACtB,wBAAwB,UAAU,EAAE;QACpC,EAAE;QACF,iBAAiB,UAAU,uBAAuB,QAAQ,oBAAoB,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM,EAAE;QAC1H,EAAE;QACF,YAAY;QACZ,EAAE;QACF,oBAAoB;QACpB,oBAAoB;QACpB,iBAAiB,OAAO,CAAC,KAAK,IAAI;QAClC,cAAc,OAAO,CAAC,MAAM,IAAI;QAChC,cAAc,OAAO,CAAC,MAAM,IAAI;QAChC,gBAAgB,OAAO,CAAC,QAAQ,IAAI;QACpC,0BAA0B,OAAO,CAAC,OAAO,IAAI,GAAG,IAAI;QACpD,0BAA0B,OAAO,CAAC,MAAM,IAAI,GAAG,IAAI;QACnD,kBAAkB,OAAO,CAAC,IAAI,IAAI,GAAG,IAAI;QACzC,yBAAyB,OAAO,CAAC,SAAS,IAAI,GAAG,IAAI;QACrD,oBAAoB,MAAM,IAAI;QAC9B,EAAE;KACH,CAAC;IAEF,IAAI,OAAO,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC/B,KAAK,CAAC,IAAI,CACR,8DAA8D,EAC9D,EAAE,CACH,CAAC;IACJ,CAAC;SAAM,CAAC;QACN,KAAK,CAAC,IAAI,CAAC,iBAAiB,EAAE,EAAE,CAAC,CAAC;QAClC,KAAK,CAAC,IAAI,CAAC,yDAAyD,CAAC,CAAC;QACtE,KAAK,CAAC,IAAI,CAAC,sDAAsD,CAAC,CAAC;QACnE,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;YACjC,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;YAClD,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,IAAI,CAAC,IAAI,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;YAC9E,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;YACrF,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU;gBAChC,CAAC,CAAC,gBAAgB,IAAI,CAAC,UAAU,GAAG;gBACpC,CAAC,CAAC,GAAG,CAAC;YACR,KAAK,CAAC,IAAI,CACR,KAAK,IAAI,CAAC,IAAI,MAAM,IAAI,IAAI,IAAI,CAAC,MAAM,MAAM,IAAI,MAAM,MAAM,MAAM,UAAU,IAAI,CAClF,CAAC;QACJ,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjB,CAAC;IAED,KAAK,CAAC,IAAI,CACR,2BAA2B,EAC3B,EAAE,EACF,iEAAiE,EACjE,EAAE,EACF,4CAA4C,EAC5C,2CAA2C,EAC3C,2BAA2B,EAC3B,EAAE,EACF,kBAAkB,EAClB,EAAE,EACF,uDAAuD,EACvD,EAAE,EACF,iBAAiB,EACjB,EAAE,EACF,4FAA4F,EAC5F,EAAE,EACF,sBAAsB,EACtB,EAAE,EACF,0EAA0E,EAC1E,EAAE,CACH,CAAC;IAEF,KAAK,CAAC,IAAI,CAAC,oBAAoB,EAAE,EAAE,CAAC,CAAC;IACrC,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,KAAK,CAAC,IAAI,CACR,4BAA4B,EAC5B,gGAAgG,EAChG,4DAA4D,EAC5D,EAAE,CACH,CAAC;IACJ,CAAC;SAAM,CAAC;QACN,KAAK,CAAC,IAAI,CAAC,qCAAqC,EAAE,EAAE,CAAC,CAAC;IACxD,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC"}
package/dist/index.js CHANGED
@@ -9,6 +9,7 @@ import { run } from "./commands/run.js";
9
9
  import { migrate } from "./commands/migrate.js";
10
10
  import { uninstall } from "./commands/uninstall.js";
11
11
  import { audit } from "./commands/audit.js";
12
+ import { explore } from "./commands/explore.js";
12
13
  const __dirname = dirname(fileURLToPath(import.meta.url));
13
14
  const pkg = JSON.parse(readFileSync(join(__dirname, "../package.json"), "utf-8"));
14
15
  const program = new Command();
@@ -42,6 +43,8 @@ program
42
43
  .option("-t, --timeout <seconds>", "Test timeout in seconds", "300")
43
44
  .option("--json", "Output results as JSON")
44
45
  .option("-g, --grep <pattern>", "Run only tests matching pattern")
46
+ .option("--smoke", "Run only smoke tests (--grep @smoke)")
47
+ .option("-w, --workers <n>", "Number of parallel workers", (v) => parseInt(v, 10), undefined)
45
48
  .option("--app-bugs <n>", "Number of app bugs (skipped tests)", (v) => parseInt(v, 10), undefined)
46
49
  .option("--healed <n>", "Number of test bugs healed by Healer", (v) => parseInt(v, 10), undefined)
47
50
  .option("--raft <n>", "Number of RAFTs detected", (v) => parseInt(v, 10), undefined)
@@ -63,5 +66,13 @@ program
63
66
  .command("audit")
64
67
  .description("Audit test files for orphaned specs, missing auth, sitemap issues")
65
68
  .action(audit);
69
+ program
70
+ .command("explore")
71
+ .description("Explore routes in parallel with Playwright")
72
+ .option("-p, --parallel <n>", "Number of parallel workers", (v) => parseInt(v, 10), 4)
73
+ .option("-n, --dry-run", "Show what would be explored without running")
74
+ .action(async (opts) => {
75
+ await explore(opts);
76
+ });
66
77
  program.parse();
67
78
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -10,17 +10,16 @@
10
10
  本规范适用于 OpenSpec + openspec-playwright 项目(Claude Code 作为开发工具)。
11
11
 
12
12
  E2E 工作流前提(由用户确保,非 AI 操作):
13
- - OpenSpec CLI:`npm install -g @fission-ai/openspec && openspec init`(提供 `/opsx:propose`、`/opsx:archive` 等命令)
13
+ - gstack:`git clone --single-branch --depth 1 https://github.com/garrytan/gstack.git ~/.claude/skills/gstack && cd ~/.claude/skills/gstack && ./setup`(提供 `/browse` 探索 + `/qa` 浏览器验证)
14
+ - OpenSpec CLI:`npm install -g @fission-ai/openspec && openspec init`(提供变更管理能力)
14
15
  - openspec-playwright:`openspec-pw init`(提供 `/opsx:e2e` 命令)
15
- - Playwright MCP:`claude mcp add playwright npx @playwright/mcp@latest`
16
+ - Playwright MCP:`claude mcp add playwright npx @playwright/mcp@latest`(用于测试执行 + Healer)
16
17
  - 浏览器已安装:`npx playwright install --with-deps`
17
18
  - 项目包含 `specs/`、`changes/`、`tests/playwright/` 目录
18
19
 
19
20
  ## 1. 浏览器操作约束
20
21
 
21
- 所有浏览器操作**必须**使用 gstack 的 `/browse`(禁止使用其他 MCP Chrome 工具)。
22
-
23
- Claude Code 会根据上下文自动调度 gstack 技能,无需手动路由。
22
+ 所有浏览器操作用 gstack 的 `/browse` 探索 + `/qa` 验证,Playwright MCP 执行测试。Claude Code 会根据上下文自动调度,无需手动路由。
24
23
 
25
24
  **冲突解决**:gstack 任何想直接修改代码的行为,都必须先确认当前 OpenSpec proposal 是否已存在并通过(检查 `changes/<name>/proposal.md` 是否处于 `approved` 状态)。
26
25
 
@@ -59,39 +58,54 @@ Claude Code 会根据上下文自动调度 gstack 技能,无需手动路由。
59
58
  ## 6. 完整生产工作流(严格执行 + 反馈循环)
60
59
 
61
60
  ```
62
- 1. 探索与提案 → /opsx:propose → 生成 proposal + scenarios
63
- 2. 产品与架构思考(按需触发)
64
- 3. 设计审查 → Build(遵守 OpenSpec delta markers)→ /review 自审
65
- 4. E2E 测试 → /opsx:e2e <change-name> → /qa(真实浏览器 + healer)
66
- 5. 验证通过后 → /opsx:archive
67
- 6. 发布 → /ship /land-and-deploy
68
- 7. 迭代回顾 → /retro
61
+ 1. 探索与提案
62
+ 2. 产品与架构评审(按需触发)
63
+ 3. 设计审查
64
+ 4. 实现 → /opsx:apply
65
+ 5. 自审 → /opsx:verify /design-review /review
66
+ 6. E2E 测试 → /opsx:e2e <change-name> /browse 探索 + /qa 验证
67
+ 7. 验证通过后归档 → /opsx:archive
68
+ 8. 发布 → /ship 或 /land-and-deploy
69
+ 9. 迭代回顾 → /retro
69
70
  ```
70
71
 
71
72
  ### 步骤详解
72
73
 
73
- **1. 探索与规范**:`/opsx:propose` → 生成 proposal + scenarios(记录到 `specs/` 和 `changes/`)。对 proposal 进行评审和调整。如需代码探索,单独使用 `/opsx:explore`(不是生成规范)。
74
+ **1. 探索与提案**:现有项目先探索(`/opsx:explore`)再写 proposal(`/opsx:propose`);新项目(greenfield)直接生成 proposal + scenarios(记录到 `specs/` 和 `changes/`)。
74
75
 
75
- **2. 产品与架构思考**(按需触发,不是每个功能都走):
76
+ **2. 产品与架构评审**(按需触发):
76
77
  - `/office-hours`:产品方向、范围、优先级不确定时
77
78
  - `/plan-ceo-review`:产品战略影响、竞争格局变化时
78
79
  - `/plan-eng-review`:架构影响(新增服务、API 契约变更、数据模型重构)时
79
80
 
80
- **3. 设计与实现**:设计审查 → Build(遵守 OpenSpec delta markers),过程中 `/review` 自审。构建完成后,E2E 测试覆盖通过才能继续。
81
+ **3. 设计审查**:在实现前进行设计评审,确保方案合理。评审通过后开始实现。
82
+
83
+ **4. 实现**:执行 `/opsx:apply` 进行实现 → `lint + typecheck` 通过才算成功。
84
+
85
+ **5. 自审**:`/opsx:verify` `/design-review` `/review` 自审实现代码,确保质量。**设计审查后**,对 HTML/CSS 文件执行 CSS 结构审计(防止 `/design-review` 靠截图漏检间距问题):
86
+
87
+ ```
88
+ 两步式审计:
89
+ 1. 确定间距基准 — 查 CSS variables / Tailwind spacing / Bootstrap $spacer
90
+ grep 'gap:|padding:|margin:' *.html → 提取值 → 对比基准,列低于基准的项
91
+ 2. 检查 margin hack — 同一 grid/flex 容器中相邻元素各自用 margin 硬撑间距
92
+ 信号:两个子元素的 margin 和 > 基准间距 2 倍 → 应改为 grid 行或 gap 控制
93
+ 例:.desc{margin-b:36}+.btn{margin-t:80} 在同一 row → grid 行分离
94
+ ```
81
95
 
82
- **4. E2E 测试生成与执行**:`/opsx:e2e <change-name>` 生成 Playwright 测试并执行执行 `/qa`(真实浏览器 + healer)验证。E2E 通过后进入发布环节。
96
+ **6. E2E 测试生成与执行**:`/opsx:e2e <change-name>` 生成 Playwright 测试`/browse` 探索真实 DOM → Healer 自动修复 → `/qa` 真实浏览器验证。E2E 通过后进入发布环节。
83
97
 
84
- **5. 验证通过后**:`/opsx:archive` 永久归档,更新 `specs/`
98
+ **7. 验证通过后归档**:`/opsx:archive` 永久归档,更新 `specs/`
85
99
 
86
- **6. 发布**:`/ship` 或 `/land-and-deploy`
100
+ **8. 发布**:`/ship` 或 `/land-and-deploy`
87
101
 
88
- **7. 迭代回顾**:`/retro`
102
+ **9. 迭代回顾**:`/retro`
89
103
 
90
104
  ### 反馈循环(生产中必然发生)
91
105
 
92
106
  | 信号 | 回到 |
93
107
  |------|------|
94
- | 测试失败(App Bug) | 步骤 3 修复后步骤 4 重新测试 |
95
- | 发现架构问题 | 步骤 2 重新评审 → 步骤 3 修复 |
96
- | proposal 需调整 | 步骤 1 重新提案 |
108
+ | 测试失败(App Bug) | 回到步骤 4 修复 → 重新测试 |
109
+ | 发现架构问题 | 回到步骤 2 重新评审 → 步骤 4 修复 |
110
+ | Proposal 需调整 | 回到步骤 1 重新提案 |
97
111
  | 评审不通过 | 回到对应步骤重新处理 |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openspec-playwright",
3
- "version": "0.2.10",
3
+ "version": "0.3.1",
4
4
  "description": "OpenSpec + Playwright E2E verification setup tool for Claude Code",
5
5
  "type": "module",
6
6
  "bin": {
@@ -22,6 +22,7 @@
22
22
  "@types/tar": "^6.1.13",
23
23
  "chalk": "^5.3.0",
24
24
  "commander": "^12.1.0",
25
+ "playwright": "^1.50.0",
25
26
  "tar": "^7.5.13"
26
27
  },
27
28
  "devDependencies": {
@@ -72,7 +72,9 @@ export default defineConfig({
72
72
  fullyParallel: true,
73
73
  forbidOnly: !!process.env.CI,
74
74
  retries: process.env.CI ? 2 : 0,
75
- workers: process.env.CI ? 1 : undefined,
75
+ // CI: respect PW_WORKERS env var (defaults to 4 for parallel execution).
76
+ // Local: undefined lets Playwright auto-select based on CPU cores.
77
+ workers: process.env.CI ? (parseInt(process.env.PW_WORKERS || '4') || 4) : undefined,
76
78
  reporter: 'list',
77
79
 
78
80
  use: {
@@ -217,7 +217,7 @@ Bypass OTP UI entirely and test the protected endpoint directly.
217
217
 
218
218
  **Test approach:**
219
219
  ```
220
- 1. Identify drag handle and drop target via Playwright MCP snapshot
220
+ 1. Identify drag handle and drop target via /browse snapshot
221
221
  2. Use `page.dragAndDrop()` or `locator.dragTo()`
222
222
  3. If custom implementation uses JS events, use `page.evaluate()` to dispatch events
223
223
  4. Assert: target state reflects the drag result