--- 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, String>`. - `create_implementation_rule(window, state, request: CreateImplementationRuleRequest) -> Result`. - `set_implementation_rule_enabled(window, state, id, enabled) -> Result`. - `mark_implementation_rule_fired(window, state, id, fired_key) -> Result`. - `delete_implementation_rule(window, state, id) -> Result<(), String>`. - Events emitted: none. - Depends on: `lumotia_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`, `enabled` (default true), `lastFiredKey: Option`. ### `ImplementationRuleDto` (`:88`) and `TryFrom` (`:100`) The DTO unpacks the storage row's `actions_json` back into typed `Vec`. 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 -> lumotia_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 Lumotia 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.