From 2a45cb8033b351ec5156091f2279453e826b81a1 Mon Sep 17 00:00:00 2001 From: jars Date: Sat, 9 May 2026 15:47:48 +0100 Subject: [PATCH] fix: model-loaded guards on transcribe_pcm, power assertion guards on tasks LLM commands transcribe_pcm and transcribe_pcm_parakeet did not check that their respective engines were loaded before clone+spawn_blocking. transcribe_file already calls ensure_model_loaded; these now mirror that posture with a friendly error when the engine is unloaded, matching what extract_content_tags_cmd does. decompose_and_store and extract_tasks_from_transcript_cmd ran multi-second LLM inference inside spawn_blocking without the PowerAssertion guard that cleanup_transcript_text_cmd and extract_content_tags_cmd already use. Both now begin a guard so the macOS App-Nap inhibitor (and the planned Linux/Windows equivalents per KI-02, KI-03) can pin the process for the duration of the inference. HANDOVER.md gets a status note clarifying it captures Phase 9 state. The schema head referenced (v14) was the head at session time; current head is v15 (`idx_transcripts_profile_created` composite index) per the architecture map. Co-Authored-By: Claude Opus 4.7 (1M context) --- HANDOVER.md | 2 ++ src-tauri/src/commands/tasks.rs | 13 +++++++++---- src-tauri/src/commands/transcription.rs | 10 ++++++++++ 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/HANDOVER.md b/HANDOVER.md index b7b396f..ce9718b 100644 --- a/HANDOVER.md +++ b/HANDOVER.md @@ -7,6 +7,8 @@ description: Session handover — 2026/04/24-25 Phase 9 polish debt mostly shipp # Magnotia Handover — 2026/04/25 +> **Note:** Session-specific handover. Migration v14 (`transcripts.llm_tags`) was the head **at the time of this session**. Subsequent work has advanced the schema; current head is v15 (`idx_transcripts_profile_created`, composite index). For the current codebase shape, see [`docs/architecture-map/`](docs/architecture-map/) and [`KNOWN-ISSUES.md`](KNOWN-ISSUES.md). + Phase 9 session. Spec + plan written from scratch and committed; plan corrections layered in after critical review against the actual codebase (Codex was unreachable for cross-model review, three retries failed at the ChatGPT-account-entitlement layer). Sub-phases 9a + 9b + sparkline polish landed end to end. Sub-phase 9c reduced to the Phase 8 carryover bug fix; sub-phase 9d's walkthrough sweeps deferred to Phase 10a QC. ## Rebrand note diff --git a/src-tauri/src/commands/tasks.rs b/src-tauri/src/commands/tasks.rs index 5c2f7a6..5f67201 100644 --- a/src-tauri/src/commands/tasks.rs +++ b/src-tauri/src/commands/tasks.rs @@ -18,6 +18,7 @@ use magnotia_storage::{ FeedbackRow, FeedbackTargetType, TaskRow, }; +use crate::commands::power::PowerAssertion; use crate::AppState; /// Frontend-facing task shape. Matches the in-memory object in page.svelte.js. @@ -319,6 +320,7 @@ pub async fn decompose_and_store( let engine = state.llm_engine.clone(); let parent_text = parent.text.clone(); let steps = tokio::task::spawn_blocking(move || { + let _power_guard = PowerAssertion::begin("magnotia LLM micro-step decomposition"); engine.decompose_task_with_feedback(&parent_text, &examples) }) .await @@ -361,10 +363,13 @@ pub async fn extract_tasks_from_transcript_cmd( .unwrap_or_default(); let engine = state.llm_engine.clone(); - tokio::task::spawn_blocking(move || engine.extract_tasks_with_feedback(&transcript, &examples)) - .await - .map_err(|e| e.to_string())? - .map_err(|e| e.to_string()) + tokio::task::spawn_blocking(move || { + let _power_guard = PowerAssertion::begin("magnotia LLM task extraction"); + engine.extract_tasks_with_feedback(&transcript, &examples) + }) + .await + .map_err(|e| e.to_string())? + .map_err(|e| e.to_string()) } #[tauri::command] diff --git a/src-tauri/src/commands/transcription.rs b/src-tauri/src/commands/transcription.rs index ec7720a..afd0b6a 100644 --- a/src-tauri/src/commands/transcription.rs +++ b/src-tauri/src/commands/transcription.rs @@ -154,6 +154,11 @@ pub async fn transcribe_pcm( profile_id: Option, ) -> Result<(), String> { ensure_main_window(&window)?; + if !state.whisper_engine.is_loaded() { + return Err( + "Whisper model not loaded. Download a Whisper model in Settings.".to_string(), + ); + } let resolved_profile_id = profile_id.unwrap_or_else(|| magnotia_storage::DEFAULT_PROFILE_ID.to_string()); @@ -352,6 +357,11 @@ pub async fn transcribe_pcm_parakeet( profile_id: Option, ) -> Result<(), String> { ensure_main_window(&window)?; + if !state.parakeet_engine.is_loaded() { + return Err( + "Parakeet model not loaded. Enable Parakeet in Settings.".to_string(), + ); + } let resolved_profile_id = profile_id.unwrap_or_else(|| magnotia_storage::DEFAULT_PROFILE_ID.to_string());