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>
91 lines
5.4 KiB
Markdown
91 lines
5.4 KiB
Markdown
---
|
|
name: Implementation intentions
|
|
type: architecture-map-page
|
|
slice: 02-tauri-runtime
|
|
last_verified: 2026/05/09
|
|
---
|
|
|
|
# `commands::intentions`
|
|
|
|
> **Where you are:** [Architecture map](../../README.md) → [Tauri runtime](../README.md) → [Commands](README.md) → Implementation intentions
|
|
|
|
**Plain English summary.** Phase 7. Tiny "if-then" automation rules: "at 09:00 each day, surface my Inbox", "when the morning triage finishes, start a 5-minute timer", "when task X is completed, speak 'Nice'". Frontend owns scheduling and execution because v1 triggers are app-local events plus a local clock check; Rust owns durable storage, validation, and a main-window-only firewall.
|
|
|
|
## At a glance
|
|
|
|
- Path: `src-tauri/src/commands/intentions.rs`.
|
|
- LOC: 308.
|
|
- Tauri commands exposed (5 total), all main-window only:
|
|
- `list_implementation_rules(window, state) -> Result<Vec<ImplementationRuleDto>, String>`.
|
|
- `create_implementation_rule(window, state, request: CreateImplementationRuleRequest) -> Result<ImplementationRuleDto, String>`.
|
|
- `set_implementation_rule_enabled(window, state, id, enabled) -> Result<ImplementationRuleDto, String>`.
|
|
- `mark_implementation_rule_fired(window, state, id, fired_key) -> Result<ImplementationRuleDto, String>`.
|
|
- `delete_implementation_rule(window, state, id) -> Result<(), String>`.
|
|
- Events emitted: none.
|
|
- Depends on: `magnotia_storage::{insert_implementation_rule, list_implementation_rules, set_implementation_rule_enabled, mark_implementation_rule_fired, delete_implementation_rule, get_task_by_id, ImplementationRuleRow}`, `uuid::Uuid`, `serde_json`. Plus `commands::security::ensure_main_window`.
|
|
- Called from frontend at: Settings → Implementation intentions (CRUD); the rule-runner module (`mark_implementation_rule_fired` after firing).
|
|
|
|
## What's in here
|
|
|
|
### Trigger and action enums (`src-tauri/src/commands/intentions.rs:21`)
|
|
|
|
- `RuleTriggerKind`: `TimeOfDay`, `TaskCompleted`, `MorningTriageFinished`. Serialises as `"time_of_day"`, `"task_completed"`, `"morning_triage_finished"`.
|
|
- `SurfaceTarget`: `Inbox`, `Today`, `Tasks`, `Task`. The `Task` variant requires a `taskId`.
|
|
- `RuleAction` (tagged enum, `kind` discriminator): `Surface { target, taskId, label }`, `StartTimer { seconds, taskId, label }`, `SpeakLine { text }`.
|
|
|
|
### `CreateImplementationRuleRequest` (`:72`)
|
|
|
|
`triggerKind`, `triggerValue: String`, `actions: Vec<RuleAction>`, `enabled` (default true), `lastFiredKey: Option<String>`.
|
|
|
|
### `ImplementationRuleDto` (`:88`) and `TryFrom` (`:100`)
|
|
|
|
The DTO unpacks the storage row's `actions_json` back into typed `Vec<RuleAction>`. Returns Err on unknown `trigger_kind` strings or invalid JSON — surfaces drift instead of silently swallowing.
|
|
|
|
### Validators
|
|
|
|
- `validate_hhmm(value)` (`:125`) — strict HH:MM, two digits each, hours 0..=23, minutes 0..=59. Tested.
|
|
- `validate_actions(state, actions)` (`:144`):
|
|
- At least one action.
|
|
- `Surface { target: Task }` requires a `taskId` and the task must exist (via `get_task_by_id`).
|
|
- `StartTimer` rejects anything other than 300 seconds (v1 fixed at 5 minutes).
|
|
- `SpeakLine` requires non-empty text and ≤ 240 chars.
|
|
- `validate_request(state, request)` (`:192`) — checks the trigger value matches the trigger kind (`TimeOfDay` requires HH:MM, the others reject any non-empty trigger_value).
|
|
|
|
### Commands (`:207` onwards)
|
|
|
|
All five commands `ensure_main_window`. CRUD is otherwise straight wrapping. `create_implementation_rule` generates a UUIDv4, serialises actions to JSON, normalises `trigger_value` (empty for non-time triggers), inserts, and returns the freshly-fetched DTO. `mark_implementation_rule_fired` requires a non-empty `fired_key` (the frontend uses ISO date strings to dedupe daily fires).
|
|
|
|
### Tests (`:296`)
|
|
|
|
`hhmm_validation_accepts_and_rejects_expected_shapes` covers a handful of valid and invalid clock strings.
|
|
|
|
## Data flow
|
|
|
|
```
|
|
Settings -> create_implementation_rule(req)
|
|
-> ensure_main_window
|
|
-> validate_request (HH:MM if applicable, action shape, task existence for surface-task)
|
|
-> uuid::v4 for id
|
|
-> serde_json serialise actions
|
|
-> magnotia_storage::insert_implementation_rule
|
|
-> list/return DTO
|
|
|
|
frontend rule-runner cron -> evaluates triggers in JS
|
|
-> when a rule fires: invoke('mark_implementation_rule_fired', id, "2026-05-09")
|
|
-> stamp the dedupe key in DB
|
|
```
|
|
|
|
## Watch-outs
|
|
|
|
- **Action timer is hard-capped to 300 seconds.** The frontend has to surface this constraint. Future versions will need to widen the validator to accept arbitrary durations.
|
|
- **Rule execution lives entirely in the frontend.** If the user closes Magnotia at 08:55 with a 09:00 rule, the rule does not fire. There is no daemon. This is documented in the module header.
|
|
- **Validation is sync against the DB.** `validate_actions` calls `get_task_by_id` for every surface-task action. Multiple actions in one rule = multiple round-trips. Acceptable today; consider batching if a rule grows huge.
|
|
- **`mark_implementation_rule_fired` requires a non-empty `fired_key`.** The frontend should always pass an ISO date for daily rules and a per-event key for task-completed rules. Empty key = command rejection.
|
|
- **No `PowerAssertion`.** None of these actions run inference. No need.
|
|
|
|
## See also
|
|
|
|
- [Tasks](tasks.md) — `Surface { target: Task }` and `StartTimer { taskId }` reference task ids.
|
|
- [TTS](tts.md) — `SpeakLine` actions are dispatched by the frontend through `tts_speak`.
|
|
- [Capabilities and ACL](../capabilities-and-acl.md) — main-window-only is the firewall here.
|