Files
Lumotia/KNOWN-ISSUES.md
Jake baab7b3c12 docs: KI-07 — llama-cpp-2 v0.1.146 panics on Qwen3.5+ FGDN tensors
Discovered while spinning up a Tauri dev session on v0.3 for live UI
review. The app aborts on startup the moment DictationPage mounts and
calls load_llm_model — llama.cpp's GGML_ASSERT on the Fused Gated Delta
Net tensor-name prefix fires before the WebView paints, taking the
parent process with it. All four configured model variants
(Qwen3.5 2B/4B/9B + Qwen3.6 27B) share the architecture, so picking
a different size does not work around it.

Adds a new LLM section with KI-07 (Status / Source / Impact / Workarounds).
Four ordered workarounds, cheapest first:
  1. Park the .gguf file — autoload bails, non-LLM features work
  2. Set aiTier = "off" in lumotia_preferences before launch
  3. Bump llama-cpp-2 to a release with the upstream FGDN fix
  4. Add a non-FGDN model variant to LlmModelId

(1) unblocked tonight's live review of the Phase 5f polish. (3) is the
real resolution.
2026-05-15 20:38:01 +01:00

11 KiB

Known issues

Tracked limitations and partial implementations in the current codebase. Each entry points at the code that owns the gap so future work can find it.

Power management

KI-01 — macOS App Nap guard pending Apple Silicon runtime verification

Status: macOS code path compiles and the registry tests pass, but runtime behaviour against actual OS idle-throttling has not been confirmed on Apple Silicon hardware. Also tracked internally as RB-08.

Source: src-tauri/src/commands/power.rs:73 (PowerAssertion::begin), src-tauri/src/commands/power.rs:140 (objc_bridge).

Impact: Long live-dictation sessions on macOS may still be slowed or paused by the OS until verified end-to-end on hardware.

Workaround: Keep the app window focused, or move the cursor periodically. For testing, App Nap can be disabled globally with defaults write NSGlobalDomain NSAppSleepDisabled -bool YES (revert with -bool NO).

KI-02 — Linux idle inhibit ✓ fixed in v0.1

Status: Resolved. acquire_idle_inhibit now calls org.freedesktop.login1.Manager.Inhibit via zbus (blocking, offloaded to spawn_blocking) on recording start, holding the returned file descriptor. release_idle_inhibit closes the fd on recording stop, which atomically releases the lock. The inhibit scope is idle:sleep:handle-lid-switch in block mode.

If D-Bus is unavailable (non-systemd containers, exotic distros), the call fails gracefully with a tracing::warn! and recording continues — the workaround below remains valid in those edge cases.

Source: src-tauri/src/commands/power.rs (acquire_idle_inhibit, release_idle_inhibit, linux_inhibit mod).

Workaround (edge cases only): Wrap launch with systemd-inhibit --what=idle:sleep:handle-lid-switch ./run.sh if logind is not available.

KI-03 — Windows sleep prevention ✓ fixed in v0.1

Status: Resolved. acquire_idle_inhibit now calls SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED) on recording start. release_idle_inhibit calls SetThreadExecutionState(ES_CONTINUOUS) to restore normal behaviour. Display sleep is intentionally NOT blocked — the user is dictating, not watching the screen.

Source: src-tauri/src/commands/power.rs (acquire_idle_inhibit, release_idle_inhibit, windows_inhibit mod).

Workaround (edge cases only): If the power plan has a policy override that blocks SetThreadExecutionState, set the active sleep timeout to "Never" while dictating.

Cloud providers

KI-04 — lumotia-cloud-providers crate is not user-exposed

Status: The crate is a declared workspace dependency in src-tauri/Cargo.toml:36 and compiles into the binary, but no Tauri command, page, or settings field invokes store_api_key / retrieve_api_key. There is no UI to enter or store a cloud API key in the current build.

Source: crates/cloud-providers/src/keystore.rs. The TODO in the doc comment captures the planned migration to OS-native credential storage (the keyring crate or equivalent) before the feature is wired up.

Impact: None for end users today (no exposed surface). When the feature is wired post-launch, cross-restart persistence must land before any "save key" UX ships, otherwise saved keys reset on every restart.

Workaround: N/A.

Frontend

KI-05 — Dual theme system: settings.theme and preferences.theme coexist

Status: Theme is stored twice. The legacy settings.theme ("Dark" / "Light" / "System", localStorage-only) is bound to two SegmentedButton controls in SettingsPage.svelte. The canonical preferences.theme ("dark" / "light" / "system", Tauri-persisted via save_preferences) is what the DOM and runtime actually consume. A migration $effect in src/routes/+layout.svelte (and the secondary +layout@.svelte files for /preview, /viewer, /float) syncs settings.themepreferences.theme whenever the legacy value changes.

Source:

Impact: No user-facing bug; theme switching works. The cost is code complexity and a small race window where preference fan-out can lag behind a settings.theme change. The $effect is lightweight (string compare plus a debounced persist when the mapped value differs), not a hot-loop performance problem despite earlier framing.

Resolution (deferred): Switch the two SettingsPage bindings to preferences.theme with mapped options, retire the migration $effect in all four route layouts, drop theme from SettingsState, and add a one-shot localStorage migration that copies any historical settings.theme into preferences on first run after the cleanup. Touches ~5 files including the 2 484-LOC SettingsPage.svelte; deferred from the v0.1 polish pass to avoid a moderate-risk frontend change immediately before launch.

Workaround: N/A.

Engine architecture (Phase A)

KI-06 — transcribe_file and live-transcription commands not yet rewired through Orchestrator

Status: Phase A introduced the TranscriptionProvider async trait (crates/cloud-providers/src/provider.rs), EngineRegistry (crates/transcription/src/registry.rs), and Orchestrator plus LocalProviderAdapter (crates/transcription/src/orchestrator.rs). The abstractions compile, are object-safe, and pass unit tests against a mock provider. The existing dictation path (src-tauri/src/commands/transcription.rs) still calls LocalEngine::transcribe_sync directly via pick_engine, not through the orchestrator. Live and meeting commands likewise route around the orchestrator.

Source: src-tauri/src/commands/transcription.rs:29 (pick_engine), src-tauri/src/commands/live.rs, src-tauri/src/commands/meeting.rs.

Impact: None at runtime. The orchestrator path is dormant until a follow-up commit migrates the call sites. Cloud providers (Phase G) cannot be exercised end-to-end until this rewire lands.

Resolution (deferred): Phase A.1 follow-up commit. Build an EngineRegistry at app boot (one LocalProviderAdapter per LocalEngine), inject Arc<Orchestrator> into AppState, replace pick_engine plus engine.transcribe_sync chunking-loop calls with orchestrator.transcribe(audio, &profile) per chunk. Keep the chunking strategy in the command layer (it is a strategy concern, not a dispatch concern). Tests should cover both paths against a mock provider before touching the real engines.

Workaround: N/A. Existing path works as before.

LLM

KI-07 — llama-cpp-2 v0.1.146 panics on Qwen3.5+ Fused Gated Delta Net (FGDN) tensors

Status: Loading any of the four configured model variants (Qwen3_5_2B_Q4, Qwen3_5_4B_Q4, Qwen3_5_9B_Q4, Qwen3_6_27B_Q4) at startup aborts the whole process with a GGML assert inside the bundled llama.cpp:

llama-cpp-sys-2-0.1.146/llama.cpp/src/llama-context.cpp:487:
GGML_ASSERT(strncmp(n->name, LLAMA_TENSOR_NAME_FGDN_AR "-", prefix_len) == 0) failed

The assert fires during llama_context construction while resolving Fused Gated Delta Net support, which is the recurrent / Mamba-style attention mechanism Qwen3.5+ uses. llama-cpp-2 v0.1.146 bundles a llama.cpp snapshot whose FGDN tensor-name prefix check rejects tensors as named in current Qwen3.5 GGUFs. Because the assert is in C++ via ggml, the panic kills the parent Tauri process — Tauri's crash handler then attaches GDB, which is what surfaces in the dev log as a stack trace ending in tao::event_loop rather than as a Rust panic.

All four enum variants share the same architecture family, so picking a different size does not work around it.

Source:

Impact: App will not start in dev (and presumably release) on any machine that has a Qwen3.5+ .gguf present and settings.aiTier !== "off". There is no UI to recover — the crash precedes the WebView paint, so the user cannot reach Settings to disable AI features or swap the model. Affects every code path that exercises the LLM (transcript cleanup, task extraction, micro-step decomposition).

Workarounds (in order of cost):

  1. Park the model file so check_llm_model returns downloaded: false and autoload silently bails:
    mv ~/.local/share/lumotia/models/llm/Qwen3.5-4B-Q4_K_M.gguf{,.crash-parked}
    
    Non-LLM features (dictation, transcript history, file imports) work normally. Reverse with mv …{.crash-parked,}.
  2. Set aiTier = "off" in lumotia_preferences before launch (also bypasses the autoload guard).
  3. Bump llama-cpp-2 to a release that includes the upstream llama.cpp FGDN fix. Track upstream PRs at https://github.com/utilityai/llama-cpp-rs/releases; verify the bundled llama.cpp SHA covers Gated Delta Net tensor-name handling before adopting.
  4. Add a non-FGDN model variant to LlmModelId (e.g. Llama 3.2 3B Q4 — well-supported in 0.1.146 — or Qwen2.5 3B) and ship a Settings UI to switch to it. Larger change because the enum, the on-disk URL/size constants, and the Settings model picker all need entries.

Resolution (deferred): Option 3 is the real fix and matches the spirit of crates/llm (Qwen3.5+ explicitly listed in the crate description). Option 4 is the right fallback if upstream is slow — having one non-FGDN model on the enum prevents the dev-blocker class of crash entirely.

How to add an entry

When shipping a partial implementation or known limitation, add a KI-NN entry here with the four standard fields: Status, Source (file:line), Impact, Workaround. Link from the affected module's doc comment back to this file by ID.