Closes the code-side v0.1 ship gate. All quality gates green: cargo fmt/clippy/test (~327 tests), npm check (0/0), vitest 13/13, scripts/dogfood-rebrand-drill.sh 8/8. Phase F — first-run onboarding promoted to v0.1 - FirstRunPage with skip-to-main + failure recovery + event recording - Six onboarding commands (record/list/has-completed + lumotia_events) - Storage migration v17 (onboarding_events + lumotia_events tables) UI hardening (in-scope items from v0.1-ui-hardening.md) - StatusPill + PostCaptureCard components, 21st preview entry - Sidebar recording-as-sacred-state (opacity + aria-disabled, reduced-motion) - Settings 6-section regroup + Help section + Activation log + Privacy toggle - Error-state copy sweep (DictationPage + SettingsPage, plain-language) - Global :focus-visible rule, textarea outlines restored - Ctrl+K / Ctrl+, / Escape bindings in +layout LLM resilience - rule_based_extract_tasks (regex-free imperative-verb extractor) + extract_tasks_with_fallback wrapper — task extraction never returns zero - tokio::time::timeout(120s) wraps cleanup/tags/tasks commands Release artefacts - LICENSE (canonical AGPL-3.0), CHANGELOG (Keep-a-Changelog format) - v0.1-release-notes, privacy-and-ai-use, install-warnings, tester-onboarding-kit, tester-acceptance-runbook, code-signing-setup, apple-silicon-rb08-runbook, virtual-audio-setup, v0.1-contrast-audit - Workspace versioning + AGPL spdx; npm exact-pin (10 ranges removed) - AppImage SHA-256 sidecar in build.yml - README v0.1 section + Reporting-issues; canonical repo slug Closure pass — items moved from human-required to code-complete - KI-02 Linux idle inhibit: zbus 5 → org.freedesktop.login1.Manager.Inhibit - KI-03 Windows sleep prevention: SetThreadExecutionState(ES_CONTINUOUS|...) - acquire/release_idle_inhibit Tauri commands, wired in DictationPage - Diagnostic-bundle frontend wire-up (Settings → Help button) - WCAG-AA contrast fix via .btn-filled-text utility (no token changes) - 8 destructive-action sites wrapped in plain-language confirm() guards - KNOWN-ISSUES.md + v0.1-known-limitations.md updated (KI-02/03 fixed) Scripts - pre-tag-verify.sh, tag-day.sh, smoke-linux + driver - parse-diagnostic-bundle.sh, parse-activation-log.py Per-item audit trail: docs/release/v0.1-completion-status.md Remaining: W-01…W-08 (signing certs, hardware probes, smoke matrix, tester recruitment) — see docs/release/v0.1-known-limitations.md.
7.5 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Project
Lumotia — a local-first, cognitive-load-aware dictation + task-capture desktop app. Tauri 2 + Svelte 5 frontend, Rust workspace backend. Pre-alpha, daily-dogfooded on Linux/Wayland (KDE). Full product context lives in README.md and docs/brief/; design principles are non-negotiable and codified in docs/whisper-ecosystem/lumotia-context.md.
Commands
Dev launch (Vite + Tauri, with supply-chain pre-flight):
./run.sh # canonical; also: npm run dev:tauri
npm run dev:frontend # frontend-only iteration, no Tauri
Install: npm ci --ignore-scripts (never bare npm install — --ignore-scripts blocks the npm-worm postinstall vector).
Tests + checks:
cargo test --workspace # all Rust tests (220+ lib, 67 Tauri-app)
cargo test -p lumotia-transcription # single crate
cargo test -p lumotia-llm <test_name> # single test (substring match)
cargo check --workspace --all-targets # what CI runs
cargo fmt --check # release gate
cargo clippy --workspace --all-targets -- -D warnings # release gate
npm run check # svelte-check, jsconfig-driven
npm run test # vitest (jsdom; *.test.ts beside source)
npm run test -- src/lib/foo.test.ts # single vitest file
Release build: npm run tauri build. CI builds installers on tag push.
Rebrand-migration end-to-end probe (Linux only — Tauri 2 ignores HOME overrides on macOS):
cargo build -p lumotia
scripts/dogfood-rebrand-drill.sh # sandbox mode
scripts/dogfood-rebrand-drill.sh --against-real-home # refuses if lumotia data exists
Architecture (the parts to internalise before editing)
Three layers, strict dependency direction: Svelte (UI) → Tauri commands (OS bridge) → Rust crates (brain). The MCP server (lumotia-mcp) is a separate read-only binary that opens the same SQLite store — Lumotia-as-primitive for external agents.
Rust workspace (crates/* + src-tauri/) holds all logic. Tauri command modules in src-tauri/src/commands/ are thin adapters and should not contain business logic. The 22 command modules map roughly 1:1 to a subsystem (audio, llm, transcription, profiles, tasks, hotkey, paste, windows, …). The utility-only modules in the same directory (mod, power, security) carry no #[tauri::command] attribute — don't add them to the invoke handler.
Engine abstractions: LocalEngine in lumotia-transcription wraps both Whisper (whisper-rs) and Parakeet (transcribe-rs ONNX) behind a common Transcriber trait. LLM surfaces (cleanup_text, decompose_task, extract_tasks) in lumotia-llm use GBNF grammars to guarantee parseable JSON. Prompt-injection-hardened cleanup prompt lives in lumotia-ai-formatting::llm_client::CLEANUP_PROMPT.
Storage is SQLite via sqlx 0.8 in lumotia-storage, with FTS5 for transcript search. The MCP binary opens this store read-only.
Frontend state model
Svelte 5 runes ($state, $derived, $effect) — no Svelte 3/4 store API. State lives in src/lib/stores/, one file per store. The central store is page.svelte.ts — transcripts, profiles, taskLists, templates, etc. are fields on it. Secondary windows (/float, /viewer, /preview) use named layouts (+layout@.svelte) to skip the main shell and run chrome-free.
Wiring contracts (enforced socially, not by the compiler)
- Every new Tauri command must be (a) implemented in
src-tauri/src/commands/<module>.rs, (b) registered in the invoke handler in src-tauri/src/lib.rs, and (c) called from the frontend. Forgetting (b) is the most common breakage. - Every Settings-visible setting needs a type field in src/lib/types/app.ts and a default in src/lib/stores/page.svelte.ts.
- Every new workspace crate needs a
descriptionin itsCargo.toml. - Smoke test per new command or crate module; workspace floor is "no regressions on main."
Platform notes that affect code
- Wayland is a first-class target. Don't assume X11. The preview overlay uses
WindowTypeHint::Utility, never steals focus, is pinned across virtual desktops, and is hidden from Alt+Tab. The paste matrix (wtype/xdotool/ydotoolon Linux, AppleScript on macOS, SendKeys on Windows) handles the focus-race against the overlay — see src-tauri/src/commands/paste.rs. - Linux hotkey is evdev, not
tauri-plugin-global-shortcut. Implemented inlumotia-hotkey. Requires the user to be in theinputgroup; the crate surfaces this as a clear error when/dev/input/event*is inaccessible. run.showns Linux rendering env vars (LIBCLANG_PATH,WEBKIT_DISABLE_DMABUF_RENDERER,GDK_BACKEND=x11on Wayland to dodge a webkit2gtk issue). Anything that pre-checks these inlib.rsis checking what the launcher set, not the user's shell.- CI runs on all three OSes (
.github/workflows/) — Linux/macOS/Windows. macOS and Windows are CI-validated but not runtime-tested; see KNOWN-ISSUES.md for tracked gaps (App Nap on Apple Silicon, idle-inhibit on X11/Linux, sleep prevention on Windows).
Privacy invariants (non-negotiable)
No voice, transcript, or task data leaves the machine unless the user explicitly sends it. No telemetry, no analytics, no crash-reporting service. Cleanup is additive — raw Whisper transcript must always be recoverable. LLM scope is narrow: transcription cleanup + task extraction only. Not a wake-word agent, not a chat UI, not a multi-provider cloud fan-out. If a change risks any of this, surface it before implementing.
v0.1 release scope (active gate)
The v0.1 ship gate lives in docs/release/v0.1-checklist.md; the UI hardening boundary is pinned in docs/release/v0.1-ui-hardening.md. All release docs live under docs/release/ — use that folder as the source of truth. Before adding a feature, check the Out of scope for v0.1 list — reopening a v0.2-flagged item moves the ship date. Garden Inbox, full Settings 7-group regroup, OpenAI Whisper API BYOK, OEM verification, Obsidian plugin, and the Phase B filter-chain refactor are all explicitly v0.2+.
The release acceptance test is the 10-step tester flow (install → onboarding → record → cleanup → task → MicroSteps + timer → history search). Warm activation target: first real recording within 3 minutes of opening the app. UI hardening is a hardening pass, not a redesign — every item must be testable, not aesthetic.
Where to look first
- Latest session context: HANDOVER.md, then dated handovers in docs/handovers/.
- Tracked limitations + per-step failure-mode catalogue: KNOWN-ISSUES.md.
- Per-platform dependency reference: docs/dev-setup.md.
- Product/strategy framing: docs/brief/ — especially
what-lumotia-is.md,design-principles.md,target-audience.md. - Active workstreams + 31-item research backlog: docs/whisper-ecosystem/brief.md,
workstream-A.md,workstream-B.md. - GPU tuning roadmap: docs/gpu-tuning/plan.md.