Phase 9 of the rebrand cascade. Sweep covers everything the Phase 8
frontend pass deliberately skipped: docs/, root markdown, scripts,
Cargo.toml descriptions, code comments that survived earlier
word-boundary sed, plus a handful of identifiers caught on the final
verify pass.
transcription-app changes:
- README.md, HANDOVER.md, KNOWN-ISSUES.md, run.sh — magnotia/Magnotia
-> lumotia/Lumotia.
- docs/ — sweep across all subdirs except docs/handovers/ (preserved
as immutable audit trail). Includes architecture-map references
to magnotia_core::*, magnotia_storage::*, etc. now pointing at
lumotia_*; dev-setup.md tracing output examples (lumotia_startup
target); brief/ + superpowers/ + issues/ + whisper-ecosystem/ +
audit/.
- Cargo.toml descriptions on 9 crates (core, audio, cloud-providers,
hotkey, llm, mcp, plus referenced others).
- crates/core/src/{error,hardware,recommendation,paths}.rs +
crates/audio/src/wav.rs + crates/llm/src/model_manager.rs +
crates/cloud-providers/src/keystore.rs + crates/mcp/src/lib.rs —
doc comments and a model-manager user-agent string.
- Caught on final pass: BroadcastChannel("magnotia_task_sync") -> ...
("lumotia_task_sync"); magnotia_locale i18n localStorage key
renamed + migration shim added; CSS keyframe names
magnotiaPulse / magnotiaBar / magnotiaFade renamed in the design-
system kit; magnotia_viewer_item / magnotia_viewer_mode handoff
keys renamed in HistoryPage + viewer/+page.svelte; src/assets/
wordmark.svg text.
- src-tauri/src/lib.rs comment cleanup ("magnotia era" was sed'd
to "lumotia era" earlier — restored).
Preserved (intentional):
- crates/core/src/paths.rs — keeps "magnotia" / "Magnotia" / ".magnotia"
legacy detection strings in legacy_and_target_paths() so the
migration shim can still find user data from the magnotia era.
- src/lib/stores/{page,focusTimer}.svelte.ts + src/lib/i18n/index.ts
— migration call sites reference the legacy magnotia keys
deliberately.
- docs/handovers/ — historical audit trail.
cargo build --workspace passes. npm run check: 0 errors / 0 warnings
(3958 files). cargo test --workspace: 339 pass / 0 fail.
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 typedlumotia_storage::Error::InvalidReference { entity: Entity::Profile, reason: "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.