Phase 6 of the rebrand cascade per locked decision D2.
src-tauri/tauri.conf.json:
- productName: "Magnotia" -> "Lumotia"
- identifier: "uk.co.corbel.magnotia" -> "consulting.corbel.lumotia"
- window title: "Magnotia" -> "Lumotia"
D2 picks the reverse-DNS of corbel.consulting (the actual domain CORBEL
trades under) over the prior uk.co.corbel.* convention. This is the
identity the OS uses for installed-app keying, so the first launch under
the new identifier will look fresh to Tauri's plugin state (window-state,
autostart). Per D1 the user-data dir is migrated by lumotia_core::paths
on first boot, so transcripts and settings survive.
cargo build --workspace passes. cargo test --workspace: 339 pass, 0 fail.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase 5 QC found two blockers + four advisories. All addressed:
B1 (FATAL) — Migration error now aborts startup instead of silently
continuing past it. Without this fix a transient EACCES / EXDEV / ENOSPC
would log a warning, init_db would create a fresh empty lumotia dir,
and the user would appear to lose their transcripts.
B2 (FATAL) — Linux dot-home vs XDG mismatch. The old probe returned
~/.magnotia as legacy but the caller passed app_data_dir() as the new
path — which could be $XDG_DATA_HOME/lumotia. fs::rename across
filesystems would EXDEV-fail; even when it succeeded the user's
storage convention silently changed.
Refactored: legacy_and_target_paths() returns the (legacy, target)
pair together. Dot-home legacy lands in ~/.lumotia; XDG-set legacy
lands in $XDG_DATA_HOME/lumotia; XDG-default legacy lands in
~/.local/share/lumotia. macOS / Windows / non-tier-1 unchanged.
migrate_legacy_data_dir() now takes no argument; src-tauri caller
updated.
A1 — Removed dead new_db.exists() check inside rename_db_file_if_present
(unreachable: rename_db is called only AFTER the legacy dir was just
renamed to the new path, so a stray lumotia.db there is impossible).
A2 — Added 4 unit tests for migrate_legacy_setting_keys: lone-magnotia,
both-present (orphan delete), no-magnotia, idempotent.
A3 — migrate_legacy_setting_keys now returns (renamed, orphans_deleted).
When both keys exist, the legacy magnotia row is DELETED in the same
transaction (lumotia row is authoritative).
A4 — Added dot-home convention regression test in paths::tests.
cargo build --workspace passes. cargo test --workspace: 339 pass, 0 fail
(up from 334 in the original Phase 5 commit; +5 new tests).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase 5 of the rebrand cascade per locked decision D1 (migrate in place).
crates/core/src/paths.rs:
- Hardcoded subdir strings renamed: magnotia/Magnotia -> lumotia/Lumotia
across all four OS branches (Linux XDG + dot-legacy, macOS Application
Support, Windows LOCALAPPDATA, fallback dot-dir).
- Database filename: magnotia.db -> lumotia.db.
- Test path fixtures renamed: /tmp/magnotia-test -> /tmp/lumotia-test.
- New MigrationStatus enum (Migrated / TargetAlreadyExists / NoLegacyFound).
- New migrate_legacy_data_dir() that probes the platform-correct legacy
magnotia path, renames it to the lumotia equivalent via fs::rename, and
also renames magnotia.db -> lumotia.db inside if found. Idempotent: safe
to call on every boot. Refuses to overwrite an existing lumotia dir to
protect user data.
- Four new unit tests covering all branches via the test-friendly
migrate_legacy_data_dir_inner that takes an explicit legacy path.
crates/storage/src/database.rs:
- New migrate_legacy_setting_keys(pool) that renames any settings rows
with key matching magnotia_* to lumotia_*. Single SQL UPDATE with
NOT EXISTS guard so it leaves rows alone if a lumotia_ row already
exists. Re-exported from lib.rs.
src-tauri/src/lib.rs:
- Calls migrate_legacy_data_dir(&app_data_dir()) at the start of setup()
BEFORE database_path() resolves the now-renamed dir. Logs migration
outcome to lumotia_startup tracing target.
- Calls migrate_legacy_setting_keys(&db) immediately after init_db
returns. Logs only when rows are actually renamed.
src-tauri/src/{lib,commands/{diagnostics,rituals,transcripts}}.rs +
crates/storage/src/database.rs:
- All in-code references to magnotia_preferences, magnotia_history (in
comments), and magnotia_morning_triage_last_shown renamed to lumotia_*.
cargo build --workspace passes. cargo test --workspace: 334 pass, 0 fail
(up from 330; +4 paths::tests).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase 4 of the rebrand cascade. Updates the default RUST_LOG filter
in src-tauri/src/lib.rs and all 'target: "magnotia_startup"' string
literals across src-tauri/src/lib.rs and src-tauri/src/commands/models.rs.
Default filter (before):
warn,magnotia=info,lumotia_lib=info,lumotia_audio=info,
magnotia_startup=info,lumotia_hotkey=info,lumotia_ai_formatting=info
Default filter (after):
warn,lumotia=info,lumotia_lib=info,lumotia_core=info,
lumotia_audio=info,lumotia_hotkey=info,lumotia_ai_formatting=info,
lumotia_llm=info,lumotia_storage=info,lumotia_transcription=info,
lumotia_startup=info
magnotia_startup was a logical tracing target (not a crate); renamed
to lumotia_startup for consistency. magnotia_lib was already renamed
to lumotia_lib in Phase 2. Added per-crate filter entries for parity
across the workspace.
cargo build --workspace passes. cargo test --workspace: 330 pass, 0 fail.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase 3 of the rebrand cascade per locked decision D4. MagnotiaError ->
Error in crates/core/src/error.rs (the crate name already qualifies it).
92 usages across 14 .rs files renamed via word-boundary sed.
One collision required disambiguation: lumotia_storage already had its
own local Error type (introduced by the slop-pass Area A residuals work).
crates/storage/src/error.rs aliases the imported core error as CoreError
on import; the From<Error> for CoreError boundary impl and the
CoreError::Storage construction site use the alias.
cargo build --workspace passes. cargo test --workspace: 330 pass, 0 fail.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase 0 QC found two real blockers in the baseline commits:
1. crates/llm/src/lib.rs:215 — early-break guard was inverted. The
guard fired when grammar.is_some() (where grammar already enforces
shape, making the check redundant) and was disabled for
grammar.is_none() (the content-tags path that actually needs it).
Flipped to grammar.is_none() so the JSON-envelope short-circuit
fires for free-form JSON generation as the commit message
implied.
2. src/lib/pages/FilesPage.svelte — handleExport() success path had
no toast, asymmetric with DictationPage which does. Added the
toasts import and a toasts.success() call after the native save
so both export surfaces give consistent feedback.
cargo test -p magnotia-llm --lib: 21 pass. npm run check: 0 errors.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
DictationPage + FilesPage handleExport() now use Tauri save() +
write_text_file_cmd when in the desktop runtime; browser blob path
remains as fallback when hasTauriRuntime() is false. saveMarkdown.ts
surfaces dialog errors via toasts. Adds txt to the extension map.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
extract_content_tags now generates with grammar=None and parses the
response via a manual brace-counting JSON envelope extractor that
handles Qwen <think>...</think> prefixes and trailing stop tokens.
Five new unit tests. Bumps llama-cpp-2 to 0.1.146. Explicit
features=[] on tauri dependency (no-op).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Smoke checks (native window, Settings, Record, no error toasts) PASS on
the post-residuals-B/tracing/Area-A stack. Hermes session crashed
before the longer-form dogfood pass (transcribe + LLM cleanup + tagging)
could run; the LLM tagging crash from 2026/05/10 remains the
outstanding release-blocker. Tracing pipeline confirmed working under
the new default filter.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Commit 52565ea migrated storage to magnotia_storage::Error and flattened
typed storage failures into MagnotiaError::Storage { kind, operation,
detail }. No production code constructs the old
MagnotiaError::StorageError String variant anymore.
Remove the legacy variant so new storage failures cannot regress back to
stringly-typed errors.
Also updates living architecture-map docs that referenced the old
variant (core-error.md variant table, storage-overview.md
SQLITE_BUSY note, storage-crud-profiles.md duplicate-name + default-
profile-rename notes, storage-crud-transcripts.md pre-flight FK check
note) and one stale code comment in crates/storage/src/database.rs's
duplicate-name test. Survey doc + old residuals plan + phase8 historical
plan deliberately left alone — they're audit trail of how the migration
was decided, not living docs.
Pre-existing doc rot flagged but not fixed (Other(String) and
Io(std::io::Error) rows in core-error.md are about variants that
already don't match the actual enum shape — separate doc cleanup pass).
Verification:
- cargo fmt --all -- --check
- cargo check -p magnotia-core
- cargo check -p magnotia-storage
- cargo check --workspace --all-targets
- cargo test -p magnotia-storage — 60 passed, 0 failed
- cargo test --workspace --lib — all green
- rg 'StorageError\(' crates/ src-tauri/src/ — zero hits
- rg 'StorageError' crates/ src-tauri/src/ docs/architecture-map/ — zero
- rg 'Other\(String\)' crates/ src-tauri/src/ — zero
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Commit 1 of 2 for engine-slop residuals Area A.
Adds a typed `magnotia_storage::Error` enum and rewires every error
construction site in the storage crate to use it. The crate boundary
into `magnotia_core::error::MagnotiaError` is handled by a
`From<storage::Error> for MagnotiaError` impl that lives inside the
storage crate (not in core) to avoid a `core -> storage` dependency
cycle. The old `MagnotiaError::StorageError(String)` variant is kept
in this commit as an unused placeholder; commit 2 deletes it.
New types in crates/storage/src/error.rs:
- pub enum Error { DatabaseOpen, Migration, Query, NotFound,
InvalidReference, Filesystem } with #[derive(thiserror::Error)]
- pub enum OpenOp { Connect, ReadOnlyConnect, ForeignKeysPragma }
- pub enum MigrationStep { SchemaVersionTableCreate, SchemaVersionQuery,
TxBegin, Apply, RecordVersion, Commit }
- pub enum Entity { Transcript, Task, Profile, ImplementationRule,
Feedback } — seeded only with entities that have a real
NotFound/InvalidReference case in the codebase
- pub type Result<T> = std::result::Result<T, Error>
- impl Error { pub fn kind() -> StorageKind, pub fn
operation_label() -> Cow<'static, str> }
- impl From<Error> for MagnotiaError
New types in crates/core/src/error.rs:
- MagnotiaError::Storage { kind: StorageKind, operation: String,
detail: String } with #[error("{detail}")] so the boundary doesn't
double-prefix Display output
- pub enum StorageKind { DatabaseOpen, Migration, Query, NotFound,
InvalidReference, Filesystem } #[derive(Serialize)]
#[serde(rename_all = "snake_case")] for future Area E
Storage crate dependency added: thiserror = "1"
Migration scope:
- migrations.rs: 6 sites → Error::Migration with structured step + version
- database.rs: 72 sites broken down as:
* 3 → Error::DatabaseOpen (init / readonly / pragma)
* 1 → Error::Filesystem (init's create_dir_all with path context)
* ~58 → Error::Query with operation labels matching the prior message
stem in snake_case; transaction sub-steps use dotted labels
("complete_subtask_and_check_parent.commit_transaction" etc.)
so transaction internals are not collapsed
* 5 → Error::NotFound (transcript / task×2 / implementation_rule×2)
* 5 → Error::InvalidReference (insert_transcript unknown profile;
Default profile rename/delete invariants ×3; feedback rating
value validation)
Survey ambiguities resolved per the locked answers:
- Q1 (schema_version_query): Migration step, not Query
- Q2 (transaction sub-steps): preserved granularity with dotted operation
labels; no collapsing
- Q3 (Entity cardinality): seeded with the 3 from the survey + 2 more
discovered during migration (ImplementationRule, Feedback), per
"add when an actual case needs it"
- Q4 (operation label type): Cow<'static, str>
- Q5 (Serialize): storage::Error is not serializable; flattens only at
the MagnotiaError boundary
- Q6 (re-exports): pub use error::{Entity, Error, MigrationStep, OpenOp,
Result} in storage::lib.rs; StorageKind belongs to magnotia_core
Two scope-discovered additions beyond the original 5 variants:
- Error::Filesystem { path, source } variant + matching StorageKind —
required because init() now returns storage::Result<SqlitePool> and
the create_dir_all call needs a typed path; doing this via
From<io::Error> for storage::Error would have lost the path so it's
explicit
- Entity::ImplementationRule and Entity::Feedback — two NotFound /
InvalidReference sites the original survey missed in the rule-CRUD
and feedback-validation areas
Verification:
- cargo fmt --all
- cargo check --workspace --all-targets — clean
- cargo test -p magnotia-storage — 60 passed, 0 failed
- cargo test --workspace — all green, ~330 tests
- rg 'StorageError\(' crates/ src-tauri/src/ — only hit is the variant
declaration in core that commit 2 will delete
- rg 'Other\(String' crates/storage/src/ — zero
- rg 'format!\("[A-Z].* failed' crates/storage/src/ — zero
Commit 2 will delete MagnotiaError::StorageError(String) once this is
in. Pre-existing working-tree churn in crates/llm/, src/lib/pages/,
src/lib/utils/saveMarkdown.ts and the untracked phase10a dogfood notes
deliberately left unstaged.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pre-migration survey for engine-slop residuals Area A. Catalogues every
MagnotiaError::StorageError construction site in the storage crate
(78 total across database.rs and migrations.rs), groups by failure
mode, proposes a typed magnotia_storage::Error enum with a serde-
friendly MagnotiaError::Storage { kind, operation, detail } variant on
top, and flags 6 ambiguities + 3 migration risk classes.
Key findings the residuals plan did not capture:
- StorageError is a String variant on MagnotiaError, not a separate
enum — there is nothing called StorageError in the storage crate.
- Actual site count is 78, not the ~25 the residuals plan estimated.
- Tauri commands stringify every error before crossing into the
frontend (Result<T, String>), so MagnotiaError's Serialize impl is
presently unused at the FE/BE boundary. Area E owns that fix.
- Zero Other(String) sites in storage — already cleaned in db654de.
No code changes. Migration awaits decisions on the 6 ambiguous cases
in §"Ambiguous cases / decisions needed".
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Follow-up to 184214b. The eprintln→tracing sweep surfaced 6 existing
log::* calls in the hotkey crate that were silent at runtime — the
workspace builds tracing-subscriber without the tracing-log feature, so
log:: events never reached the tracing pipeline.
Migrating direct rather than adding a LogTracer bridge: the repo is
already standardising on tracing, the bridge adds a second logger init
path with "already initialised" edge cases, and the bridge would
indiscriminately route all log records (including future third-party
chatter), not just these known sites.
Migrated (6 sites, 2 files):
- crates/hotkey/src/linux.rs (5) — read /dev/input error, device open
debug, device-attached info, device-listener-ended warn, event-channel-
closed warn
- crates/hotkey/src/stub.rs (1) — non-Linux no-op info
Also removed the now-unused log = "0.4" dependency from
crates/hotkey/Cargo.toml. magnotia_hotkey=info filter target was
already added to init_tracing in 184214b, so these events emit at the
default level immediately.
Verification:
- cargo fmt --all -- --check
- cargo check -p magnotia-hotkey — clean
- cargo check -p magnotia — clean
- rg 'log::|eprintln!' crates/hotkey/src/ src-tauri/src/commands/ crates/ai-formatting/src/ — zero hits
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces 22 production eprintln! sites with structured tracing events
across 8 files. Closes Area B of the post-prognosis residuals plan
(docs/superpowers/plans/2026-05-12-engine-slop-residuals.md).
Files touched (22 sites):
- crates/hotkey/src/linux.rs (2) — hotplug watcher degraded-mode warnings
- crates/ai-formatting/src/pipeline.rs (1) — LLM cleanup fallback warning
- src-tauri/src/commands/transcription.rs (1) — chunking dispatch info
- src-tauri/src/commands/diagnostics.rs (1) — crashes-dir setup warning
- src-tauri/src/commands/tasks.rs (1) — malformed feedback row warning
- src-tauri/src/commands/power.rs (3) — App Nap acquire/release/fail
- src-tauri/src/commands/models.rs (5) — Whisper warmup lifecycle
- src-tauri/src/commands/live.rs (8) — session start, chunk dispatch,
per-chunk delivery, inference errors, worker disconnects, listener
loss, status-channel cascade
Levels: error for unrecoverable failures (inference disconnect, panic,
status cascade), warn for recoverable degradation (LLM fallback,
malformed rows, App Nap fail, hotplug watcher fail), info for lifecycle
(session start, chunk processed, App Nap acquire/release, warmup
complete, chunking dispatch), debug for per-chunk noise (speech-gate
skip, chunk dispatch).
Two new dependencies and two new filter targets:
- tracing = "0.1" added to crates/hotkey and crates/ai-formatting
- Default EnvFilter in src-tauri/src/lib.rs::init_tracing extended with
magnotia_hotkey=info,magnotia_ai_formatting=info so the new targets
emit at the default level
Out of scope (intentional, left as-is):
- crates/mcp/src/main.rs — CLI binary, stderr is the log contract
(module docstring) so the JSON-RPC stdout stream stays clean
- crates/*/tests/*.rs and crates/core/examples/tuning_log_demo.rs —
test/example diagnostic output relies on --nocapture stdio semantics
Discovery during sweep (not fixed — separate follow-up): hotkey crate
has 6 existing log:: calls (log::error/warn/info/debug) but the
workspace builds tracing-subscriber without the tracing-log feature, so
those events are currently silent. Worth a follow-up to either add the
tracing-log bridge or migrate hotkey's existing log:: calls to
tracing::.
Verification:
- cargo fmt --all
- cargo check --workspace --all-targets — clean
- cargo test --workspace — 330+ tests, zero failures
- rg eprintln! src-tauri/src/commands/ crates/hotkey/src/ crates/ai-formatting/src/ — zero hits
Pre-existing working-tree churn in crates/llm/, src/lib/pages/,
src/lib/utils/saveMarkdown.ts and the untracked phase10a dogfood notes
deliberately left unstaged per Jake's instruction.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The 2026-05-12 engine-slop pass removed runtime std::env::set_var mutation from src-tauri/src/lib.rs and replaced it with warn_if_x11_env_unset_on_wayland. With no launcher owning the contract, Linux Wayland dogfood was about to regress.
run.sh:
- now owns Linux launcher env defaults via case "$(uname -s)"
LIBCLANG_PATH=/usr/lib64/llvm21/lib64 (user-set wins)
WEBKIT_DISABLE_DMABUF_RENDERER=1 (always on Linux; user-set wins)
GDK_BACKEND=x11, WINIT_UNIX_BACKEND=x11 (Wayland only; user-set wins)
- 60s Vite readiness timeout
- detects early Vite exit (kill -0 + wait) instead of hanging forever
- trap installed before wait so Ctrl-C cleans up
- args forwarded: ./run.sh --release etc.
- non-exec final Tauri launch preserved so cleanup trap fires
package.json:
- "dev:tauri": "./run.sh" — canonical discoverable dev command
Docs:
- README, dev-setup, architecture-map runtime + launcher pages updated with the new contract; canonical command is npm run dev:tauri (./run.sh as direct equivalent)
- dev-launcher-and-scripts.md replaces the incorrect "kills process group" claim with honest "kills the spawned npm process"
- dev-setup.md path /CORBEL-Projects/magnotia → /CORBEL-Projects/transcription-app
- gpu-tuning/plan.md gets a superseded note rather than rewriting the original rationale
- engine-slop-residuals.md gains Area F (packaged-binary launcher contract) with honest wrapper-vs-.desktop trade-off documented
Verification: bash -n run.sh; shellcheck clean; cargo check -p magnotia green; npm pkg get 'scripts.dev:tauri' returns "./run.sh"; stale-ref sweep clean across living docs.
Wyrdnote scales from voice-first dictation today to local-first
semantic PKM workbench tomorrow per the engine architecture spec.
The PKM phase needs a serving stack for embeddings + reranking +
entity extraction; this note bookmarks SIE (Superlinked Inference
Engine, Apache-2.0) as the trigger candidate plus six adjacents
to bake off against when the PKM phase lands.
Deferred because (1) PKM sits post-public-beta and infrastructure
decisions today would generalise poorly to a Q4 2026 selection;
(2) the candidates evolve fast; (3) Wyrdnote's local-first +
single-consumer-GPU constraint rules out a today-style bake-off
without real PKM-shape workload to test against.
Decision dimensions captured for the future evaluator: self-host
footprint, cold-start latency, throughput, quality on a Wyrdnote
eval set, reranker availability, extract/NER capability,
OEM-licensability against AGPL-3.0+dual-licence stack, telemetry
posture. Re-evaluation triggers documented.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Introduces the architectural spine for the Wyrdnote engine layer per
the spec at outputs/wyrdnote/2026-05-10-engine-architecture-spec.md
in the CORBEL-Main workspace, Codex-reviewed 2026/05/10.
Clean-room derivative of VoiceInk's TranscriptionService /
TranscriptionServiceRegistry / TranscriptionPipeline shape. Built from
the architectural pattern only; no GPL-3.0 code, comments, or
identifiers lifted. Wyrdnote names, control flow, and trait signatures
are original.
What this lands:
crates/cloud-providers/src/provider.rs (new, 184 lines)
TranscriptionProvider async trait. Object-safe via async_trait so
Arc<dyn TranscriptionProvider> is the canonical shape. Lives in
cloud-providers (not transcription) per D7 so an OEM licensee
implementing the trait depends only on this crate plus magnotia-core
(no transcription internals leaked through the trait surface).
Surrounding types: ProviderId (lower-kebab-case), ProviderKind,
NetworkRequirement, CostClass, ProviderCapabilities, EngineProfile,
ProviderTranscript. Compile-time object-safety witness in tests.
crates/cloud-providers/Cargo.toml (modified)
Adds async-trait + serde dependencies.
crates/cloud-providers/src/lib.rs (modified)
Re-exports the provider surface.
crates/transcription/src/registry.rs (new, 183 lines)
EngineRegistry: catalogue of providers keyed by ProviderId. Push-style
registration, default-provider id captured at construction. Read-only
after boot. Full unit-test coverage: empty default, register/get
round-trip, default-resolves-after-registration, re-register replaces,
ids enumeration.
crates/transcription/src/orchestrator.rs (new, 286 lines)
LocalProviderAdapter wraps Arc<LocalEngine>, presents async
TranscriptionProvider upward via tokio::task::spawn_blocking. Per D7
the adapter lives in the orchestrator, NOT as impl TranscriptionProvider
for LocalEngine — this keeps the dependency edge one-directional and
avoids leaking async-runtime requirements onto the synchronous
Transcriber trait.
Orchestrator: single transcribe() entry point; resolves a provider
from the registry, derives TranscriptionOptions from the EngineProfile,
dispatches. Returns a clear error when an unregistered provider is
named. Unit tests use a mock CannedProvider to validate dispatch,
error handling, and option routing without booting a real model.
crates/transcription/Cargo.toml (modified)
Depends on magnotia-cloud-providers + async-trait. Dev-dep tokio
gains macros + rt-multi-thread features for #[tokio::test].
crates/transcription/src/lib.rs (modified)
Exports Orchestrator, EngineRegistry, LocalProviderAdapter, and
re-exports the provider trait surface so downstream crates depend
only on magnotia-transcription for both local engines and the trait.
KNOWN-ISSUES.md (modified)
Adds KI-06: existing dictation, live, and meeting commands still call
LocalEngine::transcribe_sync directly via pick_engine. The orchestrator
path is dormant until a Phase A.1 follow-up commit migrates the call
sites. Cloud providers (Phase G) cannot be exercised end-to-end until
the rewire lands. Phasing rationale: this commit lands the abstractions
cleanly with full test coverage; the rewire is a separate diff so the
chunking + post-processing logic in transcribe_file moves without
inflating this commit beyond review fidelity.
Test results: 9 new tests pass (orchestrator: dispatches, errors-on-
unregistered, routes-initial-prompt, object-safe; registry: empty-default,
round-trip, default-resolves, re-register, ids-list). Pre-existing
59 transcription tests + 5 cloud-providers tests still green.
cargo check --workspace clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
settings.theme and preferences.theme overlap: the legacy localStorage
field still drives two SegmentedButton bindings in SettingsPage.svelte
while preferences.theme is the canonical Tauri-persisted store. A
migration \$effect in the four route +layout files syncs the legacy
value into preferences whenever it changes.
Resolution requires touching SettingsPage.svelte (2 484 LOC),
src/lib/stores/page.svelte.ts, four route layouts, and the localStorage
migration system. That is moderate-risk frontend work, not pre-launch
polish, so it lands in KNOWN-ISSUES with a concrete plan and is
deferred from v0.1.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sweep of registered-but-never-invoked commands surfaced by an audit
against the frontend invoke() call sites. Each was confirmed dead via
grep across src-tauri, src, and crates: no caller anywhere.
Removed commands:
- check_model, count_transcripts_command, get_profile_cmd,
install_update, list_feedback_examples_cmd (utility/CRUD shapes
never wired)
- save_audio, start_native_capture, stop_native_capture (native-capture
path superseded by the live transcription session)
- transcribe_pcm, transcribe_pcm_parakeet (PCM commands superseded by
live session; no frontend caller)
- close_preview_window (preview window is hidden via the
core:window:allow-hide capability, not the command)
Cascade in audio.rs (~430 lines removed):
- CaptureWorker, NativeCaptureState struct + impl, stop_worker,
append_recorded_chunk, MAX_NATIVE_CAPTURE_RETURN_SAMPLES,
persist_audio_samples
- The two cfg(test) tests that exercised stop_worker (the
recording_filename tests stay, supporting resolve_recording_path
which the live session uses)
Cascade elsewhere:
- FeedbackDto struct and its From<FeedbackRow> impl
- Stale storage imports in feedback.rs, profiles.rs, transcripts.rs
- tauri::Emitter import in transcription.rs
- app.manage(NativeCaptureState::new()) in lib.rs setup
generate_handler entries removed for all 11 commands. cargo check passes
cleanly with zero warnings; no tests reference any deleted symbols.
Net: 709 deletions, 17 insertions across 9 files.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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) <noreply@anthropic.com>
PowerAssertion file-level doc previously claimed Linux logind and
Windows SetThreadExecutionState implementations in present tense.
Both are no-ops; the macOS path compiles but is unverified on
Apple Silicon (RB-08). Rewrite top doc to state present vs
planned posture and reference KNOWN-ISSUES.md.
Surfaced as tracked limitations:
- KI-01: macOS App Nap guard pending Apple Silicon verification
- KI-02: Linux power assertion is a no-op
- KI-03: Windows power assertion is a no-op
- KI-04: magnotia-cloud-providers crate not user-exposed in v0.1
(in-memory keystore needs OS keychain before any save-key UX)
README links to KNOWN-ISSUES.md from Status, Platform support
table, and Project documentation. Platform support table notes
adjusted per OS to reflect actual idle-inhibit posture.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Five-slice navigable map of the entire codebase under
docs/architecture-map/. Each slice is a self-contained
breadcrumbed sub-tree:
01-frontend (16) Svelte/SvelteKit UI
02-tauri-runtime (26) src-tauri commands + lifecycle
03-audio-transcription (16) audio + transcription crates
04-llm-formatting-mcp (19) llm, ai-formatting, mcp, cloud
05-core-storage-hotkey-build core, storage, hotkey, workspace,
(26) CI, dev glue
Plus master README.md and data-flow-end-to-end.md tracing
audio bytes from microphone to FTS5 search to MCP read.
Generated by 5 parallel subagents on 2026/05/09 against
HEAD 3c47000. Each page has YAML frontmatter, file:line code
refs, sibling cross-links, plain-English summaries.
Aggregated debt surfaced (full lists in master README):
RB-08 macOS power assertion, schema head drift v14 vs v15,
VAD blocked on ort version conflict, streaming primitives
not wired into live.rs, no prompt versioning, MCP has no
auth, cloud-providers in-memory keystore, SettingsPage
2 484 LOC, commands/live.rs 1 737 LOC, dual theme system,
brand rename to Lumenote pending across the codebase.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Run with:
cargo run -p magnotia-core --example tuning_log_demo
Initialises a tracing-subscriber fmt layer so the inference_thread_count
INFO event is rendered to stderr. Exercises all 8 (workload,
gpu_offloaded) combos under three power scenarios: AC override,
battery override, and the real sysfs probe.
Used to validate the heuristic on 2026-05-09 against the spec's
6c12t truth table — all 6 rows match.
Adds tracing-subscriber as a [dev-dependencies] for the example.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Per the final reviewer's suggestion: the gpu_offloaded cross-check
(gpu_layers >= model.n_layer()) is trivially true when use_gpu since
we always pass u32::MAX. The check documents intent and is future-
proofed if we ever pass specific N, but a future reader might
simplify it away as dead code. Inline comment points at the spec's
out-of-scope follow-up for true residency observability.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
End-of-phase cleanup of items flagged across review passes for tasks
1.1, 1.2, and 1.3:
1. crates/core/src/lib.rs: pub mod power moved to alphabetical
position (between paths and process_watch).
2. crates/core/src/power.rs: use statements moved to top of file
(above the PowerState enum) per Rust convention.
3. crates/core/src/power.rs: parse_power_state_from_dir docstring
tightened to acknowledge that read_dir.flatten() silently skips
per-entry errors. Theoretical hazard only on world-readable
sysfs, but the previous wording overstated the guarantee.
No behavioural change. All 40 magnotia-core tests still green.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Extends the JFK thread-count sweep to print four labelled panels
(AC+CPU, AC+GPU, battery+CPU, battery+GPU) driven by
MAGNOTIA_POWER_STATE_OVERRIDE. Each panel shows the helper's
predicted thread count for its (power, gpu) combination alongside
the empirical RTF table for n_threads = [1, 2, 4, physical, logical,
maybe 8].
The actual whisper context is initialised once (Vulkan if compiled
in and resolvable, CPU otherwise), so the RTF rows themselves are
produced by the same backend across panels. The CPU vs GPU axis
controls only the helper-pick column, which is the empirical
question we want answered: is the LLM=2 / Whisper=4 GPU floor right?
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The MIN_INFERENCE_THREADS, MAX_INFERENCE_THREADS, and
inference_thread_count() defined in constants.rs were superseded by
the equivalents in tuning.rs (Task 2.1) and are no longer called by
any production code. Both call sites (whisper backend Task 4.1, LLM
generate Task 5.1) have migrated.
Constants.rs now holds only audio/mel/RAM/VAD/download constants;
inference-tuning concerns live in tuning.rs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wires the LLM call site through the new tuning helper. gpu_offloaded
reflects intent (use_gpu) cross-checked against the loaded model's
layer count: u32::MAX (when use_gpu) is trivially >= any model's
n_layer, but the explicit comparison is future-proofed if we ever
pass a specific N instead of u32::MAX.
Note: the call site is in generate() not load_model() as the plan
suggested. Context params (and thus thread count) are constructed
per-inference, not per model load, since n_ctx depends on prompt
size. The implementer adapted correctly.
The old magnotia_core::constants::inference_thread_count import is
replaced. Task 6.1 removes the constants helper now that both call
sites have migrated.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wires the whisper backend through the new tuning helper. gpu_offloaded
combines a compile-time feature check (whisper-vulkan, in default
features) with a runtime libvulkan probe via the hardware module. If
libvulkan1 is missing on Linux, whisper-rs's vulkan backend silently
falls back to CPU, so we should not reduce threads in that case.
The old magnotia_core::constants::inference_thread_count import is
replaced by tuning::{inference_thread_count, Workload}. The constants
module's helper is removed in Task 6.1 once the LLM call site has
also migrated.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Delete the local duplicate fn and libloading dependency from src-tauri;
import the canonical implementation from magnotia-core::hardware instead.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds libloading = "0.8" to magnotia-core dependencies and moves
vulkan_loader_available() into magnotia-core::hardware so non-Tauri
crates (transcription, llm) can probe the Vulkan loader without
depending on the Tauri binary. Test asserts the call completes on any
host regardless of whether libvulkan is installed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds a OnceLock<Mutex<HashSet>> cache in tuning.rs. inference_thread_count
now emits a single tracing::info! line the first time each
(workload, on_battery, gpu_offloaded) tuple is seen in the process,
recording the resolved thread count and which clamps fired (battery, gpu).
Also derives Hash on Workload and tightens the GPU clamp to only record
the "gpu" clamp when it actually reduces chosen. Smoke test added.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds battery-state awareness to the thread-count helper: when
probe_power_state() returns OnBattery, chosen is halved before the
[MIN, MAX] clamp. Also removes the leading underscore from the
workload/gpu_offloaded parameters (they are silenced via let _ = ...
until the GPU-clamp task).
New test battery_halves_thread_count verifies on_battery <= on_ac and
>= MIN when the host has more than MIN physical cores. Restructured
to sequential with_override calls (not nested) to avoid re-entrant
deadlock on power::TEST_LOCK.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Without a lock, env_var_bypasses_clamps (set_var + remove_var) and
matches_existing_clamp_when_no_clamps_apply (remove_var) race under
cargo's parallel test runner. Task 2.2 will add another remove_var
call, compounding the race.
Adds a #[cfg(test)] static THREAD_ENV_LOCK and a closure-style
with_thread_env_lock helper, mirroring power::with_override.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds crates/core/src/tuning.rs with MIN/MAX_INFERENCE_THREADS consts,
Workload enum (Llm/Whisper), and inference_thread_count() helper matching
the existing constants::inference_thread_count clamp behaviour. Three unit
tests pass. tracing dep added to Cargo.toml (used by future tasks).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Cache sits between override resolution and platform_probe; override
paths (test + env-var) bypass it entirely so they always take effect
immediately. OnceLock<Mutex<Option<CachedState>>> initialised lazily on
first probe. Two test-only helpers (force_clear_cache, force_set_cache)
expose the cache slot for unit tests. Mutex import ungated — now used in
production cache code as well as test override.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
TEST_OVERRIDE and test_get_override are now #[cfg(test)]-gated and
the read in probe_power_state is wrapped in a #[cfg(test)] block,
so test scaffolding doesn't compile into production builds.
with_override now uses a drop-guard pattern so a panic inside body
still resets TEST_OVERRIDE, preventing stale state from leaking
into subsequent tests.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds the public probe entry point with two override mechanisms:
- In-process TEST_OVERRIDE slot serialised by TEST_LOCK mutex for
unit tests (avoids races with cargo's parallel runner).
- MAGNOTIA_POWER_STATE_OVERRIDE env var for integration tests set
externally.
Platform dispatch: Linux reads /sys/class/power_supply; all others
return Unknown. Five new override tests pass alongside the six sysfs
parser tests from Task 1.2 (12 total green).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Pure function takes a directory path and returns OnAc, OnBattery, or
Unknown by reading `type` and `online` files in each supply entry.
Matches the kernel ABI documented at
Documentation/ABI/testing/sysfs-class-power. Failure modes (missing
dir, unreadable files, malformed contents) all fall through to Unknown
rather than panicking.
Seven tests cover: Mains online, battery-only, USB-PD, empty dir,
missing dir, and malformed entries. All pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Introduces crates/core/src/power.rs with the PowerState enum
(OnAc / OnBattery / Unknown) and a unit test confirming the three
variants are distinct. Registers the module in lib.rs and adds
tempfile = "3" to dev-dependencies for use in later tasks.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bite-sized TDD plan implementing the design at
docs/superpowers/specs/2026-05-09-battery-gpu-aware-thread-tuning-design.md.
8 phases, ~13 commits:
- Phase 1: power.rs (PowerState enum, sysfs parser, probe + overrides,
10s TTL cache)
- Phase 2: tuning.rs (Workload enum, helper, battery clamp, GPU clamp,
per-process logging)
- Phase 3: vulkan_loader_available moves from src-tauri to magnotia-core
- Phase 4: whisper backend wires through new helper
- Phase 5: LLM call site wires through new helper after model load
- Phase 6: remove old constants::inference_thread_count facade
- Phase 7: thread_sweep.rs prints 4-panel power-aware RTF table
- Phase 8: manual battery validation smoke
Each task is TDD: write failing test, run, implement, run, commit.
Tests live in unit-test modules inside power.rs and tuning.rs.
Override design uses an in-process with_override(state, |closure|)
helper for unit tests (cargo runs them in parallel; env-var path
would race) and MAGNOTIA_POWER_STATE_OVERRIDE env var for
integration tests like thread_sweep.rs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Spec for two new clamps on the existing inference_thread_count helper:
- Battery clamp: drop to physical/2 when on battery (Linux sysfs probe;
macOS/Windows return Unknown, treated as OnAc).
- GPU-offload clamp: 2 threads for fully-offloaded LLM, 4 for Whisper
(Whisper keeps mel spectrogram, decoder bookkeeping, and beam search
on CPU even with full Vulkan offload).
Codex + online-research consult: no prior art in llama.cpp, Ollama,
Jan, or mistral.rs. Apple's Low Power Mode is OS-level (P-core
frequency cap), not a published software API. Lumenote would be first
in the open Rust inference-wrapper space; instrumentation matters,
hence the thread_sweep.rs extension + per-process INFO log.
Architecture (Approach B):
- New crates/core/src/power.rs (PowerState, sysfs probe, 10s TTL).
- New crates/core/src/tuning.rs (Workload enum, helper).
- Move vulkan_loader_available() from src-tauri to magnotia-core
so crates/transcription can call it without a Tauri dep.
- Existing constants::inference_thread_count() becomes deprecated
facade for one commit, then removed.
GPU-offload detection is intent-based (use_gpu && requested_layers
>= model.n_layer() for LLM; cfg(whisper-vulkan) && libvulkan
resolvable for Whisper). Residency-based detection deferred:
llama-cpp-2 0.1.145 doesn't expose post-load offload count, so a
true outcome flag would need llama_log_set callback parsing or an
upstream PR. Caveat documented in the spec.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>