docs: add mandatory Playwright Phase 1 QA step before laputa-task-done
Claude Code must now run Playwright smoke tests against the dev server before firing the done signal. This catches UI/UX bugs (missing Cmd+K commands, broken shortcuts, layout issues) before Brian's native QA. - Phase 1 (Playwright, headless) = Claude Code's responsibility - Phase 2 (native Tauri, keyboard-only) = Brian's responsibility Phase 1 covers: command palette entries, keyboard shortcuts, Tab navigation, UI state changes. Phase 2 covers: file system, git, native Tauri behaviors that can't run in the browser.
This commit is contained in:
141
CLAUDE.md
141
CLAUDE.md
@@ -15,11 +15,40 @@ pre_commit_code_health_safeguard # CodeScene ≥9.2 — if it fails, fix stru
|
||||
|
||||
**CI is a safety net, not a discovery tool.** If CI catches something you didn't catch locally, that's a process failure. All these tools are available locally — use them while you code, not just at the end.
|
||||
|
||||
## ⛔ BEFORE FIRING laputa-task-done — QA on real vault
|
||||
## ⛔ BEFORE FIRING laputa-task-done — Two-phase QA (mandatory)
|
||||
|
||||
> **⚠️ TAURI APP ONLY — never test in browser (`pnpm dev` / localhost)**
|
||||
> The browser dev server has a fake HTTP server (`/api/*` routes, mock handlers) that doesn't exist in the real Tauri app.
|
||||
> If it works in the browser but crashes in Tauri, it's broken. Always test in `pnpm tauri dev`.
|
||||
### Phase 1: Playwright browser QA (headless, you do this yourself)
|
||||
|
||||
Test every acceptance criterion using Playwright against the dev server **before** marking done. This catches 80% of bugs before Brian sees them.
|
||||
|
||||
```bash
|
||||
# 1. Start the dev server (use your worktree port)
|
||||
pnpm dev --port <N> &
|
||||
DEV_PID=$!
|
||||
sleep 3 # wait for vite to be ready
|
||||
|
||||
# 2. Run Playwright smoke test for this task
|
||||
npx playwright test --headed=false tests/smoke/<slug>.spec.ts
|
||||
|
||||
# 3. Or use the MCP Playwright tool directly in your session to drive the browser
|
||||
# e.g. navigate to localhost:<N>, open Cmd+K, verify command appears, etc.
|
||||
|
||||
kill $DEV_PID
|
||||
```
|
||||
|
||||
**What to test in Playwright:**
|
||||
- Every command palette entry from the spec → open `Cmd+K`, type the command name, verify it appears and executes
|
||||
- Every keyboard shortcut → send keydown events, verify UI state changes
|
||||
- Every UI element described in the spec → verify it renders, is focusable, responds to Tab
|
||||
- Edge cases: empty state, long text, rapid keypresses
|
||||
|
||||
**Playwright is non-negotiable even if tests pass.** Unit tests verify code; Playwright verifies the user experience in the real browser. Both are required.
|
||||
|
||||
> **⚠️ Browser dev server limits**: the dev server uses mock Tauri handlers (`src/mock-tauri.ts`) — file system operations, git commands, and native dialogs are mocked. Test those via `pnpm tauri dev` in Phase 2 if the task touches them.
|
||||
|
||||
### Phase 2: Native Tauri QA (Brian does this after you push)
|
||||
|
||||
Brian installs the release build and runs keyboard-only QA on the native app. You don't do Phase 2 — but Phase 1 must pass before you fire the done signal, or Brian's QA will fail and the task goes back to To Rework.
|
||||
|
||||
1. Acquire lockfile: `echo $$ > /tmp/laputa-qa.lock && trap "rm -f /tmp/laputa-qa.lock" EXIT`
|
||||
2. Kill other instances: `pkill -x laputa 2>/dev/null || true; sleep 1`
|
||||
@@ -29,6 +58,19 @@ pre_commit_code_health_safeguard # CodeScene ≥9.2 — if it fails, fix stru
|
||||
6. If task touches file save: verify `git -C ~/Laputa diff` shows changes
|
||||
7. If QA fails → fix and re-run. Do NOT fire the signal until it passes.
|
||||
|
||||
**⚠️ QA ≠ tests. QA means using the app as a user.**
|
||||
- "Tests pass" is NOT QA. Tests verify code, QA verifies the user experience.
|
||||
- The QA comment must describe what you did as a user: "Opened app → Cmd+K → typed 'Trash' → pressed Enter → note disappeared from list → restarted app → note still not visible"
|
||||
- Every QA comment must include: the exact keyboard/command palette steps used, what was visible before and after, and any edge case tested.
|
||||
- If you cannot test a feature using keyboard only (osascript shortcuts + command palette), the feature is not keyboard-first → QA fails.
|
||||
|
||||
**⚠️ Test in a clean environment when the feature depends on state.**
|
||||
If a feature involves indexing, fresh installs, first-time setup, or anything that only runs once:
|
||||
- **Do not test in the existing dev vault** — it already has the state you're trying to test.
|
||||
- **Create a new empty vault** for the test: Cmd+K → "New Vault" (or equivalent), pick a temp folder like `/tmp/test-vault-<slug>`, then test the full first-time flow from scratch.
|
||||
- This applies to: search indexing, vault init, getting-started setup, any "on first open" logic.
|
||||
- If you can't reproduce the fresh-install scenario locally, the feature is untestable → do not fire done.
|
||||
|
||||
Fire done signal only after QA passes:
|
||||
```bash
|
||||
rm -f /tmp/laputa-qa.lock
|
||||
@@ -74,13 +116,47 @@ Tauri v2 + React + TypeScript desktop app. Reads a vault of markdown files with
|
||||
- **Never develop on `main`** — always on `task/<slug>` branch
|
||||
- **Commit every 20–30 min** — atomic commits, one concern per commit (`feat:`, `fix:`, `refactor:`, `test:`, `docs:`)
|
||||
- **Update docs/** when changing architecture, abstractions, or significant design
|
||||
- **Test as you go** — write tests alongside code, not after
|
||||
|
||||
## Testing
|
||||
## TDD — Red/Green/Refactor (mandatory)
|
||||
|
||||
**Always use test-driven development.** No production code without a failing test first.
|
||||
|
||||
The loop:
|
||||
1. **Red** — write a failing test that describes the behavior you want. Run it, confirm it fails for the right reason.
|
||||
2. **Green** — write the minimum code to make the test pass. No more, no less.
|
||||
3. **Refactor** — clean up the code (extract, rename, simplify) while keeping tests green.
|
||||
4. **Commit** — one red/green/refactor cycle = one atomic commit.
|
||||
5. Repeat.
|
||||
|
||||
**Why this matters:**
|
||||
- Forces you to think about behavior before implementation
|
||||
- Produces only code that's actually needed (no speculative abstractions)
|
||||
- Tests written first are always behavioral and structure-insensitive by construction
|
||||
- Tiny cycles = fast feedback, smaller diffs, easier to review
|
||||
|
||||
**For bug fixes:**
|
||||
1. Write a failing test that reproduces the bug (this is the regression test)
|
||||
2. Fix the bug until the test passes
|
||||
3. Commit both together: `fix: [bug] — regression test added`
|
||||
|
||||
**For Rust:**
|
||||
```bash
|
||||
cargo watch -x test # run tests on every save
|
||||
```
|
||||
|
||||
**For frontend:**
|
||||
```bash
|
||||
pnpm test --watch # run tests on every save
|
||||
```
|
||||
|
||||
**When to deviate:** Pure UI layout/styling work with no logic is the only exception. Everything else — hooks, utilities, Rust commands, state management — must be TDD.
|
||||
|
||||
## Testing (quality bar)
|
||||
|
||||
- Unit tests must cover real business logic, not "component renders"
|
||||
- Every bug fixed manually → add a regression test
|
||||
- Every new feature → new tests for new behavior paths
|
||||
- Tests test **behavior** (what the code does), not **structure** (how it does it)
|
||||
- Every bug fixed → regression test that would have caught it
|
||||
- Every new feature → TDD from the start (see above)
|
||||
- `pnpm test:coverage` and `cargo llvm-cov` must pass before committing
|
||||
|
||||
## Design File (every UI task)
|
||||
@@ -103,6 +179,33 @@ node -e "const f=JSON.parse(require('fs').readFileSync('ui-design.pen','utf8'));
|
||||
echo '{"children":[],"variables":{}}' > design/<slug>.pen
|
||||
```
|
||||
|
||||
## Vault File Retrocompatibility (mandatory for every feature that adds vault files)
|
||||
|
||||
Laputa vaults are long-lived. New app versions must work on existing vaults that were created before a feature existed.
|
||||
|
||||
**Rule: never assume a vault file exists. Always auto-create if missing.**
|
||||
|
||||
Every feature that depends on a vault file or folder must:
|
||||
1. **Auto-bootstrap on vault open** — check if the required file/folder exists; if not, create it with defaults. This must be silent and non-blocking.
|
||||
2. **Be idempotent** — creating defaults must be safe to run multiple times (never overwrite user data).
|
||||
3. **Expose a repair command** — add a `Cmd+K` command like "Restore Default Themes" or "Repair Vault Config" that explicitly re-creates missing files. Users can run this if something is broken.
|
||||
|
||||
**General "Repair Vault" command** — when adding a new vault file dependency, register it with the central repair system so that `Cmd+K → "Repair Vault"` fixes everything in one shot.
|
||||
|
||||
**Pattern:**
|
||||
```
|
||||
on vault open:
|
||||
if file X does not exist → create X with defaults ← silent auto-repair
|
||||
if file X exists but is malformed → log warning, use defaults (don't crash)
|
||||
|
||||
on "Repair Vault" command:
|
||||
for each known vault file/folder:
|
||||
if missing → create with defaults
|
||||
if present → leave untouched (idempotent)
|
||||
```
|
||||
|
||||
This principle applies to: themes, config files, type files, any `.laputa/` subfolder, or any file Laputa expects to find in a vault.
|
||||
|
||||
## macOS / Tauri Gotchas
|
||||
|
||||
- `Option+N` on macOS → special chars (`¡`, `™`), not `key:'N'`. Use `e.code` or `Cmd+N`.
|
||||
@@ -118,6 +221,28 @@ bash ~/.openclaw/skills/laputa-qa/scripts/shortcut.sh "command" "s"
|
||||
bash ~/.openclaw/skills/laputa-qa/scripts/click.sh 400 300 # logical coords
|
||||
```
|
||||
|
||||
## Menu Bar Discoverability (mandatory for every new command)
|
||||
|
||||
The command palette is powerful but not discoverable — users must already know a command exists to find it. The macOS menu bar is where users discover what an app can do.
|
||||
|
||||
**Rule: every significant command palette entry must also appear in the menu bar.**
|
||||
|
||||
When adding a new command to the palette:
|
||||
1. **Identify the right menu bar group** — File, Edit, View, Note, Vault, or create a new group if needed
|
||||
2. **Add a menu item** with the same label as the palette command
|
||||
3. **Show the keyboard shortcut** next to the menu item (if one exists)
|
||||
4. **If no direct shortcut exists**, still add the menu item — it's discoverable and triggers the same action
|
||||
|
||||
The menu bar should be organized around what Laputa does:
|
||||
- **File** — new note, open vault, switch vault, close
|
||||
- **Edit** — undo, redo, find, note actions (rename, trash, duplicate)
|
||||
- **View** — view modes, zoom, sidebar, panels
|
||||
- **Note** — note-specific actions (move to trash, archive, properties)
|
||||
- **Vault** — vault management (themes, config, repair, sync)
|
||||
- **Window / Help** — standard macOS items
|
||||
|
||||
**This is a QA requirement:** before marking any task done, verify that every new command palette entry has a corresponding menu bar item.
|
||||
|
||||
## Keyboard-First Principle (mandatory for every new feature)
|
||||
|
||||
Every feature must be reachable via keyboard. This is both a UX requirement and a QA requirement — Brian tests the native app using keyboard only (osascript key events, no mouse).
|
||||
|
||||
Reference in New Issue
Block a user