start-vibing 4.3.4 → 4.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -2
- package/template/.claude/agents/sd-audit.md +32 -0
- package/template/.claude/commands/e2e-audit.md +16 -0
- package/template/.claude/hooks/e2e-audit-session-start.sh +4 -0
- package/template/.claude/settings.json +4 -0
- package/template/.claude/skills/e2e-audit/SKILL.md +216 -0
- package/template/.claude/skills/e2e-audit/findings.schema.json +98 -0
- package/template/.claude/skills/e2e-audit/references/api-contract-playbook.md +66 -0
- package/template/.claude/skills/e2e-audit/references/auth-setup-playbook.md +78 -0
- package/template/.claude/skills/e2e-audit/references/coverage-gap-playbook.md +95 -0
- package/template/.claude/skills/e2e-audit/references/post-run-feedback-playbook.md +80 -0
- package/template/.claude/skills/e2e-audit/scripts/detect-stack.sh +205 -0
- package/template/.claude/skills/e2e-audit/scripts/detect-uncovered.sh +137 -0
- package/template/.claude/skills/e2e-audit/scripts/discover-api-surface.sh +242 -0
- package/template/.claude/skills/e2e-audit/scripts/discover-routes.sh +163 -0
- package/template/.claude/skills/e2e-audit/scripts/inventory-existing-tests.sh +161 -0
- package/template/.claude/skills/e2e-audit/scripts/verify-audit.sh +88 -0
- package/template/.claude/skills/e2e-audit/templates/auth-setup.ts.tpl +24 -0
- package/template/.claude/skills/e2e-audit/templates/base-fixture.ts.tpl +75 -0
- package/template/.claude/skills/e2e-audit/templates/findings-report.md.tpl +54 -0
- package/template/.claude/skills/e2e-audit/templates/post-run-feedback.md.tpl +36 -0
- package/template/.claude/skills/super-design/SKILL.md +42 -4
- package/template/.claude/skills/super-design/scripts/discover-surfaces.sh +197 -0
- package/template/.claude/skills/super-design/scripts/extract-project-rules.sh +240 -0
- package/template/.claude/skills/super-design/scripts/verify-audit.sh +34 -1
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "start-vibing",
|
|
3
|
-
"version": "4.
|
|
4
|
-
"description": "Setup Claude Code with 9 plugins, 6 community skills, and 8 MCP servers. Parallel install, auto-accept, superpowers + ralph-loop.
|
|
3
|
+
"version": "4.4.1",
|
|
4
|
+
"description": "Setup Claude Code with 9 plugins, 6 community skills, and 8 MCP servers. Parallel install, auto-accept, superpowers + ralph-loop. e2e-audit 0.2.0 refactor (skill-only, no agents): SessionStart hook + slash command make the skill keyword-invokable (\"e2e audit\", \"roda o e2e\", \"integration test\", \"test coverage gaps\"). Source-first discovery via detect-stack, discover-routes (Next app/pages/Remix/SvelteKit/Nuxt/Astro), discover-api-surface (HTTP handlers, tRPC procedures, GraphQL, server actions, middleware auth), inventory-existing-tests (preserve prior corpus + sha256 drift hash), and detect-uncovered (branch-diff vs origin/main finds changes not covered by existing specs). Report-then-ask between mapping and Playwright run; post-run-feedback report before writing findings. SHOT+TRACE+ASSERT+SOURCE evidence quad per non-meta finding; meta rules (coverage-gap-*, uncovered-*, test-drift, stack-detect, post-run-feedback) exempt. verify-audit.sh enforces schema + quad. Generic (no project leakage). super-design 0.7.0 carries over.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"start-vibing": "./dist/cli.js"
|
|
@@ -67,6 +67,20 @@ Read in order:
|
|
|
67
67
|
|
|
68
68
|
Run `.claude/skills/super-design/scripts/discover-routes.sh`. If incremental mode, filter to scope (read `.super-design/sessions/<id>/scope.json`).
|
|
69
69
|
|
|
70
|
+
## Step 1.5 — Source-first surface & project-rule discovery (MANDATORY, 0.7.0+)
|
|
71
|
+
|
|
72
|
+
Playwright deduction misses internal state (modals never triggered in the tested flow, forms gated behind other forms, parallel/intercepting routes). Source-first discovery reads the repo FIRST and emits two authoritative artifacts that Step 2.5 and Step 3i consume as ground truth.
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
bash .claude/skills/super-design/scripts/discover-surfaces.sh > "$SESSION_DIR/surfaces.json"
|
|
76
|
+
bash .claude/skills/super-design/scripts/extract-project-rules.sh > "$SESSION_DIR/project-rules.json"
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
- `surfaces.json` — authoritative inventory of modals, forms, triggers, internal nav, Next.js layout/error/loading/not-found/parallel/intercepting routes. Step 2.5 Phase B cross-checks runtime discovery against this list and emits `modal-coverage-gap` / `form-coverage-gap` findings for anything the source declares but Playwright never exercised.
|
|
80
|
+
- `project-rules.json` — parsed FORBIDDEN tables from `CLAUDE.md`/`AGENTS.md`/`.cursorrules`. Applicable rules (audit-scope, not code-level) are consumed by Step 3i.
|
|
81
|
+
|
|
82
|
+
Both files MUST exist before Step 2 starts. `verify-audit.sh` warns when either is missing.
|
|
83
|
+
|
|
70
84
|
## Step 2 — Launch audit loop
|
|
71
85
|
|
|
72
86
|
For each viewport ∈ [mobile 375×812, tablet 768×1024, desktop 1440×900], for each page in scope:
|
|
@@ -385,6 +399,24 @@ Cross-reference the competitor component vocabulary from
|
|
|
385
399
|
tabs on mobile and the product uses hamburger-only, density score drops AND
|
|
386
400
|
the M1 finding cites the category norm.
|
|
387
401
|
|
|
402
|
+
## Step 3i — Project-rule enforcement (MANDATORY, 0.7.0+)
|
|
403
|
+
|
|
404
|
+
Iterate the `audit_applicable: true` rules from `project-rules.json` (Step 1.5). These rules are authoritative — the project owner has already codified them as the right answer for this codebase. Each violation fires as a PRIMARY finding with `rule: project-forbidden-<slug>` keyed to the project's own wording.
|
|
405
|
+
|
|
406
|
+
```jsonc
|
|
407
|
+
{
|
|
408
|
+
"id": "F-NNNN",
|
|
409
|
+
"rule": "project-forbidden-use-cards-on-mobile",
|
|
410
|
+
"source_rule": { "raw": "Use Cards on mobile", "reason": "Waste space in flex-col", "source_file": "CLAUDE.md" },
|
|
411
|
+
"template_id": "M2",
|
|
412
|
+
"viewport": "mobile",
|
|
413
|
+
"severity": 3,
|
|
414
|
+
...
|
|
415
|
+
}
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
Do NOT downgrade or tag — project-forbidden rules ARE the rule source, not a bump on another finding. `verify-audit.sh` skips snapshot_quote verification for this rule family (evidence is aggregate, not a single DOM quote).
|
|
419
|
+
|
|
388
420
|
## Step 4 — Write findings
|
|
389
421
|
|
|
390
422
|
Append to `docs/super-design/findings/F-NNNN.md` (one file per finding) AND `.super-design/sessions/<id>/findings.json`.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Run e2e-audit (source-first integration-test audit + coverage-gap detection)
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Invoke the e2e-audit skill with flags: $ARGUMENTS
|
|
6
|
+
|
|
7
|
+
Follow SKILL.md entry flow:
|
|
8
|
+
1. Preflight — detect stack, inventory existing tests, hash for drift
|
|
9
|
+
2. Source-first discovery — routes + api-surface + uncovered (branch diff)
|
|
10
|
+
3. Report-then-ask — show map, STOP for user confirmation
|
|
11
|
+
4. Dev server + Playwright run — capture SHOT+TRACE+ASSERT+SOURCE
|
|
12
|
+
5. Post-run feedback — API errors, RBAC, console, server crashes
|
|
13
|
+
6. Write findings.json + run verify-audit.sh
|
|
14
|
+
7. Return ≤5-sentence summary
|
|
15
|
+
|
|
16
|
+
Do not paste the full map into chat.
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
cat <<'EOF'
|
|
3
|
+
{"hookSpecificOutput":{"hookEventName":"SessionStart","additionalContext":"When the user mentions e2e audit, integration test audit, test coverage gaps, 'roda o e2e', 'run the e2e', end-to-end tests, API contract check, RBAC coverage, or says anything about auditing their application's integration tests — you MUST invoke the e2e-audit skill. Do not improvise a test plan, do not manually write specs without running the skill first, do not start Playwright blind. Read .claude/skills/e2e-audit/SKILL.md first, then follow its entry flow. The skill uses source-first discovery (routes.json + api-surface.json + existing-tests.json + uncovered.json) BEFORE touching the browser. It preserves any existing tests/ directory, warns on drift between runs, and emits a post-run-feedback report before writing findings.\n\nAfter mapping finishes, STOP and ask the user before running tests — report-then-ask. Every non-meta finding must carry the SHOT+TRACE+ASSERT+SOURCE evidence quad (screenshot, Playwright trace, literal assertion, source file). Coverage gaps surface as rule=coverage-gap-* or uncovered-* meta findings."}}
|
|
4
|
+
EOF
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: e2e-audit
|
|
3
|
+
version: 0.2.0
|
|
4
|
+
description: Comprehensive E2E audit that maps all routes, APIs, tRPC procedures, middleware auth, and forms from SOURCE first, cross-references against existing tests and the current branch diff, runs Playwright against dev, then reports coverage gaps and problems with a SHOT+TRACE+ASSERT+SOURCE evidence quad. Invoke when the user mentions "e2e audit", "run the e2e", "integration test audit", "test coverage gaps", "roda o e2e", end-to-end tests, API contract check, RBAC coverage, or auditing integration tests. Report-then-ask: stop after mapping, run only on confirmation, emit a post-run-feedback report before writing findings.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# e2e-audit — source-first integration-test audit
|
|
8
|
+
|
|
9
|
+
> **Operating principle:** you cannot audit what you never opened. Playwright traffic logs only cover flows you already know. Read the source first, then drive the browser to close the gaps the source revealed.
|
|
10
|
+
|
|
11
|
+
## Entry contract (non-negotiable)
|
|
12
|
+
|
|
13
|
+
1. **Mapping before clicking.** Run discovery scripts, write all JSON inventories, then STOP and report. Do NOT spin up the browser before the user confirms scope.
|
|
14
|
+
2. **Existing tests are load-bearing.** If `tests/e2e/` (or equivalent) exists, inventory it FIRST. Reuse fixtures, auth storage state, and page objects. Warn on drift between runs.
|
|
15
|
+
3. **Evidence quad.** Every non-meta finding ships SHOT+TRACE+ASSERT+SOURCE — screenshot path, Playwright trace path, literal assertion string, and implicated source file. Coverage gaps (`rule=coverage-gap-*` / `uncovered-*`) are the only exceptions.
|
|
16
|
+
4. **Dev, not prod.** Always audit against the local dev server. Detect HTML-instead-of-JSON crashes (500 responses that render the Next/Remix error page) and surface them.
|
|
17
|
+
5. **Report-then-ask → run → feedback → findings.** Four gates, in order. Do not merge them.
|
|
18
|
+
|
|
19
|
+
## Output layout
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
.e2e-audit/<YYYY-MM-DD-HHMMSS>/
|
|
23
|
+
├── stack.json # detect-stack.sh
|
|
24
|
+
├── routes.json # discover-routes.sh
|
|
25
|
+
├── api-surface.json # discover-api-surface.sh
|
|
26
|
+
├── existing-tests.json # inventory-existing-tests.sh
|
|
27
|
+
├── uncovered.json # detect-uncovered.sh
|
|
28
|
+
├── map.md # human-readable summary of the above
|
|
29
|
+
├── traces/ # Playwright trace.zip per test
|
|
30
|
+
├── screenshots/ # PNGs per assertion moment
|
|
31
|
+
├── logs/
|
|
32
|
+
│ ├── dev-server.log # piped stdout+stderr of dev server
|
|
33
|
+
│ └── playwright.log
|
|
34
|
+
├── post-run-feedback.json # emitted AFTER runs, BEFORE findings
|
|
35
|
+
├── post-run-feedback.md # human copy
|
|
36
|
+
└── findings.json # final — schema at findings.schema.json
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Pipeline
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
PREFLIGHT → detect-stack, inventory-existing-tests, compute drift-hash
|
|
43
|
+
DISCOVERY → discover-routes, discover-api-surface, detect-uncovered
|
|
44
|
+
REPORT-THEN-ASK → write map.md, present to user, WAIT for confirmation
|
|
45
|
+
RUN → start dev server, tail logs, drive Playwright
|
|
46
|
+
FEEDBACK → post-run-feedback.json from logs + trace + console
|
|
47
|
+
FINDINGS → findings.json with SHOT+TRACE+ASSERT+SOURCE quad
|
|
48
|
+
VERIFY → bash scripts/verify-audit.sh <session_dir>
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Step 1 — Preflight
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
SESSION_DIR=".e2e-audit/$(date +%Y-%m-%d-%H%M%S)"
|
|
57
|
+
mkdir -p "$SESSION_DIR/traces" "$SESSION_DIR/screenshots" "$SESSION_DIR/logs"
|
|
58
|
+
|
|
59
|
+
bash .claude/skills/e2e-audit/scripts/detect-stack.sh > "$SESSION_DIR/stack.json"
|
|
60
|
+
bash .claude/skills/e2e-audit/scripts/inventory-existing-tests.sh > "$SESSION_DIR/existing-tests.json"
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**Drift check.** If a previous session exists at `.e2e-audit/.last-hash`, compare `existing-tests.json.hash` against it. On mismatch, surface a `test-drift` meta finding (non-fatal) showing which files were added, removed, or resized. Write the new hash after the run completes.
|
|
64
|
+
|
|
65
|
+
**Stack fallback.** If `stack.test_runner == "none"`, emit a `meta` finding prompting the user to install Playwright (`bun add -D @playwright/test`) and stop the pipeline. Do not proceed blind.
|
|
66
|
+
|
|
67
|
+
## Step 2 — Source-first discovery
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
bash .claude/skills/e2e-audit/scripts/discover-routes.sh > "$SESSION_DIR/routes.json"
|
|
71
|
+
bash .claude/skills/e2e-audit/scripts/discover-api-surface.sh > "$SESSION_DIR/api-surface.json"
|
|
72
|
+
bash .claude/skills/e2e-audit/scripts/detect-uncovered.sh \
|
|
73
|
+
"$SESSION_DIR/routes.json" \
|
|
74
|
+
"$SESSION_DIR/api-surface.json" \
|
|
75
|
+
"$SESSION_DIR/existing-tests.json" \
|
|
76
|
+
"${BASE_REF:-origin/main}" > "$SESSION_DIR/uncovered.json"
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Then write `map.md` summarising:
|
|
80
|
+
|
|
81
|
+
- **Stack**: framework + router style + test runner + auth providers + ORMs.
|
|
82
|
+
- **Surface counts**: routes, HTTP handlers, tRPC procedures (by auth tier), server actions.
|
|
83
|
+
- **Branch diff**: files changed vs `BASE_REF`; highlight those without test references.
|
|
84
|
+
- **Uncovered**: bulleted list of every item in `uncovered_routes / uncovered_http / uncovered_trpc / uncovered_actions`.
|
|
85
|
+
- **Existing test inventory**: count + hash + drift status.
|
|
86
|
+
|
|
87
|
+
## Step 3 — Report-then-ask (HARD STOP)
|
|
88
|
+
|
|
89
|
+
Present `map.md` to the user with a short prompt:
|
|
90
|
+
|
|
91
|
+
> Mapping complete. Found N routes, M uncovered surfaces, K existing specs. Scope to run:
|
|
92
|
+
> - (a) uncovered + changed (default, recommended)
|
|
93
|
+
> - (b) full suite (all existing specs + uncovered surfaces)
|
|
94
|
+
> - (c) custom subset (user lists paths)
|
|
95
|
+
> Reply with a/b/c before I touch the browser.
|
|
96
|
+
|
|
97
|
+
Do NOT proceed to Step 4 without a reply. This is the mandatory report-then-ask gate.
|
|
98
|
+
|
|
99
|
+
## Step 4 — Run against dev
|
|
100
|
+
|
|
101
|
+
1. **Start dev server in background**, redirect stdout+stderr to `$SESSION_DIR/logs/dev-server.log`:
|
|
102
|
+
```bash
|
|
103
|
+
nohup sh -c "$(jq -r .dev_command "$SESSION_DIR/stack.json")" \
|
|
104
|
+
> "$SESSION_DIR/logs/dev-server.log" 2>&1 &
|
|
105
|
+
echo $! > "$SESSION_DIR/logs/dev.pid"
|
|
106
|
+
```
|
|
107
|
+
2. **Wait** for `$(jq -r .base_url stack.json)` to respond 200 within 90s. Fail loud if not.
|
|
108
|
+
3. **Auth setup**: if `stack.auth` is non-empty, use/create `storageState` per role. Start from any existing state in `existing-tests.storage_states`; only synthesize new states via explicit user-provided credentials (never read env files and print them). See `references/auth-setup-playbook.md`.
|
|
109
|
+
4. **Spec selection** per Step 3 answer. Prefer existing specs when coverage exists.
|
|
110
|
+
5. **Run Playwright** with tracing forced on:
|
|
111
|
+
```bash
|
|
112
|
+
npx playwright test \
|
|
113
|
+
--trace=on \
|
|
114
|
+
--output="$SESSION_DIR/traces" \
|
|
115
|
+
--reporter=list,json \
|
|
116
|
+
2>&1 | tee "$SESSION_DIR/logs/playwright.log"
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Capture for each test:
|
|
120
|
+
|
|
121
|
+
- Screenshot at the key assertion step (`await page.screenshot({path, fullPage: true})`).
|
|
122
|
+
- Trace zip (auto when `--trace=on`).
|
|
123
|
+
- All `page.on('console')` messages with level + URL + line.
|
|
124
|
+
- All responses via `page.on('response')`: filter 4xx/5xx on `/api/` `/v1/` `/trpc/` paths.
|
|
125
|
+
- HTML-instead-of-JSON: any response where `Content-Type: text/html` hits an API path → `server-crash` rule.
|
|
126
|
+
|
|
127
|
+
## Step 5 — Post-run feedback
|
|
128
|
+
|
|
129
|
+
BEFORE writing `findings.json`, consolidate into `post-run-feedback.json`:
|
|
130
|
+
|
|
131
|
+
```jsonc
|
|
132
|
+
{
|
|
133
|
+
"session": "<session_dir>",
|
|
134
|
+
"duration_s": 128,
|
|
135
|
+
"tests_total": 42,
|
|
136
|
+
"tests_failed": 3,
|
|
137
|
+
"problems": [
|
|
138
|
+
{ "kind": "api-5xx", "where": "POST /api/users", "count": 2, "sample_trace": "traces/users-create-1.zip" },
|
|
139
|
+
{ "kind": "console-error", "where": "dashboard", "count": 7, "sample": "Uncaught TypeError: Cannot read ..." },
|
|
140
|
+
{ "kind": "rbac-bypass", "where": "member sees /admin", "count": 1 },
|
|
141
|
+
{ "kind": "server-crash", "where": "POST /api/x returned text/html 500" },
|
|
142
|
+
{ "kind": "auth-flow-broken","where": "login redirect loop after valid credentials" },
|
|
143
|
+
{ "kind": "dev-server-log", "where": "unhandledRejection at server:1234" }
|
|
144
|
+
],
|
|
145
|
+
"uncovered_carried_forward": { "routes": 4, "http": 2, "trpc": 9, "actions": 1 }
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Also mirror to `post-run-feedback.md`. Present a short summary to the user; do not dump the full JSON.
|
|
150
|
+
|
|
151
|
+
## Step 6 — Write findings
|
|
152
|
+
|
|
153
|
+
For every problem in `post-run-feedback.problems` that is tied to a specific failure, emit one finding. Allocate IDs `E2E-0001`, `E2E-0002`, … sequentially.
|
|
154
|
+
|
|
155
|
+
- Evidence quad required (`screenshot_path`, `trace_path`, `assertion`, `source_file`).
|
|
156
|
+
- `source_file` must point at the route handler / procedure / action / middleware implicated — not the spec file.
|
|
157
|
+
- Add `http.method`, `http.path`, `http.status`, `http.response_snippet` for api-contract + server-crash findings.
|
|
158
|
+
|
|
159
|
+
Meta findings (no evidence quad required):
|
|
160
|
+
|
|
161
|
+
- `coverage-gap-routes` / `coverage-gap-http` / `coverage-gap-trpc` / `coverage-gap-actions` — one per non-empty `uncovered.*` array, with the array echoed into `detail`.
|
|
162
|
+
- `test-drift` — emitted by Step 1 when the test-corpus hash changed since last run.
|
|
163
|
+
- `stack-detect` — info-level snapshot of `stack.json` for traceability.
|
|
164
|
+
- `post-run-feedback` — aggregate, links to `post-run-feedback.json`.
|
|
165
|
+
|
|
166
|
+
Schema: `.claude/skills/e2e-audit/findings.schema.json`. Validate with `jq --slurpfile schema findings.schema.json` or skip strict validation and lean on `verify-audit.sh`.
|
|
167
|
+
|
|
168
|
+
## Step 7 — Verify + persist
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
bash .claude/skills/e2e-audit/scripts/verify-audit.sh "$SESSION_DIR"
|
|
172
|
+
jq -r '.hash' "$SESSION_DIR/existing-tests.json" > .e2e-audit/.last-hash
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Kill the dev server: `kill "$(cat "$SESSION_DIR/logs/dev.pid")"`.
|
|
176
|
+
|
|
177
|
+
## Final response to user
|
|
178
|
+
|
|
179
|
+
≤5 sentences. Report: session dir, # findings, # coverage gaps, # problems, and one-line guidance on whether to invoke a fix agent or hand-fix. Do NOT paste `map.md` or `findings.json` bodies.
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## Invocation triggers (already enforced by SessionStart hook)
|
|
184
|
+
|
|
185
|
+
Keywords that MUST trigger this skill: `e2e audit`, `roda o e2e`, `run the e2e`, `integration test audit`, `test coverage gaps`, `coverage gap`, `audit my tests`, `api contract check`, `rbac coverage`, `end-to-end tests`. Claude must read this file before improvising a plan.
|
|
186
|
+
|
|
187
|
+
## Boundaries (what this skill does NOT do)
|
|
188
|
+
|
|
189
|
+
- Does not write fixes. Fix work is out of scope; hand the finding list to a sd-fix-style agent or the user.
|
|
190
|
+
- Does not audit design / UX — that's `super-design`. If the user asked for a UX audit, hand off.
|
|
191
|
+
- Does not run against production. Only local dev. If `stack.base_url` points to prod, refuse.
|
|
192
|
+
- Does not invent credentials. Never read `.env*` files; only use credentials the user provides inline for the session.
|
|
193
|
+
- Does not delete existing tests. Drift is reported, never "resolved" by removing specs.
|
|
194
|
+
|
|
195
|
+
## References
|
|
196
|
+
|
|
197
|
+
- `references/auth-setup-playbook.md` — storageState + role patterns per auth provider.
|
|
198
|
+
- `references/api-contract-playbook.md` — HTTP-4xx / HTTP-5xx / HTML-instead-of-JSON detection.
|
|
199
|
+
- `references/coverage-gap-playbook.md` — how to translate `uncovered.*` into meta findings + suggested specs.
|
|
200
|
+
- `references/post-run-feedback-playbook.md` — how to consolidate Playwright run signals into feedback.
|
|
201
|
+
|
|
202
|
+
## Templates
|
|
203
|
+
|
|
204
|
+
- `templates/base-fixture.ts.tpl` — `test.extend` with `apiErrors` + `authenticatedPage` fixtures.
|
|
205
|
+
- `templates/auth-setup.ts.tpl` — globalSetup shape that writes storageState per role.
|
|
206
|
+
- `templates/findings-report.md.tpl` — human-readable summary rendered from findings.json.
|
|
207
|
+
- `templates/post-run-feedback.md.tpl` — the mirror of post-run-feedback.json.
|
|
208
|
+
|
|
209
|
+
## Attention points
|
|
210
|
+
|
|
211
|
+
- **tRPC v10 vs v11.** Procedure nesting works differently; `createCaller()` exists in both but the router introspection APIs diverge. Treat `discover-api-surface.sh` output as names-only.
|
|
212
|
+
- **Route groups.** Next `(marketing)` style segments are stripped in URL computation; don't emit findings that name the parenthesis.
|
|
213
|
+
- **Parallel & intercepting routes.** `@modal` slots and `(.)photo` shortcuts are surfaces that Playwright can miss; the route discovery already flags them — propose specs that hit them directly.
|
|
214
|
+
- **Middleware.** If `middleware.has_auth_guard == true` and a public matcher exists, any public URL the audit hit should not have triggered auth redirects. Mismatches = findings.
|
|
215
|
+
- **Windows paths.** `.claude/skills/e2e-audit/scripts/*.sh` must run via Git Bash or WSL. If `bash` isn't available, abort with a meta finding; never fall back to half-runs.
|
|
216
|
+
- **Dev server crashes mid-run.** If `dev.pid` exits unexpectedly during Playwright execution, mark all remaining tests as inconclusive and emit `server-crash` findings with the last 40 lines of `dev-server.log` in `detail`.
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://start-vibing.dev/schema/e2e-audit-findings-0.2.0.json",
|
|
4
|
+
"title": "e2e-audit findings v0.2.0",
|
|
5
|
+
"description": "Output contract for e2e-audit. Every finding carries an SHOT+TRACE+ASSERT+SOURCE quad; aggregate (meta) findings are exempt.",
|
|
6
|
+
"type": "array",
|
|
7
|
+
"items": {
|
|
8
|
+
"type": "object",
|
|
9
|
+
"required": ["id", "rule", "severity", "summary", "files_affected"],
|
|
10
|
+
"additionalProperties": true,
|
|
11
|
+
"properties": {
|
|
12
|
+
"id": {
|
|
13
|
+
"type": "string",
|
|
14
|
+
"pattern": "^E2E-\\d{4}$",
|
|
15
|
+
"description": "Stable finding id. Format E2E-NNNN. Allocate sequentially per session."
|
|
16
|
+
},
|
|
17
|
+
"rule": {
|
|
18
|
+
"type": "string",
|
|
19
|
+
"description": "Machine-matchable rule slug (kebab-case). Examples: auth-flow-broken, api-500, rbac-bypass, console-error, uncovered-route, coverage-gap-trpc, test-drift, post-run-feedback."
|
|
20
|
+
},
|
|
21
|
+
"category": {
|
|
22
|
+
"type": "string",
|
|
23
|
+
"enum": [
|
|
24
|
+
"auth",
|
|
25
|
+
"api-contract",
|
|
26
|
+
"rbac",
|
|
27
|
+
"console",
|
|
28
|
+
"server-crash",
|
|
29
|
+
"coverage-gap",
|
|
30
|
+
"test-drift",
|
|
31
|
+
"flake",
|
|
32
|
+
"ui-regression",
|
|
33
|
+
"a11y",
|
|
34
|
+
"meta"
|
|
35
|
+
]
|
|
36
|
+
},
|
|
37
|
+
"severity": {
|
|
38
|
+
"type": "string",
|
|
39
|
+
"enum": ["critical", "high", "medium", "low", "info"]
|
|
40
|
+
},
|
|
41
|
+
"summary": { "type": "string", "minLength": 3 },
|
|
42
|
+
"detail": { "type": "string" },
|
|
43
|
+
"files_affected": {
|
|
44
|
+
"type": "array",
|
|
45
|
+
"items": { "type": "string" },
|
|
46
|
+
"description": "Source files this finding implicates. sd-fix-style guards MUST prevent edits outside this list unless risk is reclassified."
|
|
47
|
+
},
|
|
48
|
+
"viewport": {
|
|
49
|
+
"type": "string",
|
|
50
|
+
"enum": ["mobile", "tablet", "desktop"],
|
|
51
|
+
"description": "Optional — only set when a viewport-specific flow reproduced the issue."
|
|
52
|
+
},
|
|
53
|
+
"test_file": { "type": "string", "description": "Path to the spec that reproduced this (if any)." },
|
|
54
|
+
"test_title": { "type": "string", "description": "Title of the failing/passing test." },
|
|
55
|
+
|
|
56
|
+
"screenshot_path": { "type": "string", "description": "SHOT — non-empty PNG captured at the assertion moment." },
|
|
57
|
+
"trace_path": { "type": "string", "description": "TRACE — Playwright trace.zip or equivalent recording." },
|
|
58
|
+
"assertion": { "type": "string", "description": "ASSERT — the literal assertion that fired, e.g. `expect(response.status()).toBe(200)` observed 500." },
|
|
59
|
+
"source_file": { "type": "string", "description": "SOURCE — the source file the assertion implicates (router.ts, page.tsx, action.ts, ...)." },
|
|
60
|
+
"source_quote": { "type": "string", "description": "Optional verbatim quote from source_file explaining the implicated code." },
|
|
61
|
+
|
|
62
|
+
"http": {
|
|
63
|
+
"type": "object",
|
|
64
|
+
"description": "Optional — populated for api-contract / server-crash rules.",
|
|
65
|
+
"properties": {
|
|
66
|
+
"method": { "type": "string" },
|
|
67
|
+
"path": { "type": "string" },
|
|
68
|
+
"status": { "type": "integer" },
|
|
69
|
+
"response_snippet": { "type": "string", "maxLength": 512 },
|
|
70
|
+
"content_type": { "type": "string" }
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
"console_messages": {
|
|
74
|
+
"type": "array",
|
|
75
|
+
"items": {
|
|
76
|
+
"type": "object",
|
|
77
|
+
"required": ["level", "text"],
|
|
78
|
+
"properties": {
|
|
79
|
+
"level": { "type": "string", "enum": ["log", "warning", "error", "debug", "info"] },
|
|
80
|
+
"text": { "type": "string" },
|
|
81
|
+
"url": { "type": "string" },
|
|
82
|
+
"line": { "type": "integer" }
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
"suggested_fix": {
|
|
88
|
+
"type": "object",
|
|
89
|
+
"description": "Advisory only. sd-fix-style agents may consume this.",
|
|
90
|
+
"properties": {
|
|
91
|
+
"kind": { "type": "string", "enum": ["add-test", "fix-route", "fix-zod", "fix-rbac", "fix-auth", "add-fixture", "advisory"] },
|
|
92
|
+
"notes": { "type": "string" },
|
|
93
|
+
"files": { "type": "array", "items": { "type": "string" } }
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# api-contract-playbook (e2e-audit 0.2.0)
|
|
2
|
+
|
|
3
|
+
> How to turn Playwright's network observations into contract findings.
|
|
4
|
+
|
|
5
|
+
## Observations to capture per test
|
|
6
|
+
|
|
7
|
+
Wire these fixtures once (see `templates/base-fixture.ts.tpl`):
|
|
8
|
+
|
|
9
|
+
- `page.on('response')` — filter by `url` prefix matching `/api/`, `/v1/`, `/trpc/`.
|
|
10
|
+
- `page.on('console')` — levels `error` and `warning`.
|
|
11
|
+
- `page.on('pageerror')` — unhandled runtime errors in the SPA.
|
|
12
|
+
- `request.response()` — for explicit `page.request` calls in specs.
|
|
13
|
+
|
|
14
|
+
## Detection rules
|
|
15
|
+
|
|
16
|
+
### HTTP 4xx on a flow that should succeed
|
|
17
|
+
|
|
18
|
+
- **Rule:** `api-4xx`
|
|
19
|
+
- **Severity:** `high` (user-blocking) or `medium` (inconsistent UX)
|
|
20
|
+
- **Signal:** response status in [400, 499] on a request the test expected to succeed.
|
|
21
|
+
- **Evidence:** trace zip + screenshot at the assertion moment + response snippet (first 400 chars) + source_file = the route handler or tRPC procedure file.
|
|
22
|
+
|
|
23
|
+
### HTTP 5xx anywhere
|
|
24
|
+
|
|
25
|
+
- **Rule:** `api-5xx`
|
|
26
|
+
- **Severity:** `critical`
|
|
27
|
+
- **Always fails the test.** Trace zip is mandatory; Playwright's trace viewer will show the request/response payloads.
|
|
28
|
+
|
|
29
|
+
### HTML-instead-of-JSON (server crash signal)
|
|
30
|
+
|
|
31
|
+
Next.js and most frameworks render an HTML error page when the server throws. Detection:
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
if (
|
|
35
|
+
res.status() >= 500 &&
|
|
36
|
+
(res.headers()['content-type'] || '').includes('text/html') &&
|
|
37
|
+
/\/api\/|\/trpc\//.test(res.url())
|
|
38
|
+
) emit({ rule: 'server-crash', ... });
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Surface the last ~40 lines of `dev-server.log` in `detail` so the user can see the stack trace without opening the trace.
|
|
42
|
+
|
|
43
|
+
### Zod validation gap
|
|
44
|
+
|
|
45
|
+
- **Rule:** `zod-validation-missing`
|
|
46
|
+
- **Severity:** `medium`
|
|
47
|
+
- **Signal:** `api-surface.http_routes[].zod_schema_found == false` AND the route accepts `POST`/`PUT`/`PATCH`.
|
|
48
|
+
- **Evidence:** SHOT is waived (meta-ish), but `source_file` is required.
|
|
49
|
+
|
|
50
|
+
### RBAC bypass
|
|
51
|
+
|
|
52
|
+
- **Rule:** `rbac-bypass`
|
|
53
|
+
- **Severity:** `critical`
|
|
54
|
+
- **Signal:** an endpoint tagged `auth: "protected"` in `api-surface.json` returned 200 for a role that should be rejected.
|
|
55
|
+
- **Evidence:** trace showing the call under the "wrong" storageState + source_file (the middleware or procedure).
|
|
56
|
+
|
|
57
|
+
## What NOT to flag as a contract problem
|
|
58
|
+
|
|
59
|
+
- 404 on a page navigation the user typed manually — that's a route-missing finding, not a contract one.
|
|
60
|
+
- 401 on an endpoint protected BEFORE login — that's expected behavior; only flag after valid login.
|
|
61
|
+
- Third-party hosts (analytics, stripe, intercom) — ignore by prefix match on `stack.base_url`.
|
|
62
|
+
- `/api/auth/*` during login form submit — transient 4xx (invalid-credentials) is expected; only flag if the happy-path login produced it.
|
|
63
|
+
|
|
64
|
+
## Sampling
|
|
65
|
+
|
|
66
|
+
To avoid flooding findings, de-dupe by `(method, path, status)` and keep the first occurrence's trace. Add `http.count` for repeats.
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# auth-setup-playbook (e2e-audit 0.2.0)
|
|
2
|
+
|
|
3
|
+
> How to obtain and reuse authenticated browser state, per auth provider. Goal: one `storageState.json` per role × per audit session, produced exactly once.
|
|
4
|
+
|
|
5
|
+
## Principles
|
|
6
|
+
|
|
7
|
+
1. **Never read `.env*`** inside this skill. If credentials are needed, ask the user inline. State files live under `$SESSION_DIR/auth/` and are gitignored (caller's responsibility).
|
|
8
|
+
2. **Reuse before synthesize.** If `existing-tests.storage_states` contains files, use them. Check freshness: any file older than 7 days is considered stale; regenerate.
|
|
9
|
+
3. **One role per file.** `owner.json`, `admin.json`, `member.json` — do not collapse roles.
|
|
10
|
+
4. **State files contain secrets.** They must not enter `findings.json`, `map.md`, or any output that ships to the user beyond the session dir.
|
|
11
|
+
|
|
12
|
+
## Playwright baseline
|
|
13
|
+
|
|
14
|
+
```ts
|
|
15
|
+
// playwright.config.ts
|
|
16
|
+
import { defineConfig, devices } from '@playwright/test';
|
|
17
|
+
|
|
18
|
+
export default defineConfig({
|
|
19
|
+
projects: [
|
|
20
|
+
{
|
|
21
|
+
name: 'setup',
|
|
22
|
+
testMatch: /.*\.setup\.ts/,
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
name: 'authed',
|
|
26
|
+
dependencies: ['setup'],
|
|
27
|
+
use: {
|
|
28
|
+
...devices['Desktop Chrome'],
|
|
29
|
+
storageState: '.e2e-audit/current/auth/owner.json',
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
],
|
|
33
|
+
});
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Per-provider recipes
|
|
37
|
+
|
|
38
|
+
### next-auth / Auth.js (credentials provider)
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
// tests/e2e/auth.setup.ts
|
|
42
|
+
import { test as setup } from '@playwright/test';
|
|
43
|
+
setup('authenticate owner', async ({ page }) => {
|
|
44
|
+
await page.goto('/signin');
|
|
45
|
+
await page.getByLabel('Email').fill(process.env.E2E_OWNER_EMAIL!);
|
|
46
|
+
await page.getByLabel('Password').fill(process.env.E2E_OWNER_PASSWORD!);
|
|
47
|
+
await page.getByRole('button', { name: /sign in/i }).click();
|
|
48
|
+
await page.waitForURL(/\/(dashboard|home)/);
|
|
49
|
+
await page.context().storageState({ path: '.e2e-audit/current/auth/owner.json' });
|
|
50
|
+
});
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Clerk
|
|
54
|
+
|
|
55
|
+
Use Clerk's `@clerk/testing` helper. It writes a session via `setupClerkTestingToken()` and then calls `storageState`.
|
|
56
|
+
|
|
57
|
+
### better-auth / Lucia
|
|
58
|
+
|
|
59
|
+
Same pattern as next-auth: drive the login form, then persist storage state. Both libraries store a session cookie which storageState captures.
|
|
60
|
+
|
|
61
|
+
### Supabase (JWT)
|
|
62
|
+
|
|
63
|
+
After login, `localStorage` holds the session. `storageState` serializes localStorage so no extra work is needed. If the dev project uses PKCE, ensure the setup runs in a chromium context.
|
|
64
|
+
|
|
65
|
+
### Custom (cookie-session / JWT header)
|
|
66
|
+
|
|
67
|
+
If the app does not have a login form (API-only auth), seed storage via `page.context().addCookies([...])` using a short-lived token the user pastes in. Never store long-lived tokens in `auth/*.json`.
|
|
68
|
+
|
|
69
|
+
## RBAC coverage
|
|
70
|
+
|
|
71
|
+
For every role declared in `stack.auth`:
|
|
72
|
+
|
|
73
|
+
1. Drive one happy-path login per role.
|
|
74
|
+
2. For each `trpc_procedures[]` with `auth == "protected"`, attempt the call with a role that should be forbidden. Expect 401 or 403. If 200, emit an `rbac-bypass` finding.
|
|
75
|
+
|
|
76
|
+
## Global teardown
|
|
77
|
+
|
|
78
|
+
Do NOT teardown or delete storageState files at the end of a run — the skill keeps them inside `$SESSION_DIR`, which is the audit's own sandbox.
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# coverage-gap-playbook (e2e-audit 0.2.0)
|
|
2
|
+
|
|
3
|
+
> How to translate `uncovered.json` into actionable meta findings + spec suggestions.
|
|
4
|
+
|
|
5
|
+
## Input
|
|
6
|
+
|
|
7
|
+
`detect-uncovered.sh` produces four uncovered arrays:
|
|
8
|
+
|
|
9
|
+
- `uncovered_routes` — user-facing pages the branch changed with no test referencing their URL.
|
|
10
|
+
- `uncovered_http` — REST handlers the branch changed with no test referencing their path.
|
|
11
|
+
- `uncovered_trpc` — tRPC procedures the branch changed with no test referencing their name.
|
|
12
|
+
- `uncovered_actions` — server actions the branch changed with no test referencing their name.
|
|
13
|
+
|
|
14
|
+
## One finding per category
|
|
15
|
+
|
|
16
|
+
Emit at most four meta findings:
|
|
17
|
+
|
|
18
|
+
```json
|
|
19
|
+
{
|
|
20
|
+
"id": "E2E-00XX",
|
|
21
|
+
"rule": "coverage-gap-routes",
|
|
22
|
+
"category": "coverage-gap",
|
|
23
|
+
"severity": "medium",
|
|
24
|
+
"summary": "N routes changed on branch without E2E coverage",
|
|
25
|
+
"detail": "<bulleted list of paths + files>",
|
|
26
|
+
"files_affected": ["<every file from the uncovered entries>"],
|
|
27
|
+
"suggested_fix": { "kind": "add-test", "files": ["tests/e2e/<slug>.spec.ts"] }
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Severities:
|
|
32
|
+
|
|
33
|
+
- `coverage-gap-trpc` — `medium` (contract risk)
|
|
34
|
+
- `coverage-gap-http` — `medium` (contract risk)
|
|
35
|
+
- `coverage-gap-routes` — `low` if diff is cosmetic, `medium` otherwise
|
|
36
|
+
- `coverage-gap-actions` — `medium`
|
|
37
|
+
|
|
38
|
+
## Spec suggestions per surface type
|
|
39
|
+
|
|
40
|
+
**Page (route)**
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
test('GET /users/[id] renders user dashboard', async ({ authenticatedPage }) => {
|
|
44
|
+
const res = await authenticatedPage.goto('/users/1');
|
|
45
|
+
await expect(res!.status()).toBeLessThan(400);
|
|
46
|
+
await expect(authenticatedPage.getByRole('heading')).toBeVisible();
|
|
47
|
+
});
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
**HTTP route handler**
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
test('POST /api/users creates user (200)', async ({ request }) => {
|
|
54
|
+
const res = await request.post('/api/users', { data: { email: 'u@test', name: 't' } });
|
|
55
|
+
expect(res.status()).toBe(200);
|
|
56
|
+
expect((await res.json()).id).toBeTruthy();
|
|
57
|
+
});
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
**tRPC procedure**
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
test('users.create rejects invalid input (400)', async ({ request }) => {
|
|
64
|
+
const res = await request.post('/api/trpc/users.create?batch=1', {
|
|
65
|
+
data: { 0: { json: { email: 'not-an-email' } } },
|
|
66
|
+
});
|
|
67
|
+
expect(res.status()).toBeGreaterThanOrEqual(400);
|
|
68
|
+
});
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
**Server action**
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
test('createUser action returns redirect', async ({ page }) => {
|
|
75
|
+
await page.goto('/users/new');
|
|
76
|
+
await page.getByLabel('Email').fill('u@test');
|
|
77
|
+
await page.getByRole('button', { name: /create/i }).click();
|
|
78
|
+
await page.waitForURL(/\/users\/\d+/);
|
|
79
|
+
});
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## What to NOT flag
|
|
83
|
+
|
|
84
|
+
- Unchanged surfaces. If a file is not in `uncovered.diff_files`, it did not change on this branch — no finding, even if tests are missing. Coverage auditing of the whole app is out-of-scope for branch-diff mode.
|
|
85
|
+
- `loading.tsx`, `error.tsx`, `layout.tsx` in Next.js — these are treated separately. A coverage gap finding should not fire for them; they are covered transitively by any page that renders through them.
|
|
86
|
+
|
|
87
|
+
## suggested_fix files
|
|
88
|
+
|
|
89
|
+
For each uncovered entry, suggest a plausible spec filename:
|
|
90
|
+
|
|
91
|
+
- `/users/[id]` → `tests/e2e/users-id.spec.ts`
|
|
92
|
+
- `POST /api/users` → `tests/e2e/api-users.spec.ts`
|
|
93
|
+
- `users.create` → `tests/e2e/trpc-users.spec.ts`
|
|
94
|
+
|
|
95
|
+
Do not CREATE the files. This skill stops at reporting.
|