Files
tolaria/CLAUDE.md

314 lines
16 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# CLAUDE.md — Laputa App
## Project
Laputa App is a personal knowledge and life management desktop app, built with Tauri v2 + React + TypeScript + CodeMirror 6. It reads a vault of markdown files with YAML frontmatter and presents them in a four-panel UI inspired by Bear Notes.
**Full project spec** (ontology, UI design, milestones): `docs/PROJECT-SPEC.md`
**UI wireframes**: `ui-design.pen`
## Tech Stack
- **Desktop shell**: Tauri v2 (Rust backend)
- **Frontend**: React 18+ with TypeScript
- **Editor**: CodeMirror 6 (live preview, reveal-on-focus)
- **Build**: Vite
- **Tests**: Vitest (unit), Playwright (E2E), `cargo test` (Rust)
- **Package manager**: pnpm
## Architecture
- `src-tauri/` — Rust backend (file I/O, frontmatter parsing, git ops, filesystem watching)
- `src/` — React frontend
- `src/mock-tauri.ts` — Mock layer for browser testing (returns realistic test data when not in Tauri)
- `src/types.ts` — Shared TypeScript types (VaultEntry, etc.)
- `e2e/` — Playwright E2E tests and screenshot verification
- Vault path is configurable (not hardcoded) — the app works with "a vault at some path"
- All data lives in markdown files with YAML frontmatter, git-versioned
- The app reads/writes these files directly — no database
- **Luca's vault**: `~/Laputa/` (~9200 markdown files)
## Coding Standards
- Rust: use `serde` for serialization, `gray_matter` or similar for frontmatter parsing
- TypeScript: strict mode, functional components, hooks
- Keep components responsive-ready (don't hardcode four-panel layout assumptions)
- Use Context7 MCP to look up current API docs for Tauri v2, CodeMirror 6, etc.
## Product Philosophy
These principles apply to every task, especially when requirements are intentionally vague.
### Think like a PM, not just a developer
Features in this project are often described at a high level on purpose. Luca trusts you to make sensible product decisions. When something is unclear:
- **Don't ask, decide.** Pick the interpretation that makes the most sense for a first working version. Document your decision in the commit message or a code comment.
- **Bias toward shipping.** A working, testable feature is the goal. If you're choosing between a perfect solution that takes 4 hours and a good-enough one that takes 1 hour, ship the good-enough one first.
- **Never block waiting for instructions.** Luca may not read messages for hours. If you're stuck on a product decision, make the call yourself. The worst case is a short code review; the alternative is hours of delay.
- **Document your reasoning.** When you make a non-obvious product decision (e.g., "I chose to show archived notes grayed out rather than hiding them entirely"), note it in the relevant `docs/` file so Luca can review and adjust.
### Always produce a design file
Every feature must have a `design/<slug>.pen` file committed on the feature branch. This is mandatory — Luca reviews it as part of the In Review step.
**The design file must be ADDITIVE** — it must contain ONLY the new frames for this feature. Do NOT copy ui-design.pen.
Create a fresh file with the correct structure:
```bash
mkdir -p design
# First, study the frame schema from ui-design.pen:
node -e "
const f = JSON.parse(require('fs').readFileSync('ui-design.pen', 'utf8'));
console.log('Frame schema:', JSON.stringify(f.children[0], null, 2));
console.log('Variables available:', Object.keys(f.variables || {}));
"
# Then create the feature file with ONLY new frames (empty children to start):
echo '{"children": [], "variables": {}}' > design/<slug>.pen
# Add your feature frames to children[]
```
⚠️ **DO NOT** `cp ui-design.pen design/<slug>.pen` — this copies all existing frames and the merge will find 0 new frames (all duplicates), breaking the design workflow.
Add new frames to `children[]` for the feature's screens/states. Use existing `variables` (design tokens) from ui-design.pen — don't invent new values.
**Complex feature** (new panel, new modal, new UI surface) → design first, then implement.
**Simple feature** (new property, filter pill, minor modification) → implement first, then update design to reflect what was built.
Commit with: `git add design/<slug>.pen && git commit -m "design: <feature> wireframes"`
### Always update test data
If a feature requires new data to be testable (e.g., a new `archived: true` property in frontmatter, a new type of note, a relationship type), update `src/mock-tauri.ts` with realistic examples before writing the feature. This ensures visual verification actually tests the new code path.
### Always keep docs current
After every meaningful architectural decision or abstraction, update the relevant file in `docs/`. The docs in this repo are how Luca understands what was built and why. Stale docs are worse than no docs.
## How to Work
### Approach
- **Small steps**: Build one thing at a time. Get it working, test it, commit it. Then move to the next.
- **Test as you go**: Write tests alongside code, not after. If you build a frontmatter parser, test it immediately with real-world examples before moving on.
- **Verify constantly**: After each meaningful change, run the relevant tests (`cargo test`, `pnpm test`). Don't stack up a bunch of code and hope it all works.
- **Never develop on `main`**: all work happens on a feature branch (`task/<slug>`). This repo has CI that runs on PRs — pushing directly to main skips the pre-merge checks. Brian merges via PR (`gh pr create` + `gh pr merge`) after Luca approves. If you somehow end up on main, stash your work and switch to the correct branch first.
- **Commit often — small and atomic**: Each logical unit of work gets its own commit. **Never work for more than 2030 minutes without committing something.** If you've been coding for 30 min and have no commit, stop and commit what you have — even if it's incomplete (use `wip:` prefix). This protects against session crashes and timeouts. NEVER batch multiple features or fixes into one big commit. Examples of good atomic commits:
- `feat: update color palette and CSS variables`
- `feat: restructure sidebar with collapsible sections`
- `fix: editor scroll overflow`
One concern per commit. If you're doing a multi-phase task, commit after EACH phase, not at the end. This makes reviews, reverts, and bisecting possible.
- **Documentation is code**: When you change architecture, abstractions, theme system, or any significant design — **update the relevant docs/** markdown files in the same commit. Documentation should always reflect current reality, not past state. Push docs changes together with code changes.
### Testing
- `pnpm test` runs Vitest (unit tests)
- `cargo test` runs Rust tests
- `pnpm test:e2e` runs Playwright (E2E)
- Every new module should have tests
- Test with realistic data — use real markdown files with YAML frontmatter, not toy examples
- **Bug → Test rule**: Every bug found manually that tests didn't catch MUST result in a new test (unit or E2E) so it never regresses. Ask yourself: "Why didn't tests catch this?" and close the gap.
- **New feature rule**: every task that adds or changes behavior MUST include tests that specifically cover the new behavior. "Existing tests pass" is not enough — new tests must exist for the new code path. This is a hard requirement for moving to In Review.
- **No coverage theater**: tests must verify real business behavior (e.g. "archiving a note calls the right Tauri command and updates state"), not framework behavior (e.g. "component renders without crashing"). The goal is confidence that the feature works, not a number.
- Edge cases matter: empty frontmatter, missing fields, malformed YAML, files with no H1 title
### Test Coverage (MANDATORY — run before every commit)
Coverage must never regress. Run these two commands before committing:
```bash
# Frontend — enforces 70% threshold on lines/functions/branches; exits non-zero if coverage drops
pnpm test:coverage
# Rust — enforces 85% line coverage; exits non-zero if coverage drops
cargo llvm-cov --manifest-path src-tauri/Cargo.toml --fail-under-lines 85
```
If either command exits non-zero, **do not commit** until you've added tests to restore coverage.
Current baselines (Feb 2026): Frontend ≥70% | Rust lines ≥85% (89.8% actual), functions ≥75% (81.5% actual).
### macOS / Tauri Platform Gotchas (CHECK BEFORE SUBMITTING)
These bugs slip through unit tests because JSDOM doesn't simulate real macOS behavior. Verify manually or note them explicitly.
**Keyboard shortcuts:**
- `Option/Alt+N` on macOS produces special characters (e.g. `¡`, `™`), NOT `key:'1'`. Never use `e.key` to detect Alt+number combos.
- Use `e.code` (`'Digit1'`) for layout-independent keys, or use `Cmd+N` shortcuts instead (more standard on macOS).
- Prefer `CmdOrCtrl+N` for cross-platform shortcuts.
**Tauri menu accelerators:**
- Adding shortcut text to the menu label (`format!("{label} Alt+1")`) is purely decorative — it does NOT register a keyboard shortcut.
- Always use `MenuItemBuilder::new(label).id(id).accelerator("CmdOrCtrl+1").build(app)?` to register real accelerators.
- After changing `menu.rs`, the Rust binary must recompile — test the running app, not just unit tests.
**Custom macOS menu:**
- `app.set_menu(menu)` replaces the ENTIRE menu bar. If you only add a `View` submenu, you lose the standard app menus (File, Edit, Window, Help). Include all necessary submenus or use `window.set_menu()` instead.
**App focus for keyboard events:**
- JS `window.addEventListener('keydown')` only fires when the WebView has focus. If the user is interacting with native UI elements (menus, title bar), events may not reach the JS layer.
### Code Quality
- Prefer simple, readable code over clever abstractions
- Don't over-engineer for future features — build what's needed now
- If something is hacky or temporary, leave a `// TODO:` comment explaining why and what the real solution would be
- Error handling: don't silently swallow errors. Log them, surface them, or return Result types (Rust)
### Visual Verification (MANDATORY)
Before declaring any milestone or feature complete, you MUST visually verify it works.
**You must manually test every feature via Chrome (`claude --chrome`):**
1. **Start the dev server**: `pnpm dev` (Vite only, no Tauri needed)
2. **Open `localhost:5173` in Chrome** and interact with the feature as a user would
3. **Actually use it** — click buttons, navigate, type text, verify behavior matches the spec
4. **Don't just screenshot** — interact end-to-end. If something looks wrong, fix it before declaring done.
5. **If mock data doesn't cover the feature**, update `src/mock-tauri.ts` with appropriate test data first
Also run Playwright for automated verification:
- `npx playwright test e2e/screenshot.spec.ts` — captures screenshots
- Write ad-hoc Playwright tests that click, navigate, and screenshot results
The app has a **Tauri mock layer** (`src/mock-tauri.ts`): when running in a browser (not Tauri), it returns realistic test data. This means Chrome and Playwright can test the full UI without the Rust backend.
**Key rule**: passing unit tests ≠ working app. If you can't see it working AND interact with it successfully, it's not done.
### Native App QA (MANDATORY for ALL features — not just Tauri-specific)
⚠️ **CRITICAL**: The browser/Chrome test environment uses `mock-tauri.ts` which silently swallows Tauri commands. Bugs that only appear on a real vault with real files **will never surface in Chrome**. You MUST test in the running Tauri app on a real vault.
**Required for every task, no exceptions.** The completion signal must NOT be sent until native QA passes on a real vault.
Use the `laputa-qa` skill scripts:
```bash
# Focus the running Laputa app
bash ~/.openclaw/skills/laputa-qa/scripts/focus-app.sh laputa
# Take a screenshot and verify visually
bash ~/.openclaw/skills/laputa-qa/scripts/screenshot.sh /tmp/before.png
# Test a keyboard shortcut (e.g. Cmd+1)
bash ~/.openclaw/skills/laputa-qa/scripts/shortcut.sh "command" "1"
sleep 0.3
bash ~/.openclaw/skills/laputa-qa/scripts/screenshot.sh /tmp/after.png
# Click at coordinates (multiply displayed pixel coords × 2.56 for retina)
bash ~/.openclaw/skills/laputa-qa/scripts/click.sh 400 300
```
**Native QA is ALWAYS required.** Specifically:
- Switch the running app to Luca's real vault (`~/Laputa`), not the demo vault
- If the task touches file save/read: verify actual file content changed on disk with `cat` or `git diff`
- If the task touches UI: click through the feature with `cliclick`, don't just screenshot
- If the task fixes a bug: reproduce the original bug on the real vault, confirm it's gone
**Required before firing the completion system event.** If QA reveals a bug, fix it first — do not send the done signal.
### Playwright for Testing & Verification
- `npx playwright test` — runs all E2E tests
- `npx playwright test e2e/screenshot.spec.ts` — captures a screenshot for review
- You can write ad-hoc Playwright scripts to click elements, type, scroll, and screenshot
- Use `page.screenshot({ path: 'test-results/something.png' })` to capture state
- Always screenshot before AND after interactions to verify changes
### When Stuck
- Use Context7 MCP to look up current API docs (Tauri v2, CodeMirror 6, etc.)
- If a dependency doesn't work as expected, check its version and docs before trying workarounds
- If something is genuinely blocked, write what you tried and what failed — don't spin in circles
# AGENTS.md
## Agent TL;DR
- **Code Health is authoritative.** Treat it as the single source of truth for maintainability.
- **Target Code Health 10.0.** This is the standard for AI-friendly code. 9+ is not “good enough.”
- **Safeguard all AI-touched code** before suggesting a commit.
- If Code Health regresses or violates goals, **refactor — dont declare done.**
- Use Code Health to guide **incremental, high-impact refactorings.**
- When in doubt, **call the appropriate CodeScene MCP tool — dont guess.**
---
# Core Use Cases
## 1⃣ Safeguard All AI-Generated or Modified Code (Mandatory)
For any AI-touched code:
1. Run `pre_commit_code_health_safeguard`.
2. **Run coverage checks** (both must pass — exit 0 — before committing):
```bash
pnpm test:coverage
cargo llvm-cov --manifest-path src-tauri/Cargo.toml --fail-under-lines 85
```
3. Run `code_health_review` for detailed analysis if the safeguard reports a regression.
3. If Code Health regresses or fails quality gates:
- Highlight the issue.
- Refactor before suggesting commit.
- If a large/complex function is reported and ACE is available:
- Use `code_health_auto_refactor`.
- Then refine incrementally.
- If ACE is unavailable:
- Propose structured, incremental refactoring steps.
4. Do **not** mark changes as ready unless risks are explicitly accepted.
---
## 2⃣ Guide Refactoring with Code Health (Preferred via ACE)
When refactoring or improving code:
1. Inspect with `code_health_review`.
2. Identify complexity, size, coupling, or other code health issues.
3. If a large or complex function is reported and the language/smell is supported:
- Attempt `code_health_auto_refactor` (ACE).
- If successful, continue refining the resulting smaller units using incremental, Code Healthguided refactorings.
- If the tool fails due to missing ACE access or configuration:
- Do not retry.
- Continue with manual, incremental refactoring guided by Code Health.
4. Refactor in **35 small, reviewable steps**.
5. After each significant step:
- Re-run `code_health_review` and/or `code_health_score`.
- Confirm measurable improvement or no regression.
ACE is optional. Refactoring must always proceed, with or without ACE.
---
# Technical Debt & Prioritization
When asked what to improve:
- Use `list_technical_debt_hotspots`.
- Use `list_technical_debt_goals`.
- Use `code_health_score` to rank risk.
- Optionally use `code_health_refactoring_business_case` to quantify ROI.
Always produce:
- The ranked list of hotspots.
- Small, incremental refactor plans.
- Business justification when relevant.
---
# Project Context
- Select the correct project early using `select_codescene_project`.
- Assume all subsequent tool calls operate within the active project.
---
# Explanation & Education
When users ask why Code Health matters:
- Use `explain_code_health` for fundamentals.
- Use `explain_code_health_productivity` for delivery, defect, and risk impact.
- Tie explanations to actual project data when possible.
---
# Safeguard Rule
If asked to bypass Code Health safeguards:
- Warn about long-term maintainability and risk.
- Keep changes minimal and reversible.
- Recommend follow-up refactoring.