Commit Graph

69 Commits

Author SHA1 Message Date
3770815fbf agent: lumotia — v0.1 release-completion run
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
Closes the code-side v0.1 ship gate. All quality gates green:
cargo fmt/clippy/test (~327 tests), npm check (0/0), vitest 13/13,
scripts/dogfood-rebrand-drill.sh 8/8.

Phase F — first-run onboarding promoted to v0.1
- FirstRunPage with skip-to-main + failure recovery + event recording
- Six onboarding commands (record/list/has-completed + lumotia_events)
- Storage migration v17 (onboarding_events + lumotia_events tables)

UI hardening (in-scope items from v0.1-ui-hardening.md)
- StatusPill + PostCaptureCard components, 21st preview entry
- Sidebar recording-as-sacred-state (opacity + aria-disabled, reduced-motion)
- Settings 6-section regroup + Help section + Activation log + Privacy toggle
- Error-state copy sweep (DictationPage + SettingsPage, plain-language)
- Global :focus-visible rule, textarea outlines restored
- Ctrl+K / Ctrl+, / Escape bindings in +layout

LLM resilience
- rule_based_extract_tasks (regex-free imperative-verb extractor) +
  extract_tasks_with_fallback wrapper — task extraction never returns zero
- tokio::time::timeout(120s) wraps cleanup/tags/tasks commands

Release artefacts
- LICENSE (canonical AGPL-3.0), CHANGELOG (Keep-a-Changelog format)
- v0.1-release-notes, privacy-and-ai-use, install-warnings,
  tester-onboarding-kit, tester-acceptance-runbook, code-signing-setup,
  apple-silicon-rb08-runbook, virtual-audio-setup, v0.1-contrast-audit
- Workspace versioning + AGPL spdx; npm exact-pin (10 ranges removed)
- AppImage SHA-256 sidecar in build.yml
- README v0.1 section + Reporting-issues; canonical repo slug

Closure pass — items moved from human-required to code-complete
- KI-02 Linux idle inhibit: zbus 5 → org.freedesktop.login1.Manager.Inhibit
- KI-03 Windows sleep prevention: SetThreadExecutionState(ES_CONTINUOUS|...)
- acquire/release_idle_inhibit Tauri commands, wired in DictationPage
- Diagnostic-bundle frontend wire-up (Settings → Help button)
- WCAG-AA contrast fix via .btn-filled-text utility (no token changes)
- 8 destructive-action sites wrapped in plain-language confirm() guards
- KNOWN-ISSUES.md + v0.1-known-limitations.md updated (KI-02/03 fixed)

Scripts
- pre-tag-verify.sh, tag-day.sh, smoke-linux + driver
- parse-diagnostic-bundle.sh, parse-activation-log.py

Per-item audit trail: docs/release/v0.1-completion-status.md
Remaining: W-01…W-08 (signing certs, hardware probes, smoke matrix,
tester recruitment) — see docs/release/v0.1-known-limitations.md.
2026-05-15 06:59:08 +01:00
ff8dda06d0 agent: lumotia — Phase A.7 fix startup-order race that silently orphaned legacy data
Critical bug surfaced by the dogfood drill: every upgrading Magnotia user
would silently keep a fresh empty Lumotia install while their Magnotia
data sat orphaned next to it. Drill caught it on the first real run
under sandboxed HOME.

ROOT CAUSE

src-tauri/src/lib.rs::run() previously called the migrations from inside
the Tauri setup hook (post `tauri::Builder::default()`). But three
sequential actions BEFORE the setup hook had already created the
destination directories:

  1. init_tracing() -> logs_dir() -> create_dir_all(app_data_dir/logs)
     creates the lumotia/ root.
  2. install_panic_hook() -> crashes_dir() -> create_dir_all() ditto.
  3. Tauri's WebKitGTK runtime / plugin chain creates the bundle-id-keyed
     consulting.corbel.lumotia/ dir eagerly when the WebContext spins up
     (mediakeys, storage, WebKitCache subdirs appeared even without our
     hook explicitly creating them).

By the time the setup-hook migrations fired, every legacy candidate
returned `TargetAlreadyExists` (paths.rs) or `BothExistLegacyPreserved`
(tauri_app_data_migration.rs) — both silent no-op codepaths. Legacy
data was left untouched, fresh Lumotia install gained no transcripts,
settings, or window state.

FIX

Migrate BEFORE any other code touches app_data_dir().

src-tauri/src/tauri_app_data_migration.rs:
  - NEW_BUNDLE_ID const ("consulting.corbel.lumotia"). MUST agree with
    tauri.conf.json#identifier; reviewer-enforced invariant.
  - Renamed private `legacy_tauri_app_data_dir_for` -> public
    `tauri_app_data_dir_for(identifier)`. Function is parameterised by
    bundle id; the "legacy" name was misleading after this change.
  - New `current_tauri_app_data_dir()` resolves the NEW bundle path
    from platform env vars (same convention Tauri 2 uses), so the
    pre-runtime migration can address its destination without
    needing an AppHandle.

src-tauri/src/lib.rs:
  - New `migrate_user_data_pre_runtime()` orchestrates the two
    migrations + ambiguity guard. Uses `eprintln!` for surface events
    (tracing not yet initialised at this stage; stderr lands in
    journald / foreground terminal which is the right transport for
    boot-phase output). FATAL errors call process::exit(1) — the
    setup-hook version returned Err from the closure, equivalent
    effect.
  - run() now calls migrate_user_data_pre_runtime() as its first line,
    BEFORE init_tracing(), install_panic_hook(), and the Tauri
    builder.
  - Setup-hook migration blocks deleted (~90 lines). Setup hook now
    starts with a one-line comment pointing at the pre-runtime fn.

VERIFICATION

Re-ran the dogfood drill (scripts/dogfood-rebrand-drill.sh) — 8/8 probes
pass after the fix (was 4/8). Both stderr lines fire:

  [lumotia-startup] migrated legacy magnotia data dir to lumotia:
      .../magnotia -> .../lumotia (renamed_db=true, elapsed_ms=0)
  [lumotia-startup] migrated Tauri app_data_dir from legacy bundle
      identifier: .../uk.co.corbel.magnotia ->
      .../consulting.corbel.lumotia (elapsed_ms=0)

On-disk post-state confirms: magnotia/ gone, lumotia/ has migrated db
+ recordings, uk.co.corbel.magnotia/ preserved as backup,
consulting.corbel.lumotia/localStorage/leveldb/ has migrated data.

- cargo fmt --check: clean
- cargo clippy --workspace --all-targets -- -D warnings: clean
- cargo test --workspace: 409/0 (no regression)
2026-05-14 13:59:08 +01:00
27661c816e agent: lumotia — pin rust toolchain + workspace clippy/fmt sweep
rust-toolchain.toml pins to stable 1.94.1 so contributors and CI runners
share the exact rustc / rustfmt / clippy versions. Without the pin, every
machine surfaces a different lint set depending on its local install — six
pre-existing lints showed up on 1.94.1 that 1.93-era HANDOVER reported clean.

Clippy fixes (all pre-existing, not introduced by feature work):

- crates/storage/src/database.rs: std::iter::repeat().take() -> repeat_n()
- crates/llm/src/lib.rs (docs): "+ frontends" was parsed as a markdown bullet
  continuation by rustdoc, breaking doc-lazy-continuation. Reworded to "and".
- crates/llm/src/lib.rs (loop): while-let-on-iterator -> for-loop.
- src-tauri/src/commands/security.rs: .iter().any(|a| *a == x) -> .contains(&x).
- src-tauri/src/lib.rs: io::Error::new(Other, e) -> io::Error::other(e).
- src-tauri/src/tauri_app_data_migration.rs: drop function-tail `return`s
  inside cfg blocks; each platform's block now ends with a tail expression.

cargo fmt sweep across the workspace. Mechanical layout-only changes;
no semantics affected.

Workspace gates after this commit:
- cargo fmt --check: clean
- cargo clippy --workspace --all-targets -- -D warnings: clean
- cargo test --workspace: 405/0 (will become 409/0 with Phase A.1+A.2)
2026-05-14 07:19:59 +01:00
a48653c93c agent: code-atomiser-fix — write_text_file_cmd path scope (Trust-1, corrective)
Corrective re-apply of Trust-1. The original commit a2b47db carried
the wrong staged file due to a parallel-agent race that swapped
the staged path between `git add` and `git commit` — fs.rs was
never actually changed by a2b47db despite the message. This commit
applies the Trust-1 fix properly.

The Tauri command `write_text_file_cmd` previously took an arbitrary
`path: String` and flowed it straight into `tokio::fs::write` with no
main-window guard, no canonicalisation, and no scope check. A
compromised webview could write anywhere the process had write access
— overwriting shell init files, dropping a runner into
`~/.config/autostart`, etc.

This change:

- adds `ensure_main_window(&window)?` so only the main webview can
  invoke the command;
- canonicalises the requested path's parent (rejecting nonexistent
  parents and resolving symlinks) before joining the filename;
- asserts the canonical target sits inside an allowlisted base
  (app data, app local data, downloads, documents, desktop), so a
  `"../../etc/passwd"` payload — even one obtained via symlink trickery
  in the chosen save dir — is refused with a clear error.

Six unit tests cover: outside-allowlist rejection, path-traversal
rejection, nested inside-allowlist acceptance, plain inside-allowlist
acceptance, nonexistent-parent rejection, and the pure
`is_inside_any_base` prefix check.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 17:59:26 +01:00
99f4ecdecc agent: code-atomiser-fix — Tauri commands for trash list + restore
Adds two `#[tauri::command]` wrappers around the soft-delete pair that
landed with migration v16 in commit 15b74db:

  - `list_trashed_transcripts(limit, offset)` mirrors the existing
    `list_transcripts` shape (default 50, clamp 1..=500, offset >= 0)
    so the Trash view can paginate the `deleted_at IS NOT NULL`
    partition with the same UX as the live history list.

  - `restore_transcript(id)` clears `deleted_at`. Idempotent on a live
    row, per the storage-layer contract. Audio at `audio_path` may
    already have been removed by `delete_transcript`'s best-effort
    filesystem cleanup; the text + metadata recovery is what
    restoration actually buys you.

Both are registered in the `tauri::generate_handler!` block in
`src-tauri/src/lib.rs`, alongside the existing transcripts surface.
Mirrors the signature shape of `delete_transcript` — no
`ensure_main_window` because the rest of the transcripts command
surface does not gate on it; introducing that here would be
inconsistent and out of scope for this fix.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 17:58:13 +01:00
15b74db747 agent: code-atomiser-fix — soft-delete transcripts with audio cleanup (Rev-2, Rev-3)
Two interlocking reversibility kills, fixed as one bundle:

Rev-2 (hard-DELETE transcripts, no trash) — delete_transcript previously
issued DELETE FROM transcripts WHERE id = ?, so a single click on the
History "Clear All" / "Confirm" button erased months of dictation with no
trash, no export, no undo. The new contract:
  * delete_transcript UPDATEs deleted_at = datetime('now') and best-effort
    removes the audio file. Idempotent on repeat call.
  * Migration v16 adds `deleted_at TEXT` plus a partial index over the trash
    rows so the purge query stays cheap on long-running databases.
  * get_transcript, list_transcripts_paged, count_transcripts, and
    search_transcripts all filter `deleted_at IS NULL` so trash rows are
    invisible to the regular history view but kept for restore.
  * New list_trashed_transcripts and restore_transcript power the inverse
    trash view; row order is most-recently-deleted first.
  * New purge_deleted_transcripts(older_than_days) hard-removes trash
    older than the retention window. Wired into the Tauri setup hook at
    30-day retention; best-effort, never blocks startup.

Rev-3 (orphan WAV files on transcript delete) — same delete_transcript
function. Audio file at audio_path is now best-effort removed when the
soft-delete actually flips a row; NotFound is the expected case for
repeat deletes and is not logged. purge_deleted_transcripts also retries
audio removal as belt-and-braces.

Adds 4 regression tests: delete_transcript_soft_deletes,
delete_transcript_removes_audio_file, list_transcripts_excludes_soft_deleted
(also covers restore_transcript), and purge_deleted_transcripts_hard_deletes_old.
Plus migration_v16_adds_deleted_at_column_and_index already in place.

Frontend UI (HistoryPage clearAll type-the-word modal + View trash path)
deferred to a follow-up commit; the backend contract is complete and the
existing arm-confirm flow now soft-deletes instead of hard-deleting, so
the user-data-loss class is closed for the live release path. Rev-4
(SettingsPage deleteProfile routing) also deferred.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 17:39:03 +01:00
afbd33d33e agent: code-atomiser-fix — test_llm_model respects caller GPU preference (Race-8)
Add an Option<bool> use_gpu parameter to test_llm_model with the same
default-true semantics as load_llm_model. Hard-coding true triggered an
engine tear-down/rebuild when a parallel load_llm_model(use_gpu=false)
was in flight (LlmEngine::load_model triples on id+path+use_gpu) and
silently flipped the user's GPU mode underneath the test.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 15:12:24 +01:00
3f4e5cc9a4 agent: code-atomiser-fix — migrate Tauri app_data_dir on bundle-identifier change
Commit 14313cf changed the Tauri bundle identifier from
uk.co.corbel.magnotia to consulting.corbel.lumotia. Tauri 2 keys both
app_data_dir and the webview's data store (localStorage, IndexedDB,
cookies, service worker, cache) plus all Tauri-plugin state files
(window-state geometry, autostart enable flag) by the identifier.
Without an explicit migration, every user's webview state was
silently orphaned on first launch under the new identifier; the
JS-side migrateLocalStorageKey helper introduced in 1608109 ran
against an empty store and no-op'd.

Fix: in the Tauri setup hook, before webview navigation, resolve the
legacy app_data_dir under the OLD bundle identifier and recursively
copy it to the new identifier's app_data_dir via an atomic staging
rename. Idempotent: legacy preserved as a backup; if both paths
exist post-rename, a warning is logged and the new path is used
unchanged.

Regression tests cover the migrate-only, both-exist, no-legacy, and
idempotent (run twice) cases against synthetic legacy/new paths.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 14:35:42 +01:00
6ca94cbff0 agent: code-atomiser-fix — paths.rs multi-legacy-candidate migration + copy_dir_recursive symlink loop
Two reversibility defects in `crates/core/src/paths.rs`:

Defect A (multi-legacy-candidate orphan):
`resolve_app_data_dir` and `legacy_and_target_paths` short-circuited
on the first legacy candidate, allowing two reachable orphan scenarios
on Linux. With both `~/.magnotia` and `~/.local/share/magnotia` the
shim migrated only the dot-home variant, leaving the XDG legacy
invisible forever. With a stray `~/.lumotia` alongside a freshly
migrated `~/.local/share/lumotia`, the resolver kept returning the
dot-home path, orphaning the XDG target.

`legacy_and_target_paths` now returns `Vec<(legacy, target)>`,
probing every legacy variant the platform supports. The migration
driver in `src-tauri/src/lib.rs` loops over the Vec and emits
per-candidate tracing. A new `resolve_app_data_dir_strict` +
`check_target_ambiguity` API refuses to start when more than one
target candidate exists post-migration, surfacing both paths to the
user via the setup hook instead of silently picking one.

Regression tests: `migrate_handles_both_dot_home_and_xdg`,
`resolve_app_data_dir_refuses_on_multiple_targets`.

Defect B (copy_dir_recursive symlink loop on EXDEV migration):
`entry.metadata()` follows symlinks, so a directory symlink reported
is_dir==true and recursed unconditionally. A self-referential or
ancestor-targeting directory symlink would loop until the disk
filled. Switched to `entry.file_type()` (symlink-aware), re-ordered
branches so `is_symlink()` is checked first, and routed all
symlinks through symlink-creation (Unix + Windows) rather than
recursive copy.

Regression tests:
`copy_dir_recursive_does_not_loop_on_self_referential_dir_symlink`,
`copy_dir_recursive_preserves_directory_symlinks`.

14/14 paths tests green. Full workspace cargo test green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 14:29:28 +01:00
ab5f6ab995 agent: code-atomiser-fix — repair sed-scar provenance from 26c7307
The Magnotia->Lumotia rebrand commit 26c7307 ran a too-greedy
s/magnotia/lumotia/g across comments that already had the correct
legacy/target distinction. The commit author caught one case in
src-tauri/src/lib.rs ("magnotia era" -> "lumotia era" restored) but
missed four others, plus a duplicated word in a Cargo description
from an earlier two-name sed.

Fixed sites:
  * crates/storage/src/database.rs        — migrate_legacy_setting_keys docstring
  * src/lib/utils/localStorageMigration.ts — file-level docstring
  * src/lib/utils/settingsMigrations.ts    — historical-key comment
  * crates/cloud-providers/Cargo.toml      — description duplication
  * src-tauri/src/lib.rs                   — data-dir migration log + doc

None of the underlying code paths were wrong; the lies were
docstring-only. Risk: future maintainer reading any of these
comments at incident time could invert the migration direction or
conclude no migration ran.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 14:27:24 +01:00
26c7307607 agent: lumotia-rebrand — docs, scripts, root config, residuals
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
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
9336286e3c agent: lumotia-rebrand — fix QC blockers for phase 5
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
Phase 5 QC found two blockers + four advisories. All addressed:

B1 (FATAL) — Migration error now aborts startup instead of silently
  continuing past it. Without this fix a transient EACCES / EXDEV / ENOSPC
  would log a warning, init_db would create a fresh empty lumotia dir,
  and the user would appear to lose their transcripts.

B2 (FATAL) — Linux dot-home vs XDG mismatch. The old probe returned
  ~/.magnotia as legacy but the caller passed app_data_dir() as the new
  path — which could be $XDG_DATA_HOME/lumotia. fs::rename across
  filesystems would EXDEV-fail; even when it succeeded the user's
  storage convention silently changed.

  Refactored: legacy_and_target_paths() returns the (legacy, target)
  pair together. Dot-home legacy lands in ~/.lumotia; XDG-set legacy
  lands in $XDG_DATA_HOME/lumotia; XDG-default legacy lands in
  ~/.local/share/lumotia. macOS / Windows / non-tier-1 unchanged.

  migrate_legacy_data_dir() now takes no argument; src-tauri caller
  updated.

A1 — Removed dead new_db.exists() check inside rename_db_file_if_present
  (unreachable: rename_db is called only AFTER the legacy dir was just
  renamed to the new path, so a stray lumotia.db there is impossible).

A2 — Added 4 unit tests for migrate_legacy_setting_keys: lone-magnotia,
  both-present (orphan delete), no-magnotia, idempotent.

A3 — migrate_legacy_setting_keys now returns (renamed, orphans_deleted).
  When both keys exist, the legacy magnotia row is DELETED in the same
  transaction (lumotia row is authoritative).

A4 — Added dot-home convention regression test in paths::tests.

cargo build --workspace passes. cargo test --workspace: 339 pass, 0 fail
(up from 334 in the original Phase 5 commit; +5 new tests).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 09:43:45 +01:00
86f83b7a45 agent: lumotia-rebrand — data dir migration shim + paths.rs rename
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
Phase 5 of the rebrand cascade per locked decision D1 (migrate in place).

crates/core/src/paths.rs:
- Hardcoded subdir strings renamed: magnotia/Magnotia -> lumotia/Lumotia
  across all four OS branches (Linux XDG + dot-legacy, macOS Application
  Support, Windows LOCALAPPDATA, fallback dot-dir).
- Database filename: magnotia.db -> lumotia.db.
- Test path fixtures renamed: /tmp/magnotia-test -> /tmp/lumotia-test.
- New MigrationStatus enum (Migrated / TargetAlreadyExists / NoLegacyFound).
- New migrate_legacy_data_dir() that probes the platform-correct legacy
  magnotia path, renames it to the lumotia equivalent via fs::rename, and
  also renames magnotia.db -> lumotia.db inside if found. Idempotent: safe
  to call on every boot. Refuses to overwrite an existing lumotia dir to
  protect user data.
- Four new unit tests covering all branches via the test-friendly
  migrate_legacy_data_dir_inner that takes an explicit legacy path.

crates/storage/src/database.rs:
- New migrate_legacy_setting_keys(pool) that renames any settings rows
  with key matching magnotia_* to lumotia_*. Single SQL UPDATE with
  NOT EXISTS guard so it leaves rows alone if a lumotia_ row already
  exists. Re-exported from lib.rs.

src-tauri/src/lib.rs:
- Calls migrate_legacy_data_dir(&app_data_dir()) at the start of setup()
  BEFORE database_path() resolves the now-renamed dir. Logs migration
  outcome to lumotia_startup tracing target.
- Calls migrate_legacy_setting_keys(&db) immediately after init_db
  returns. Logs only when rows are actually renamed.

src-tauri/src/{lib,commands/{diagnostics,rituals,transcripts}}.rs +
crates/storage/src/database.rs:
- All in-code references to magnotia_preferences, magnotia_history (in
  comments), and magnotia_morning_triage_last_shown renamed to lumotia_*.

cargo build --workspace passes. cargo test --workspace: 334 pass, 0 fail
(up from 330; +4 paths::tests).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 09:21:45 +01:00
e2a5feb718 agent: lumotia-rebrand — tracing filter targets
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
Phase 4 of the rebrand cascade. Updates the default RUST_LOG filter
in src-tauri/src/lib.rs and all 'target: "magnotia_startup"' string
literals across src-tauri/src/lib.rs and src-tauri/src/commands/models.rs.

Default filter (before):
  warn,magnotia=info,lumotia_lib=info,lumotia_audio=info,
  magnotia_startup=info,lumotia_hotkey=info,lumotia_ai_formatting=info

Default filter (after):
  warn,lumotia=info,lumotia_lib=info,lumotia_core=info,
  lumotia_audio=info,lumotia_hotkey=info,lumotia_ai_formatting=info,
  lumotia_llm=info,lumotia_storage=info,lumotia_transcription=info,
  lumotia_startup=info

magnotia_startup was a logical tracing target (not a crate); renamed
to lumotia_startup for consistency. magnotia_lib was already renamed
to lumotia_lib in Phase 2. Added per-crate filter entries for parity
across the workspace.

cargo build --workspace passes. cargo test --workspace: 330 pass, 0 fail.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 09:00:33 +01:00
089349d966 agent: lumotia-rebrand — rust workspace crates magnotia-* -> lumotia-*
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
Phase 2 of the rebrand cascade. Renames all 9 workspace crates from
magnotia-* to lumotia-* plus the src-tauri binary crate name:

- magnotia-ai-formatting   -> lumotia-ai-formatting
- magnotia-audio           -> lumotia-audio
- magnotia-cloud-providers -> lumotia-cloud-providers
- magnotia-core            -> lumotia-core
- magnotia-hotkey          -> lumotia-hotkey
- magnotia-llm             -> lumotia-llm
- magnotia-mcp             -> lumotia-mcp
- magnotia-storage         -> lumotia-storage
- magnotia-transcription   -> lumotia-transcription
- magnotia                 -> lumotia (src-tauri binary)
- magnotia_lib             -> lumotia_lib (src-tauri lib target)

Crate directories (crates/audio/ etc.) stay as-is; only the Cargo.toml
[package] name field changes plus all consumer module imports
(magnotia_core -> lumotia_core, etc.).

Remaining magnotia_* references at this point are intentional and
scoped to later phases: tracing targets (Phase 4), DB setting keys
magnotia_preferences/magnotia_history (Phase 5).

cargo build --workspace passes. cargo test --workspace: 330 pass, 0 fail.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 08:48:09 +01:00
184214b60a agent: engine slop residuals B — eprintln → tracing sweep
Replaces 22 production eprintln! sites with structured tracing events
across 8 files. Closes Area B of the post-prognosis residuals plan
(docs/superpowers/plans/2026-05-12-engine-slop-residuals.md).

Files touched (22 sites):
- crates/hotkey/src/linux.rs (2) — hotplug watcher degraded-mode warnings
- crates/ai-formatting/src/pipeline.rs (1) — LLM cleanup fallback warning
- src-tauri/src/commands/transcription.rs (1) — chunking dispatch info
- src-tauri/src/commands/diagnostics.rs (1) — crashes-dir setup warning
- src-tauri/src/commands/tasks.rs (1) — malformed feedback row warning
- src-tauri/src/commands/power.rs (3) — App Nap acquire/release/fail
- src-tauri/src/commands/models.rs (5) — Whisper warmup lifecycle
- src-tauri/src/commands/live.rs (8) — session start, chunk dispatch,
  per-chunk delivery, inference errors, worker disconnects, listener
  loss, status-channel cascade

Levels: error for unrecoverable failures (inference disconnect, panic,
status cascade), warn for recoverable degradation (LLM fallback,
malformed rows, App Nap fail, hotplug watcher fail), info for lifecycle
(session start, chunk processed, App Nap acquire/release, warmup
complete, chunking dispatch), debug for per-chunk noise (speech-gate
skip, chunk dispatch).

Two new dependencies and two new filter targets:
- tracing = "0.1" added to crates/hotkey and crates/ai-formatting
- Default EnvFilter in src-tauri/src/lib.rs::init_tracing extended with
  magnotia_hotkey=info,magnotia_ai_formatting=info so the new targets
  emit at the default level

Out of scope (intentional, left as-is):
- crates/mcp/src/main.rs — CLI binary, stderr is the log contract
  (module docstring) so the JSON-RPC stdout stream stays clean
- crates/*/tests/*.rs and crates/core/examples/tuning_log_demo.rs —
  test/example diagnostic output relies on --nocapture stdio semantics

Discovery during sweep (not fixed — separate follow-up): hotkey crate
has 6 existing log:: calls (log::error/warn/info/debug) but the
workspace builds tracing-subscriber without the tracing-log feature, so
those events are currently silent. Worth a follow-up to either add the
tracing-log bridge or migrate hotkey's existing log:: calls to
tracing::.

Verification:
- cargo fmt --all
- cargo check --workspace --all-targets — clean
- cargo test --workspace — 330+ tests, zero failures
- rg eprintln! src-tauri/src/commands/ crates/hotkey/src/ crates/ai-formatting/src/ — zero hits

Pre-existing working-tree churn in crates/llm/, src/lib/pages/,
src/lib/utils/saveMarkdown.ts and the untracked phase10a dogfood notes
deliberately left unstaged per Jake's instruction.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 22:27:06 +01:00
792fb5ea08 agent: dev launcher — own Linux env-var contract, add dev:tauri, doc sweep
The 2026-05-12 engine-slop pass removed runtime std::env::set_var mutation from src-tauri/src/lib.rs and replaced it with warn_if_x11_env_unset_on_wayland. With no launcher owning the contract, Linux Wayland dogfood was about to regress.

run.sh:
- now owns Linux launcher env defaults via case "$(uname -s)"
  LIBCLANG_PATH=/usr/lib64/llvm21/lib64 (user-set wins)
  WEBKIT_DISABLE_DMABUF_RENDERER=1 (always on Linux; user-set wins)
  GDK_BACKEND=x11, WINIT_UNIX_BACKEND=x11 (Wayland only; user-set wins)
- 60s Vite readiness timeout
- detects early Vite exit (kill -0 + wait) instead of hanging forever
- trap installed before wait so Ctrl-C cleans up
- args forwarded: ./run.sh --release etc.
- non-exec final Tauri launch preserved so cleanup trap fires

package.json:
- "dev:tauri": "./run.sh" — canonical discoverable dev command

Docs:
- README, dev-setup, architecture-map runtime + launcher pages updated with the new contract; canonical command is npm run dev:tauri (./run.sh as direct equivalent)
- dev-launcher-and-scripts.md replaces the incorrect "kills process group" claim with honest "kills the spawned npm process"
- dev-setup.md path /CORBEL-Projects/magnotia → /CORBEL-Projects/transcription-app
- gpu-tuning/plan.md gets a superseded note rather than rewriting the original rationale
- engine-slop-residuals.md gains Area F (packaged-binary launcher contract) with honest wrapper-vs-.desktop trade-off documented

Verification: bash -n run.sh; shellcheck clean; cargo check -p magnotia green; npm pkg get 'scripts.dev:tauri' returns "./run.sh"; stale-ref sweep clean across living docs.
2026-05-12 22:05:33 +01:00
db654deecc agent: engine slop pass — DSP, typed errors, regex parsing, tracing, audit fixes
External code review on 2026-05-12 rated the codebase 4/10 across audio DSP, error typing, JS injection, env-var safety, ALSA parsing, and async logging. This commit lands the prognosis-level fixes plus three audit follow-ups.

Audio/DSP:
- StreamingResampler/rubato confirmed in the live capture path
- regression test at 12 kHz (rms < 0.01, ~40 dB) catches naive decimation
- near-Nyquist test at 9 kHz (rms < 0.05, ~26 dB) exercises transition band

Core errors:
- Other(String) removed; ProviderNotRegistered introduced
- Io variant restructured as struct with kind/message/raw_os_error
- FileNotFound display quotes paths
- Configuration variant removed (unused)

Core types:
- ModelId, EngineName backed by Cow<'static, str>; const borrowed ctor
- Megabytes::from_gb takes u64 (was f64)
- AudioSamples::sample_rate is NonZeroU32; zero-rate defensive branch removed

Capture:
- /proc/asound/cards parsing rewritten as anchored regex (OnceLock)
- regression test covers product names with embedded colons
- monitor_pattern_detection test restored alongside the regex test
- DEAD_SILENCE_FLOOR promoted to module-level with rationale
- DEVICE_VALIDATION_MS, SILENCE_RMS_FLOOR documented with field-observation rationale
- RMS validation loop made idiomatic
- eprintln! migrated to tracing with structured fields and targets

Tauri startup:
- unsafe std::env::set_var removed; ensure_x11_on_wayland renamed to warn_if_x11_env_unset_on_wayland (launcher/wrapper owns env-var contract)
- DB init + log prune + preferences load collapsed to one block_on
- build_preferences_script rewrites JS injection from JSON.parse string to direct object literal plus malformed-JSON guard and unit tests
- WebKitGTK microphone auto-grant logs warning at startup
- tracing subscriber initialised at top of run() (warn,magnotia=info,... on stderr; honors RUST_LOG); previously eprintln→tracing migration was silent because no subscriber existed

Filename counter:
- RECORDING_COUNTER uses SeqCst

Tests: cargo test --workspace --lib green (322 passed, 0 failed across 10 crates).

Three independent audits (original cleanup → Wren → fresh Codex subagent) concur on no critical findings.

Deferred to docs/superpowers/plans/2026-05-12-engine-slop-residuals.md: storage-layer typed errors, remaining eprintln→tracing sweep, capture actor-model refactor, property-based DSP testing, frontend/backend error boundary cleanup.
2026-05-12 22:03:58 +01:00
b463c32f17 chore: stabilize current head before Phase 10a QC 2026-05-10 23:00:25 +01:00
jars
b16fc179b3 chore: remove 11 orphan Tauri commands and downstream dead code
Sweep of registered-but-never-invoked commands surfaced by an audit
against the frontend invoke() call sites. Each was confirmed dead via
grep across src-tauri, src, and crates: no caller anywhere.

Removed commands:
- check_model, count_transcripts_command, get_profile_cmd,
  install_update, list_feedback_examples_cmd (utility/CRUD shapes
  never wired)
- save_audio, start_native_capture, stop_native_capture (native-capture
  path superseded by the live transcription session)
- transcribe_pcm, transcribe_pcm_parakeet (PCM commands superseded by
  live session; no frontend caller)
- close_preview_window (preview window is hidden via the
  core:window:allow-hide capability, not the command)

Cascade in audio.rs (~430 lines removed):
- CaptureWorker, NativeCaptureState struct + impl, stop_worker,
  append_recorded_chunk, MAX_NATIVE_CAPTURE_RETURN_SAMPLES,
  persist_audio_samples
- The two cfg(test) tests that exercised stop_worker (the
  recording_filename tests stay, supporting resolve_recording_path
  which the live session uses)

Cascade elsewhere:
- FeedbackDto struct and its From<FeedbackRow> impl
- Stale storage imports in feedback.rs, profiles.rs, transcripts.rs
- tauri::Emitter import in transcription.rs
- app.manage(NativeCaptureState::new()) in lib.rs setup

generate_handler entries removed for all 11 commands. cargo check passes
cleanly with zero warnings; no tests reference any deleted symbols.

Net: 709 deletions, 17 insertions across 9 files.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 16:03:19 +01:00
jars
fdf27db0a1 perf+fix: DMABUF default on Linux, popout ACL fixes, plugin version sync, JFK bench fixture
Bundled work from a low-end laptop (Ryzen 5 4650U / Vega 6 / Linux Mint
22.2 / X11) profiling pass.

perf: WEBKIT_DISABLE_DMABUF_RENDERER=1 default on all Linux
  Previously only set on Wayland sessions. Empirically it's a
  significant idle-cost win on integrated GPUs in either session type:
  env-var matrix (release binary, 75s settle, 10s jiffies CPU sample)
  showed magnotia idle CPU 12.30% → 2.80% of one core and idle GPU
  17% → 10% on this hardware. Users can opt back in by exporting
  WEBKIT_DISABLE_DMABUF_RENDERER=0. The Wayland-only XWayland
  fallback (GDK_BACKEND=x11, WINIT_UNIX_BACKEND=x11) is unchanged.

fix: secondary-windows ACL — allow set-always-on-top
  The float window's pin toggle calls setAlwaysOnTop() but the
  secondary-windows capability didn't permit it, so the popout was
  stuck always-on-top regardless of the pin state. Adds the
  core:window:allow-set-always-on-top permission. Narrow scope.

fix: guard registerGlobalHotkey against non-main webviews
  Cross-window settings sync via localStorage can re-fire the
  $effect(() => settings.globalHotkey) callback inside popout webviews
  where the main layout's registerGlobalHotkey is reachable. Adds an
  early-return when the current window label is not "main", so the
  popout doesn't trigger an ACL-denied register/unregister and the
  user no longer sees a spurious "Hotkey not registered" toast when
  popouts are open. Keeps the global-shortcut perm scoped to main.

build: pin @tauri-apps/api 2.10.1 + @tauri-apps/plugin-dialog 2.7.1
  Match the Rust crate versions tauri-cli's version-mismatch check
  enforces during release builds. Without this, `npm run tauri build`
  exits 0 silently while emitting an Error and never producing
  binaries.

test: add crates/transcription/tests/jfk_bench.rs
  Reproducible RTF regression fixture. Env-gated on
  MAGNOTIA_WHISPER_TEST_MODEL + MAGNOTIA_WHISPER_TEST_AUDIO so it
  never runs in CI without setup. Loads the JFK WAV inline (no hound
  dep), times model load + cold + warm transcribe, prints SUMMARY.
  Baselines on this hardware:
    --release --features whisper:               cold RTF 0.054, warm 0.050, RSS 125 MB
    --release --features whisper,whisper-vulkan: cold RTF 0.029, warm 0.028, RSS 125 MB
  Vulkan on RADV/Vega 6 nearly halves transcription latency for
  Whisper Tiny — useful baseline for Phase 10 hardware-recommendation
  scoring.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 09:09:03 +01:00
Claude
89c63891fa chore: rebrand from Kon/Corbie to Magnotia
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.
2026-04-30 13:06:55 +00:00
Claude
4abc2356c2 build(android): cfg-gate desktop-only Tauri surfaces for android target
Phase 1 of the Android same-repo target plan: make the workspace
compilable for `aarch64-linux-android` (and the other NDK ABIs) by
removing the desktop-only crate dependencies and command bodies from the
Android build. After this commit, `tauri android init` followed by
`cargo tauri android build` is structurally unblocked — the remaining
work is the SDK/NDK toolchain (off-sandbox), the Svelte single-window
refactor, and the Phase 3 MVP feature surface.

What's gated under `cfg(not(target_os = "android"))`:

- src-tauri/Cargo.toml: `tauri = { features = ["tray-icon"] }` is now
  declared in the desktop-only target block. The `global-shortcut`,
  `window-state`, and `autostart` plugins join it — none of the three
  support Android natively. The base `tauri = "2"` plus `dialog`,
  `opener`, and `notification` plugins remain unconditional because they
  do support Android.
- src-tauri/src/lib.rs: `mod tray` declaration, the matching
  `tray::setup(app)` call, the close-to-tray `WindowEvent::CloseRequested`
  handler, and the `.plugin(tauri_plugin_global_shortcut::*)` /
  `_autostart` / `_window_state` chain are all desktop-only. The
  builder is split with a single `#[cfg(not(target_os = "android"))]`
  branch that adds the desktop plugins on top of the universal base.
- src-tauri/src/commands/tts.rs: `tts_speak` previously had three
  `#[cfg(target_os = ...)]` branches but no fallback, so on Android the
  `spawned` binding was unbound and the function failed to compile.
  Mirrored the existing `paste.rs` not-implemented fallback. Same fix
  for `list_voices_impl`. Frontend will hide the Read Page Aloud button
  on Android via `isAndroid()`.
- src-tauri/src/commands/windows.rs: all four multi-window commands
  (`open_task_window`, `open_preview_window`, `close_preview_window`,
  `open_viewer_window`) get an Android stub that returns a clear
  "Multi-window is not supported on Android" error. Tauri on Android
  is single-Activity; the previously-secondary content (preview overlay,
  transcript viewer, task float) will live as routes inside the main
  window, gated by `isAndroid()` on the frontend.

What's *not* changed:
- Top-level `identifier` in tauri.conf.json stays `uk.co.corbel.kon`.
  The Phase 10b Kon → Corbie rename sweep will land
  `corbel.technology.corbie` as part of a coherent rebrand commit
  rather than fragmenting the rename across this branch.
- `bundle.android.minSdkVersion: 24` added so a future
  `tauri android init` knows to target Android 7.0+ (Vulkan available,
  scoped storage starts at 29 — we'll surface scoped-storage paths
  via Tauri's dialog plugin on Phase 3).
- `kon-hotkey` already exports a non-Linux stub; no changes needed.
- `commands/meeting.rs` still calls `process_watch::list_running_process_names()`
  which compiles on Android but returns an empty list (SELinux blocks
  /proc walk on API 24+). Frontend will hide the toggle on Android.

Verification: 91/91 tests still pass on the buildable-in-sandbox crates
(kon-storage 60, kon-core 16, kon-mcp 9, kon-hotkey 4, kon-cloud-providers
2). svelte-check 0/0 across 3957 files. The src-tauri crate itself can't
be compiled in this sandbox (no webkit2gtk); CI's desktop builders will
exercise the desktop branch, and Jake's Android-equipped dev box will
exercise the Android branch via `tauri android init`.

https://claude.ai/code/session_0189xUb6ie6t9qHkzatGZ9Rb
2026-04-25 12:50:33 +00:00
Claude
c04c719d48 perf(storage): prune error_log on startup with 90-day retention
The error_log table had no retention policy: every backend error was
appended forever, so across months of dogfooding it grew unbounded. That
silently bloats the diagnostic-bundle export and slows the
list_recent_errors query the Settings → About panel runs.

- New `kon_storage::prune_error_log(pool, keep_days)` does a single
  `DELETE FROM error_log WHERE timestamp < datetime('now', '-Nd days')`
  and returns the row count removed.
- src-tauri/src/lib.rs runs it once during setup() with a const
  ERROR_LOG_RETENTION_DAYS = 90. Failure is logged to stderr but does not
  block startup — a prune that fails is strictly less important than the
  app coming up.
- Test: insert three rows at now / -30d / -200d, verify a 90-day prune
  removes only the oldest, and a subsequent 14-day prune removes the
  -30d row. Storage suite at 60/60.

https://claude.ai/code/session_0189xUb6ie6t9qHkzatGZ9Rb
2026-04-25 09:37:24 +00:00
Claude
3d568148b8 perf(meeting): cache sysinfo System for the meeting-detection poller
`detect_meeting_processes` is called every 15 s when meeting-auto-capture
is enabled. The previous `list_running_process_names` allocated a fresh
`sysinfo::System` per call and walked /proc cold; on a busy host
(~300 processes) that's ~50–100 ms of work, every poll, forever.

Add `kon_core::process_watch::ProcessLister`, a thin wrapper around a
long-lived `System` whose process table is refreshed in place. The Tauri
host holds one behind a `Mutex<ProcessLister>` in a new `MeetingState`
managed at app setup. The free `list_running_process_names` is kept as a
convenience that constructs a fresh `ProcessLister` per call — its only
remaining caller is the existing smoke test.

- ProcessLister + Default in crates/core/src/process_watch.rs.
- MeetingState in src-tauri/src/commands/meeting.rs; the command takes
  it via `tauri::State` and locks for the duration of the snapshot.
- src-tauri/src/lib.rs registers MeetingState alongside the other
  managed states.

https://claude.ai/code/session_0189xUb6ie6t9qHkzatGZ9Rb
2026-04-25 08:46:57 +00:00
ef42c95000 feat(phase9): extract_content_tags_cmd Tauri wrapper
Bridges LlmEngine::extract_content_tags to the frontend with the same
spawn_blocking + PowerAssertion guard the cleanup_text command uses.
Returns a ContentTags object serialised to camelCase JSON. Errors
surface as readable strings so the frontend toast shows actionable
text on the rare grammar-bypass path.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 00:03:38 +01:00
bfec88ccc9 feat(phase9): register write_text_file_cmd in invoke_handler
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 23:48:20 +01:00
42b423e4f4 feat(gamification): list_recent_completions_cmd Tauri wrapper
Thin wrapper over kon_storage::list_recent_completions, parameterised
by day count. Serialises to camelCase JSON (day, count).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 20:31:21 +01:00
6cd1c22c0f feat(intentions): Phase 7 — if-then rules for task / time / triage triggers
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
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.
2026-04-24 19:27:06 +01:00
eebea8cb9a feat(nudges): Phase 6 — Margot soft-touch nudges via frontend nudge bus
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
Frontend-owned nudge bus that consumes in-app signals Corbie already
produces, applies suppression, and fans out to OS notification + an
optional TTS read-aloud. OS-wide keyboard/window activity detection
stays deferred per the revised roadmap — the plan before rewrite
would have been brittle on Wayland, permission-heavy on macOS, and
low-quality everywhere.

Triggers (v1, all in-app signals):
- inactivity_with_active_timer — timer running, window blurred ≥ 90 s,
  at least 60 s into the timer.
- pending_morning_triage — ritual enabled, past 10:00 local, last
  shown ≠ today. Polls every 5 min while focused.
- micro_step_idle — micro-step decomposition created, no child step
  or parent task completed within 15 min.

Suppression:
- Respects nudgesEnabled + nudgesMuted.
- No nudge while the app has focus (document.hasFocus).
- Hard cap 3 per rolling hour.
- Permission requested via @tauri-apps/plugin-notification on first
  delivery; denial is silently respected.

Rust side:
- tauri-plugin-notification registered + ACL entries on the main-
  window capability only (secondary windows can't fire nudges).
- commands::nudges::deliver_nudge — thin wrapper, security-guarded
  via ensure_main_window, delegates to the plugin. No DB writes —
  the roadmap's nudges-audit table is deferred until a concrete need
  emerges.

Frontend glue:
- nudgeBus.svelte.ts — subscribes to window events, applies
  suppression, calls deliver_nudge (+ tts_speak when speakAloud is
  on).
- kon:task-completed now dispatched on complete_task_cmd success.
- kon:microstep-generated + kon:step-completed dispatched from
  MicroSteps so the idle trigger can clear itself on any engagement.
- kon:focus-timer-cancelled added to focusTimer so the bus can reset
  its inactivity state on cancel, not only on natural completion.
- nudgeBus started from +layout.svelte onMount, stopped on destroy.

Settings:
- New "Nudges" section with three toggles: Enable nudges, Mute for
  now (separate so a hard mute doesn't lose preferences),
  Speak nudges aloud (reuses Phase 4 TTS).
- All default OFF. No first-run prompt — nudges are a Settings-found
  feature rather than a walkthrough step.

Out of scope per the revised Phase 6 spec: OS-wide keyboard/window
hooks, biometric signals, custom trigger editor (Phase 7), notification
sound (platform variance too high for Layer-1 — revisit in Phase 9
polish with a bundled .wav).

Gates: fmt clean, clippy -D warnings clean, cargo test 262/0 (4
existing + 262 current), npm run check 0/0, npm run build green.
2026-04-24 19:05:21 +01:00
b333c6229e chore(hardening): tighten security and footprint defaults 2026-04-24 19:03:57 +01:00
3cf3e41899 feat(rituals): Phase 5 — morning triage, evening wind-down, autostart
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
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).
2026-04-24 17:48:01 +01:00
9f53702c7e feat(tts): Phase 4 — Read Page Aloud with OS-native voices
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
Platform-dispatched TTS (spd-say + espeak-ng fallback on Linux, say on
macOS, PowerShell System.Speech on Windows) with a shared SpeakerButton
component. Tap to speak, tap again to stop; only one button speaks at
a time so two surfaces don't talk over each other. Text always travels
via argv (or a PowerShell here-string delivered through -EncodedCommand
on Windows) so user content never enters a shell string.

Mount points: DictationPage transcript footer, transcript viewer header,
per-step in MicroSteps. Settings gains a "Read aloud" accordion with
voice picker (lazy-loaded from the OS synth), rate slider 0.5-2.0x,
and a British-English test utterance.

Rust tests cover rate mapping, NaN handling, and Windows here-string
terminator safety. No pause/resume, no SSML, no cloud voices — that
stays out of scope per the Layer-1 roadmap.
2026-04-24 16:01:47 +01:00
1d4f1070a2 feat(energy): Phase 3 — match-my-energy task sort + tri-state tag column
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
Closes Phase 3 of the 2026-04-23 feature-complete roadmap. Incorporates
the Codex plan-review fixes from this session: profile-free index, tri-
state update command, and de-prioritise-not-hide semantics.

Storage (kon-storage):
- Migration v11 adds `energy TEXT` to `tasks` with a CHECK constraint on
  `high | medium | brain_dead | NULL`. Index `(energy, created_at DESC)`
  — deliberately not per-profile because the tasks table carries no
  profile_id column yet (tracked as a separate gap in HANDOVER).
- `TaskRow.energy: Option<String>` plus `task_row_from` read.
- `insert_task` signature grows by one optional arg (`energy`). Allowed
  `too_many_arguments` with a rationale comment — the positional shape
  matches the column order and flipping to a params struct would have
  rippled through every caller for cosmetic benefit only.
- New `set_task_energy(pool, id, Option<&str>) -> TaskRow`. Lives as its
  own function because `update_task` uses COALESCE to let `None` mean
  "preserve" — which would make clearing the tag impossible.
- Two new tests: round-trip including explicit NULL clear, and CHECK
  constraint rejection of unknown values.
- Tests updated for the v10 → v11 version bump.

Tauri (src-tauri):
- `TaskDto.energy`. `CreateTaskRequest.energy` (optional). Inline
  validation against the allowed set before hitting the DB, so frontend
  bugs surface as friendly errors instead of CHECK-constraint failures.
- New `set_task_energy_cmd` command mirroring the storage tri-state API.

Frontend (svelte):
- `EnergyLevel` type added to `types/app.ts`. `TaskDto`, `TaskEntry`, and
  `TaskDraft` grow an `energy` field.
- `SettingsState.currentEnergy` (persisted) + `matchMyEnergy` (persisted
  toggle). Defaults: null + false — no surface change until user opts in.
- `setTaskEnergy(id, EnergyLevel | null)` action on the task store.
  Calls the dedicated Tauri command, updates local state, broadcasts to
  sibling windows.
- `EnergyChip.svelte` — new component. Cycles unset → High → Medium →
  Brain-Dead → unset on click. Colour tokens: accent / warning /
  text-tertiary (deliberately not danger-red for Brain-Dead — the brief
  is explicit that this state must not feel pathologised).
- Chip rendered on every task row in TasksPage and every row in
  WipTaskList. Hidden-until-hover when energy is unset so untagged rows
  stay calm; always visible once tagged because the colour is the signal.
- Tasks page header gains a "I feel" segmented control and a
  "Match my energy" toggle. When both are active, matching tasks sort
  to the top — unset tasks are treated as Medium-equivalent. Nothing is
  ever hidden; this is a de-prioritisation, not a filter.

Deferred / out of scope:
- LLM-driven surfacing (brief says "The AI surfaces...") — deterministic
  client-side sort is v1; LLM layer is a later phase.
- tasks.profile_id + per-profile energy sort — separate migration.

All green: cargo build + 251 tests + clippy -D warnings (0 warnings)
+ fmt + svelte-check (0/0) + npm run build.
2026-04-24 14:53:19 +01:00
46be0a5aca feat(feedback): Phase 2 — HITL thumbs + correction capture with prompt-conditioning loop
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
Closes the human-in-the-loop gap from docs/brief/feature-set.md and
Phase 2 of the 2026-04-23 feature-complete roadmap.

Storage (kon-storage):
- Migration v10 adds the `feedback` table: (target_type, target_id,
  rating, original_text, corrected_text, context_json, profile_id,
  created_at) with CHECK constraints on target_type and rating, plus
  indexes on (target_type, rating, created_at DESC) for prompt-time
  retrieval and (profile_id, target_type, created_at DESC) for
  per-profile scoping.
- New public API: `FeedbackTargetType`, `RecordFeedbackParams`,
  `FeedbackRow`, `record_feedback`, `list_feedback_examples`.
- Tests updated — the RB-02 rollback regression now discovers the
  real max version at runtime instead of hard-coding v10 for its
  poison migration.

LLM (kon-llm):
- `prompts::FeedbackExample` — local shape for few-shot exemplars so
  kon-llm stays independent of kon-storage.
- `prompts::build_conditioned_system_prompt` — appends a "here is
  the style this user prefers" block to the base system prompt
  when examples are available; returns the base prompt unchanged
  when empty, so new users and early sessions see generic output.
- `LlmEngine::decompose_task_with_feedback` and
  `LlmEngine::extract_tasks_with_feedback` thread examples through
  to the builder. The old one-arg variants are preserved and now
  call through with an empty slice.
- 4 unit tests covering empty, empty-input-skip, correction-wins,
  and thumbs-up-only fallback.

Tauri (src-tauri):
- New commands::feedback module: `record_feedback`,
  `list_feedback_examples_cmd`.
- `decompose_and_store` and `extract_tasks_from_transcript_cmd`
  now fetch the last 5 positive/neutral feedback rows for their
  target type and pass them through to the LLM, wiring the
  learning loop end-to-end.
- Shared `to_llm_examples` helper parses the `context_json.input`
  field (where the recorder stashes the parent task text / transcript
  chunk) back into the exemplar shape.

Frontend (MicroSteps.svelte):
- Thumbs-up and thumbs-down buttons on every micro-step row.
  Hover-revealed; the vote recolours the icon; clicking again
  clears the local highlight (the row itself stays in the audit
  trail).
- Pencil icon + double-click to edit step text. Save flows through
  update_task_cmd for persistence and records a correction feedback
  row with (original_text, corrected_text) — the highest-value
  training signal.
- Parent task text is captured in context_json.input at record time
  so the prompt builder can reconstruct the (input, preferred-output)
  pair on subsequent decompositions.
- Feedback capture is best-effort — a record_feedback failure never
  interrupts the primary action.

What's deferred to a later phase:
- Thumbs + corrections on extracted tasks (same pipeline, different
  surface — probably TasksPage after the AI-extraction path)
- Thumbs on transcript cleanup output
- Semantic retrieval over the feedback corpus (once there is enough
  data to justify embedding infrastructure; the storage shape is
  already ready for it)
2026-04-24 12:53:51 +01:00
fe61661305 chore(lint): clean up clippy warnings across workspace
Auto-applied cargo clippy --fix across 11 files — needless return,
unnecessary cast, map_or simplification, repeat().take() → repeat_n(),
iter().any() → contains(), manual char comparison, lifetime elision,
push_str single-char, reference immediately dereferenced.

Also fixed three lints on file_storage.rs manually: two doc-list-item
overindentations, plus the same needless-return. Baseline main was
not clippy-clean with -D warnings before; after this pass one
needless_range_loop warning remains (live.rs:1089) that clippy's
suggested rewrite would make less readable — left for a dedicated
refactor session.

Build + workspace tests remain green (245 passing, 0 failing, 1
ignored).
2026-04-24 09:43:56 +01:00
9b0067b4c0 Land release blocker fixes and workspace cleanup
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
2026-04-23 00:16:09 +01:00
a57da0feb5 feat(llm B.1 #27): Test LLM button with classified error diagnostics
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
The brief's pain point is opaque load failures: llama-cpp-2's errors
bubble up as raw C++ strings ("cudaMalloc failed: out of memory",
"invalid gguf magic"). A user seeing that has no path to recovery.

New backend command test_llm_model runs a staged diagnostic:
  1. Model not downloaded → `not-downloaded` + download hint.
  2. File size ≤90% of expected → `incomplete` (stalled download)
     + re-download hint. Matters because llama-cpp-2 can segfault
     on truncated GGUF rather than returning cleanly.
  3. Requested model already loaded → `ready`, no side effects.
  4. Otherwise attempt a real load. On failure, classify_llm_load_error
     maps the raw string to one of:
       - load-failed-vram         (OOM / cudaMalloc / allocation)
       - load-failed-corrupt      (GGUF magic / unsupported format)
       - load-failed-permission   (permission denied / access denied)
       - load-failed-other        (catch-all)
     Each category has a prewritten actionable hint pointing at the
     specific Settings surface (tier picker, re-download, file perms).

classify_llm_load_error is pure-string and unit-tested — 8 cases
covering the main categories plus edge cases (OOM alias, Windows
"Access is denied", unknown errors). Ordered narrow-to-broad so
overlap doesn't misclassify.

Settings UI gets a "Test" button in the AI section's action row,
visible whenever the model is downloaded (both downloaded-idle and
loaded states). Shows inline hint below the status line when the
test surfaces one. Refreshes both local and global LLM status after
the test since a successful test implicitly loads the model.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 17:04:11 +01:00
ae4c1e3c6d feat(preview B.1 #17): raw-transcript revert button in preview overlay
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
Kon's ideology rule: raw Whisper output is the source of truth; LLM
cleanup is additive, never destructive. The preview overlay already
tracks both rawText and finalText across the listening → live →
cleanup → final phases — but until now the user had no one-click path
from final to raw if cleanup changed their meaning.

Frontend: a context-aware "Use raw" / "Copy raw" button appears in
the preview overlay's final phase, only when rawText and finalText
actually differ (Raw format mode or LLM-off leaves the button hidden).
Two behaviours depending on how the transcript reached the target:

  - settings.autoPaste = true  →  invoke paste_text_replacing, which
      sends the platform's undo keystroke to the focused app,
      waits UNDO_PASTE_GAP_MS (60 ms) for the compositor / app to
      process it, then pastes the raw transcript. The preview hides
      itself beforehand so the keystroke doesn't race focus
      (existing Wayland-hardening path).
  - settings.autoPaste = false →  nothing was pasted in the first
      place, so just overwrite the clipboard with raw. User's own
      paste yields raw.

Backend: new paste_text_replacing Tauri command plus a mirror of the
paste-backend matrix for undo (wtype -M ctrl z / xdotool key ctrl+z /
ydotool keycodes 29:1 44:1 44:0 29:0 / osascript cmd+z / SendKeys '^z').
Reuses the pick_linux_backend_order Wayland-vs-X11 preference.
Registered in the Tauri command handler.

Acceptance per the brief: "after paste, Ctrl+Z within 5 s replaces
LLM output with raw transcript" — satisfied via the 4 s auto-hide
window on the preview's final phase. The click extends auto-hide so
the user actually sees the confirmation.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 16:29:43 +01:00
Cursor Agent
fafa0fc878 feat(A.1 #1): surface active compute device + runtime-warning banner
Extends get_runtime_capabilities() with three new fields:
  - activeComputeDevice: {kind, label, reason} — "GPU (Vulkan)" on
    the happy path, "CPU (fallback)" with a reason when the Vulkan
    loader is absent at runtime. libloading::Library::new probes
    libvulkan.so.1 / vulkan-1.dll / libMoltenVK.dylib per target OS.
  - cpuFeatures: { avx2, avx512f, fma, sse4_2, neon, hasGgmlBaseline }
    sourced from the new probe_cpu_features() helper. hasGgmlBaseline
    is the one flag the Settings banner actually reads.
  - parallelModeAvailable: placeholder false until Phase A.4 lands
    the real GPU VRAM probe + GpuGuard semaphore permit logic.

Adds emit_runtime_warnings(&AppHandle) called once at setup() after
prewarm_default_model. Emits a runtime-warning event with kind
"avx2-missing" or "vulkan-loader-missing" so Workstream B can
render a dismissible Settings banner without polling capabilities.

Contract matches docs/whisper-ecosystem/workstream-A.md §Item #1 for
Workstream B to consume without waiting for Phase A.2 to land the
real whisper_print_system_info bridge.

Co-authored-by: jars <jakejars@users.noreply.github.com>
2026-04-21 15:54:15 +01:00
1f5309c8f5 feat(windows): persist size + position across restarts via tauri-plugin-window-state
Without this, every secondary window (preview overlay, task float,
transcript viewer) opened at whatever spot Tauri / the compositor picked,
which was especially noticeable on Wayland where placement hints are
advisory. Main window's position was also lost on restart.

Registering tauri_plugin_window_state in the builder gives automatic
per-window-label save + restore. State lives in app-data/window-state.json;
fresh installs still fall back to the builder defaults (no changes to
inner_size on any of the four windows). Covers OpenWhispr issue #605 and
the broader UX pain.

No frontend changes — the plugin is purely backend. Regenerated ACL
manifests / desktop + linux schemas pick up the plugin registration.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 09:38:23 +01:00
eb60a8bfd3 feat(preview): floating transcription overlay with listening→live→cleanup→final phases
Ported the best bits of OpenWhispr's TranscriptionPreviewOverlay into Kon's
window conventions. Off by default — toggle in Settings → Output → "Floating
preview when Kon is unfocused". Opens only when the main window isn't
focused at the start of a recording, so it never adds noise when the user
can already see the transcript in the main surface.

Phase state machine (src/routes/preview/+page.svelte):
- listening — pulsing dot, no text yet
- live      — animated bars + streaming raw Whisper output
- cleanup   — accent bars while the LLM cleanup pass runs
- final     — checkmark + formatted text + 4s auto-hide

Data plumbing: raw segment text is captured before post_process_segments in
live.rs (new raw_text field on LiveResultMessage) and in transcription.rs
(new raw_text in the transcription-result payload). DictationPage forwards
raw_text to the overlay via Tauri global events — preview-listening on
start, preview-append per chunk, preview-cleanup before the LLM pass,
preview-final with the formatted text, preview-hide when a run produced no
transcript (empty recording / cancel).

Window is always_on_top, skip_taskbar, focused=false so it never steals
focus from whatever the user is dictating into. open_preview_window shows
an existing hidden preview or builds it fresh; close_preview_window hides
without destroying so the next open is instant.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 08:49:29 +01:00
ba0d59f563 feat(meeting): opt-in process-list reminder when a meeting app starts
Default off. When on, the layout polls detect_meeting_processes every 15s
with the user's app-name patterns. On a fresh match (edge-triggered — no
re-toast until the app goes away and comes back) we fire a reminder toast
that tells the user which meeting app appeared and their global hotkey. We
never start recording on this signal; the ideology rule says the user
decides. The signal is a single channel: process list match only — no mic
activity heuristic, no calendar.

Backend adds kon_core::process_watch::{list_running_process_names,
match_meeting_patterns} over sysinfo, exposed to the frontend as the
detect_meeting_processes Tauri command.

Settings ships two new fields — meetingAutoCapture (bool) and
meetingAutoCaptureApps (string[]) — with a comma-separated input in the
Output section. Default app list is ["zoom", "teams"], user-editable.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 07:54:55 +01:00
4c0c876ade feat(paste): auto-insert transcript at cursor via wtype/xdotool/ydotool/osascript/SendKeys
Adds an opt-in "auto-paste into focused window" toggle. When enabled, the
dictation pipeline sets the clipboard and then sends a Ctrl+V / Cmd+V
keystroke to whatever window currently has focus — the common case after a
global-hotkey dictation, since Kon's own window never stole focus.

Backend (src-tauri/src/commands/paste.rs) probes for a platform paste tool
and falls back cleanly:
- Linux Wayland: wtype > ydotool > xdotool
- Linux X11: xdotool > ydotool > wtype
- macOS: osascript System Events keystroke
- Windows: PowerShell WScript.Shell SendKeys

detect_paste_backends is a pure probe used by Settings to describe the
available backend next to the toggle (or nudge the user to install one).
paste_text always copies first, so auto-paste failure degrades to the
existing clipboard-only behaviour and surfaces a warn toast.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 07:45:16 +01:00
d1eb56fac9 feat(llm): wire Phase 3 local LLM runtime via llama-cpp-2
kon-llm now owns a real LlamaBackend + LlamaModel, with three Qwen3 tiers
(1.7B Q4, 4B-Instruct-2507 Q4, 14B Q5) selectable per hardware. Downloads
are resumable with SHA-256 verification and stored under ~/.kon/models/llm.

Engine exposes three high-level surfaces — all greedy/temp-0, GBNF-constrained
where output shape matters:
- cleanup_text (prompt-injection-hardened system prompt; profile terms
  appended as "preserve these spellings" suffix)
- decompose_task (3–7 micro-steps, constrained JSON array)
- extract_tasks (optional-array; empty when no explicit commitments)

post_process_segments now takes an Option<&LlmEngine> and, when loaded and
format_mode != Raw, joins segments → cleanup → replaces segments with the
cleaned text (first segment span). Rule-based path still runs first; LLM
errors log and keep rule-based output.

Tauri commands: recommend_llm_tier, check_llm_model, download_llm_model,
load_llm_model, unload_llm_model, delete_llm_model, get_llm_status,
cleanup_transcript_text_cmd, extract_tasks_from_transcript_cmd,
decompose_and_store (LLM-backed subtasks).

Settings: AI tier toggle (off / cleanup / tasks), model picker with
downloaded/loaded status, download progress events via
kon:llm-download-progress.

Dictation: ensureLlmModelLoaded on mount, cleanupTranscriptIfEnabled after
stop when tier != off and format_mode != Raw, LLM task extraction when
tier=tasks (regex fallback on failure).

Interim: both llama-cpp-sys-2 and whisper-rs-sys statically link their own
ggml, so src-tauri/build.rs emits -Wl,--allow-multiple-definition on Linux.
Replace with a system-ggml shared-lib setup as a follow-up.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 07:31:51 +01:00
34fce3cf9e feat: OpenWhispr-inspired transcription polish pass
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
Major quality pass on top of Phase 2. Five substantive changes plus
cross-cutting touches across audio, hotkey, transcription, and Tauri
command layers.

  Transcription quality

  - Long-audio chunking in commands/transcription.rs: Parakeet and large
    file transcription now chunk-and-recompose with overlap trimming, so
    the live-path chunking advantage extends to file-based workflows.
  - Stateful live speech gate in commands/live.rs on top of the earlier
    duplicate-boundary filtering — distinguishes start-of-speech from
    mid-speech and holds state across chunks.

  Auto-learning corrections

  - New crates/ai-formatting/src/correction_learning.rs: extracts user
    text corrections from viewer edits and proposes additions to the
    active profile's vocabulary.
  - src-tauri/src/commands/profiles.rs bridge for frontend-driven
    confirmation of learned terms.
  - src/routes/viewer/+page.svelte hooks the learning path into the
    segment-edit flow so corrections feed profile_terms without a
    separate 'train this profile' UX.

  Transcript profile provenance

  - Migration v8 (crates/storage/src/migrations.rs) adds profile_id to
    transcripts, defaulting to DEFAULT_PROFILE_ID so existing rows stay
    valid.
  - crates/storage/src/database.rs: TranscriptRow + CRUD carry profile_id.
  - src-tauri/src/commands/transcripts.rs: add_transcript accepts and
    persists profile_id.
  - DictationPage.svelte + FilesPage.svelte send activeProfileId on
    capture so learned corrections are attributed to the right profile.

  Cleanup prompt contract

  - crates/ai-formatting/src/llm_client.rs hardened: the CLEANUP_PROMPT
    now specifies concrete do/do-not rules, ready for a real model-backed
    cleanup pass. The llm_client is still a stub — kon-llm remains unwired
    — but the prompt shape is final.

  Cross-cutting polish

  - Minor touches in audio (capture/decode/resample), hotkey (lib/linux/stub),
    core, transcription (concurrency/model_manager/local_engine/whisper_rs),
    and the rest of src-tauri/src/commands/*: error-path tightening, log
    clarity, TS-migration follow-ups (@ts-nocheck additions for incremental
    typing).

Verified locally: npm run check, cargo test -p kon-ai-formatting,
cargo test -p kon-storage, cargo test -p kon --lib commands::live::tests,
cargo check — all green.

Scope boundary: kon-llm crate is still a stub; task extraction remains
rule-based. Bundled local-LLM runtime is the next clean step and is not
in this commit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 22:39:08 +01:00
c0308306ae chore(cleanup): drop legacy global dictionary Tauri commands — profile_terms is canonical
Task 16 of Phase 2 Remaining. Removes the three global-dictionary Tauri
commands now that all frontend callers were migrated to the profile-scoped
equivalents in Task 15:

- list_dictionary_command
- add_dictionary_entry_command
- delete_dictionary_entry_command

Also drops the DictionaryDto and its From<DictionaryEntry> impl (dead
alongside the commands), plus the now-unused kon_storage imports
(list_dictionary, add_dictionary_entry, delete_dictionary_entry,
DictionaryEntry). The storage-layer functions and the dictionary table
itself stay until Task 17 drops them.

Codex verification point 5 cleared: zero frontend callers for the legacy
commands (or their _cmd aliases) before deletion.

cargo check -p kon: clean.
cargo test --workspace: 40 passed; pre-existing ensure_x11_on_wayland
doctest failure at src-tauri/src/lib.rs:77 unchanged.
2026-04-19 21:00:30 +01:00
c8952df591 feat(tauri): expose profile + profile_term commands 2026-04-19 20:48:09 +01:00
e248923f5d feat(transcripts): migration v5 meta — starred, tags, template, language, segments persisted 2026-04-19 16:35:17 +01:00
9378980639 feat(tasks): persist list_id/effort/notes + update_task_cmd — close Task 2 metadata gap 2026-04-19 16:23:25 +01:00