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.8 KiB
name, type, slice, last_verified
| name | type | slice | last_verified |
|---|---|---|---|
| Storage tasks and subtasks CRUD | architecture-map-page | 05-core-storage-hotkey-build | 2026/05/09 |
Storage tasks and subtasks CRUD
Where you are: Architecture map → Core, Storage, Hotkey, Build → Storage tasks CRUD
Plain English summary. Tasks are the things the user wants to do. Subtasks are tasks with a parent_task_id set. Both live in the same tasks table with a self-referential foreign key. The completion path includes a small bit of business logic: completing the last subtask auto-completes the parent (and stamps auto_completed = 1 so the daily-completion analytics can exclude cascade-completed parents).
At a glance
- Source:
crates/storage/src/database.rs:301-631. - Public surface:
insert_task,list_tasks,get_task_by_id,update_task,set_task_energy,complete_task,uncomplete_task,delete_task,insert_subtask,list_subtasks,complete_subtask_and_check_parent,list_recent_completions. - Public types:
TaskRow,DailyCompletionCount. - Consumers: slice 2 task commands; the LLM micro-step decomposer (slice 4) calls
insert_subtask; the gamification badge on the Tasks page readslist_recent_completions.
Public types
TaskRow — crates/storage/src/database.rs:825
pub struct TaskRow {
pub id: String,
pub text: String,
pub bucket: String,
pub list_id: Option<String>,
pub effort: Option<String>,
pub notes: String, // v4
pub done: bool,
pub done_at: Option<String>,
pub created_at: String,
pub source_transcript_id: Option<String>,
pub parent_task_id: Option<String>, // v3
pub energy: Option<String>, // v11; "high" | "medium" | "brain_dead" | None
}
DailyCompletionCount — crates/storage/src/database.rs:573
#[derive(serde::Serialize)]
pub struct DailyCompletionCount {
pub day: String, // "YYYY-MM-DD" in local time
pub count: u32,
}
Serialised because slice 2 forwards it to the frontend sparkline.
Functions
insert_task(pool, id, text, bucket, source_transcript_id, list_id, effort, energy) — crates/storage/src/database.rs:301
Positional signature, eight arguments. Documented at database.rs:293 as deliberately flat — it mirrors the tasks schema columns. Refactor to a params struct only if another nullable lands after energy.
list_tasks(pool) -> Result<Vec<TaskRow>> — crates/storage/src/database.rs:328
SELECT ... FROM tasks ORDER BY created_at DESC. Returns every row, including completed ones. Frontend filters.
get_task_by_id(pool, id) -> Result<Option<TaskRow>> — crates/storage/src/database.rs:341
Single-row select.
update_task(pool, id, text, bucket, list_id, effort, notes, energy) — crates/storage/src/database.rs:361
Updates the user-editable fields. done and done_at are not updated through this function — completion has its own pair (complete_task / uncomplete_task).
set_task_energy(pool, id, energy) -> Result<TaskRow> — crates/storage/src/database.rs:402
Single-column update. Returns the row post-update so the frontend gets confirmation. Energy must be one of "high", "medium", "brain_dead", or None — enforced by the DB-level CHECK from migration v11.
insert_subtask(pool, parent_id, id, text, bucket) — crates/storage/src/database.rs:415
Same as insert_task but with parent_task_id set. Inherits the bucket from the parent.
list_subtasks(pool, parent_id) -> Result<Vec<TaskRow>> — crates/storage/src/database.rs:431
SELECT ... FROM tasks WHERE parent_task_id = ? ORDER BY created_at ASC.
complete_subtask_and_check_parent(pool, subtask_id) -> Result<()> — crates/storage/src/database.rs:447
Business logic, in one function so it lives in one transaction:
- Mark the subtask done.
- If every sibling subtask is now done, mark the parent done with
auto_completed = 1.
The auto_completed flag (v13) lets list_recent_completions exclude cascade-completed parents from the daily-count sparkline so the user sees one count per real action, not one count per parent action plus one per subtask completion.
complete_task(pool, id) — crates/storage/src/database.rs:498
UPDATE tasks SET done = 1, done_at = datetime('now') WHERE id = ?.
uncomplete_task(pool, id) — crates/storage/src/database.rs:507
UPDATE tasks SET done = 0, done_at = NULL, auto_completed = 0 WHERE id = ?.
This is the function flagged in the 2026-04-22 review as RB-15 ("doesn't reopen auto-completed parents"). The current implementation only un-completes the row passed in. If a user un-completes a child of an auto-completed parent, the parent stays done. The fix is to walk up the parent_task_id chain and reset auto-completed parents whose subtasks are no longer all-done. Tracked as a release-blocker (per code-review-2026-04-22.md); resolution status to verify against current HANDOVER.md.
delete_task(pool, id) — crates/storage/src/database.rs:550
DELETE FROM tasks WHERE id = ?. The parent_task_id REFERENCES tasks(id) ON DELETE CASCADE from migration v3 removes child subtasks automatically.
list_recent_completions(pool, days) -> Result<Vec<DailyCompletionCount>> — crates/storage/src/database.rs:578
The Phase 8 momentum sparkline. Steps:
- Clamp
daysto[1, 365]. - Group
tasksrows byDATE(done_at, 'localtime')fordone = 1 AND auto_completed = 0. - Compose a fixed-length output array by left-joining the grouped result against an N-day "spine" generated by re-querying SQLite for each offset (so SQLite owns the calendar arithmetic, including DST and month boundaries).
Today the frontend always asks for days = 7. The clamp prevents a wild value producing an empty or huge series.
Watch-outs
uncomplete_taskdoes not reopen auto-completed parents. RB-15 in the 2026-04-22 review. Status: see slice debt section inREADME.md.list_tasksreturns every task ever created. Soft-delete is not modelled; deleted tasks are gone. Completed tasks pile up over time. Frontend is responsible for filtering.- Energy column accepts NULL but the
CHECKonly fires on non-NULL values. Migration v11's check isCHECK (energy IN ('high', 'medium', 'brain_dead') OR energy IS NULL). Worth a regression test that asserts an invalid string fails. auto_completedflag is set in code, not via trigger. A future migration that adds another path to "complete this task" (eg an LLM-driven auto-complete) needs to remember to set the flag. Worth a row-level audit.
See also
- Schema and migrations — v3, v4, v11, v13 are the relevant migrations.
- Storage overview
- Slice 4 LLM decompose-task — caller of
insert_subtask.