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>
6.6 KiB
name, type, slice, last_verified
| name | type | slice | last_verified |
|---|---|---|---|
| Storage transcripts CRUD | architecture-map-page | 05-core-storage-hotkey-build | 2026/05/09 |
Storage transcripts CRUD
Where you are: Architecture map → Core, Storage, Hotkey, Build → Storage transcripts CRUD
Plain English summary. All the read/write operations against the transcripts table. Inserts validate the profile FK at the application layer in addition to the database constraint so the error message is friendly. Updates split between content updates (update_transcript) and metadata updates (update_transcript_meta).
At a glance
- Source:
crates/storage/src/database.rs:80-289. - Public surface:
insert_transcript,get_transcript,list_transcripts,list_transcripts_paged,count_transcripts,update_transcript,update_transcript_meta,delete_transcript,search_transcripts. - Public types:
InsertTranscriptParams<'a>(input),TranscriptRow(output). - Consumers: slice 2 transcription command (the post-inference write), the history command (reads), the export plumbing, the MCP server.
Public types
InsertTranscriptParams<'a> — crates/storage/src/database.rs:61
Borrowed-string parameter struct. 16 fields; one-to-one with the v1+v8+v14 column set:
pub struct InsertTranscriptParams<'a> {
pub id: &'a str,
pub text: &'a str,
pub source: &'a str,
pub profile_id: &'a str,
pub title: Option<&'a str>,
pub audio_path: Option<&'a str>,
pub duration: f64,
pub engine: Option<&'a str>,
pub model_id: Option<&'a str>,
pub inference_ms: Option<i64>,
pub sample_rate: Option<i64>,
pub audio_channels: Option<i64>,
pub format_mode: Option<&'a str>,
pub remove_fillers: bool,
pub british_english: bool,
pub anti_hallucination: bool,
}
segments_json, manual_tags, template, language, starred, llm_tags are not on the insert struct — they default at the column level ('' or 0) and are populated later via update_transcript_meta.
TranscriptRow — crates/storage/src/database.rs:793
pub struct TranscriptRow {
pub id: String,
pub text: String,
pub source: String,
pub profile_id: String,
pub title: Option<String>,
pub audio_path: Option<String>,
pub duration: f64,
pub engine: Option<String>,
pub model_id: Option<String>,
pub inference_ms: Option<i64>,
pub sample_rate: Option<i64>,
pub audio_channels: Option<i64>,
pub format_mode: Option<String>,
pub remove_fillers: bool,
pub british_english: bool,
pub anti_hallucination: bool,
pub created_at: String,
pub starred: bool,
pub manual_tags: String,
pub template: String,
pub language: String,
pub segments_json: String,
pub llm_tags: String, // v14
}
Functions
insert_transcript(pool, ¶ms) -> Result<()> — crates/storage/src/database.rs:80
- Pre-flight FK check.
profile_exists(pool, params.profile_id)(private,database.rs:1094) runsSELECT 1 FROM profiles WHERE id = ?. If the profile is not present, returns a friendlyMagnotiaError::StorageError("Insert transcript failed: unknown profile id '...'"). Without this, sqlite would raiseFOREIGN KEY constraint failedwhich the frontend cannot easily disambiguate. - Single
INSERT INTO transcripts (...) VALUES (...)with all 16 fields.
get_transcript(pool, id) -> Result<Option<TranscriptRow>> — crates/storage/src/database.rs:117
Single-row select. Returns Ok(None) for missing id (not Err).
list_transcripts(pool, limit) -> Result<Vec<TranscriptRow>> — crates/storage/src/database.rs:129
Calls list_transcripts_paged(pool, limit, 0).
list_transcripts_paged(pool, limit, offset) -> Result<Vec<TranscriptRow>> — crates/storage/src/database.rs:135
SELECT ... FROM transcripts ORDER BY created_at DESC LIMIT ? OFFSET ?. The History page in slice 1 paginates through this.
count_transcripts(pool) -> Result<i64> — crates/storage/src/database.rs:153
SELECT COUNT(*) FROM transcripts. Used by the History page to render (N total) and to decide whether to render the empty state.
update_transcript(pool, id, ...) -> Result<()> — crates/storage/src/database.rs:168
Updates the content fields: text, title, language, segments_json. Triggers transcripts_au so the FTS index is refreshed.
update_transcript_meta(pool, id, ...) -> Result<()> — crates/storage/src/database.rs:221
Updates the metadata fields: starred, manual_tags, template, plus llm_tags (post-v14). Separate from update_transcript because the user-driven star / manual-tag actions are common and we do not want to rebuild FTS index for a starring change. The transcripts_au trigger does fire either way (it watches AFTER UPDATE ON transcripts with no column predicate), so worth verifying that single-column updates do not bloat the FTS table over time.
delete_transcript(pool, id) -> Result<()> — crates/storage/src/database.rs:257
DELETE FROM transcripts WHERE id = ?. The cascade on segments.transcript_id removes child rows. The transcripts_ad trigger removes the row from the FTS index.
search_transcripts(pool, query, limit) -> Result<Vec<TranscriptRow>> — crates/storage/src/database.rs:270
Full-text search via FTS5. Detailed under storage-fts5-search.md.
Data flow / contract
- All reads return
TranscriptRowshaped from the same private helpertranscript_row_from(&SqliteRow)atdatabase.rs:856. - All writes propagate to FTS via the schema-level triggers; no application-level FTS handling is needed.
- The
profile_idis required at insert time; there is no path to an orphaned transcript post-v9.
Watch-outs
update_transcript_metadoes not bumpcreated_at. Correct behaviour: starring a transcript shouldn't change its sort position.- Stamp columns (
engine,model_id,inference_ms,sample_rate,audio_channels,format_mode) are written once at insert and never updated. They are provenance, not state. remove_fillers,british_english,anti_hallucinationare also stamped once at insert. They record what setting was active when the transcript was produced, not the user's current preference.- No bulk insert. Each transcript is one round-trip. A future bulk-import path would batch
BEGIN; INSERT...x; COMMITfor throughput.
See also
- Schema and migrations
- FTS5 search
- Storage overview
- Slice 2 transcription command — the immediate caller of
insert_transcript.