dropped_chunks was incremented on cpal-callback channel-full and validation requeue overflow but never read by the live session, so the UI's dropped_audio_ms missed callback-level losses entirely. Architecture doc had flagged this as a TODO. Also: the 350ms validation buffer was requeued via try_send into the same 32-slot channel, silently dropping past the cap on small-buffer audio hosts (WASAPI exclusive, low-latency ALSA at 256 frames -> ~65 chunks). Fix: live runtime reads MicrophoneCapture::dropped_chunks() on each recv_audio tick (LiveSessionRuntime::poll_capture_drops) and converts the per-chunk-duration delta into the dropped_audio_ms surfaced to the UI overload status. Per-chunk duration is derived from the most recent AudioChunk's sample_rate + samples-per-channel so it adapts to whatever rate cpal is delivering at. Validation requeue moved from try_send into the bounded channel onto a VecDeque<AudioChunk> returned alongside the Receiver; ActiveCapture drains the replay buffer before reading rx in recv_audio, bypassing the 32-slot cap entirely. Architecture doc updated to remove the TODO and document the new pre-roll path. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
name, type, last_verified
| name | type | last_verified |
|---|---|---|
| Lumotia architecture map | architecture-map-root-index | 2026/05/09 |
Lumotia architecture map
Where you are: Architecture map root. Reading order if this is your first time: read this page top to bottom, pick the slice that matches what you are trying to do, follow the breadcrumb trail.
What Lumotia is in 60 seconds
Lumotia is a local-first dictation desktop app. You press a hotkey, you speak, your speech becomes formatted text on disk and on the clipboard. Everything that touches your voice or your transcript runs on your own machine. Zero telemetry, zero analytics, no calls home unless you turn on auto-update.
It is a Tauri 2 app. The shell is Svelte 5. The brain is a Rust workspace of nine crates. Speech-to-text runs through Whisper (whisper-rs) by default with Parakeet (transcribe-rs ONNX) as an alternative. Optional cleanup runs through a local llama-cpp-2 LLM (Qwen3.5 / 3.6, four-tier model picker). Transcripts live in a SQLite database with a FTS5 full-text index. A standalone lumotia-mcp binary opens that database read-only and serves four tools over the Model Context Protocol so external agents can read your history without write access.
Targets: Linux (primary, Wayland-friendly), macOS, Windows. Android is on the roadmap (a few crate features carry android cfgs).
Brand rename in flight. Lumotia was approved as "Lumenote" on 2026/05/08. The codebase still says "lumotia" and "Lumotia" everywhere. The rename is a separate session.
The three layers
┌─────────────────────────────────────────────────────────────────────┐
│ Frontend (slice 01) │
│ Svelte 5 + SvelteKit. 4 windows, 7 pages, 25 components, 10 stores. │
│ ~17 200 LOC. │
├─────────────────────────────────────────────────────────────────────┤
│ Tauri runtime (slice 02) │
│ src-tauri/. 91 commands across 22 modules + 3 utility modules, │
│ system tray, ACL capabilities, plugins, panic + log + CSP guards. │
├─────────────────────────────────────────────────────────────────────┤
│ Rust workspace │
│ ┌────────────────────┬──────────────────────┬─────────────────────┐ │
│ │ Audio + │ LLM + Formatting + │ Core + Storage + │ │
│ │ Transcription │ MCP + Cloud │ Hotkey + Build │ │
│ │ (slice 03) │ (slice 04) │ (slice 05) │ │
│ │ lumotia-audio │ lumotia-llm │ lumotia-core │ │
│ │ lumotia- │ lumotia- │ lumotia-storage │ │
│ │ transcription │ ai-formatting │ lumotia-hotkey │ │
│ │ │ lumotia-mcp │ workspace + CI │ │
│ │ │ lumotia- │ │ │
│ │ │ cloud-providers │ │ │
│ └────────────────────┴──────────────────────┴─────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
The frontend never touches the filesystem, the audio device, the models, or the LLM directly. Everything that crosses the WebView boundary uses invoke() (calling a Rust command) or Tauri events (one direction or the other). The Rust workspace is the brain. The Tauri layer is the integration surface.
Slices
| # | Slice | Owns | Files |
|---|---|---|---|
| 01 | Frontend | Svelte UI, routes, pages, components, stores, i18n, design system, the WebView side of the bridge | 16 |
| 02 | Tauri runtime | App lifecycle, 91 commands, system tray, ACL, plugins, configs, build.rs | 26 |
| 03 | Audio + Transcription | Microphone capture, resampling, VAD, file decode, Whisper + Parakeet engines, streaming primitives, model manager | 16 |
| 04 | LLM + Formatting + MCP | Llama-cpp engine, four LLM surfaces (cleanup, decompose, extract tasks, content tags), formatting pipeline, MCP server, cloud-provider stubs | 19 |
| 05 | Core + Storage + Hotkey + Build | Shared types, hardware probe, power, thread tuning, model registry, SQLite + FTS5 + 15 migrations, evdev hotkey listener, workspace, CI | 26 |
103 markdown pages in total under this directory.
Data flow
For an end-to-end trace of a single recording session from microphone to FTS5 search to MCP read, see data-flow-end-to-end.md.
"I want to find X"
Cross-cutting concerns and known debt (aggregated 2026/05/09)
Open items surfaced by the slice maps. Each slice's README has its own debt section with the full detail.
Release blockers (gating v0.1)
- RB-08 macOS power assertion (MAJOR, open).
PowerAssertion::beginis a non-functional stub awaiting anobjc2rewrite plus runtime verification on Apple Silicon. Source: 02-tauri-runtime/commands/power-and-security.md. RefHANDOVER.md. PowerAssertionis a no-op on Linux and Windows too (separate from RB-08). Doc comments promise logind inhibitors andSetThreadExecutionState; neither implemented. Long Linux dictation can be idled by the compositor.
Brand rename pending
- The codebase still says "lumotia" and "Lumotia" everywhere. Strings include
"lumotia live dictation session",lumotia_preferencessettings key,lumotia_settingslocalStorage key, prefixed events (lumotia:hotkey-pressed,lumotia:llm-download-progress,lumotia:open-wind-down,lumotia:preferences-changed), the bundle identifieruk.co.corbel.lumotia, every crate name (lumotia-core,lumotia-audio, etc), thelumotia-mcpbinary, the brand asset folders. A rebrand sweep is a single dedicated session.
Bridge surface drift
- Slice 02 registers 91 Tauri commands. Slice 01 invokes 60. The 31 unaccounted commands are likely registered for tests, internal lifecycle, or removed callers. Worth an audit pass.
- Backend events emitted but not listened to (apparent).
runtime-warning,transcription-result,native-pcm,lumotia:hotkey-released. Some may be deprecated, some may be observed only in specific routes. Verify before any cleanup.
Schema drift vs HANDOVER
HANDOVER.md(2026/04/25) quotes schema head v14. Disk head is v15 (idx_transcripts_profile_created). HANDOVER needs a refresh or a session-end note.
Audio pipeline gaps
- VAD is a stub in
lumotia-audiobecause both candidate Silero crates pinort 2.0.0-rc.10while transcribe-rs (Parakeet) needs2.0.0-rc.12. Live capture leans on the energy-basedRmsVadChunkerinlumotia-transcriptionas a working fallback. Two VADs in two crates with no shared code or thresholds. - Streaming primitives (
RmsVadChunker,LocalAgreement,trim_buffer_to_commit_point) are unit-tested but not yet wired intosrc-tauri/src/commands/live.rs. Comments instreaming/mod.rs,buffer_trim.rs,commit_policy.rsflag the integration as a follow-up.
LLM and formatting
- No prompt versioning. No
PROMPT_VERSIONconstant. Cached cleanup output will silently drift across prompt revisions. - Trivial-true GPU cross-check (commit
052265b).gpu_offloadedderived fromuse_gpu && gpu_layers >= n_layeris provably true today becausegpu_layers = u32::MAXwheneveruse_gpuis set. Real residency observability needs llama.cpp log parsing. - MAX_CONTEXT_TOKENS is a process-wide cap of 8192 regardless of model tier. The 27B tier has a much larger native context that is never advertised.
LUMOTIA_LLM_TEST_MODELenv-var gate is the only thing keeping the LLM smoke tests honest. Worth documenting in a contributor note.- British English regex misses
-ingforms of-izeverbs (e.g.organizingslips through unconverted). - Multi-token phrase repetition not detected by the anti-hallucination filter. Five repetitions of
"thank you"does not trigger.
MCP and security
- MCP has no auth. Stdio is the trust boundary. Any future HTTP / SSE transport needs an auth layer before it ships.
lumotia-cloud-providersis empty scaffolding. In-memory keystore only. API keys vanish on restart. Documented TODO forkeyringmigration.
Frontend debt
SettingsPage.svelteis 2 484 LOC and Phase 9c handover already deferred the seven-group restructure plus search box.- Dual theme system.
settings.theme("Light" / "Dark" / "System") andpreferences.theme("light" / "dark" / "system") coexist with a migration$effectrunning every cycle. @ts-nocheckon six files including+layout.svelteand four pages.- Triple transcript font-size knobs are in tension and likely drifted.
- i18n is a stub. Each locale carries 19 lines.
- Two font directories (
src/fonts/andstatic/fonts/) ship the same five woff2 files. - Default SvelteKit demo assets (
static/svelte.svg,tauri.svg,vite.svg) likely unused. - Dead
profilesroute. Rewritten tosettingsat+page.svelte:13.
Tauri runtime debt
commands::liveis 1 737 LOC in one file. Splitting per concern (runtime, speech gate, dedup, chunker, progressive WAV) is overdue.build.rslink arg--allow-multiple-definitionmasks a real ggml duplication issue betweenwhisper-rs-sysandllama-cpp-sys-2. Documented as INTERIM.- Updater stubs ship disabled.
install_updatereturns "Updates are disabled until release signing is configured." Matchingtauri.conf.jsonplugins.updaterblock is absent. - Production crash dumps lack stack traces because
RUST_BACKTRACEis not set in the packaged binary and the panic hook does not capture a backtrace. commands::tasks::decompose_and_storeandextract_tasks_from_transcript_cmdlackPowerAssertion::begindespite running multi-second LLM inference.commands::llm::cleanup_transcript_text_cmdhas it; tasks should too.commands::transcribe_pcmdoes not callensure_model_loadedunlike its file sibling. Asymmetry could panic if invoked before a load step.tauri-plugin-cloud-providerscrate is depended-on inCargo.tomlbut no command file imports it. Reserved for future upload flow.parallel_mode_availablehard-wired tofalseincommands/models.rs:509. Phase A.4 marker for unblocking.
Build / CI / dev glue
run.shhard-codesLIBCLANG_PATH=/usr/lib64/llvm21/lib64. Fedora 41 default. Breaks on Debian / Ubuntu / macOS without override.src-tauri/.cargo/config.tomlhard-codesLIBCLANG_PATH = "C:\\Program Files\\LLVM\\bin"for Windows.- No code signing. Apple and Windows signing env blocks are commented out. First-run users hit Gatekeeper / SmartScreen.
tauri-action@v0is unpinned. Floats. Pin before v0.1.pcm-processor.jshard-codes16000and8000which must move in lock-step withlumotia_core::constants::{WHISPER_SAMPLE_RATE, MIN_CHUNK_SAMPLES}.uuidCargo feature isv4despite the storageCargo.tomlcomment claiming "v7 random". Cosmetic but worth cleaning up.- GPU probe is a stub.
lumotia_core::hardware::probe_gpu()returnsNone; no NVIDIA / AMD / Intel / Apple discrimination at runtime. - Power probe is Linux-only. macOS and Windows return
PowerState::Unknown, which callers treat asOnAc.
How this map was generated
Five subagents ran in parallel on 2026/05/09, one per slice. Each read the code under its slice, wrote breadcrumbed markdown with file:line refs, returned an executive summary, a cross-slice dependency list, a public-surface inventory, and a debt list. This master page is the synthesis. The data-flow trace is a separate page that follows the audio bytes from microphone to FTS5 search to MCP read.
Refresh history
- 2026/05/09 — initial generation. Five slices, 103 pages. Generated against codebase head per
git logat the time of capture (most recent commit3c47000 chore(core): add tuning_log_demo example for live heuristic validation).
When to refresh
Re-run the five-slice generation when any of these is true:
- A new Rust crate is added or removed from the workspace.
- A new top-level frontend route or window is added.
- The schema head moves by more than one migration.
- A new MCP tool ships.
- A new top-level Tauri plugin is wired.
- Any slice's debt section has been materially knocked down.
- Brand rename lands (a sweep across every page is needed).
The procedure for a refresh is in /home/jake/.claude/projects/-home-jake-Documents-CORBEL-Main/memory/ — search for "architecture-map".