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>
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: `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<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
|
|
-> 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.
|