openspec-playwright 0.2.5 → 0.2.7

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.
@@ -5,7 +5,7 @@ license: MIT
5
5
  compatibility: Requires openspec CLI, Playwright (with browsers installed), and @playwright/mcp (globally installed via `claude mcp add playwright npx @playwright/mcp@latest`).
6
6
  metadata:
7
7
  author: openspec-playwright
8
- version: "2.13"
8
+ version: "2.19"
9
9
  ---
10
10
 
11
11
  ## Input
@@ -20,6 +20,7 @@ metadata:
20
20
  - **Page Objects** (all mode): `tests/playwright/pages/<Route>Page.ts`
21
21
  - **Auth setup**: `tests/playwright/auth.setup.ts` (if auth required)
22
22
  - **Report**: `openspec/reports/playwright-e2e-<name>-<timestamp>.md`
23
+ - **App Bug Registry**: `openspec/reports/app-bug-registry.md` (cumulative, per-project)
23
24
  - **Test plan**: `openspec/changes/<name>/specs/playwright/test-plan.md` (change mode only)
24
25
 
25
26
  ## Architecture
@@ -59,6 +60,46 @@ Can the user SEE this on screen?
59
60
  → No → Record reason → page.request acceptable
60
61
  ```
61
62
 
63
+ **Business logic assertion rule — numerical/calculated values MUST use API assertion**:
64
+
65
+ UI assertions verify **rendering** correctness, not **calculation** correctness. A UI that correctly displays a wrong value will pass UI-only tests.
66
+
67
+ ```
68
+ Is the assertion about a computed/counted/calculated value?
69
+ (e.g., balance, total, discount, count, percentage, score, ranking)
70
+ → Yes → Use page.request to fetch backend data → assert the raw value
71
+ → No → UI assertion is sufficient
72
+ ```
73
+
74
+ **Examples where API assertion is required:**
75
+
76
+ ```typescript
77
+ // ❌ UI-only assertion — hides calculation bugs
78
+ await page.getByText('¥800').click(); // buy item
79
+ await expect(page.getByText('总金额: ¥800')).toBeVisible(); // passes even if backend rounded wrong
80
+
81
+ // ✅ API assertion — catches calculation bugs
82
+ const order = await page.request.get(`${BASE_URL}/api/orders/${orderId}`);
83
+ const body = await order.json();
84
+ expect(body.total).toBe(800); // backend calculation is verified
85
+
86
+ // ❌ UI-only assertion — hides optimistic update failures
87
+ await page.getByRole('button', { name: '点赞' }).click();
88
+ await expect(page.getByText('1 个赞')).toBeVisible(); // passes even if POST failed silently
89
+
90
+ // ✅ API assertion — catches backend sync failures
91
+ const post = await page.request.get(`${BASE_URL}/api/posts/${postId}`);
92
+ const body = await post.json();
93
+ expect(body.likeCount).toBeGreaterThan(0); // backend state is verified
94
+
95
+ // ✅ Optimistic update with API verification
96
+ await page.getByRole('button', { name: '点赞' }).click();
97
+ await expect(page.getByText('1 个赞')).toBeVisible(); // optimistic UI
98
+ await page.waitForResponse(r => r.url().includes('/api/like')); // wait for backend
99
+ const post = await page.request.get(`${BASE_URL}/api/posts/${postId}`);
100
+ expect((await post.json()).likeCount).toBeGreaterThan(0); // verify persistence
101
+ ```
102
+
62
103
  **Never use API calls to replace routine UI flows.** If a test completes in < 200ms, it is almost certainly using `page.request` instead of real UI interactions.
63
104
 
64
105
  ## Steps
@@ -231,76 +272,17 @@ From `browser_snapshot` + `browser_evaluate`, identify these special elements pe
231
272
  | File upload | `<input type="file">` | `accept` attribute, `multiple` flag | Medium |
232
273
  | Drag-and-drop | drag events in JS | Simulate DnD via coordinate clicks | Low |
233
274
  | Date picker | specific `data-testid` or class patterns | Click triggers → evaluate value | Low (skip unless specs mention) |
234
- | Infinite scroll | Dynamic row insertion | Count elements before/after scroll | Low |
235
- | WebSocket / SSE | No DOM signal | Check `browser_console_messages` for WS events | Low |
236
-
237
- **For each detected special element, capture:**
238
-
239
- ```javascript
240
- // Canvas get metadata (check WebGL first to avoid consuming 2D context)
241
- const canvasData = await browser_evaluate(() => {
242
- const c = document.querySelector('canvas');
243
- if (!c) return null;
244
- // getContext consumes the context check WebGL2 first, then WebGL1, then 2D
245
- let context = 'unknown';
246
- if (c.getContext('webgl2')) context = 'webgl2';
247
- else if (c.getContext('webgl')) context = 'webgl';
248
- else if (c.getContext('2d')) context = '2d';
249
- return {
250
- id: c.id || '',
251
- context,
252
- width: c.width,
253
- height: c.height,
254
- };
255
- });
256
-
257
- // Iframe — record frameLocator
258
- // Note: iframe has src or name attribute
259
-
260
- // Rich text editor — get content
261
- const editorContent = await browser_evaluate(() => {
262
- const el = document.querySelector('[contenteditable]');
263
- return el ? { tag: el.tagName, content: el.innerHTML, length: el.textContent.length } : null;
264
- });
265
-
266
- // Video / Audio — get state via evaluate (snapshot doesn't expose tagName)
267
- const mediaState = await browser_evaluate(() => {
268
- const v = document.querySelector('video');
269
- if (v) return { type: 'video', paused: v.paused, duration: v.duration };
270
- const a = document.querySelector('audio');
271
- if (a) return { type: 'audio', paused: a.paused, duration: a.duration };
272
- return null;
273
- });
274
-
275
- // contenteditable — detect via evaluate
276
- const isContentEditable = await browser_evaluate(() => {
277
- const el = document.querySelector('[contenteditable]');
278
- return !!el;
279
- });
280
-
281
- // CAPTCHA — detect type
282
- const captchaInfo = await browser_evaluate(() => {
283
- const recaptcha = document.querySelector('.g-recaptcha, [data-sitekey]');
284
- if (recaptcha) return { type: 'recaptcha', sitekey: recaptcha.getAttribute('data-sitekey') };
285
- const hcaptcha = document.querySelector('.h-captcha');
286
- if (hcaptcha) return { type: 'hcaptcha', sitekey: hcaptcha.getAttribute('data-sitekey') };
287
- const turnstile = document.querySelector('[data-sitekey*="cloudflare"]');
288
- if (turnstile) return { type: 'turnstile' };
289
- const canvas = document.querySelector('canvas[class*="captcha"]');
290
- if (canvas) return { type: 'canvas-captcha' };
291
- const slider = document.querySelector('[class*="slider"], [class*="drag"]');
292
- if (slider) return { type: 'slider-captcha' };
293
- return null;
294
- });
295
-
296
- // OTP input — detect
297
- const otpInfo = await browser_evaluate(() => {
298
- const inputs = document.querySelectorAll('input');
299
- const otpInputs = Array.from(inputs).filter(i => i.maxLength === 1 && i.type === 'text' || i.type === 'tel');
300
- if (otpInputs.length >= 4) return { type: 'otp-sms', digits: otpInputs.length };
301
- return null;
302
- });
303
- ```
275
+ | Infinite scroll | Dynamic row insertion | Count elements before/after scroll | Low (skip unless specs mention dynamic lists/pagination) |
276
+ | WebSocket / SSE | No DOM signal | Check `browser_console_messages` for WS events | Low (check only if app uses real-time features) |
277
+
278
+ **For each detected special element, capture via `browser_evaluate` with targeted DOM queries:**
279
+ - Canvas: `getContext('webgl2'/'webgl'/'2d')`, `width`, `height`
280
+ - Iframe: `src` attribute → use `frameLocator` in tests
281
+ - CAPTCHA: `.g-recaptcha`, `.h-captcha`, `[data-sitekey]`, canvas+slider detection
282
+ - OTP: `input` elements with `maxLength === 1` or `type === 'tel'`
283
+ - Rich text: `[contenteditable]` → `innerHTML`, `textContent.length`
284
+ - Video/Audio: `querySelector('video'/'audio')` `paused`, `duration`
285
+ - Shadow DOM: `role="generic"` with no children check `shadowRoot`
304
286
 
305
287
  Record findings in `app-exploration.md` → **Special Elements Detected** table.
306
288
 
@@ -377,7 +359,7 @@ Reply **yes** to proceed, or tell me to exclude routes or adjust strategies.
377
359
 
378
360
  Template: `.claude/skills/openspec-e2e/templates/test-plan.md`
379
361
 
380
- **Idempotency**: If test-plan.md exists → read and use, do NOT regenerate.
362
+ **Idempotency**: If test-plan.md exists → read and use, **but you MAY supplement missing test cases**. "Do not regenerate" means: do not discard existing cases, but you CAN add new ones discovered during Step 4 exploration that weren't in the original spec (e.g., empty states, error paths found during DOM exploration).
381
363
 
382
364
  **⚠️ Human verification — STOP before generating code.**
383
365
 
@@ -408,15 +390,9 @@ After creating (or reading existing) test-plan.md, **stop and display the test p
408
390
  - `<element or scenario not testable>`
409
391
  ````
410
392
 
411
- Then ask: "Does this coverage match your intent? Reply **yes** to proceed, or tell me what to add/change."
412
-
413
- **Why this matters**: Step 5 is the last human-reviewable checkpoint before code generation. Once test code is written, fixes address *how* tests run, not *what* they verify. Reviewing the test plan takes seconds and catches logic errors that Healer cannot fix.
393
+ **Important**: Only list special elements that were actually detected in Step 4. Do not pre-populate with all possible types. If no special elements were found → omit the **Special Elements** section entirely.
414
394
 
415
- **Confirmation criteria**:
416
- - All scenarios from OpenSpec specs are covered
417
- - Special elements (Canvas, Iframe, Video, Audio, CAPTCHA, OTP, File Upload, Drag-drop, WebSocket) have correct automation strategy
418
- - Auth states and roles are accurate
419
- - Nothing important is missing
395
+ Then ask: "Does this coverage match your intent? Reply **yes** to proceed, or tell me what to add/change."
420
396
 
421
397
  If the user requests changes → update test-plan.md → re-display summary → re-confirm → proceed.
422
398
 
@@ -424,7 +400,7 @@ If the user requests changes → update test-plan.md → re-display summary →
424
400
 
425
401
  **"all" mode**: Build and expand Page Objects for future Change tests.
426
402
 
427
- **Prerequisite**: If `app-exploration.md` does not exist → **STOP**. Run Step 4 first. All mode explores routes via browser MCP to build exploration data.
403
+ **Prerequisite** (change mode only): If `app-exploration.md` does not exist → **STOP**. Run Step 4 first. For **all mode**, exploration is embedded dynamically in Step 6 — no pre-existing app-exploration.md is required.
428
404
 
429
405
  **Page Object pattern** — read before writing any page file:
430
406
 
@@ -466,7 +442,7 @@ For each discovered route:
466
442
  2. Navigate to route with correct auth state
467
443
  3. browser_snapshot to extract interactive elements (see 4.3 table)
468
444
  4. Write or update `pages/<Route>Page.ts` — extend with newly discovered elements
469
- 5. Also write `tests/playwright/app-all.spec.ts` — smoke test (route loads without crash)
445
+ 5. Also write `tests/playwright/app-all.spec.ts` — smoke test. **Minimum standard**: verify at least one heading or key interactive element is visible — not just "no crash". If the page loads but shows an empty shell or an error, the test must fail.
470
446
 
471
447
  **Output priority**: Page Objects (`pages/*.ts`) are the primary asset. Smoke test is secondary. Existing Page Objects are never overwritten — only extended.
472
448
 
@@ -495,84 +471,13 @@ Is this assertion about a visible UI result?
495
471
 
496
472
  **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.
497
473
 
498
- **Test coverage — special elements**: Check `app-exploration.md` → **Special Elements Detected** table. For each special element:
499
-
500
- ```typescript
501
- // Canvas screenshot + dimensions
502
- test('canvas renders with correct dimensions', async ({ page }) => {
503
- await page.goto(`${BASE_URL}/<route>`);
504
- const canvas = page.locator('canvas');
505
- await expect(canvas).toBeVisible();
506
- const box = await canvas.boundingBox();
507
- expect(box.width).toBeGreaterThan(0);
508
- await canvas.screenshot({ path: '__screenshots__/canvas.png' });
509
- });
510
-
511
- // Canvas — 2D pixel verification
512
- test('canvas 2D content is not blank', async ({ page }) => {
513
- await page.goto(`${BASE_URL}/<route>`);
514
- const hasContent = await page.evaluate(() => {
515
- const c = document.querySelector('canvas');
516
- if (!c) return false;
517
- const ctx = c.getContext('2d');
518
- if (!ctx) return false;
519
- const data = ctx.getImageData(0, 0, c.width, c.height).data;
520
- return data.some((v, i) => i % 4 !== 3 && v !== 0); // non-transparent non-black pixel
521
- });
522
- expect(hasContent).toBe(true);
523
- });
524
-
525
- // Canvas — WebGL screenshot
526
- test('canvas WebGL renders', async ({ page }) => {
527
- await page.goto(`${BASE_URL}/<route>`);
528
- const canvas = page.locator('canvas');
529
- await expect(canvas).toBeVisible();
530
- await canvas.screenshot({ path: '__screenshots__/webgl.png' });
531
- // No pixel comparison — WebGL rendering may vary
532
- });
533
-
534
- // Iframe — switch context
535
- test('iframe content is accessible', async ({ page }) => {
536
- await page.goto(`${BASE_URL}/<route>`);
537
- const frame = page.frameLocator('iframe[name="<name>"]');
538
- await expect(frame.locator('<selector-inside-frame>')).toBeVisible();
539
- });
540
-
541
- // Rich text editor — evaluate content
542
- test('editor content persists', async ({ page }) => {
543
- await page.goto(`${BASE_URL}/<route>`);
544
- const editor = page.locator('[contenteditable]');
545
- await editor.click();
546
- await page.keyboard.type('Hello E2E');
547
- const content = await page.evaluate(() => {
548
- const el = document.querySelector('[contenteditable]');
549
- return el?.textContent;
550
- });
551
- expect(content).toContain('Hello E2E');
552
- });
553
-
554
- // Video — playback state
555
- test('video can be played', async ({ page }) => {
556
- await page.goto(`${BASE_URL}/<route>`);
557
- const video = page.locator('video');
558
- await expect(video).toBeVisible();
559
- await video.evaluate((v: HTMLVideoElement) => { v.play(); });
560
- const isPlaying = await video.evaluate((v: HTMLVideoElement) => !v.paused);
561
- expect(isPlaying).toBe(true);
562
- });
563
-
564
- // Audio — playback state
565
- test('audio can be played', async ({ page }) => {
566
- await page.goto(`${BASE_URL}/<route>`);
567
- const audio = page.locator('audio');
568
- await expect(audio).toBeVisible();
569
- await audio.evaluate((a: HTMLAudioElement) => { a.play(); });
570
- const isPlaying = await audio.evaluate((a: HTMLAudioElement) => !a.paused);
571
- expect(isPlaying).toBe(true);
572
- });
573
- ```
574
-
575
- See `.claude/skills/openspec-e2e/templates/test-plan.md` → **Special Element Test Cases** for full templates including Canvas, Video, Audio, Iframe, and Rich Text Editor.
474
+ **Test coverage — special elements**: Check `app-exploration.md` → **Special Elements Detected** table. For each special element, generate tests using templates from `.claude/skills/openspec-e2e/templates/test-plan.md` → **Special Element Test Cases**:
475
+ - Canvas: screenshot + boundingBox → dimensions > 0, or 2D pixel verification
476
+ - WebGL: screenshot only (no pixel comparison — rendering varies)
477
+ - Iframe: `frameLocator` + assert inner content visible
478
+ - Rich text: `contenteditable` type + `textContent` assertion
479
+ - Video/Audio: `play()` → assert `!paused`
480
+ - CAPTCHA/OTP/File upload/Drag-drop: See AI-Opaque Elements section in templates
576
481
 
577
482
  **Test coverage — AI-opaque elements**: For CAPTCHA, OTP, slider CAPTCHA, file upload, and drag-drop — elements that Playwright cannot reliably automate:
578
483
 
@@ -584,24 +489,7 @@ See `.claude/skills/openspec-e2e/templates/test-plan.md` → **Special Element T
584
489
  - **Drag-drop**: Use `page.dragAndDrop()` or `page.evaluate()` with custom event dispatching
585
490
  3. If the element is truly non-automatable, write `test.skip()` with a comment explaining why, and mark with `/handoff` for manual testing
586
491
 
587
- **Test coverage — performance**: Verify Core Web Vitals metrics. If the app specifies performance targets, generate a test:
588
-
589
- ```typescript
590
- // Performance — Core Web Vitals
591
- test('page loads within performance budget', async ({ page }) => {
592
- await page.goto(`${BASE_URL}/<route>`);
593
- await expect(page.getByRole('heading')).toBeVisible();
594
- const timings = await page.evaluate(() => {
595
- const nav = performance.getEntriesByType('navigation')[0] as PerformanceNavigationTiming;
596
- return {
597
- ttfb: nav.responseStart - nav.requestStart,
598
- lcp: nav.loadEventEnd - nav.requestStart,
599
- };
600
- });
601
- expect(timings.ttfb).toBeLessThan(500);
602
- expect(timings.lcp).toBeLessThan(2500);
603
- });
604
- ```
492
+ **Test coverage — performance**: Generate a Core Web Vitals test **only if** the OpenSpec spec or app-exploration.md specifies explicit performance targets (e.g., "LCP must be under 2s"). If no business target is defined, skip performance testing — hard-coded thresholds (lcp < 2500ms) produce false passes and add noise.
605
493
 
606
494
  ```typescript
607
495
  // 🚫 Avoid for special elements:
@@ -665,83 +553,14 @@ test('user can login', async ({ page }) => {
665
553
 
666
554
  **If a shared page object doesn't exist yet**: define it inline in the spec AND write it to `tests/playwright/pages/<PageName>.ts` so future tests can reuse it.
667
555
 
668
- #### 6.2. Selector anti-patterns
556
+ #### 6.2. Selector patterns
669
557
 
670
- ```typescript
671
- // 🚫 FragileCSS class selectors break on style refactors
672
- page.locator('.notification-bell')
673
- page.locator('.header-bar')
674
- page.locator('.skeleton-overlay')
675
-
676
- // ✅ Robust — semantic selectors survive style changes
677
- page.getByRole('button', { name: '通知' })
678
- page.getByTestId('header-bar')
679
- page.getByText('加载中')
680
-
681
- // 🚫 Fragile — CSS ID selectors can duplicate in React HMR
682
- page.locator('#avatarBtn')
683
- page.locator('#userAvatarBtn')
684
-
685
- // ✅ Robust — prefer role/label/testid over CSS ID
686
- page.getByTestId('user-avatar')
687
- page.getByRole('button', { name: '用户菜单' })
688
-
689
- // 🚫 Missing wait — leads to random CI failures
690
- await page.locator('.submit-btn').click();
691
-
692
- // ✅ Safe — scroll into view first
693
- await page.locator('.submit-btn').scrollIntoViewIfNeeded();
694
- await page.locator('.submit-btn').click();
695
-
696
- // ✅ Better — use BasePage click with built-in wait
697
- const app = new AppPage(page);
698
- await app.click(app.byRole('button', { name: '提交' }));
699
- ```
700
-
701
- **Code examples — UI first:**
702
-
703
- ```typescript
704
- // ✅ UI 测试 — fill 后必须验证值,确保框架同步完成
705
- const app = new AppPage(page);
706
- await app.goto(`${BASE_URL}/orders`);
707
- await app.click(app.byRole('button', { name: '新建订单' }));
708
- await app.fillAndVerify(app.byLabel('订单名称'), 'Test Order');
709
- await app.click(app.byRole('button', { name: '提交' }));
710
- await expect(page.getByText('订单创建成功')).toBeVisible();
711
-
712
- // ✅ Error path
713
- await page.goto(`${BASE_URL}/orders`);
714
- await page.getByRole("button", { name: "提交" }).click();
715
- await expect(page.getByRole("alert")).toContainText("名称不能为空");
716
-
717
- // ✅ API fallback (only when UI cannot reach the scenario)
718
- const res = await page.request.get(`${BASE_URL}/api/orders/99999`);
719
- expect(res.status()).toBe(404);
720
-
721
- // ✅ Auth guard — fresh browser context (no cookies)
722
- test("redirects to login when unauthenticated", async ({ browser }) => {
723
- const freshPage = await browser.newContext().newPage();
724
- await freshPage.goto(`${BASE_URL}/dashboard`);
725
- await expect(freshPage).toHaveURL(/login|auth/);
726
- });
558
+ | Prefer (robust) | Avoid (fragile) |
559
+ | ||
560
+ | `getByRole`, `getByTestId`, `getByLabel` | CSS class (`'.notification-bell'`), CSS ID (`'#avatarBtn'`) |
561
+ | `waitForSelector(targetElement)` | hardcoded `200ms` / `500ms` delays |
727
562
 
728
- // Session logout clears protected state
729
- await page.getByRole("button", { name: "退出登录" }).click();
730
- await expect(page).toHaveURL(/login|auth/);
731
- const freshPage2 = await browser.newContext().newPage();
732
- await freshPage2.goto(`${BASE_URL}/dashboard`);
733
- await expect(freshPage2).toHaveURL(/login|auth/); // session revoked
734
-
735
- // ✅ Browser history — SPA back/forward
736
- await page.goto(`${BASE_URL}/list`);
737
- await page.getByRole("link", { name: "详情" }).first().click();
738
- await expect(page).toHaveURL(/detail/);
739
- await page.goBack();
740
- await expect(page).toHaveURL(/list/);
741
-
742
- // ✅ File uploads
743
- await page.locator('input[type="file"]').setInputFiles("/path/to/file.pdf");
744
- ```
563
+ See `.claude/skills/openspec-e2e/templates/e2e-test.ts` for full examples of Page Object pattern, UI-first flows, error paths, auth guards, session handling, and visual regression.
745
564
 
746
565
  If the file exists → diff against test-plan, add only missing test cases.
747
566
 
@@ -795,13 +614,15 @@ If playwright.config.ts exists → READ first, preserve ALL existing fields, add
795
614
  ### 9. Execute tests
796
615
 
797
616
  ```bash
798
- openspec-pw run <name> [--project <role>]
617
+ openspec-pw run <name> [--project <role>] [--headed] [--update-snapshots]
799
618
  ```
800
619
 
801
620
  The CLI handles: server lifecycle, port mismatch, report generation.
802
621
 
803
622
  If tests fail → use Playwright MCP tools to inspect UI, fix selectors, re-run.
804
623
 
624
+ **Browser visibility**: The Healer uses browser MCP tools (snapshot, screenshot, console messages) to inspect failures — no need for `--headed`. If you want to **watch the browser yourself** during debugging, add `--headed`: `openspec-pw run <name> --headed`.
625
+
805
626
  **Healer MCP tools** (in order of use):
806
627
 
807
628
  | Tool | Purpose |
@@ -815,22 +636,67 @@ If tests fail → use Playwright MCP tools to inspect UI, fix selectors, re-run.
815
636
 
816
637
  **Healer — Phase 1: Triage**
817
638
 
818
- When a test fails, classify before attempting repair:
639
+ When a test fails, classify before attempting repair.
640
+
641
+ **Batch Failure Detection — run this FIRST when multiple tests fail:**
642
+
643
+ ```
644
+ Collect ALL failing test names + their failure reasons.
645
+ Group by: same route + same action + same error pattern.
646
+ If ≥2 tests fall into the same group:
647
+ → Pause individual healing
648
+ → Navigate to that route + perform the action manually
649
+ → Check browser_console_messages + browser_network_requests
650
+ → If console error or 4xx/5xx present:
651
+ → This is an App Bug (backend/API change), NOT Test Bugs
652
+ → Classify all tests in this group as App Bug
653
+ → Skip all → record 1 App Bug in registry (not N bugs)
654
+ → Skip the rest of individual Triage for this group
655
+ → If no console/network error but all still fail:
656
+ → Likely a shared state issue → RAFT
657
+ → Skip all → note RAFT in report
658
+ Proceed with individual Triage only for tests NOT in a batch failure group.
659
+ ```
660
+
661
+ **After Batch Detection, individual Triage:**
819
662
 
820
663
  | Failure Type | Signal | Classification | Action |
821
664
  | — | — | — | — |
822
- | **Network/Backend** | `net::ERR`, 4xx/5xx in console/network | **App Bug** | `test.skip()` + report as app bug |
823
- | **JS Runtime Error** | Console error (non-network) | **App Bug** | `test.skip()` + report as app bug |
665
+ | **Network/Backend** | `net::ERR`, 4xx/5xx in console/network | **App Bug** | `test.skip()` + record in `app-bug-registry.md` |
666
+ | **JS Runtime Error** | Console error (non-network) | **App Bug** | `test.skip()` + record in `app-bug-registry.md` |
824
667
  | **Auth Expired** | Redirected to login mid-test | **Flaky** | Re-run auth.setup → re-run |
825
668
  | **Selector Not Found** | Element not found | **Test Bug** | → Phase 2 Healer |
826
669
  | **Assertion Mismatch** | Wrong content/value | **Ambiguous** | → Phase 2 Healer |
827
670
  | **Timeout** | waitFor/evaluate timeout | **Flaky** | Retry isolated: `openspec-pw run <name> --grep "<test-name>"` (1×, not counted in heal attempts). If it passes isolated but fails in suite → **RAFT**. If it consistently times out → check framework: React 19 / Next.js App Router: add `page.waitForLoadState('networkidle')`. Vue/Angular/React 18 / Plain JS / jQuery: use `waitForSelector(targetElement)` instead of timeout tuning. |
828
671
  | **Same test fails in suite, passes isolated** | — | **RAFT** | `test.skip()` in suite, note RAFT in report |
829
672
 
830
- - **App Bug** → skip immediately (no healing needed)
673
+ - **App Bug** → skip immediately (no healing needed) → record in App Bug Registry
831
674
  - **Flaky** → retry once isolated
832
675
  - **Test Bug / Ambiguous** → Phase 2
833
676
 
677
+ #### App Bug Registry
678
+
679
+ For every App Bug classified in Phase 1, record it in `openspec/reports/app-bug-registry.md` (create if missing):
680
+
681
+ ```markdown
682
+ # App Bug Registry
683
+
684
+ <!-- Auto-generated. Do not edit manually. -->
685
+
686
+ ## Active App Bugs
687
+
688
+ | # | Test | Route | Signal | First Detected | Status |
689
+ |---|------|-------|--------|---------------|--------|
690
+ | 1 | test-name | /route | net::ERR_CONNECTION_REFUSED | 2026-04-09 | open |
691
+ ```
692
+
693
+ **Update rules**:
694
+ - **New App Bug**: Append new row, increment `#`
695
+ - **App Bug re-run and now passes**: Keep row, change status to `resolved` + add `Resolved` column with date
696
+ - **Keep all rows** (never delete) — the resolved count is the signal that bugs are being fixed
697
+
698
+ > **Why this matters**: `test.skip()` hides App Bugs from the pass/fail count. Without an explicit registry, "all tests passed" is a false positive when App Bugs exist. The registry makes the invisible visible.
699
+
834
700
  > **Global attempt guard**: Each test has an independent heal counter (max 3 per test). If the same test enters Phase 2 more than once and reaches the cap each time → treat as "consecutive escalation without progress" → Phase 3 immediately.
835
701
 
836
702
  > **Type ≠ Blame**: "Test Bug" means the assertion or selector is wrong — it does NOT mean "blame the test author." The test was generated from the spec. Root cause may be spec ambiguity, spec→test generation error, or app→spec deviation. Only a human can determine blame.
@@ -848,13 +714,38 @@ After Triage classifies failure as "Test Bug" or "Ambiguous":
848
714
  MATCH: <yes/no>
849
715
  ```
850
716
  4. If MATCH=no:
851
- - Is `ACTUAL` reasonable per the test's intended spec behavior?
852
- - If yes fix the assertion to match ACTUAL (app behavior is correct)
853
- - If uncertain → **Phase 3**
717
+
718
+ **⚠️ Assertion modification guard never skip Phase 3 unless ALL conditions are met:**
719
+
720
+ - The test has **never passed with the current assertion** (newly generated test, or this is the first time this assertion fails)
721
+ - The ACTUAL value is **verifiably** from a different spec section (e.g., this test was in the wrong describe block)
722
+ - You can point to the **specific line in the spec** that defines the ACTUAL behavior
723
+
724
+ **If ANY condition is uncertain → Phase 3 immediately.** Do NOT modify the assertion.
725
+
726
+ **Safe to fix without Phase 3:**
727
+ - Typo in assertion (e.g., "Submmit" vs "Submit" in the expected text)
728
+ - Selector was correct but the element was moved to a different location (same text, different selector)
729
+ - Explicit spec drift confirmed by reading the spec (e.g., spec says "button says Submit" but test says "button says Submit Form")
730
+
731
+ **Never fix without Phase 3:**
732
+ - 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)
733
+ - Data values differ (e.g., expected "¥1000" but got "¥999" → **Phase 3**, could be rounding, discount, or calculation bug)
734
+ - Missing elements after interaction (e.g., "after creating order, success message should appear" → no message → **Phase 3**)
735
+
854
736
  5. If selector issue → find equivalent stable selector from snapshot
855
737
  6. Apply fix → re-run **only that test** (attempt 1/3)
856
738
  7. If healed → append to `app-knowledge.md` → **Selector Fixes** table (route, old → new selector, reason)
857
739
 
740
+ **Element Missing handling (when browser_snapshot shows element not found):**
741
+
742
+ | Situation | Check | Action |
743
+ | — | — | — |
744
+ | JS error in console after action | `browser_console_messages` | **App Bug** → Phase 1 → App Bug classification |
745
+ | Auth redirected mid-action | URL changed to `/login` | **Flaky** → re-run with fresh auth |
746
+ | SPA route didn't update | URL is correct but element missing | Wait for SPA hydration → `page.waitForLoadState('networkidle')` or `waitForSelector(target)` |
747
+ | Element genuinely missing | None of the above | **Test Bug** → find alternative selector or **Phase 3** if no equivalent exists |
748
+
858
749
  **Healer — Phase 3: Escalate**
859
750
 
860
751
  When Phase 2 tried ≥3 heals without success, OR ASSERTION vs ACTUAL comparison is ambiguous:
@@ -900,7 +791,7 @@ openspec-pw run <change-name>
900
791
  ```
901
792
  `/opsx:e2e <change-name>` re-runs the full 11-step workflow — unnecessary after Phase 3. The test file and auth context are already correct. Use `openspec-pw run` to verify fixes directly.
902
793
 
903
- ### 10. False Pass Detection + RAFT Detection
794
+ ### 10. False Pass Detection + RAFT Detection + App Bug Accumulation
904
795
 
905
796
  Run after test suite completes (even if all pass).
906
797
 
@@ -916,11 +807,23 @@ Run after test suite completes (even if all pass).
916
807
  - This is **NOT** a test bug or app bug. Mark as RAFT, add `test.skip()` in suite, note in report
917
808
  - RAFTs are infrastructure coupling issues (CPU/memory/I/O contention), not fixable by changing test or app
918
809
 
810
+ **App Bug Accumulation Detection** (most critical):
811
+
812
+ 1. Read `openspec/reports/app-bug-registry.md`
813
+ 2. Count `open` status rows
814
+ 3. If ≥ 3 active App Bugs → add "⚠️ App Bug accumulation: N bugs unresolved" to report summary. **Do NOT suppress or hide this warning.** The test suite may report "all passed" (skipped ≠ failed), but N broken features is not a passing system.
815
+ 4. Check for **App Bugs that became "resolved"** (passing on re-run) → this is the signal that bugs were fixed. Update `app-bug-registry.md` accordingly, and remove `test.skip()` from those tests so they re-enter the regression suite.
816
+
919
817
  ### 11. Report results
920
818
 
921
819
  Read report at `openspec/reports/playwright-e2e-<name>-<timestamp>.md`. Present:
922
820
 
923
- - Summary table with failure type breakdown (App Bugs, Test Bugs/healed, Flaky-RAFT, Human Escalations)
821
+ - **Summary table** with failure type breakdown (App Bugs, Test Bugs/healed, Flaky-RAFT, Human Escalations)
822
+ - **App Bug Summary**: Table of all active App Bugs from `app-bug-registry.md` — test name, route, signal, first detected. If ≥ 3 active → display "⚠️ App Bug accumulation warning" prominently.
823
+ - **Conditional "All Pass" conclusion**:
824
+ - ✅ **"All tests passed"** — only if 0 active App Bugs and 0 skipped tests
825
+ - ⚠️ **"All tests passed (N skipped)"** — if skipped tests exist but no active App Bugs
826
+ - ⚠️ **"All tests passed (N skipped, M App Bugs unresolved)"** — if active App Bugs exist
924
827
  - Failure Classification table (test, type, action, healed?)
925
828
  - Auto-heal log (assertion vs actual comparison, fix applied, result)
926
829
  - RAFT Summary (if any detected)
@@ -929,11 +832,9 @@ Read report at `openspec/reports/playwright-e2e-<name>-<timestamp>.md`. Present:
929
832
 
930
833
  Report template: `.claude/skills/openspec-e2e/templates/report.md`
931
834
 
932
- **Update tasks.md** if all tests pass: find E2E-related items, append `✅ Verified via Playwright E2E (<timestamp>)`.
933
-
934
- ## Report Structure
935
-
936
- Reference: `.claude/skills/openspec-e2e/templates/report.md`
835
+ **Update tasks.md**:
836
+ - If 0 active App Bugs → find E2E-related items, append `✅ Verified via Playwright E2E (<timestamp>)`.
837
+ - If active App Bugs exist → **do not mark as verified**. Append instead: `⚠️ App Bug blocked: <bug summary> (<timestamp>)`.
937
838
 
938
839
  ## Graceful Degradation
939
840
 
@@ -945,11 +846,12 @@ Reference: `.claude/skills/openspec-e2e/templates/report.md`
945
846
  | JS errors or HTTP 5xx during exploration | **STOP** → user fixes app → re-run `/opsx:e2e <name>` to re-explore from Step 4 |
946
847
  | Sitemap fails ("all" mode) | Continue with homepage links fallback |
947
848
  | File already exists (app-exploration, test-plan, app-all.spec.ts, Page Objects) | Read and use — never regenerate |
948
- | Test fails (network/backend) | **App Bug** — `test.skip()` + report |
849
+ | Test fails (network/backend) | **App Bug** — `test.skip()` + record in `app-bug-registry.md` |
949
850
  | Test fails (selector/assertion) | **Test Bug/Ambiguous** — Healer Phase 1→2 (≤3 attempts) |
950
851
  | RAFT detected (suite fail, isolated pass) | **Flaky** — `test.skip()` in suite, note RAFT in report |
951
852
  | Phase 3 escalation | **Human needed** — STOP + ask user |
952
853
  | False pass detected | Add "⚠️ Coverage Gap" to report |
854
+ | App Bug skip accumulation (≥3 active App Bugs) | **Warning** — add "⚠️ App Bug accumulation: N bugs unresolved" to report summary. Do not suppress. |
953
855
 
954
856
  ## Guardrails
955
857
 
@@ -968,6 +870,6 @@ Reference: `.claude/skills/openspec-e2e/templates/report.md`
968
870
 
969
871
  > `tests/playwright/` — spec files, Page Objects, auth, credentials, app-knowledge.md
970
872
  > `openspec/changes/<name>/specs/playwright/` — app-exploration.md, test-plan.md (change mode)
971
- > `openspec/reports/` — test reports
873
+ > `openspec/reports/` — test reports, app-bug-registry.md
972
874
 
973
875
  **Never write to:** any other directory
package/README.md CHANGED
@@ -36,6 +36,8 @@ Claude Code — E2E workflow is driven by SKILL.md using Playwright MCP tools (`
36
36
  openspec-pw init # Initialize integration (one-time setup)
37
37
  openspec-pw update # Update CLI and commands to latest version
38
38
  openspec-pw doctor # Check prerequisites
39
+ openspec-pw audit # Audit tests for orphaned specs and issues
40
+ openspec-pw migrate # Migrate old test files to new structure
39
41
  openspec-pw uninstall # Remove integration from the project
40
42
  ```
41
43
 
@@ -156,6 +158,7 @@ CLI (openspec-pw)
156
158
  ├── update → Syncs commands, skill & templates from npm
157
159
  ├── run → Executes E2E tests with server lifecycle
158
160
  ├── migrate → Migrates old test files to new structure
161
+ ├── audit → Audits tests for orphaned specs and issues
159
162
  ├── doctor → Checks prerequisites
160
163
  └── uninstall → Removes integration from the project
161
164
 
package/README.zh-CN.md CHANGED
@@ -49,6 +49,8 @@ Claude Code — E2E 工作流由 SKILL.md 驱动,使用 Playwright MCP 工具
49
49
  openspec-pw init # 初始化集成(一次性设置)
50
50
  openspec-pw update # 更新 CLI 和命令到最新版本
51
51
  openspec-pw doctor # 检查前置条件
52
+ openspec-pw audit # 检查测试文件是否有孤儿文件和配置问题
53
+ openspec-pw migrate # 迁移旧测试文件到新目录结构
52
54
  openspec-pw uninstall # 移除项目中的集成
53
55
  ```
54
56
 
@@ -157,6 +159,7 @@ CLI (openspec-pw)
157
159
  ├── update → 从 npm 同步命令、skill 和模板
158
160
  ├── run → 执行 E2E 测试并管理服务器生命周期
159
161
  ├── migrate → 迁移旧测试文件到新目录结构
162
+ ├── audit → 检查测试文件是否有孤儿文件和配置问题
160
163
  ├── doctor → 检查前置条件
161
164
  └── uninstall → 移除项目中的集成
162
165
 
@@ -0,0 +1 @@
1
+ export declare function audit(): Promise<void>;
@@ -0,0 +1,190 @@
1
+ import { execSync } from "child_process";
2
+ import { existsSync, readdirSync, readFileSync, } from "fs";
3
+ import { join } from "path";
4
+ import chalk from "chalk";
5
+ export async function audit() {
6
+ const projectRoot = process.cwd();
7
+ const testsDir = join(projectRoot, "tests", "playwright");
8
+ if (!existsSync(testsDir)) {
9
+ console.log(chalk.yellow(" tests/playwright/ not found. Run `openspec-pw init` first.\n"));
10
+ return;
11
+ }
12
+ console.log(chalk.blue("\n🔍 OpenSpec Playwright: Audit\n"));
13
+ const results = [];
14
+ // 1. Get sitemap routes
15
+ const sitemapRoutes = await getSitemapRoutes(projectRoot);
16
+ const allRoutes = sitemapRoutes ?? [];
17
+ // 2. Get OpenSpec change names
18
+ const changeNames = await getChangeNames(projectRoot);
19
+ // 3. Scan all spec files recursively
20
+ const specFiles = collectSpecFiles(testsDir);
21
+ // 4. Audit each spec file
22
+ const SHARED_FILES = new Set([
23
+ "seed.spec.ts",
24
+ "app-all.spec.ts",
25
+ "auth.setup.ts",
26
+ "credentials.yaml",
27
+ "app-knowledge.md",
28
+ "playwright.config.ts",
29
+ "mcp-tools.md",
30
+ ]);
31
+ for (const file of specFiles) {
32
+ const relPath = file.replace(testsDir + "/", "");
33
+ const content = readFileSync(file, "utf-8");
34
+ // Skip shared files
35
+ const fileName = relPath.split("/").pop() ?? "";
36
+ if (SHARED_FILES.has(fileName))
37
+ continue;
38
+ // 4a. Orphaned spec file: no matching OpenSpec change
39
+ const changeName = fileName.replace(".spec.ts", "");
40
+ // Check if this is a root-level old-style file
41
+ if (!relPath.includes("/")) {
42
+ if (changeNames.length > 0 && !changeNames.includes(changeName)) {
43
+ results.push({
44
+ fileName: relPath,
45
+ issue: "Orphaned spec file",
46
+ detail: `No matching OpenSpec change found. Consider migrating to tests/playwright/changes/${changeName}/`,
47
+ });
48
+ }
49
+ }
50
+ // 4b. Check for hardcoded URLs not in sitemap
51
+ const urlMatches = content.match(/https?:\/\/[^\s'"]+/g);
52
+ if (urlMatches) {
53
+ for (const url of urlMatches) {
54
+ const pathname = new URL(url).pathname;
55
+ if (pathname !== "/" &&
56
+ allRoutes.length > 0 &&
57
+ !allRoutes.includes(pathname) &&
58
+ !allRoutes.some((r) => pathname.startsWith(r))) {
59
+ results.push({
60
+ fileName: relPath,
61
+ issue: "Route not in sitemap",
62
+ detail: `Found URL: ${url}`,
63
+ });
64
+ }
65
+ }
66
+ }
67
+ }
68
+ // 5. Check for missing auth.setup when tests reference protected routes
69
+ const needsAuth = specFiles.some((file) => {
70
+ const content = readFileSync(file, "utf-8");
71
+ const fileName = file.split("/").pop() ?? "";
72
+ return (!SHARED_FILES.has(fileName) &&
73
+ (content.includes("storageState") ||
74
+ content.includes("auth.setup") ||
75
+ content.includes("authenticated") ||
76
+ content.includes("dashboard") ||
77
+ content.includes("profile")));
78
+ });
79
+ if (needsAuth && !existsSync(join(testsDir, "auth.setup.ts"))) {
80
+ results.push({
81
+ fileName: "auth.setup.ts",
82
+ issue: "Missing auth setup",
83
+ detail: "Tests reference protected routes but auth.setup.ts is not found",
84
+ });
85
+ }
86
+ // 6. Check for deprecated old-style file locations
87
+ const rootSpecFiles = readdirSync(testsDir).filter((f) => f.endsWith(".spec.ts") && !SHARED_FILES.has(f));
88
+ for (const f of rootSpecFiles) {
89
+ results.push({
90
+ fileName: f,
91
+ issue: "Old-style file location",
92
+ detail: `Run \`openspec-pw migrate\` to move to tests/playwright/changes/${f.replace(".spec.ts", "")}/`,
93
+ });
94
+ }
95
+ // 7. Output results
96
+ if (results.length === 0) {
97
+ console.log(chalk.green(" ✅ No issues found. All tests look healthy.\n"));
98
+ return;
99
+ }
100
+ console.log(chalk.yellow(`─── Found ${results.length} issue(s) ───`));
101
+ // Group by issue type
102
+ const grouped = {};
103
+ for (const r of results) {
104
+ if (!grouped[r.issue])
105
+ grouped[r.issue] = [];
106
+ grouped[r.issue].push(r);
107
+ }
108
+ for (const [issue, items] of Object.entries(grouped)) {
109
+ console.log(chalk.yellow(`\n ⚠ ${issue}`));
110
+ for (const item of items) {
111
+ console.log(chalk.gray(` - ${item.fileName}`));
112
+ if (item.detail) {
113
+ console.log(chalk.gray(` → ${item.detail}`));
114
+ }
115
+ }
116
+ }
117
+ console.log(chalk.blue("\n─── Suggested fixes ───"));
118
+ if (Object.keys(grouped).some((k) => k.includes("Old-style"))) {
119
+ console.log(chalk.green(" Run `openspec-pw migrate` to reorganize file structure."));
120
+ }
121
+ if (Object.keys(grouped).some((k) => k.includes("Missing auth"))) {
122
+ console.log(chalk.green(" Run `openspec-pw init` with auth credentials configured."));
123
+ }
124
+ if (Object.keys(grouped).some((k) => k.includes("Route not in sitemap"))) {
125
+ console.log(chalk.green(" Update sitemap or verify route is intentional."));
126
+ }
127
+ console.log();
128
+ }
129
+ // ─── Helpers ─────────────────────────────────────────────────────────────────
130
+ async function getSitemapRoutes(projectRoot) {
131
+ try {
132
+ const baseUrl = process.env.BASE_URL || "http://localhost:3000";
133
+ const result = execSync(`curl -s "${baseUrl}/sitemap.xml" | grep -oP '(?<=<loc>)[^<]+' | head -50`, {
134
+ cwd: projectRoot,
135
+ encoding: "utf-8",
136
+ timeout: 10000,
137
+ });
138
+ const urls = result
139
+ .split("\n")
140
+ .filter(Boolean)
141
+ .map((u) => {
142
+ try {
143
+ return new URL(u).pathname;
144
+ }
145
+ catch {
146
+ return null;
147
+ }
148
+ })
149
+ .filter(Boolean);
150
+ return [...new Set(urls)];
151
+ }
152
+ catch {
153
+ return null;
154
+ }
155
+ }
156
+ async function getChangeNames(projectRoot) {
157
+ try {
158
+ const result = execSync("npx openspec list --json", {
159
+ cwd: projectRoot,
160
+ encoding: "utf-8",
161
+ timeout: 30000,
162
+ });
163
+ const data = JSON.parse(result);
164
+ return Array.isArray(data)
165
+ ? data.map((c) => c.name)
166
+ : Object.keys(data);
167
+ }
168
+ catch {
169
+ return [];
170
+ }
171
+ }
172
+ function collectSpecFiles(dir, collected = []) {
173
+ const entries = readdirSync(dir, { withFileTypes: true });
174
+ for (const entry of entries) {
175
+ const fullPath = join(dir, entry.name);
176
+ if (entry.isDirectory()) {
177
+ // Skip node_modules, .auth, __snapshots__ etc.
178
+ if (!entry.name.startsWith(".") &&
179
+ entry.name !== "node_modules" &&
180
+ entry.name !== "__snapshots__") {
181
+ collectSpecFiles(fullPath, collected);
182
+ }
183
+ }
184
+ else if (entry.name.endsWith(".spec.ts")) {
185
+ collected.push(fullPath);
186
+ }
187
+ }
188
+ return collected;
189
+ }
190
+ //# sourceMappingURL=audit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit.js","sourceRoot":"","sources":["../../src/commands/audit.ts"],"names":[],"mappings":"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"}
@@ -8,6 +8,7 @@ export interface RunOptions {
8
8
  raft?: number;
9
9
  escalated?: number;
10
10
  headed?: boolean;
11
+ updateSnapshots?: boolean;
11
12
  }
12
13
  export declare function run(changeName: string, options: RunOptions): Promise<void>;
13
14
  interface TestResults {
@@ -45,6 +45,9 @@ export async function run(changeName, options) {
45
45
  if (options.headed) {
46
46
  args.push("--headed");
47
47
  }
48
+ if (options.updateSnapshots) {
49
+ args.push("--update-snapshots");
50
+ }
48
51
  let testOutput = "";
49
52
  try {
50
53
  const result = execSync(args.join(" "), {
@@ -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":"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"}
package/dist/index.js CHANGED
@@ -8,6 +8,7 @@ import { doctor } from "./commands/doctor.js";
8
8
  import { run } from "./commands/run.js";
9
9
  import { migrate } from "./commands/migrate.js";
10
10
  import { uninstall } from "./commands/uninstall.js";
11
+ import { audit } from "./commands/audit.js";
11
12
  const __dirname = dirname(fileURLToPath(import.meta.url));
12
13
  const pkg = JSON.parse(readFileSync(join(__dirname, "../package.json"), "utf-8"));
13
14
  const program = new Command();
@@ -46,6 +47,7 @@ program
46
47
  .option("--raft <n>", "Number of RAFTs detected", (v) => parseInt(v, 10), undefined)
47
48
  .option("--escalated <n>", "Number of human escalations", (v) => parseInt(v, 10), undefined)
48
49
  .option("--headed", "Show browser during test run (default: headless)")
50
+ .option("--update-snapshots", "Update screenshot baselines before running tests")
49
51
  .action(run);
50
52
  program
51
53
  .command("migrate")
@@ -57,5 +59,9 @@ program
57
59
  .command("uninstall")
58
60
  .description("Remove OpenSpec + Playwright E2E integration from the current project")
59
61
  .action(uninstall);
62
+ program
63
+ .command("audit")
64
+ .description("Audit test files for orphaned specs, missing auth, sitemap issues")
65
+ .action(audit);
60
66
  program.parse();
61
67
  //# 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":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAC1C,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAC9C,OAAO,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAC;AACxC,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACpD,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAE5C,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAC1D,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CACpB,YAAY,CAAC,IAAI,CAAC,SAAS,EAAE,iBAAiB,CAAC,EAAE,OAAO,CAAC,CAC1D,CAAC;AAEF,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,aAAa,CAAC;KACnB,WAAW,CAAC,mDAAmD,CAAC;KAChE,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;AAExB,OAAO;KACJ,OAAO,CAAC,MAAM,CAAC;KACf,WAAW,CACV,yEAAyE,CAC1E;KACA,MAAM,CAAC,qBAAqB,EAAE,qBAAqB,EAAE,SAAS,CAAC;KAC/D,MAAM,CAAC,UAAU,EAAE,mCAAmC,CAAC;KACvD,MAAM,CAAC,QAAQ,EAAE,qDAAqD,CAAC;KACvE,MAAM,CAAC,WAAW,EAAE,uCAAuC,CAAC;KAC5D,MAAM,CAAC,IAAI,CAAC,CAAC;AAEhB,OAAO;KACJ,OAAO,CAAC,QAAQ,CAAC;KACjB,WAAW,CAAC,0CAA0C,CAAC;KACvD,MAAM,CAAC,QAAQ,EAAE,wBAAwB,CAAC;KAC1C,MAAM,CAAC,MAAM,CAAC,CAAC;AAElB,OAAO;KACJ,OAAO,CAAC,QAAQ,CAAC;KACjB,WAAW,CAAC,qDAAqD,CAAC;KAClE,MAAM,CAAC,UAAU,EAAE,iBAAiB,CAAC;KACrC,MAAM,CAAC,YAAY,EAAE,2BAA2B,CAAC;KACjD,MAAM,CAAC,MAAM,CAAC,CAAC;AAElB,OAAO;KACJ,OAAO,CAAC,mBAAmB,CAAC;KAC5B,WAAW,CAAC,iDAAiD,CAAC;KAC9D,MAAM,CACL,sBAAsB,EACtB,+CAA+C,CAChD;KACA,MAAM,CAAC,yBAAyB,EAAE,yBAAyB,EAAE,KAAK,CAAC;KACnE,MAAM,CAAC,QAAQ,EAAE,wBAAwB,CAAC;KAC1C,MAAM,CAAC,sBAAsB,EAAE,iCAAiC,CAAC;KACjE,MAAM,CAAC,gBAAgB,EAAE,oCAAoC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,SAAS,CAAC;KACjG,MAAM,CAAC,cAAc,EAAE,sCAAsC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,SAAS,CAAC;KACjG,MAAM,CAAC,YAAY,EAAE,0BAA0B,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,SAAS,CAAC;KACnF,MAAM,CAAC,iBAAiB,EAAE,6BAA6B,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,SAAS,CAAC;KAC3F,MAAM,CAAC,UAAU,EAAE,kDAAkD,CAAC;KACtE,MAAM,CAAC,oBAAoB,EAAE,kDAAkD,CAAC;KAChF,MAAM,CAAC,GAAG,CAAC,CAAC;AAEf,OAAO;KACJ,OAAO,CAAC,SAAS,CAAC;KAClB,WAAW,CACV,2FAA2F,CAC5F;KACA,MAAM,CACL,eAAe,EACf,kDAAkD,CACnD;KACA,MAAM,CAAC,aAAa,EAAE,8CAA8C,CAAC;KACrE,MAAM,CAAC,OAAO,CAAC,CAAC;AAEnB,OAAO;KACJ,OAAO,CAAC,WAAW,CAAC;KACpB,WAAW,CACV,uEAAuE,CACxE;KACA,MAAM,CAAC,SAAS,CAAC,CAAC;AAErB,OAAO;KACJ,OAAO,CAAC,OAAO,CAAC;KAChB,WAAW,CAAC,mEAAmE,CAAC;KAChF,MAAM,CAAC,KAAK,CAAC,CAAC;AAEjB,OAAO,CAAC,KAAK,EAAE,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openspec-playwright",
3
- "version": "0.2.5",
3
+ "version": "0.2.7",
4
4
  "description": "OpenSpec + Playwright E2E verification setup tool for Claude Code",
5
5
  "type": "module",
6
6
  "bin": {