openspec-playwright 0.1.51 → 0.1.53
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.
|
@@ -52,7 +52,7 @@ Read all files from `openspec/changes/<name>/specs/*.md`. Extract functional req
|
|
|
52
52
|
|
|
53
53
|
Detect if auth is required only when BOTH conditions are met:
|
|
54
54
|
|
|
55
|
-
**Condition A — Explicit markers**: "login", "signin", "logout", "authenticate", "protected", "authenticated", "session", "unauthorized"
|
|
55
|
+
**Condition A — Explicit markers**: "login", "signin", "logout", "authenticate", "protected", "authenticated", "session", "unauthorized", "jwt", "token", "refresh", "middleware"
|
|
56
56
|
|
|
57
57
|
**Condition B — Context indicators**: Protected routes ("/dashboard", "/profile", "/admin"), role mentions ("admin", "user"), redirect flows
|
|
58
58
|
|
|
@@ -76,44 +76,33 @@ This validates: app server reachable, auth fixtures initialized, Playwright work
|
|
|
76
76
|
|
|
77
77
|
### 4. Explore application
|
|
78
78
|
|
|
79
|
-
|
|
79
|
+
Explore to collect real DOM data before writing test plan. This eliminates blind selector guessing.
|
|
80
80
|
|
|
81
81
|
**Prerequisites**: seed test pass. If auth is required, ensure `auth.setup.ts` has been run (Step 7). BASE_URL must be verified reachable (see 4.1).
|
|
82
82
|
|
|
83
|
-
#### 4.1. Verify BASE_URL + Read app-knowledge.md + Extract routes
|
|
83
|
+
#### 4.1. Verify BASE_URL + Read app-knowledge.md + Extract routes
|
|
84
84
|
|
|
85
|
-
**
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
// Confirm page loaded → proceed
|
|
89
|
-
// If error → check BASE_URL in seed.spec.ts or vite.config.ts
|
|
90
|
-
```
|
|
91
|
-
A correct BASE_URL prevents wasted exploration time on unreachable routes.
|
|
92
|
-
|
|
93
|
-
**Then**, read `tests/playwright/app-knowledge.md` to understand:
|
|
94
|
-
- **Known risks**: SPA behavior, dynamic content patterns, auth method
|
|
95
|
-
- **Project conventions**: preferred selector strategy, credential format, BASE_URL
|
|
96
|
-
|
|
97
|
-
This is context, not constraint — explore with open eyes even if patterns differ from history.
|
|
98
|
-
|
|
99
|
-
**Then**, read all files in `openspec/changes/<name>/specs/*.md`. Extract every URL, route, or path mentioned:
|
|
100
|
-
|
|
101
|
-
- Full URLs: `http://localhost:3000/dashboard`, `BASE_URL + /admin`
|
|
102
|
-
- Relative paths: `/dashboard`, `/api/auth/login`, `/admin/settings`
|
|
103
|
-
- Infer routes from text: "navigate to the dashboard" → check if `/dashboard` exists
|
|
104
|
-
|
|
105
|
-
Group routes by role:
|
|
106
|
-
- **Guest routes**: `/`, `/login`, `/about` (no auth needed)
|
|
107
|
-
- **Protected routes**: `/dashboard`, `/profile`, `/admin` (auth required)
|
|
85
|
+
1. **Verify BASE_URL**: `browser_navigate(BASE_URL)` → check HTTP status. If unreachable or HTTP 5xx → **STOP: app has a bug. Tell user to fix the server first.**
|
|
86
|
+
2. **Read app-knowledge.md**: understand known risks (SPA, dynamic content) and project conventions
|
|
87
|
+
3. **Read specs**: extract all URLs/paths, group by Guest vs Protected
|
|
108
88
|
|
|
109
89
|
#### 4.2. Explore each route via Playwright MCP
|
|
110
90
|
|
|
111
|
-
For each route
|
|
91
|
+
For each route:
|
|
112
92
|
|
|
113
93
|
```
|
|
114
|
-
browser_navigate → browser_snapshot → browser_take_screenshot
|
|
94
|
+
browser_navigate → browser_console_messages → browser_snapshot → browser_take_screenshot
|
|
115
95
|
```
|
|
116
96
|
|
|
97
|
+
**After navigating, check for app-level errors**:
|
|
98
|
+
|
|
99
|
+
| Signal | Meaning | Action |
|
|
100
|
+
|--------|---------|--------|
|
|
101
|
+
| HTTP 5xx or unreachable | Server error | **STOP** — tell user: "App has a bug (HTTP <code>). Fix it, then re-run /opsx:e2e." |
|
|
102
|
+
| JS error in console | App runtime error | **STOP** — tell user: "Page has JS errors. Fix them, then re-run /opsx:e2e." |
|
|
103
|
+
| HTTP 404 | Route not in app (metadata issue) | Continue — mark `⚠️ route not found` in app-exploration.md |
|
|
104
|
+
| Auth required, no credentials | Missing auth setup | Continue — skip protected routes, explore login page |
|
|
105
|
+
|
|
117
106
|
**For guest routes** (no auth):
|
|
118
107
|
```javascript
|
|
119
108
|
// Navigate directly
|
|
@@ -122,103 +111,56 @@ await browser_navigate(`${BASE_URL}/<route>`)
|
|
|
122
111
|
|
|
123
112
|
**For protected routes** (auth required):
|
|
124
113
|
```javascript
|
|
125
|
-
// Option A: use existing storageState (recommended
|
|
114
|
+
// Option A: use existing storageState (recommended)
|
|
126
115
|
// Option B: navigate to /login first, fill form, then navigate to target
|
|
127
116
|
// Option C: use browser_run_code to set auth cookies directly
|
|
128
117
|
```
|
|
129
118
|
|
|
130
119
|
**If credentials are not yet available**:
|
|
131
|
-
1.
|
|
132
|
-
2.
|
|
133
|
-
3.
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
4. Run `npx playwright test --project=setup`
|
|
140
|
-
5. Re-run exploration for protected routes
|
|
141
|
-
|
|
142
|
-
Wait for page stability after navigation:
|
|
143
|
-
- Prefer waiting for a specific element: `browser_wait_for` with text or selector
|
|
144
|
-
- Avoid `networkidle` / `load` — they are too slow or unreliable
|
|
145
|
-
- Use a "page ready" signal: look for a heading, a loading spinner disappearing, or a URL change
|
|
120
|
+
1. Skip protected routes — mark `⚠️ auth needed — explore after auth.setup.ts`
|
|
121
|
+
2. Explore the login page itself (guest route) — extract form selectors
|
|
122
|
+
3. After auth.setup.ts runs, re-run exploration for protected routes
|
|
123
|
+
|
|
124
|
+
Wait for page stability:
|
|
125
|
+
- Prefer `browser_wait_for` with text or selector
|
|
126
|
+
- Avoid `networkidle` / `load` — too slow or unreliable
|
|
127
|
+
- Ready signal: heading, spinner disappears, or URL change
|
|
146
128
|
|
|
147
129
|
#### 4.3. Parse the snapshot
|
|
148
130
|
|
|
149
131
|
From `browser_snapshot` output, extract **interactive elements** for each route:
|
|
150
132
|
|
|
151
|
-
| Element type | What to capture |
|
|
133
|
+
| Element type | What to capture | Selector priority |
|
|
152
134
|
|---|---|---|
|
|
153
|
-
| **Buttons** | text, selector
|
|
154
|
-
| **Form fields** | name, type, label, selector | data-testid > name > label |
|
|
155
|
-
| **Navigation links** | text, href, selector | text > href |
|
|
135
|
+
| **Buttons** | text, selector | `[data-testid]` > `getByRole` > `getByLabel` > `getByText` |
|
|
136
|
+
| **Form fields** | name, type, label, selector | `[data-testid]` > `name` > `label` |
|
|
137
|
+
| **Navigation links** | text, href, selector | `text` > `href` |
|
|
156
138
|
| **Headings** | text content, selector | for assertions |
|
|
157
139
|
| **Error messages** | text patterns, selector | for error path testing |
|
|
158
|
-
| **Dynamic content** | structure
|
|
159
|
-
|
|
160
|
-
**Selector strategy** (in priority order):
|
|
161
|
-
1. `[data-testid="..."]` — most stable, prefer these
|
|
162
|
-
2. `getByRole('button', { name: '...' })` — semantic, stable
|
|
163
|
-
3. `getByLabel('...')` — for form fields
|
|
164
|
-
4. `getByText('...')` — fallback, fragile
|
|
165
|
-
5. CSS selectors — last resort
|
|
140
|
+
| **Dynamic content** | structure — row counts, card layouts | for data-driven tests |
|
|
166
141
|
|
|
167
142
|
#### 4.4. Write app-exploration.md
|
|
168
143
|
|
|
169
144
|
Output: `openspec/changes/<name>/specs/playwright/app-exploration.md`
|
|
170
145
|
|
|
171
|
-
|
|
172
|
-
# App Exploration — <name>
|
|
173
|
-
Generated: <timestamp>
|
|
174
|
-
BASE_URL: <from env or seed.spec.ts>
|
|
175
|
-
|
|
176
|
-
## Route: /
|
|
177
|
-
- **Auth**: none
|
|
178
|
-
- **URL**: ${BASE_URL}/
|
|
179
|
-
- **Ready signal**: page has heading
|
|
180
|
-
- **Elements**:
|
|
181
|
-
- login link: `a:text("登录")`
|
|
182
|
-
- signup link: `[data-testid="signup-link"]`
|
|
183
|
-
- **Screenshot**: `__screenshots__/index.png`
|
|
184
|
-
|
|
185
|
-
## Route: /dashboard (user)
|
|
186
|
-
- **Auth**: required (storageState: playwright/.auth/user.json)
|
|
187
|
-
- **URL**: ${BASE_URL}/dashboard
|
|
188
|
-
- **Ready signal**: [data-testid="dashboard-heading"] visible
|
|
189
|
-
- **Elements**:
|
|
190
|
-
- heading: `[data-testid="page-title"]`
|
|
191
|
-
- logout btn: `[data-testid="logout-btn"]`
|
|
192
|
-
- profile form: `form >> input[name="displayName"]`
|
|
193
|
-
- settings link: `nav >> text=Settings`
|
|
194
|
-
- **Screenshot**: `__screenshots__/dashboard-user.png`
|
|
195
|
-
|
|
196
|
-
## Route: /admin (admin)
|
|
197
|
-
- **Auth**: required (storageState: playwright/.auth/admin.json)
|
|
198
|
-
- **URL**: ${BASE_URL}/admin
|
|
199
|
-
- **Ready signal**: [data-testid="admin-panel"] visible
|
|
200
|
-
- **Elements**:
|
|
201
|
-
- admin panel: `[data-testid="admin-panel"]`
|
|
202
|
-
- user table: `table#user-table tbody tr`
|
|
203
|
-
- add user btn: `[data-testid="add-user-btn"]`
|
|
204
|
-
- delete btn: `button:has-text("Delete")`
|
|
205
|
-
- **Screenshot**: `__screenshots__/admin-panel.png`
|
|
206
|
-
|
|
207
|
-
## Exploration Notes
|
|
208
|
-
- Route /admin → user gets redirected to /login (no admin role)
|
|
209
|
-
- /dashboard loads user-specific data (test assertions should use toContainText, not toHaveText)
|
|
210
|
-
```
|
|
146
|
+
Use template: `openspec/schemas/playwright-e2e/templates/app-exploration.md`
|
|
211
147
|
|
|
212
|
-
|
|
148
|
+
Key fields per route:
|
|
149
|
+
- **URL**: `${BASE_URL}<path>`
|
|
150
|
+
- **Auth**: none / required (storageState: `<path>`)
|
|
151
|
+
- **Ready signal**: how to know the page is loaded
|
|
152
|
+
- **Elements**: interactive elements with verified selectors (see 4.3 table)
|
|
153
|
+
- **Screenshot**: `__screenshots__/<slug>.png`
|
|
213
154
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
|
155
|
+
After exploration, add route-level notes (redirects, dynamic content → see 4.5).
|
|
156
|
+
|
|
157
|
+
#### 4.5. Exploration behavior notes
|
|
158
|
+
|
|
159
|
+
| Situation | Action |
|
|
160
|
+
|-----------|--------|
|
|
219
161
|
| SPA routing (URL changes but page doesn't reload) | Explore via navigation clicks from known routes, not direct URLs |
|
|
220
|
-
| Page loads but no interactive elements |
|
|
221
|
-
| Dynamic content (user-specific) | Record structure
|
|
162
|
+
| Page loads but no interactive elements | Wait longer for SPA hydration |
|
|
163
|
+
| Dynamic content (user-specific) | Record structure — use `toContainText`, not `toHaveText` |
|
|
222
164
|
|
|
223
165
|
**Idempotency**: If `app-exploration.md` already exists → read it, verify routes still match specs, update only new routes or changed pages.
|
|
224
166
|
|
|
@@ -256,18 +198,9 @@ Create test cases:
|
|
|
256
198
|
- List each functional requirement as a test case
|
|
257
199
|
- Mark with `@role(user|admin|guest|none)` and `@auth(required|none)`
|
|
258
200
|
- Include happy path AND error paths
|
|
259
|
-
- Reference the **real route URL** from app-exploration.md
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
```markdown
|
|
263
|
-
### User can view dashboard
|
|
264
|
-
- **Route**: /dashboard (from app-exploration.md)
|
|
265
|
-
- **Auth**: required (user storageState)
|
|
266
|
-
- **Test steps**:
|
|
267
|
-
1. Go to `/dashboard`
|
|
268
|
-
2. Assert page heading: `[data-testid="page-title"]` contains "Dashboard"
|
|
269
|
-
3. Assert logout button visible: `[data-testid="logout-btn"]`
|
|
270
|
-
```
|
|
201
|
+
- Reference the **real route URL** and **verified selectors** from app-exploration.md
|
|
202
|
+
|
|
203
|
+
Example: see `openspec/schemas/playwright-e2e/templates/test-plan.md`
|
|
271
204
|
|
|
272
205
|
**Idempotency**: If test-plan.md already exists → read it, use it, do NOT regenerate.
|
|
273
206
|
|
|
@@ -283,7 +216,7 @@ Create `tests/playwright/<name>.spec.ts`:
|
|
|
283
216
|
|
|
284
217
|
#### Verify selectors before writing
|
|
285
218
|
|
|
286
|
-
**For each test case, verify selectors in a real browser BEFORE writing
|
|
219
|
+
**For each test case, verify selectors in a real browser BEFORE writing test code.**
|
|
287
220
|
|
|
288
221
|
```
|
|
289
222
|
For each test case in test-plan.md:
|
|
@@ -297,7 +230,7 @@ For each test case in test-plan.md:
|
|
|
297
230
|
a. Check if selector exists in app-exploration.md
|
|
298
231
|
b. If yes → verify it's still valid via browser_snapshot
|
|
299
232
|
c. If no → find equivalent from current snapshot
|
|
300
|
-
d. Selector priority:
|
|
233
|
+
d. Selector priority: see 4.3 table
|
|
301
234
|
7. Write test code with verified selectors
|
|
302
235
|
8. If selector cannot be verified → note it for Healer (Step 9)
|
|
303
236
|
```
|
|
@@ -306,7 +239,7 @@ This ensures every selector in the generated test code has been validated agains
|
|
|
306
239
|
|
|
307
240
|
**Generate** Playwright code for each verified test case:
|
|
308
241
|
- Follow `seed.spec.ts` structure
|
|
309
|
-
- Prefer `data-testid
|
|
242
|
+
- Prefer `data-testid` selectors (see 4.3 table for priority)
|
|
310
243
|
- Include happy path AND error/edge cases
|
|
311
244
|
- Use `test.describe(...)` for grouping
|
|
312
245
|
- Each test: `test('描述性名称', async ({ page }) => { ... })`
|
|
@@ -340,34 +273,29 @@ test('redirects unauthenticated user to login', async ({ browser }) => {
|
|
|
340
273
|
|
|
341
274
|
These are the most common mistakes that cause test failures. **Always follow these rules:**
|
|
342
275
|
|
|
343
|
-
**API calls — use `request`
|
|
276
|
+
**API calls — use `page.request` directly:**
|
|
344
277
|
```typescript
|
|
345
|
-
// ❌ WRONG — page.evaluate timeout,
|
|
278
|
+
// ❌ WRONG — page.evaluate with fetch has timeout, CORS, and context issues
|
|
346
279
|
const result = await page.evaluate(async () => {
|
|
347
280
|
const res = await fetch('/api/data');
|
|
348
281
|
return res.json();
|
|
349
282
|
});
|
|
350
283
|
|
|
351
|
-
// ✅ CORRECT —
|
|
284
|
+
// ✅ CORRECT — page.request is already an APIRequestContext, use directly
|
|
352
285
|
const res = await page.request.get(`${BASE_URL}/api/data`);
|
|
353
286
|
expect(res.status()).toBe(200);
|
|
354
287
|
const data = await res.json();
|
|
355
288
|
```
|
|
356
289
|
|
|
357
|
-
**Browser context
|
|
290
|
+
**Browser context — use `close()` for BrowserContext, no cleanup for APIRequestContext:**
|
|
358
291
|
```typescript
|
|
359
|
-
//
|
|
360
|
-
const ctx = await page.request.newContext();
|
|
361
|
-
await ctx.dispose(); // actually correct, but this is CommonContext
|
|
362
|
-
const context = await browser.newContext();
|
|
363
|
-
await context.close(); // ← WRONG
|
|
364
|
-
|
|
365
|
-
// ✅ CORRECT
|
|
292
|
+
// ✅ BrowserContext — close it when done
|
|
366
293
|
const context = await browser.newContext();
|
|
367
|
-
await context.close(); //
|
|
294
|
+
await context.close(); // ← correct
|
|
368
295
|
|
|
369
|
-
//
|
|
370
|
-
|
|
296
|
+
// ✅ APIRequestContext — page.request is already one, no cleanup needed
|
|
297
|
+
const res = await page.request.get(`${BASE_URL}/api/data`);
|
|
298
|
+
// No dispose() or close() needed
|
|
371
299
|
```
|
|
372
300
|
|
|
373
301
|
**File uploads — use `setInputFiles()`, NOT `page.evaluate()` + fetch:**
|
|
@@ -465,18 +393,15 @@ If tests fail → use Playwright MCP tools to inspect UI, fix selectors, re-run.
|
|
|
465
393
|
| **Selector changed** | Element not found | `browser_snapshot` → fix selector → re-run |
|
|
466
394
|
| **Assertion mismatch** | Wrong content/value | `browser_snapshot` → compare → fix assertion → re-run |
|
|
467
395
|
| **Timing issue** | `waitFor`/`page.evaluate` timeout | Switch to `request` API or add `waitFor` → re-run |
|
|
468
|
-
| **
|
|
469
|
-
| **Auth expired** | 401 Unauthorized | Token may have expired — if long suite, recommend splitting or re-auth |
|
|
470
|
-
| **page.evaluate failure** | `fetch` in browser context, CORS errors | Switch to `page.request` API → re-run |
|
|
396
|
+
| **page.evaluate with fetch** | `fetch` in browser context, CORS errors | Switch to `page.request` API → re-run |
|
|
471
397
|
|
|
472
|
-
3. **
|
|
398
|
+
3. **Heal** (≤3 attempts): snapshot → fix → re-run
|
|
473
399
|
4. **After 3 failures**: collect evidence checklist → `test.skip()` if app bug, report recommendation if test bug
|
|
474
400
|
|
|
475
401
|
### 10. False Pass Detection
|
|
476
402
|
|
|
477
|
-
Run after test suite completes (even if all pass):
|
|
478
|
-
|
|
479
|
-
- **Conditional logic**: Look for `if (locator.isVisible().catch(() => false))` — if test passes, locator may not exist
|
|
403
|
+
Run after test suite completes (even if all pass). Common patterns (see Step 6 Anti-Pattern Warnings for fixes):
|
|
404
|
+
- **Conditional visibility**: `if (locator.isVisible().catch(() => false))` — if test passes, locator may not exist
|
|
480
405
|
- **Too fast**: < 200ms for a complex flow is suspicious
|
|
481
406
|
- **No fresh auth context**: Protected routes without `browser.newContext()`
|
|
482
407
|
|
|
@@ -489,45 +414,13 @@ Read report at `openspec/reports/playwright-e2e-<name>-<timestamp>.md`. Present:
|
|
|
489
414
|
- Auto-heal notes
|
|
490
415
|
- Recommendations with `file:line` references
|
|
491
416
|
|
|
417
|
+
Report template: `openspec/schemas/playwright-e2e/templates/report.md`
|
|
418
|
+
|
|
492
419
|
**Update tasks.md** if all tests pass: find E2E-related items, append `✅ Verified via Playwright E2E (<timestamp>)`.
|
|
493
420
|
|
|
494
421
|
## Report Structure
|
|
495
422
|
|
|
496
|
-
|
|
497
|
-
# Playwright E2E Report — <name>
|
|
498
|
-
|
|
499
|
-
## Summary
|
|
500
|
-
| Tests | Passed | Failed | Duration | Status |
|
|
501
|
-
|-------|--------|--------|----------|--------|
|
|
502
|
-
| N | N | N | Xm Xs | ✅/❌ |
|
|
503
|
-
|
|
504
|
-
## Results
|
|
505
|
-
### Passed
|
|
506
|
-
| Test | Duration | Notes |
|
|
507
|
-
|------|----------|-------|
|
|
508
|
-
| ... | ... | ... |
|
|
509
|
-
|
|
510
|
-
### Failed
|
|
511
|
-
| Test | Error | Recommendation |
|
|
512
|
-
|------|-------|----------------|
|
|
513
|
-
| ... | ... | file:line — fix |
|
|
514
|
-
|
|
515
|
-
## Auto-Heal Log
|
|
516
|
-
- Attempt N: selector fix → result
|
|
517
|
-
|
|
518
|
-
## Coverage
|
|
519
|
-
- [x] Requirement 1
|
|
520
|
-
- [ ] Requirement 2 (unverified)
|
|
521
|
-
|
|
522
|
-
## ⚠️ Coverage Gaps
|
|
523
|
-
> Tests passed but coverage gaps detected.
|
|
524
|
-
|
|
525
|
-
| Test | Gap | Recommendation |
|
|
526
|
-
|------|-----|----------------|
|
|
527
|
-
| ... | Conditional visibility check | file:line — use `expect().toBeVisible()` |
|
|
528
|
-
| ... | Auth guard uses inherited session | Add fresh context test |
|
|
529
|
-
| ... | Suspiciously fast execution (<200ms) | Verify test logic executed |
|
|
530
|
-
```
|
|
423
|
+
Reference: `openspec/schemas/playwright-e2e/templates/report.md`
|
|
531
424
|
|
|
532
425
|
## Graceful Degradation
|
|
533
426
|
|
|
@@ -535,6 +428,7 @@ Read report at `openspec/reports/playwright-e2e-<name>-<timestamp>.md`. Present:
|
|
|
535
428
|
|----------|----------|
|
|
536
429
|
| No specs | Stop — E2E requires specs |
|
|
537
430
|
| Seed test fails | Stop — fix environment |
|
|
431
|
+
| App has JS errors or HTTP 5xx during exploration | **STOP** — tell user to fix the app first |
|
|
538
432
|
| No auth required | Skip auth setup |
|
|
539
433
|
| app-exploration.md exists | Read and use (never regenerate) |
|
|
540
434
|
| app-knowledge.md exists | Read and use (append new patterns only) |
|
|
@@ -551,7 +445,7 @@ Read report at `openspec/reports/playwright-e2e-<name>-<timestamp>.md`. Present:
|
|
|
551
445
|
- Read specs from `openspec/changes/<name>/specs/` as source of truth
|
|
552
446
|
- Do NOT generate tests that contradict the specs
|
|
553
447
|
- **DO generate real, runnable Playwright test code** — not placeholders or TODOs
|
|
554
|
-
- Do NOT overwrite files outside: `specs/playwright/`, `tests/playwright/`, `openspec/reports/`, `playwright.config.ts`, `auth.setup.ts
|
|
448
|
+
- Do NOT overwrite files outside: `specs/playwright/`, `tests/playwright/`, `openspec/reports/`, `playwright.config.ts`, `auth.setup.ts`
|
|
555
449
|
- **Always explore before generating** — Step 4 is mandatory for accurate selectors
|
|
556
450
|
- Cap auto-heal at 3 attempts
|
|
557
451
|
- If no change specified → always ask user to select
|
|
Binary file
|
package/package.json
CHANGED
package/release-notes.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
## What's Changed
|
|
2
2
|
|
|
3
|
-
- fix(SKILL):
|
|
3
|
+
- fix(SKILL): stop exploration on app-level errors, continue on metadata issues
|
|
4
4
|
|
|
5
|
-
**Full Changelog**: https://github.com/wxhou/openspec-playwright/releases/tag/v0.1.
|
|
5
|
+
**Full Changelog**: https://github.com/wxhou/openspec-playwright/releases/tag/v0.1.53
|
|
Binary file
|