Phase 0 update: replaces the loose "quick wins" list with a structured Fix Areas section organised into impact tiers (A: high-impact README truth fixes, B: low-effort self-violation fixes, C: structural smells deferred to Phase 2, D: hygiene). Each task names the file, the change, the verification command, and the effort estimate. New playbook: docs/audit/phases-1-8-playbook.md — step-by-step acquisition-grade audit procedure for the remaining seven phases. Each phase has goal, inputs, procedure (with concrete commands), deliverable, acceptance criteria, and time estimate. Designed to be picked up independently from any phase.
332 lines
19 KiB
Markdown
332 lines
19 KiB
Markdown
# Phase 0 — Cartography
|
||
|
||
*Acquisition-grade audit, Phase 0 deliverable. Date: 2026-04-30. Branch: `claude/rebrand-to-magnotia-UWYkg`.*
|
||
|
||
This is a survey, not a verdict. It maps what exists, sizes the surface, and flags every place the README disagrees with the code. Phase 1 (lean-pass) and Phase 2 (architecture conformance) consume this as input.
|
||
|
||
---
|
||
|
||
## 1. Workspace shape
|
||
|
||
| Layer | Path | Notes |
|
||
|---|---|---|
|
||
| Rust workspace root | `Cargo.toml` | `members = ["src-tauri", "crates/*"]`, `resolver = "2"` |
|
||
| Tauri app crate | `src-tauri/` | Library `magnotia_lib` + binary `magnotia` |
|
||
| Library crates | `crates/*` (×9) | See §3 |
|
||
| MCP standalone binary | `crates/mcp/` | Bin `magnotia-mcp` (separate process from main app) |
|
||
| Svelte frontend | `src/` | SvelteKit, Svelte 5 runes, Tailwind 4 |
|
||
| Static assets | `static/`, `src-tauri/icons/`, `src-tauri/resources/` | |
|
||
|
||
**Binaries shipped (2):** `magnotia` (Tauri app), `magnotia-mcp` (stdio MCP server).
|
||
|
||
---
|
||
|
||
## 2. Lines of code
|
||
|
||
| Area | LOC | Files |
|
||
|---|---:|---:|
|
||
| `crates/` (Rust, all 9 crates) | 13,261 | 52 |
|
||
| `src-tauri/` (Rust) | 8,330 | 27 |
|
||
| Frontend (Svelte/TS/JS, excl. design-system) | 15,192 | ~80 |
|
||
| `src/design-system/` (reference kit, not live code) | 1,412 | — |
|
||
| **Total active** | **~36,800** | |
|
||
|
||
### Largest files (top complexity candidates)
|
||
|
||
| LOC | File |
|
||
|---:|---|
|
||
| 2,534 | `crates/storage/src/database.rs` |
|
||
| 2,250 | `src/lib/pages/SettingsPage.svelte` |
|
||
| 1,737 | `src-tauri/src/commands/live.rs` |
|
||
| 1,185 | `crates/storage/src/migrations.rs` |
|
||
| 1,081 | `src/lib/pages/DictationPage.svelte` |
|
||
| 897 | `src/lib/pages/HistoryPage.svelte` |
|
||
| 790 | `src-tauri/src/commands/paste.rs` |
|
||
| 735 | `crates/transcription/src/streaming/rms_vad.rs` |
|
||
| 725 | `src/lib/pages/TasksPage.svelte` |
|
||
| 720 | `src-tauri/src/commands/models.rs` |
|
||
| 697 | `src/lib/stores/page.svelte.ts` |
|
||
|
||
> **Phase 1 candidate.** `database.rs` (2.5k), `SettingsPage.svelte` (2.25k), and `live.rs` (1.7k) are each large enough to deserve a structural review in isolation. HANDOVER.md already flags `SettingsPage` as needing decomposition into 7 progressive-disclosure groups.
|
||
|
||
---
|
||
|
||
## 3. Crate inventory
|
||
|
||
| Crate | LOC | Files | `pub` items (lib.rs / total) | Tests |
|
||
|---|---:|---:|---:|---:|
|
||
| `magnotia-core` | 1,212 | 9 | 10 / 104 | 16 |
|
||
| `magnotia-audio` | 1,533 | 8 | 14 / 38 | 14 |
|
||
| `magnotia-transcription` | 2,617 | 12 | 13 / 51 | 51 |
|
||
| `magnotia-llm` | 1,330 | 6 | 27 / 56 | 17 |
|
||
| `magnotia-ai-formatting` | 1,502 | 6 | 9 / 21 | 47 |
|
||
| `magnotia-storage` | 3,771 | 4 | 6 / 69 | 60 |
|
||
| `magnotia-hotkey` | 632 | 3 | 5 / 14 | 4 |
|
||
| `magnotia-cloud-providers` | 80 | 2 | 2 / 3 | 2 |
|
||
| `magnotia-mcp` | 584 | 2 | 8 / 8 | 9 |
|
||
| `src-tauri` (`magnotia` + `magnotia_lib`) | 8,330 | 27 | n/a | 67 |
|
||
| **Total** | **21,591** | | | **287** |
|
||
|
||
**Outliers worth a Phase-2 look:**
|
||
- `magnotia-core` exposes 104 public items — high for a "shared types" crate; likely leakage of internals.
|
||
- `magnotia-storage` exposes 69 public items across only 4 files; the file split is suspect (2.5k-line `database.rs`).
|
||
- `magnotia-cloud-providers` is 80 LOC and 3 public items — README calls it "empty scaffolding" (verified: just an in-memory keystore + env-var fallback). Either grow it or remove it; it currently earns nothing.
|
||
|
||
---
|
||
|
||
## 4. Crate dependency graph
|
||
|
||
```
|
||
magnotia-core ──┬─→ magnotia-audio
|
||
├─→ magnotia-transcription
|
||
├─→ magnotia-llm ──→ magnotia-ai-formatting
|
||
├─→ magnotia-cloud-providers
|
||
├─→ magnotia-hotkey
|
||
└─→ magnotia-storage ──→ magnotia-mcp
|
||
|
||
magnotia (src-tauri)
|
||
└─→ all 8 library crates (NOT magnotia-mcp — separate binary)
|
||
```
|
||
|
||
**Observations:**
|
||
- `magnotia-core` is the workspace floor; nothing depends on it depending on something else. Good.
|
||
- DAG is clean — no cycles, no upward dependencies.
|
||
- `magnotia-mcp` correctly depends only on `magnotia-storage` (its sole job is to read the SQLite DB). The Tauri app does **not** depend on it, confirming the "separate process" claim in the README.
|
||
- `magnotia-ai-formatting` depends on both `core` and `llm`. Reasonable.
|
||
|
||
> **Phase 2 will verify:** every `pub` item in the leaf crates (`audio`, `transcription`, `llm`, `storage`, `hotkey`) has at least one external consumer. Internal-only items shouldn't be `pub`.
|
||
|
||
---
|
||
|
||
## 5. External surfaces
|
||
|
||
### 5.1 Tauri commands
|
||
|
||
**102 `#[tauri::command]` attributes** across **22 of 25** modules in `src-tauri/src/commands/`.
|
||
- `power.rs` and `security.rs` are utility modules (no commands; helpers only).
|
||
- `mod.rs` is the registry.
|
||
|
||
| Module | # commands | Module | # commands |
|
||
|---|---:|---|---:|
|
||
| `tasks` | 12 | `intentions` | 5 |
|
||
| `models` | 12 | `audio` | 4 |
|
||
| `llm` | 10 | `tts` | 3 |
|
||
| `profiles` | 9 | `transcription` | 3 |
|
||
| `transcripts` | 8 | `paste` | 3 |
|
||
| `windows` | 8 | `update` | 2 |
|
||
| `diagnostics` | 6 | `rituals` | 2 |
|
||
| `hotkey` | 5 | `live` | 2 |
|
||
| | | `hardware` | 2 |
|
||
| | | `feedback` | 2 |
|
||
| | | `nudges`, `meeting`, `fs`, `clipboard` | 1 each |
|
||
|
||
**Every one of these is a trust boundary** — Phase 4 (security) will audit input validation per command.
|
||
|
||
### 5.2 MCP tools (read-only stdio)
|
||
|
||
Confirmed in `crates/mcp/src/lib.rs`:
|
||
- `list_transcripts`
|
||
- `get_transcript`
|
||
- `search_transcripts`
|
||
- `list_tasks`
|
||
|
||
The `init_readonly` connection mode in `magnotia-storage` is opened at OS level (`SQLITE_OPEN_READONLY`), per `crates/mcp/src/main.rs:18` — Phase 4 will confirm with a write-attempt test.
|
||
|
||
### 5.3 Frontend route surface
|
||
|
||
| Route | Purpose | Layout |
|
||
|---|---|---|
|
||
| `/` | Main dictation shell | `+layout.svelte` (sidebar + chrome) |
|
||
| `/float` | Tasks float window | `+layout@.svelte` (chrome-free) |
|
||
| `/viewer` | Transcript editor | `+layout@.svelte` (chrome-free) |
|
||
| `/preview` | Live transcription overlay | `+layout@.svelte` (chrome-free) |
|
||
|
||
**Pages:** `DictationPage`, `SettingsPage`, `HistoryPage`, `TasksPage`, `FilesPage`, `FirstRunPage`, `ShutdownRitualPage` (7).
|
||
|
||
**Stores:** `page`, `preferences`, `profiles`, `toasts`, `focusTimer`, `llmStatus`, `nudgeBus`, `implementationIntentions`, `completionStats`, `speaker` (10).
|
||
|
||
**Components:** 25 in `src/lib/components/`.
|
||
|
||
**i18n locales:** `en`, `es`, `de` (scaffolding only — most strings are still hard-coded; the migration is incremental per README).
|
||
|
||
### 5.4 Model registry
|
||
|
||
7 models declared in `crates/core/src/model_registry.rs`:
|
||
- Whisper (6): `whisper-tiny-en`, `whisper-base-en`, `whisper-small-en`, `whisper-distil-small-en`, `whisper-medium-en`, `whisper-distil-large-v3`
|
||
- Parakeet (1): `parakeet-ctc-0.6b-int8`
|
||
|
||
> Moonshine is mentioned in the README's `magnotia-core` description ("Moonshine entries") but **no Moonshine entry exists in the registry**. See §7.
|
||
|
||
---
|
||
|
||
## 6. Test floor
|
||
|
||
| Location | Count |
|
||
|---|---:|
|
||
| `crates/*/src/` (lib tests) | 217 |
|
||
| `crates/*/tests/` (integration) | 3 |
|
||
| `src-tauri/src/` + `src-tauri/tests/` | 67 |
|
||
| **Total** | **287** |
|
||
|
||
Only 3 cross-crate integration tests is light. Per-crate lib tests dominate. Phase 5 (test integrity) will mutation-test the heavy crates (`storage`, `transcription`, `llm`) to grade whether these tests actually pin behaviour.
|
||
|
||
---
|
||
|
||
## 7. README ↔ code drift
|
||
|
||
Items where the README disagrees with the code as-of this audit:
|
||
|
||
| README claim | Reality | Severity |
|
||
|---|---|---|
|
||
| "245 automated lib tests across 10 crates" (line 14) | **287 tests** total (220 lib + 67 src-tauri); 220 lib-only | LOW — undercount (good direction, but stale) |
|
||
| "10 crates" (line 14) | 9 library crates + 1 app crate (`src-tauri`) — depends how you count; technically the workspace has 10 packages | OK if counting `src-tauri`; misleading otherwise |
|
||
| "Commands: audio, clipboard, diagnostics, hotkey, live, llm, meeting, models, paste, power, profiles, tasks, transcription, transcripts, update, windows" (line 95-97) — 16 listed | **22 modules with commands**: README missing `feedback`, `fs`, `intentions`, `nudges`, `rituals`, `tts`. Architecture diagram and §"Tauri commands" table both stale. | **MED** — visible to anyone evaluating the codebase |
|
||
| "18 Tauri command modules" (line 117) | **25 files** in `commands/` (22 with command attrs + `mod`, `power`, `security`) | MED |
|
||
| `magnotia-core` "model registry (Whisper + Parakeet + Moonshine entries)" (line 165) | **No Moonshine entries** in `model_registry.rs`. 6 Whisper + 1 Parakeet only. | **MED** — claims an unimplemented feature |
|
||
| Stores listed: `settings, profiles, tasks, history, taskLists, templates, page, toasts, preferences` (line 202) | Actual stores: `page, preferences, profiles, toasts, focusTimer, llmStatus, nudgeBus, implementationIntentions, completionStats, speaker`. README list is **largely fictional** — there is no `tasks`, `history`, `taskLists`, `templates`, or `settings` store as a separate file. | **HIGH** — describes architecture that doesn't exist |
|
||
| `magnotia-cloud-providers` "BYOK cloud-STT provider stubs… Currently empty scaffolding. When populated: OpenAI-compatible endpoint + Anthropic" (line 172) | Crate has an in-memory API-key store with env-var fallback — not "empty scaffolding". No HTTP code, no provider implementations. | LOW — partial |
|
||
| "Every new workspace crate needs a `description` in its `Cargo.toml`" (line 362, contributing rule) | **`crates/llm/Cargo.toml` has no `description` field.** Self-violation of the contribution rule. | LOW — easy fix |
|
||
| README §"Architecture" Rust crate list spelling (line 102-104) | Correct, but the manual line-break formatting got mangled by the rebrand sweep — visible whitespace inconsistency. | TRIVIAL |
|
||
|
||
> **Phase 0 verdict on documentation truth:** The README is **mostly right but actively misleading in two places** — the stores list and the Moonshine claim. Both will fail an acquirer's first sanity-check (`grep -r "Moonshine" crates/`) and erode trust in the rest of the doc.
|
||
|
||
---
|
||
|
||
## 8. HANDOVER files
|
||
|
||
| File | Date | Topic | Status |
|
||
|---|---|---|---|
|
||
| `HANDOVER.md` | 2026-04-25 | Latest session (Phase 9a–9d) | Active reference |
|
||
| `HANDOVER-2026-04-24.md` | 2026-04-24 | Phase 8 close | Historical |
|
||
| `HANDOVER-2026-04-19.md` | 2026-04-19 | Earlier session | Historical |
|
||
| `HANDOVER-2026-04-18.md` | 2026-04-18 | Earlier session | Historical |
|
||
| `HANDOVER-2026-04-17.md` | 2026-04-17 | Earliest in tree | Historical |
|
||
|
||
Five handovers in the repo root is unusual — most projects keep one. Phase 8 (docs truth) will recommend either archiving them under `docs/handovers/` or rotating to a single `HANDOVER.md` with prior content moved.
|
||
|
||
The post-rebrand state: all five handovers were rewritten by today's sweep (line counts identical, words different). They now reference `magnotia` paths but their **content** describes work done under the `kon` / `corbie` names — there's a temporal-vs-naming mismatch a reader has to mentally track. Acquirer-friendly fix: add a one-line note at the top of each historical handover saying "Originally written when the product was named X; references rewritten 2026-04-30."
|
||
|
||
---
|
||
|
||
## 9. Fix areas — actionable tasks
|
||
|
||
Each task below is concrete: file, change, verification, effort. Pick any in any order; they don't depend on later phases. Items are grouped by impact tier.
|
||
|
||
### Tier A — High impact, do first
|
||
|
||
#### A1. README stores list is fiction
|
||
- **File:** `README.md`, line 202
|
||
- **Current:** `Reactive stores (src/lib/stores/page.svelte.ts): settings, profiles, tasks, history, taskLists, templates, page, toasts, preferences.`
|
||
- **Reality:** stores are `page`, `preferences`, `profiles`, `toasts`, `focusTimer`, `llmStatus`, `nudgeBus`, `implementationIntentions`, `completionStats`, `speaker` — each in its own `*.svelte.ts` file under `src/lib/stores/`.
|
||
- **Fix:** rewrite the bullet to enumerate the actual ten store files, and clarify that `page.svelte.ts` is the central app-state store (transcripts, profiles, taskLists, etc. live as fields on it).
|
||
- **Verify:** `ls src/lib/stores/` matches the README list 1:1.
|
||
- **Effort:** 10 min.
|
||
|
||
#### A2. Moonshine claim has no implementation
|
||
- **File:** `README.md`, line 165 (`magnotia-core` row in the crate table)
|
||
- **Current:** `model registry (Whisper + Parakeet + Moonshine entries)`
|
||
- **Reality:** `crates/core/src/model_registry.rs` has 6 Whisper + 1 Parakeet entries. Zero Moonshine.
|
||
- **Fix (pick one):**
|
||
- (a) Remove the Moonshine reference from the README. Cheapest.
|
||
- (b) Add a `// TODO(moonshine): not yet wired` constant in `model_registry.rs` and a roadmap entry under §Roadmap, so the claim is at least flagged as forthcoming.
|
||
- **Verify:** `grep -ri moonshine crates/ src-tauri/ src/` returns no orphan references.
|
||
- **Effort:** 5 min (option a) / 30 min (option b).
|
||
|
||
#### A3. Six Tauri command modules undocumented
|
||
- **File:** `README.md`, lines 95-97 (Architecture diagram) and 175-195 (Tauri commands table)
|
||
- **Missing:** `feedback`, `fs`, `intentions`, `nudges`, `rituals`, `tts`
|
||
- **Fix:** add a one-line description for each in the §Tauri commands table; add the names to the Architecture-diagram bullet list.
|
||
- **Verify:** `ls src-tauri/src/commands/*.rs | xargs basename -s .rs | sort` matches the README table 1:1 (excluding `mod`, `power`, `security`, which are utility modules — note that explicitly).
|
||
- **Effort:** 20 min.
|
||
|
||
### Tier B — Low effort, removes self-violations
|
||
|
||
#### B1. `crates/llm/Cargo.toml` missing `description`
|
||
- **File:** `crates/llm/Cargo.toml`
|
||
- **Current:** `[package]` block has `name`, `version`, `edition` only.
|
||
- **Reality:** README §Contributing line 362 declares this a hard rule. Self-violation.
|
||
- **Fix:** add `description = "Local LLM engine for Magnotia (Qwen3 via llama-cpp-2). Cleanup, task extraction, content tags."` (or similar). Match the prose style of the other 8 crates' descriptions.
|
||
- **Verify:** `for d in crates/*/Cargo.toml src-tauri/Cargo.toml; do grep -L "^description" "$d"; done` returns empty.
|
||
- **Effort:** 2 min.
|
||
|
||
#### B2. Stale test-count claim
|
||
- **File:** `README.md`, line 14
|
||
- **Current:** `245 automated lib tests across 10 crates, all passing`
|
||
- **Reality:** 287 tests total (220 lib + 67 src-tauri); 220 lib-only.
|
||
- **Fix:** decide on a number that's automatable, not a snapshot. Either: (a) replace with `220+ lib tests across 9 library crates plus 67 Tauri-app tests`, or (b) drop the absolute number and say `comprehensive automated test floor — see CI for current count`.
|
||
- **Verify:** `grep -rE '#\[(test|tokio::test)\]' crates/*/src/ | wc -l` matches whatever number you ship.
|
||
- **Effort:** 5 min.
|
||
|
||
#### B3. Crate count claim ambiguity
|
||
- **File:** `README.md`, line 14 ("10 crates")
|
||
- **Reality:** 9 library crates + 1 Tauri app crate. The README's own crate table only documents 9.
|
||
- **Fix:** say "9 library crates plus the Tauri app crate" — or just "9 library crates" and let the Tauri app stand separately, which matches the existing prose.
|
||
- **Effort:** 2 min.
|
||
|
||
### Tier C — Structural smells (defer to Phase 2 but flag now)
|
||
|
||
#### C1. `magnotia-core` over-exports
|
||
- **File(s):** `crates/core/src/lib.rs` and the modules it re-exports
|
||
- **Symptom:** 104 public items in a "shared types" crate. High blast radius for any change.
|
||
- **Fix (Phase 2 work, do not touch yet):** audit every `pub` item; demote anything not used outside the crate to `pub(crate)`. The expected outcome is a 30–60% reduction in public surface.
|
||
- **Verify:** after the demotion pass, `cargo +nightly rustdoc` should still succeed and downstream crates should still compile without changes.
|
||
- **Effort:** ~½ day (Phase 2 scope).
|
||
|
||
#### C2. `magnotia-storage::database.rs` is 2,534 lines
|
||
- **File:** `crates/storage/src/database.rs`
|
||
- **Symptom:** single file holds CRUD for transcripts, tasks, subtasks, profiles, profile-terms, settings, error log, FTS5. No internal module boundaries.
|
||
- **Fix (Phase 2):** split by domain — `database/transcripts.rs`, `database/tasks.rs`, `database/profiles.rs`, etc. Keep the public re-export shape unchanged so callers don't move.
|
||
- **Verify:** `cargo test -p magnotia-storage` still passes; no public-API changes.
|
||
- **Effort:** ~2-4 hours.
|
||
|
||
#### C3. `SettingsPage.svelte` is 2,250 lines
|
||
- **File:** `src/lib/pages/SettingsPage.svelte`
|
||
- **Symptom:** HANDOVER.md already flags this; `SettingsGroup.svelte` was prepared but the seven-group split was deferred.
|
||
- **Fix (Phase 2):** complete the planned restructure. Pick this up from HANDOVER.md §"9c — Settings (scaled down)".
|
||
- **Effort:** ~½ day.
|
||
|
||
#### C4. `magnotia-cloud-providers` does not earn its existence
|
||
- **Files:** `crates/cloud-providers/` (80 LOC across 2 files)
|
||
- **Symptom:** crate contains an in-memory keystore with env-var fallback. Not "empty scaffolding" as the README says — but also not provider-specific. No HTTP code, no providers.
|
||
- **Fix (decide, then act):**
|
||
- (a) **Fold** into `magnotia-core::keystore` (preferred — it's a generic key store, nothing cloud-specific). Drop the crate. README §Architecture and the dependency graph simplify.
|
||
- (b) **Grow** it: actually implement an OpenAI-compatible STT client and an Anthropic STT client, gated behind a `cloud-stt` feature flag. Earn the boundary.
|
||
- **Verify (option a):** workspace builds with `cloud-providers` removed from `Cargo.toml` members; the two consumers (`commands/llm.rs` and wherever else) re-import from `magnotia-core::keystore`.
|
||
- **Effort:** ~1 hour (option a) / multi-day (option b).
|
||
|
||
### Tier D — Hygiene (Phase 1 / Phase 8)
|
||
|
||
#### D1. Five HANDOVER files in repo root
|
||
- **Files:** `HANDOVER.md`, `HANDOVER-2026-04-{17,18,19,24}.md`
|
||
- **Symptom:** root noise; rebrand also rewrote their content so they describe `kon`/`corbie` work but read as `magnotia`.
|
||
- **Fix:**
|
||
- Move the four dated files under `docs/handovers/`.
|
||
- Add a one-line italic note at the top of each historical file: *"Originally written when the product was named Kon (and briefly Corbie); references rewritten in the 2026-04-30 rebrand sweep."*
|
||
- Keep the latest as `HANDOVER.md` in root, or also move under `docs/handovers/HANDOVER-latest.md` with a symlink — pick one.
|
||
- **Effort:** 15 min.
|
||
|
||
#### D2. Tauri command total count drift
|
||
- **README** says "18 Tauri command modules" (line 117); actual is 22 modules with commands (plus 3 utility modules in the same dir).
|
||
- **Fix:** update line 117 to "22 Tauri command modules + 3 utility modules (`mod`, `power`, `security`)".
|
||
- **Effort:** 1 min — usually folded into A3.
|
||
|
||
---
|
||
|
||
## 10. Phase 1 entry plan
|
||
|
||
Tier A and Tier B fixes above (≈45 min total) bring the README back into truth and close the self-imposed Cargo.toml rule. Do these as a warm-up before Phase 1 proper; they make every subsequent phase' "what does the README say?" comparison cheaper.
|
||
|
||
Phase 1 (Lean-pass) — see [`phases-1-8-playbook.md`](phases-1-8-playbook.md) — should then target, in order:
|
||
|
||
1. `cargo machete` + `cargo udeps` workspace-wide → unused deps kill list.
|
||
2. `knip` on the frontend → unused TS/Svelte modules.
|
||
3. Manual review of the **5 files >1k LOC** for duplicate logic (`SettingsPage.svelte`, `database.rs`, `live.rs`, `migrations.rs`, `DictationPage.svelte`).
|
||
4. Grep audit of `TODO` / `FIXME` / `unimplemented!` / `unwrap()` outside tests → tech-debt log.
|
||
5. Apply the Tier C structural smells if Phase 2 is being done immediately afterwards.
|
||
|
||
Estimated time: **1 working day** for Phase 1 in full, plus ~45 min of Tier A/B fixes.
|
||
|
||
---
|
||
|
||
*End of Phase 0 cartography.*
|