Brand guidelines (docs/brand/magnotia-brand-guidelines.md §4) say "Never go
below 12px for any text" and "tertiary text must be ≥18px bold or ≥24px
regular". Audit found 246 sub-12px className sites and 166 cases of
text-text-tertiary paired with body sizes.
Bumped text-[9/10/11px] → text-[12px] across 26 .svelte files in src/lib
and src/routes. Promoted text-text-tertiary → text-text-secondary at body
sizes (12-14px) where context allowed; left placeholder pseudo-states,
ternary-conditional inactive states, and line-through "done" states alone
(8 residual pairings, all decorative).
Affects neurodivergent users on 1366×768 budget hardware most directly.
svelte-check: 0 errors, 0 warnings.
- Add --color-overlay-dim token to app.css @theme (rgba(15,14,12,0.7) dark, rgba(26,24,22,0.55) light); mirror as --overlay-dim in design-system/colors_and_type.css.
- MorningTriageModal: replace inlined rgba scrim with var(--color-overlay-dim), drop backdrop-blur-sm (brand opposes glassmorphism by default), bump container to z-[60] so it sits above the .grain z-50 overlay.
- FilesPage: collapse two near-identical drop-zone blocks into one bordered region with conditional inner content; preserves isDragOver hover behaviour and aria region.
Replace all instances of the legacy product names "Kon" and "Corbie" with
"Magnotia" across user-facing copy, code identifiers, package names, bundle
ids, file paths, and documentation. Preserves the unrelated "konsole" (KDE
terminal) reference and the parent CORBEL company name.
- Renames 10 Rust crates (kon-* → magnotia-*) and the tauri binary
- Updates package.json, tauri.conf.json (productName + identifier)
- Renames CSS classes (kon-rh-* → magnotia-rh-*) and animations
- Renames brand and roadmap docs
- Regenerates Cargo.lock and package-lock.json
Verified: svelte-check passes; pure-rust crates compile under new names.
Mechanical fixes from `npx impeccable detect`:
- Sidebar.svelte: replace `transition: width, min-width` on the aside
with a wrapping CSS-grid container animating `grid-template-columns`.
Avoids per-frame layout cost from animating `width` directly.
- MorningTriageModal.svelte: swap pure `bg-black/50` overlay for the
brand deep-neutral `rgba(26, 24, 22, 0.5)` (#1a1816 @ 50%). TODO left
in source to promote this to a `--color-overlay` token in app.css.
- Toggle.svelte: drop bouncy `cubic-bezier(0.34, 1.56, 0.64, 1)`
(1.56 overshoot) for ease-out-quart `cubic-bezier(0.16, 1, 0.3, 1)`.
Still snappy, no overshoot — better fit for a toggle.
- TasksPage.svelte: same grid-template-columns refactor as Sidebar
for the list-sidebar `transition: width` declaration.
Verification:
- npm run check: 1 pre-existing error (vite.config.js:5), 1 unrelated
warning in SettingsGroup (out of scope, owned by parallel subagent).
- npm run build: clean.
- cargo clippy --all-targets -- -D warnings: clean (with LIBCLANG_PATH).
- cargo test --workspace: 283 passed, 0 failed.
- cargo fmt --check: pre-existing diffs in main.rs / lib.rs (no Rust
files were touched in this commit).
Manual smoke deferred — `npm run dev` is in use by the SettingsPage
subagent, so the build-clean signal is the proxy.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase 10a a11y audit (2026-04-29) flagged the morning triage modal as
having role="dialog" and aria-modal="true" but no focus trap, so Tab
leaks out to the sidebar/page beneath. Escape-to-close was already
wired and is preserved.
Behaviour now matches the W3C dialog pattern:
- On open, capture the invoking element (document.activeElement) and
move focus to the first focusable inside the dialog.
- Tab cycles forward; Shift+Tab cycles backward. Wrap-around between
first and last focusable.
- On close, restore focus to the captured invoker.
- The dialog container itself gets tabindex=-1 so it can hold focus
if no children are focusable (e.g. during the loading state).
The focusable selector excludes aria-hidden elements and elements with
no offsetParent (display:none / visibility:hidden), so dynamic content
between loading -> tasks -> action buttons stays in sync.
Resolves: G4.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Small if-then automation layer. Rules persist in SQLite; the runner
lives on the frontend and binds to the Phase 6 event bus so the rule
pipeline reuses the same delivery primitives (timer events, TTS,
Tasks navigation).
Storage:
- Migration v12 adds implementation_rules (id, enabled, trigger_kind,
trigger_value, actions_json, last_fired_key, created_at,
updated_at) with enabled+trigger_kind index for the runner's hot
path.
- CRUD helpers: insert / list / get / set-enabled / mark-fired /
delete, plus a round-trip test.
Commands (all main-window-guarded via ensure_main_window):
- list_implementation_rules
- create_implementation_rule — validates HH:MM, checks the target
task exists at save time for surface-task actions, caps
speak-line at 240 chars, pins v1 timers to 5 minutes.
- set_implementation_rule_enabled
- mark_implementation_rule_fired — main-thread idempotency shim so
the runner can atomically claim a fire.
- delete_implementation_rule
Runner (implementationIntentions.svelte.ts):
- Subscribes to kon:task-completed and kon:morning-triage-finished
(MorningTriageModal now emits on all three exit paths — empty,
skipped, picked — so skip counts as finishing).
- 30 s poll for time-of-day rules, plus an immediate check on startup
so a rule whose time has already passed today catches up once.
- Idempotency via last_fired_key composed as YYYY-MM-DD@HH:MM for
time rules; new time rules whose HH:MM has already passed today
are pre-seeded so they don't fire retroactively on save.
- Rules are paused when Nudges "Mute for now" is on — a hard mute
stops all rule delivery in addition to OS notifications.
- Stale-task safety: if a surface-task action's target has been
deleted, the runner opens Tasks and warns clearly rather than
pretending to surface something that's gone.
Editor (ImplementationRulesEditor.svelte):
- Lives in Settings under a new "If-then rules" accordion section.
- `If` picker: time of day (with time input), a task completes,
morning triage finishes.
- `Then` composer: optional surface (inbox / today / all tasks /
specific task), optional 5-min timer, optional speak-aloud line.
- Saved rules list with enable toggle + delete.
Rules table integration for Phase 10b rename sweep: add
implementation_rules to the kon.db → corbie.db migration shim when
that phase lands.
Gates: fmt, clippy -D warnings, cargo test 265/0, svelte-check
0/0, npm build green. Pre-existing Vite chunk warning on sounds.ts
is unrelated to Phase 7.
Three opt-in rituals, all default OFF. Research-anchored (Barkley's
point-of-performance, Sweller cognitive-load theory, Newport shutdown
ritual, Gollwitzer implementation intentions, Thaler/Sunstein nudge
with informed consent for the ADHD audience).
Morning triage: modal gated on ritualsMorning toggle, configurable
trigger time (default 08:00 to respect ADHD sleep inertia rather than
the spec's 06:00), "pick up to three for today" with a gentle swap
message on the fourth attempt. Skip sets last-shown-today so it never
re-prompts the same calendar day. last-shown persists via kon_storage.
Evening wind-down: dedicated page, user-triggered only (tray menu +
Settings button). Mechanical closure + physical reset + intentional
cue — the whole Newport template. Open loops are read-only reflection;
Tasks page owns transactions. Copy is additive throughout: "you
finished X today", never "you didn't finish Y".
Autostart: tauri-plugin-autostart registered (LaunchAgent on macOS,
.desktop on Linux, registry Run on Windows). No bespoke Rust commands
— frontend calls the plugin's invoke-handlers directly. Toggle in
Settings is one-way (click → OS call → state update) to avoid the UI
lying during the round-trip. First-run presents a forced-choice prompt
for all three options, with "skip all" escape hatches per step.
Copy audit against RSD literature: no "overdue", "failed", or
day-to-day comparison framing anywhere in ritual surfaces.
Post-v0.1 ideas captured in the roadmap: calendar integration
(read-only ICS as interim, cloud sync parked) and right-click-to-task
(in-app simple, system-wide a separate phase).