docs: restructure CLAUDE.md into 4 clear sections (workflow/process/product/reference)
This commit is contained in:
226
CLAUDE.md
226
CLAUDE.md
@@ -1,69 +1,49 @@
|
||||
# CLAUDE.md — Laputa App
|
||||
|
||||
## ⛔ BEFORE EVERY COMMIT
|
||||
> Quick links: [Project Spec](docs/PROJECT-SPEC.md) · [Architecture](docs/ARCHITECTURE.md) · [Abstractions](docs/ABSTRACTIONS.md) · [Wireframes](ui-design.pen)
|
||||
|
||||
---
|
||||
|
||||
## 1. Task Workflow
|
||||
|
||||
This is how you pick up and complete a task. Follow this order every time.
|
||||
|
||||
### 1a. Pick up a task
|
||||
|
||||
Your task comes from Todoist (project `6g3XjQFwv9V8Pxfv`). When you start:
|
||||
1. Read the task description fully
|
||||
2. For To Rework tasks: read the ❌ QA failed comment to understand what went wrong
|
||||
3. Check `docs/adr/` for relevant architecture decisions before making structural choices
|
||||
|
||||
### 1b. Implement
|
||||
|
||||
- Work on `main` branch — **no branches, no PRs, ever**
|
||||
- Commit every 20–30 min: `feat:`, `fix:`, `refactor:`, `test:`, `docs:`
|
||||
- **⛔ NEVER use --no-verify**
|
||||
- For UI tasks: open `ui-design.pen` first, study the visual language, design in light mode
|
||||
|
||||
### 1c. When done — three mandatory steps
|
||||
|
||||
**Step 1: Move task to In Review**
|
||||
```bash
|
||||
pnpm lint && npx tsc --noEmit
|
||||
pnpm test
|
||||
pnpm test:coverage # frontend ≥70%
|
||||
cargo test
|
||||
cargo llvm-cov --manifest-path src-tauri/Cargo.toml --no-clean --fail-under-lines 85
|
||||
```
|
||||
|
||||
**CodeScene Code Health** — the pre-commit and pre-push hooks enforce:
|
||||
- Hotspot Code Health ≥ 9.5 (most-edited files)
|
||||
- Average Code Health ≥ 9.31 (project-wide, ALL files)
|
||||
|
||||
**Both gates block commit/push.** If either fails: extract hooks, split large components, reduce function complexity. Never add `// eslint-disable`, `#[allow(...)]`, or `as any` to pass the gate. Check both scores via MCP CodeScene after every significant change:
|
||||
- `hotspot_code_health.now` ≥ 9.5
|
||||
- `code_health.now` ≥ 9.31 (average — do NOT ignore this one)
|
||||
|
||||
If Average Code Health is below 9.0, you must fix regressions before pushing — even in files you didn't directly modify, if your changes indirectly affected complexity.
|
||||
|
||||
**Boy Scout Rule (Robert C. Martin):** Leave every file you touch better than you found it. When working on any task:
|
||||
1. Before modifying a file, check its CodeScene health: `mcp__codescene__code_health_review`
|
||||
2. If the file has issues (complexity, duplication, large functions), fix them as part of your work
|
||||
3. After your changes, verify the file's score is higher than before: `mcp__codescene__code_health_score`
|
||||
4. The goal: every commit either maintains or raises the overall average. No commit should lower it.
|
||||
|
||||
This is not optional — it's how we incrementally raise the codebase quality with every task.
|
||||
|
||||
## ⛔ BEFORE FIRING laputa-task-done — Two-phase QA
|
||||
|
||||
### Phase 1: Playwright (you do this)
|
||||
|
||||
Write a test in `tests/smoke/<slug>.spec.ts` that covers every acceptance criterion. The test must fail before your fix and pass after. Run it:
|
||||
|
||||
```bash
|
||||
pnpm dev --port 5201 &
|
||||
sleep 3
|
||||
BASE_URL="http://localhost:5201" npx playwright test tests/smoke/<slug>.spec.ts
|
||||
```
|
||||
|
||||
**If your task touches filesystem, git, AI, MCP, or any native Tauri command**: also test with `pnpm tauri dev` against `~/Laputa` (not demo vault). Use `osascript` keyboard events — no mouse, no `cliclick`.
|
||||
|
||||
### Phase 2: Native QA (Brian does this after push)
|
||||
|
||||
Brian installs the release build and runs keyboard-only QA. Phase 1 must pass first or the task goes to To Rework.
|
||||
|
||||
Fire done signal only after Phase 1 passes — **three steps, all required**:
|
||||
|
||||
```bash
|
||||
# 1. Move task to In Review on Todoist
|
||||
curl -s -X POST "https://api.todoist.com/api/v1/tasks/<task_id>/move" \
|
||||
-H "Authorization: Bearer $TODOIST_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"section_id": "6g3XjX33FF4Vj86M"}'
|
||||
```
|
||||
|
||||
# 2. Notify Brian
|
||||
**Step 2: Notify Brian**
|
||||
```bash
|
||||
openclaw system event --text "laputa-task-done:<task_id>" --mode now
|
||||
```
|
||||
|
||||
# 3. Self-dispatch: pick next task autonomously
|
||||
# Priority: To Rework first, then Open (sorted by Todoist priority p1→p4)
|
||||
# Skip To Rework tasks whose last comment is ❌ QA failed with no follow-up human feedback
|
||||
# If next task found: move it to In Progress and start working on it immediately
|
||||
# If nothing available: exit — Brian's watchdog (laputa-dispatch cron, hourly) will restart you
|
||||
python3 - <<'PYEOF'
|
||||
**Step 3: Self-dispatch — pick the next task**
|
||||
|
||||
Check Todoist for the next task (To Rework first, then Open, sorted by priority). Skip To Rework tasks whose last comment is ❌ with no follow-up human feedback. If a task is available, move it to In Progress (`6g3XjWjfmJFcGgHM`) and start immediately. If nothing is available, exit — the watchdog will restart you.
|
||||
|
||||
```python
|
||||
# Todoist section IDs:
|
||||
# To Rework: 6g6QqvR9rRpvJWvv | Open: 6g3XjWR832hVHhCM | In Progress: 6g3XjWjfmJFcGgHM
|
||||
import os, json, urllib.request, sys
|
||||
|
||||
token = os.environ["TODOIST_API_KEY"]
|
||||
@@ -87,27 +67,25 @@ def get_comments(task_id):
|
||||
return data if isinstance(data, list) else data.get("results", [])
|
||||
|
||||
def skip_rework_task(task_id):
|
||||
"""Return True if this To Rework task has no follow-up human feedback after the last QA fail."""
|
||||
comments = get_comments(task_id)
|
||||
last_fail_idx = -1
|
||||
for i, c in enumerate(comments):
|
||||
if isinstance(c, dict) and "\u274c" in c.get("content", ""):
|
||||
last_fail_idx = i
|
||||
if last_fail_idx == -1:
|
||||
return False # No QA fail found — can proceed
|
||||
return False
|
||||
after = comments[last_fail_idx + 1:]
|
||||
human_keywords = ["feedback", "Luca", "Brian", "\u26a0\ufe0f", "fix", "should", "must", "rework"]
|
||||
for c in after:
|
||||
if any(kw.lower() in c.get("content", "").lower() for kw in human_keywords):
|
||||
return False # Human weighed in — can proceed
|
||||
return True # No human feedback yet — skip
|
||||
return False
|
||||
return True
|
||||
|
||||
for section_id, is_rework in [("6g6QqvR9rRpvJWvv", True), ("6g3XjWR832hVHhCM", False)]:
|
||||
tasks = sorted(get_tasks(section_id), key=lambda t: t.get("priority", 4), reverse=True)
|
||||
for task in tasks:
|
||||
if is_rework and skip_rework_task(task["id"]):
|
||||
continue
|
||||
# Move to In Progress
|
||||
req = urllib.request.Request(
|
||||
f"https://api.todoist.com/api/v1/tasks/{task['id']}/move",
|
||||
data=json.dumps({"section_id": "6g3XjWjfmJFcGgHM"}).encode(),
|
||||
@@ -116,86 +94,120 @@ for section_id, is_rework in [("6g6QqvR9rRpvJWvv", True), ("6g3XjWR832hVHhCM", F
|
||||
)
|
||||
with urllib.request.urlopen(req) as r:
|
||||
t = json.load(r)
|
||||
print(f"NEXT_TASK_ID={t['id']}")
|
||||
print(f"NEXT_TASK_TITLE={t['content']}")
|
||||
print(f"Next task: {t['content']} ({t['id']})")
|
||||
sys.exit(0)
|
||||
|
||||
print("NO_TASKS")
|
||||
PYEOF
|
||||
print("NO_TASKS — exiting")
|
||||
```
|
||||
|
||||
**After running step 3:** if output contains `NEXT_TASK_ID=...`, read that task's full description from Todoist and implement it (repeating this entire CLAUDE.md flow). If output is `NO_TASKS`, you are done — exit cleanly.
|
||||
### 1d. QA phases
|
||||
|
||||
## Project
|
||||
**Phase 1 (you):** Write a Playwright smoke test in `tests/smoke/<slug>.spec.ts` covering every acceptance criterion. Must fail before fix, pass after.
|
||||
|
||||
Tauri v2 + React + TypeScript desktop app. Reads a vault of markdown files with YAML frontmatter.
|
||||
```bash
|
||||
pnpm dev --port 5201 &
|
||||
sleep 3
|
||||
BASE_URL="http://localhost:5201" npx playwright test tests/smoke/<slug>.spec.ts
|
||||
```
|
||||
|
||||
- **Spec**: `docs/PROJECT-SPEC.md` | **Architecture**: `docs/ARCHITECTURE.md` | **Abstractions**: `docs/ABSTRACTIONS.md`
|
||||
- **Wireframes**: `ui-design.pen` | **Luca's vault**: `~/Laputa/` (~9200 markdown files)
|
||||
- Stack: Rust backend, React + BlockNote editor, Vitest + Playwright + cargo test, pnpm
|
||||
For tasks touching filesystem, git, AI, MCP, or native Tauri: also test with `pnpm tauri dev` against `demo-vault-v2/` using `osascript` keyboard events.
|
||||
|
||||
## How to Work
|
||||
**Phase 2 (Brian):** Installs release build, runs native QA. You don't need to do anything — just make sure Phase 1 passes before firing the done signal.
|
||||
|
||||
- **Push directly to main** — no PRs ever. The pre-push hook runs all checks.
|
||||
- **⛔ NEVER open a PR** — branches diverge and cause rebase churn.
|
||||
---
|
||||
|
||||
## 2. Development Process
|
||||
|
||||
### Commits & pushes
|
||||
|
||||
- Push directly to `main` — no PRs, no branches
|
||||
- The pre-push hook runs the full check suite (build + tests + Playwright + CodeScene)
|
||||
- **⛔ NEVER use --no-verify**
|
||||
- Commit every 20–30 min: `feat:`, `fix:`, `refactor:`, `test:`, `docs:`
|
||||
- Commit message format: `feat:`, `fix:`, `refactor:`, `test:`, `docs:`
|
||||
|
||||
## TDD (mandatory)
|
||||
### TDD (mandatory)
|
||||
|
||||
Red → Green → Refactor → Commit. One cycle per commit. For bugs: write a failing regression test first, then fix. Exception: pure CSS/layout with no logic.
|
||||
Red → Green → Refactor → Commit. One cycle per commit.
|
||||
For bugs: write a failing regression test first, then fix.
|
||||
Exception: pure CSS/layout changes with no logic.
|
||||
|
||||
**Test quality (Kent Beck's Desiderata):** every test must be Isolated (no shared state), Deterministic (no flakiness), Fast, Behavioral (tests behavior not implementation), Structure-insensitive (refactoring doesn't break it), Specific (failure points to exact cause), Predictive (all pass = production-ready). Fix flaky/non-deterministic tests before adding new ones. E2E tests over unit tests for user flows.
|
||||
**Test quality (Kent Beck's Desiderata):** Isolated · Deterministic · Fast · Behavioral · Structure-insensitive · Specific · Predictive. Fix flaky tests before adding new ones. Prefer E2E over unit tests for user flows.
|
||||
|
||||
## ⛔ Docs — Keep docs/ in sync
|
||||
### Code health (mandatory)
|
||||
|
||||
After adding a Tauri command, new component/hook, data model change, or new integration: update `docs/ARCHITECTURE.md`, `docs/ABSTRACTIONS.md`, and/or `docs/GETTING-STARTED.md` in the same commit. Use Mermaid for diagrams (not ASCII). Exception: spatial wireframe layouts.
|
||||
Pre-commit and pre-push hooks enforce:
|
||||
- **Hotspot Code Health ≥ 9.5** (most-edited files)
|
||||
- **Average Code Health ≥ 9.31** (project-wide)
|
||||
|
||||
## Architecture Decision Records (ADRs)
|
||||
Both gates block commit/push. Never add `// eslint-disable`, `#[allow(...)]`, or `as any` to pass them.
|
||||
|
||||
ADRs live in `docs/adr/`. Before making an architectural choice, check existing ADRs there first.
|
||||
**Before every commit:** run checks via MCP CodeScene:
|
||||
- `mcp__codescene__code_health_review` — check file before touching it
|
||||
- `mcp__codescene__code_health_score` — verify score is higher after your changes
|
||||
|
||||
**When to create one**: storage strategy, new dependency, platform support, core abstraction change, cross-cutting concern. Use `/create-adr` for the full template and instructions.
|
||||
**Boy Scout Rule:** every file you touch must leave with a higher score than it had. If Average drops below 9.0, fix regressions before pushing.
|
||||
|
||||
**Timing**: create the ADR **in the same commit as the code** that implements the decision — never before, never after. An ADR committed without the corresponding code is invalid.
|
||||
### Check suite (runs on every push)
|
||||
```bash
|
||||
pnpm lint && npx tsc --noEmit
|
||||
pnpm test
|
||||
pnpm test:coverage # frontend ≥70%
|
||||
cargo test
|
||||
cargo llvm-cov --manifest-path src-tauri/Cargo.toml --no-clean --fail-under-lines 85
|
||||
```
|
||||
|
||||
**When your work supersedes an existing ADR**: do not edit the existing file — use `/create-adr` which covers the superseding flow.
|
||||
### Architecture Decision Records (ADRs)
|
||||
|
||||
**Do not create ADRs for**: bug fixes, UI styling, refactors, or test additions.
|
||||
ADRs live in `docs/adr/`. Check them before making structural choices.
|
||||
|
||||
## Design File (UI tasks)
|
||||
**When to create one:** storage strategy, new dependency, platform support, core abstraction change, cross-cutting concern. Use `/create-adr` for the template.
|
||||
|
||||
1. Open `ui-design.pen` first — study existing frames for visual language.
|
||||
2. Design in light mode. Create `design/<slug>.pen` for the task.
|
||||
3. On merge to main: merge frames into `ui-design.pen`, delete `design/<slug>.pen`.
|
||||
**Timing:** create the ADR **in the same commit as the code** — never before, never after.
|
||||
|
||||
## ⛔ Never modify the user vault for testing
|
||||
**Superseding:** never edit an existing ADR — create a new one that supersedes it.
|
||||
|
||||
`~/Laputa/` is Luca's real vault. **Never create, edit, or delete notes there for testing purposes.**
|
||||
**Don't create ADRs for:** bug fixes, UI styling, refactors, test additions.
|
||||
|
||||
Use the demo vault for all testing:
|
||||
- Playwright / Vitest: use the fixtures in `tests/` or `demo-vault-v2/`
|
||||
- `pnpm tauri dev` manual testing: open `demo-vault-v2/` as the vault, not `~/Laputa/`
|
||||
- If a test genuinely requires the real vault (e.g. verifying git history), read only — never write
|
||||
### Keep docs/ in sync
|
||||
|
||||
Any commit that touches `~/Laputa/` content is a bug. If you accidentally created test notes there, delete them before committing.
|
||||
After adding a Tauri command, new component/hook, data model change, or new integration: update `docs/ARCHITECTURE.md`, `docs/ABSTRACTIONS.md`, and/or `docs/GETTING-STARTED.md` in the same commit.
|
||||
|
||||
## Vault Retrocompatibility
|
||||
---
|
||||
|
||||
Every feature that depends on vault files must auto-bootstrap: check if file/folder exists on vault open, create with defaults if missing (silent, idempotent). Register with the central `Cmd+K → "Repair Vault"` command.
|
||||
## 3. Product Rules
|
||||
|
||||
## Keyboard-First + Menu Bar (mandatory)
|
||||
### Keyboard-first (mandatory)
|
||||
|
||||
Every feature must be reachable via keyboard. Every new command palette entry must also appear in the macOS menu bar (File / Edit / View / Note / Vault / Window). This is a QA requirement.
|
||||
|
||||
## macOS / Tauri Gotchas
|
||||
### Never modify the user vault for testing
|
||||
|
||||
- `Option+N` → special chars on macOS. Use `e.code` or `Cmd+N`.
|
||||
- Tauri menu accelerators: `MenuItemBuilder::new(label).accelerator("CmdOrCtrl+1")`.
|
||||
- `app.set_menu()` replaces the ENTIRE menu bar — include all submenus.
|
||||
- `mock-tauri.ts` silently swallows Tauri calls — not a substitute for native app testing.
|
||||
`~/Laputa/` is Luca's real vault. Never create, edit, or delete notes there for any reason.
|
||||
|
||||
## QA Scripts
|
||||
Use `demo-vault-v2/` for all testing. If a test genuinely needs real vault data (e.g. git history), read only — never write. Any commit that touches `~/Laputa/` content is a bug.
|
||||
|
||||
### Vault retrocompatibility
|
||||
|
||||
Every feature that depends on vault files must auto-bootstrap: check if file/folder exists on vault open, create with defaults if missing (silent, idempotent). Register with `Cmd+K → "Repair Vault"`.
|
||||
|
||||
### UI design
|
||||
|
||||
1. Open `ui-design.pen` first — study existing frames for visual language
|
||||
2. Design in light mode. Create `design/<slug>.pen` for the task
|
||||
3. On completion: merge frames into `ui-design.pen`, delete `design/<slug>.pen`
|
||||
|
||||
---
|
||||
|
||||
## 4. Reference
|
||||
|
||||
### macOS / Tauri gotchas
|
||||
|
||||
- `Option+N` → special chars on macOS. Use `e.code` or `Cmd+N`
|
||||
- Tauri menu accelerators: `MenuItemBuilder::new(label).accelerator("CmdOrCtrl+1")`
|
||||
- `app.set_menu()` replaces the ENTIRE menu bar — include all submenus
|
||||
- `mock-tauri.ts` silently swallows Tauri calls — not a substitute for native testing
|
||||
|
||||
### QA scripts
|
||||
|
||||
```bash
|
||||
bash ~/.openclaw/skills/laputa-qa/scripts/focus-app.sh laputa
|
||||
@@ -203,6 +215,6 @@ bash ~/.openclaw/skills/laputa-qa/scripts/screenshot.sh /tmp/out.png
|
||||
bash ~/.openclaw/skills/laputa-qa/scripts/shortcut.sh "command" "s"
|
||||
```
|
||||
|
||||
## Documentation Diagrams
|
||||
### Diagrams
|
||||
|
||||
Prefer Mermaid for all diagrams (`flowchart`, `sequenceDiagram`, `classDiagram`, `stateDiagram-v2`). ASCII only for spatial wireframe layouts. GitHub renders Mermaid natively.
|
||||
Prefer Mermaid (`flowchart`, `sequenceDiagram`, `classDiagram`, `stateDiagram-v2`). ASCII only for spatial wireframe layouts.
|
||||
|
||||
Reference in New Issue
Block a user