Installs into .claude/skills of the current project.
Are you the author of Test And Verify Walnut?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/evanzhang008-test-and-verify-walnut)
---
name: test-and-verify-walnut
description: "4-agent pipeline: design + implement code tests + Playwright UI tests + quality gate. Reads walnut-console-ops first."
---
# /test-and-verify-walnut
Every test must answer: **"If I reverted my code changes, would this test fail?"** NO → delete it.
**FIRST**: Read `.claude/skills/walnut-console-ops/SKILL.md` for UI layout and interaction patterns.
```
Main Agent (context) → Agent 1 (design) → Agent 2 (code) ∥ Agent 3 (Playwright) → Agent 4 (quality gate)
```
---
## Phase 0: Main Agent — Context
1. `git diff --stat HEAD~1` + `git log --oneline -5`
2. Read plan files: `.plan`, `~/.claude/plans/`, `.tasks/*/TASK.md`
3. Classify change type:
| Change Type | Primary Tests |
|---|---|
| Frontend-only | Playwright UI |
| Backend-only | API + unit + server logs |
| Full-stack | Console E2E + Playwright + unit |
| Bug fix | Reproduce → fix → re-verify |
4. Bundle context → pass to all agents.
---
## Phase 1: Agent 1 — Test Designer (read-only)
Design tests in 2 categories. Do NOT implement.
**Category A — UI (Playwright)**: 2-4 scenarios. For each: pre-conditions, steps (real clicks, NO `page.goto()` SPA nav), assertions (must include downstream verification — see below), screenshot points.
**Category B — Code (vitest)**: 2-4 tests, as E2E as possible. For each: name, tier, setup, exercise, assert (HTTP response + WS event + persisted data).
**Self-check each test**: "Would this pass with code reverted?" YES = delete.
**E2E means full consequence**: Every test must verify the **downstream effect** of the action, not just the immediate UI change. If the action triggers a backend round-trip, the test must wait for and verify the backend result.
Anti-patterns (test is SHALLOW or WORTHLESS if it only checks these):
- "page loads", "component renders", "no console errors"
- "API returns 200" without checking response body
- "clicked button and UI closed" without verifying the backend outcome
- "status badge changed" without verifying the underlying operation succeeded
- Testing mock config instead of production code
---
## Phase 2: Agent 2 — Code Test Writer (∥ Agent 3)
Implement Category B. Rules:
- Real server: `startServer({ port: 0, dev: true })`. Only mock Claude CLI (`tests/providers/mock-claude.mjs`).
- Assert on HTTP responses, WS events, file contents — not internal state.
- Place: `tests/e2e/` (pipeline), `tests/web/routes/` (API), `tests/core/` (logic), `tests/e2e/browser/` (Playwright code).
- Run and iterate until green.
---
## Phase 3: Agent 3 — Playwright Executor (∥ Agent 2)
**First**: Read `.claude/skills/walnut-console-ops/SKILL.md`.
Execute Category A:
- `mkdir -p /tmp/test-and-verify/`, build SPA: `cd web && npx vite build`
- Real UI clicks only. Screenshot every step to `/tmp/test-and-verify/<scenario>-step<N>.png`
- DOM snapshot before each action. Verify screenshots with `Read` tool.
- Act → Wait (5-20s) → Screenshot → Snapshot → verify.
**CRITICAL — Verify full consequence, not just UI reaction**:
Every UI action that triggers backend work must be verified **through to the downstream effect**. The test is NOT done when the UI element changes — it's done when the **outcome is confirmed**.
The question: **"Did the thing I triggered actually work?"** — not **"Did the UI react to my click?"**
**BAD (shallow)** — stops at UI reaction:
```
# Testing model switch:
Click "Opus 1M" → picker closes → screenshot → ✅ DONE
# WRONG: only proved the picker UI works. Did the session actually restart
# with the new model? Did the next message succeed? No idea.
# Testing task creation:
Click "Add" → task appears in list → screenshot → ✅ DONE
# WRONG: only proved the React component rendered. Did the task persist
# to disk? Can you reload and still see it? No idea.
# Testing session start:
Click "Start session" → status shows "Running" → screenshot → ✅ DONE
# WRONG: only proved the status badge updated. Did the session actually
# produce output? Did the CLI process spawn? No idea.
```
**GOOD (full consequence)** — verifies the downstream effect:
```
# Testing model switch:
Click "Opus 1M" → picker closes → send a message in session input →
wait for response → verify response text appears (no "API Error",
no "invalid model") → screenshot response → ✅ DONE
# Testing task creation:
Click "Add" → task appears in list → click the task → verify detail
panel shows correct title/category/project → ✅ DONE
# Testing session start:
Click "Start session" → status shows "Running" → wait for first
assistant message to appear in chat → verify message content →
screenshot → ✅ DONE
```
The pattern: **Action → UI reacts → wait for backend round-trip → verify outcome → screenshot proof**. If you stop at "UI reacts", the test is SHALLOW.
---
## Phase 4: Agent 4 — Quality Verifier (after 2+3)
For each test, ask TWO questions:
1. "Would this pass if feature was reverted?" YES = **WORTHLESS**, delete it.
2. "Does this verify the **downstream effect**, or just the immediate UI change?" UI-only = **SHALLOW**, must extend.
- **GENUINE**: Triggers action AND verifies the full backend/system consequence
- **SHALLOW**: Verifies UI reacted (button clicked, picker closed, badge changed) but NOT that the triggered operation succeeded. Must be extended to verify downstream.
- **WORTHLESS**: Would pass without code changes
A Playwright test that clicks a button and screenshots is **SHALLOW** unless it also waits for and verifies what that button *caused* (response arrived, status updated, data persisted, no errors).
Overall: PASS / NEEDS WORK / FAIL
---
## Phase 5: Report
```markdown
## Test & Verify Report: <Feature>
### Build — Server: PASS/FAIL, Frontend: PASS/FAIL
### Code Tests — <N> tests, <N> genuine
### UI Tests — <N> scenarios, <N> genuine
### Overall — PASS / NEEDS WORK / FAIL
```
## Phase 6: Learning Loop
New patterns → append to Learned Patterns below. Console-related → also update `walnut-console-ops/SKILL.md`.
---
## Test Reference
```bash
npm test # Unit + Integration (~5s)
npm run test:e2e # E2E real server (~15s)
npm run test:all # Everything
npx vitest run <file> # Single test
cd web && npx vite build # Build SPA before Playwright
npx playwright test # Browser tests
```
**Pyramid**: Live → Playwright → **E2E** (most important) → Integration → Unit
**Mock only**: Claude CLI. Everything else real.
**Production safety**: `OPEN_WALNUT_HOME=/tmp/walnut-test-*/`. Never `~/.open-walnut/`. Port 3456 = production, never touch. Live checks with a real CLI run on `npm run dev:ephemeral`.
**Open source**: No internal tool names, employer references, or personal usernames.
### E2E Template
```typescript
vi.mock('../../src/constants.js', () => createMockConstants('walnut-e2e-FEATURE'));
import { startServer, stopServer } from '../../src/web/server.js';
let server, port;
beforeAll(async () => {
server = await startServer({ port: 0, dev: true });
port = (server.address() as any).port;
});
afterAll(() => stopServer());
it('REST → Core → Bus → WS', async () => {
const ws = await connectWs();
const event = waitForWsEvent(ws, 'task:created');
const res = await fetch(`http://localhost:${port}/api/tasks`, {
method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ title: 'test' }),
});
expect(res.status).toBe(201);
expect((await event).data.task.title).toBe('test');
ws.close();
});
```
### Session E2E: Mock CLI wrapper
```typescript
const MOCK_CLI = path.resolve(import.meta.dirname, '../providers/mock-claude.mjs');
sessionRunner.setCliCommand(MOCK_WRAPPER); // before startServer
```
### Architecture (for test design)
- **Event Bus**: backbone — test WS delivery for entity CRUD
- **Phases**: TODO → IN_PROGRESS → AGENT_COMPLETE → AWAIT_HUMAN_ACTION → COMPLETE
- **Sessions**: 2-slot (plan + exec), PID monitoring, FIFO stall
- **Concurrency**: 2-layer locks. Test concurrent writes.
- **WS**: `{ type: 'event', name, data }` / RPC: `{ type: 'req', id, method, payload }`
### Gotchas
- WS events async — use `waitForWsEvent()` with timeout
- Build SPA before Playwright or you test stale HTML
- Click sidebar links, don't `page.goto('/route')` in SPA
- Concurrent tests need separate WALNUT_HOME dirs
---
## Learned Patterns
- **Real-session browser specs are now possible**: `tests/e2e/browser/test-server.ts` wires a
MockDaemon + mock CLI (`sessionRunner.setCliCommand` + `setTestDaemonUrl`) into the Playwright
server, so specs can create LIVE sessions via the `session:start` WS RPC and exercise the real
event pipeline (no `__capturedWs` injection needed). Disable with `PW_NO_MOCK_DAEMON=1` when
bisecting whether a failure comes from this wiring. See
`tests/e2e/browser/single-timeline-real-pipeline.spec.ts` for the pattern (start session →
poll `/api/sessions/task/:id` for the `mock-session-*` id → open `/sessions?id=`).
- **`chunk-delay:<ms>` mock-CLI prefix**: spaces out `content_block_delta` emissions in
`stream-partial-thinking-then-text` so browser tests can observe partial text mid-turn
(the default burst is synchronous and races any DOM poll). Composable: `chunk-delay:250
stream-partial-thinking-then-text`.
- **MockDaemon JSONL is NOT where history REST reads**: the MockDaemon captures CLI output in its
own tmpDir; `GET /:id/history` falls back to `SESSION_STREAMS_DIR/<sid>.jsonl`. Vitest tests
that need real-parser history must copy `daemon.streamFilePath(sid)` →
`SESSION_STREAMS_DIR` first (see `publishStreamsFile` in
`tests/web/stream/single-timeline-real-pipeline.test.ts`). Symptom otherwise: history 200 with
0 messages forever.
- **Full-suite failure triage**: `npx playwright test` failures may come from PARALLEL agents'
uncommitted UI changes, not yours. Bisect by re-running the failing spec against the HEAD
version of the shared harness file you touched (`git checkout <file>` → run → restore) before
assuming a regression.