Files
Lumotia/docs/architecture-map/02-tauri-runtime/commands/tasks.md
Jake 26c7307607
Some checks failed
check / cargo check (macos-latest) (push) Has been cancelled
check / cargo check (ubuntu-22.04) (push) Has been cancelled
check / cargo check (windows-latest) (push) Has been cancelled
check / svelte build + lint (push) Has been cancelled
agent: lumotia-rebrand — docs, scripts, root config, residuals
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>
2026-05-13 12:38:03 +01:00

120 lines
8.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
name: Tasks and decomposition
type: architecture-map-page
slice: 02-tauri-runtime
last_verified: 2026/05/09
---
# `commands::tasks`
> **Where you are:** [Architecture map](../../README.md) → [Tauri runtime](../README.md) → [Commands](README.md) → Tasks
**Plain English summary.** Eleven commands wrapping the `lumotia_storage` task CRUD plus two LLM-driven actions. Standard CRUD: create / list / update / complete / uncomplete / delete a task, plus subtask CRUD (insert / list / complete) and a daily-completion-counts query for the Phase 8 Tasks-page momentum sparkline. The two LLM actions: `decompose_and_store` (break a parent task into subtasks via the local LLM with HITL feedback as few-shot exemplars) and `extract_tasks_from_transcript_cmd` (extract task lines from a recently-finished transcript, again with feedback exemplars).
## At a glance
- Path: `src-tauri/src/commands/tasks.rs`.
- LOC: 402.
- Tauri commands exposed (11 total):
- `create_task_cmd(state, request: CreateTaskRequest) -> Result<TaskDto, String>`.
- `list_tasks_cmd(state) -> Result<Vec<TaskDto>, String>`.
- `update_task_cmd(state, id, patch: UpdateTaskRequest) -> Result<TaskDto, String>`. Patch shape (text / bucket / list_id / effort / notes).
- `complete_task_cmd(state, id) -> Result<(), String>`. Stamps server-side `done_at`.
- `delete_task_cmd(state, id) -> Result<(), String>`.
- `uncomplete_task_cmd(state, id) -> Result<(), String>`. Clears `done_at`.
- `set_task_energy_cmd(state, id, energy: Option<String>) -> Result<TaskDto, String>`. Always writes; passes `None` to clear.
- `decompose_and_store(state, parent_task_id, profile_id) -> Result<Vec<TaskDto>, String>`.
- `extract_tasks_from_transcript_cmd(state, transcript, profile_id) -> Result<Vec<String>, String>`.
- `list_subtasks_cmd(state, parent_task_id) -> Result<Vec<TaskDto>, String>`.
- `complete_subtask_cmd(state, subtask_id) -> Result<(), String>`.
- `list_recent_completions_cmd(state, days) -> Result<Vec<DailyCompletionCount>, String>`.
- Events emitted: none.
- Depends on: `lumotia_storage::{insert_task, list_tasks, update_task, complete_task, uncomplete_task, delete_task, set_task_energy, get_task_by_id, insert_subtask, list_subtasks, complete_subtask_and_check_parent, list_recent_completions, list_feedback_examples, FeedbackTargetType, TaskRow, DailyCompletionCount, FeedbackRow}`, `lumotia_llm::prompts::FeedbackExample`, `uuid::Uuid`.
- Called from frontend at: Tasks page (CRUD, subtasks, sparkline), dictation result panel ("Extract tasks" button), parent-task expand UI ("Decompose").
## What's in here
### `TaskDto` (`src-tauri/src/commands/tasks.rs:24`)
camelCase frontend shape with id, text, bucket, listId, effort, notes, done, doneAt, createdAt, sourceTranscriptId, parentTaskId, energy.
### Energy validation (`:60`)
`ENERGY_LEVELS = ["high", "medium", "brain_dead"]` — kept as a const so frontend and storage validate against the same set. SQLite migration v11 enforces the same set with a CHECK constraint.
### CRUD wrappers
- `create_task_cmd` (`:92`) inserts and re-fetches so the frontend gets the canonical row (server-side timestamps, defaulted columns).
- `update_task_cmd` (`:140`) — patch shape, COALESCE-style updates in storage. `done` / `doneAt` are deliberately absent — those flow through `complete_task_cmd` / `uncomplete_task_cmd`.
- `set_task_energy_cmd` (`:200`) — separate from update because update uses `COALESCE` semantics where `None` means "preserve". This command always writes, so passing `None` actually clears the column.
### `to_llm_examples` (`:222`)
Converts feedback rows from storage into the few-shot exemplar shape the LLM crate consumes. Reconstructs `input` from `context_json` (parent task text or transcript chunk). Rows with malformed `context_json` are logged and dropped instead of silently filtered, so data-integrity regressions surface.
### `example_char_cost` (`:263`) and `trim_to_budget` (`:276`)
Char budget for the few-shot exemplars: keeps the prompt under token budget regardless of how many feedback rows the user has accumulated. Cap at 5 examples AND a char budget.
### `decompose_and_store` (`:290`)
1. Fetch the parent task or 404.
2. Pull up to 5 micro-step feedback exemplars filtered by profile, char-trimmed.
3. `spawn_blocking` runs `engine.decompose_task_with_feedback(parent_text, &examples)`.
4. Insert each generated step as a subtask (`uuid::v4()` for the id), re-fetch, accumulate.
5. Return the list.
NO `PowerAssertion::begin` here — the App Nap pattern is consistent in `commands::llm` but not yet uniformly applied here. Flag for follow-up.
### `extract_tasks_from_transcript_cmd` (`:345`)
Same shape as `decompose_and_store` but with `FeedbackTargetType::TaskExtraction` and the engine's `extract_tasks_with_feedback`. Returns the raw task strings — the frontend decides whether to insert them.
### `list_subtasks_cmd` / `complete_subtask_cmd` (`:370`, `:381`)
`complete_subtask_cmd` calls `complete_subtask_and_check_parent` which is the storage helper that stamps the subtask done and, if all siblings are done, also stamps the parent (Phase 8 micro-completion bookkeeping).
### `list_recent_completions_cmd` (`:394`)
Fixed-length oldest-first daily counts. Empty days are explicit zeros. The Tasks-page sparkline reads this directly.
## Data flow
```
Tasks page mount -> list_tasks_cmd -> [TaskDto, ...]
Tasks page create -> create_task_cmd(req) -> TaskDto
Tasks page edit -> update_task_cmd(id, patch) -> TaskDto
Tasks page check -> complete_task_cmd(id) -> ()
Tasks page reopen -> uncomplete_task_cmd(id) -> ()
Tasks page energy chip -> set_task_energy_cmd(id, energy) -> TaskDto
Tasks page sparkline -> list_recent_completions_cmd(days) -> [{date, count}, ...]
Tasks page Decompose button -> decompose_and_store(parent_id, profile_id)
-> get parent task
-> list 5 micro-step feedback rows (profile-scoped)
-> trim_to_budget
-> spawn_blocking(engine.decompose_task_with_feedback)
-> insert subtasks
-> [TaskDto, ...]
Dictation panel Extract tasks -> extract_tasks_from_transcript_cmd(text, profile_id)
-> list 5 task-extraction feedback rows
-> spawn_blocking(engine.extract_tasks_with_feedback)
-> [String, ...]
```
## Watch-outs
- **No `ensure_main_window` guard.** Tasks UI lives in the main window AND in the always-on-top task float (which has the secondary-windows capability). The float can call list / complete / set-energy / list-recent-completions etc. — that's intentional — but it can also fire `decompose_and_store` and `extract_tasks_from_transcript_cmd`, which spend LLM tokens. If you want to lock this down, add the guard to the LLM-spending commands.
- **No `PowerAssertion`.** `decompose_and_store` and `extract_tasks_from_transcript_cmd` both run synchronous LLM inference for several seconds. macOS can App Nap them. Add `PowerAssertion::begin("lumotia LLM task decomposition")` and similar.
- **The patch shape passes `Option<String>` for every column.** Currently the storage layer's `update_task` uses COALESCE: `Some` overwrites, `None` preserves. This means there's no way to clear `notes` or `effort` to empty via `update_task_cmd` — you can only set them to a non-empty string. If a user wants to clear a field, they'd need a fresh task or a dedicated clear command.
- **Decompose stores subtasks one-by-one in a loop** (`:329`). Each iteration is a separate DB transaction. Acceptable for typical 37-step decompositions; if a future LLM produces 50 steps, batch the inserts.
- **`extract_tasks_from_transcript_cmd` returns just `Vec<String>` and does NOT insert.** Frontend chooses what to insert. Confusing because the sibling `decompose_and_store` *does* insert. Convention is dictated by UX (decomposition is one-click, extraction reviews-then-inserts).
## See also
- [LLM](llm.md) — the engine that runs the decomposition / extraction.
- [Feedback](feedback.md) — the table that feeds the few-shot exemplars.
- [Profiles](profiles.md) — `profile_id` is the scoping key for feedback rows.
- [Window management](windows.md) — the task-float window (`tasks-float`) that consumes `list_tasks_cmd`.