crates/storage/src/migrations.rs:
Replace naive sql.split(';') with split_statements() that tracks
BEGIN...END depth, so migrations containing trigger definitions
execute correctly instead of being split mid-block.
crates/transcription/src/lib.rs:
Re-export transcribe_rs::SpeechModel so callers in src-tauri can
reference it without adding transcribe-rs as a direct dependency.
src-tauri/src/commands/models.rs:
Use Box<dyn SpeechModel + Send> as the load_model_from_disk return
type, matching the trait object that transcription crates produce.
src-tauri/src/commands/transcripts.rs:
Remove the stale .map(|v| v as i32) casts on sample_rate and
audio_channels. InsertTranscriptParams now stores these as i64
(ebf449b), matching the i64 fields on CreateTranscriptRequest;
casting to i32 first would silently truncate large sample rates.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Front-to-back QC pass turned up two independent missing-module
showstoppers (workspace did not compile; frontend did not load) plus a
handful of HANDOVER-claimed features that were wired but dead. Fixes:
P0 — gets the app booting again:
- Add the never-committed src/lib/utils/runtime.js (hasTauriRuntime
detection); 5 imports were resolving to nothing.
- Add the never-committed crates/audio/src/streaming_resample.rs
(rubato-backed StreamingResampler with new/push_samples/flush);
declared in lib.rs and used 3x by live.rs but had no impl.
- Drop the duplicate hasTauriRuntime import in routes/+layout.svelte.
- Allow the transcript-viewer window to use the default capability
(was missing from capabilities/default.json:windows, so the viewer
window could open but not invoke any Tauri command).
P1 — features documented as working but actually dead:
- Pump MicrophoneCapture::take_error_rx() into LiveStatusMessage::
Warning each loop iteration in commands/live.rs. The HANDOVER
promised cpal stream errors would surface as toasts; the channel
was created and never read.
- Replace .expect() on the WebKit media-permission setup with a
logged warning. Failure no longer aborts the whole process.
- Toast on save_preferences failure (preferences.svelte.js had a
silent console.error — now warns once per failure run via the
existing toasts store).
P2 — correctness/robustness:
- add_dictionary_entry: switch INSERT OR IGNORE to ON CONFLICT
DO UPDATE ... RETURNING id so duplicate terms get the real row id
instead of a stale auto-increment.
- search_transcripts: qualify ORDER BY fts.rank.
- InsertTranscriptParams + TranscriptRow: bump sample_rate /
audio_channels from i32 to i64 to match the Tauri DTO and avoid
silent truncation at the boundary.
- Drop the unused tauri-plugin-mcp dependency.
- Promote sqlx in src-tauri/Cargo.toml from linux-only to
unconditional (lib.rs names sqlx::SqlitePool unconditionally —
macOS/Windows builds were latently broken).
- hotkey/linux.rs: stop panicking the hotplug task on inotify
failure; degrade to "no hotplug" with a stderr warning.
- layout.svelte: store the global error/unhandledrejection handler
refs and remove them in onDestroy so HMR/window teardown doesn't
leak listeners.
Verified: cargo check -p kon-core -p kon-storage -p kon-cloud-providers
passes. cargo check on src-tauri/kon-audio/kon-hotkey requires alsa +
gtk system libs not present in this sandbox; their changes are
syntactically and type-checked against the rest of the workspace.
svelte-check requires npm install which is not available here.
https://claude.ai/code/session_018ozAs4UcRC8jbJbddqJtEw
CROSS-PLATFORM AUDIT:
- Linux x86_64 (Fedora 43, KDE Wayland): HIGH confidence (the dev target)
- Linux other distros / X11: MEDIUM (tested patterns, untested distros)
- Windows 10/11: LOW — theoretically supported via Tauri + CPAL + whisper.cpp
+ tauri-plugin-global-shortcut (the custom evdev hotkey is no-op on
Windows); has had zero hands-on testing
- macOS Apple Silicon / Intel: LOW — same; macOS Info.plist needs
NSMicrophoneUsageDescription for the bundled app, and the path bug
fixed in this commit
REAL BUG FIXED — macOS app_data_dir was Unix-style:
crates/storage/src/file_storage.rs::app_data_dir() previously fell into
the "Unix" branch on macOS and wrote to ~/.kon/, which violates Apple
guidelines and confuses install/uninstall tooling. Now correctly:
- Windows: %LOCALAPPDATA%/kon (unchanged)
- macOS: ~/Library/Application Support/Kon/
- Linux: $XDG_DATA_HOME/kon or ~/.local/share/kon (XDG Base Directory),
with legacy ~/.kon/ fallback so existing installs keep working
- Other Unix: ~/.kon/ (unchanged)
OS DETECTION LAYER:
- New get_os_info Tauri command in commands/diagnostics.rs returns:
{os, arch, family, usesCmd, isWayland, customHotkeyBackend,
primaryModifierLabel}
- New src/lib/utils/osInfo.js helper:
- async loadOsInfo() warms a cache, called once at root layout mount
- then isMac() / isWindows() / isLinux() / modKeyLabel() / isWayland()
are synchronous
- browser-preview fallback reads navigator.platform
- Lets components localise hotkey labels (Cmd vs Ctrl), file-picker
copy ("Open Finder" vs "Open Explorer"), etc, without recomputing in
every place.
cargo check -p kon-storage clean. Updated HANDOVER-2026-04-17.md with
a per-platform confidence table.
THE BRIEF:
For the friends beta we need verbal feedback AND technical feedback —
some bugs the user cannot describe but a stack trace can. Built it in
two layers, with Layer 3 deferred until there is real volume.
PRIVACY POSTURE (matches Kon's local-first positioning):
- All capture is to disk only. Nothing is transmitted.
- The manual report bundler shows the user exactly what would be
shared and lets them choose to copy / save it.
- No remote endpoint, no Sentry, no opt-out telemetry.
LAYER 1 — Always-on local capture:
- Rust panic hook (commands/diagnostics.rs::install_panic_hook) writes
each panic to ~/.kon/crashes/<unix-ts>-<short-id>.crash. Captures
thread name, OS/arch, RUST_BACKTRACE state, and the panic info.
Installed before tauri::Builder so it catches setup-time panics too.
- Frontend window.onerror + window.unhandledrejection in
src/routes/+layout.svelte forward to the new log_frontend_error
Tauri command, which inserts into the existing error_log SQLite
table. Best-effort: errors in the error handler itself are swallowed
so logging can never crash the app.
- Existing error_log table (migration v1) is now actually used —
closes the TODO from crates/storage/src/database.rs:409.
LAYER 2 — Manual diagnostic report:
- Settings → About → Diagnostics section: Generate report → Preview →
Copy / Save.
- Report is plain markdown so it pastes cleanly into email, Discord,
GitHub issues. Sections: app version + OS, sanitised settings JSON,
recent error_log rows, last 5 crash dumps with previews, log file
tail (8KB).
- Preview is shown in a <details> with the full text the user can
inspect before deciding to share.
- Save writes to ~/.kon/diagnostic-reports/kon-diagnostic-<ts>.md.
NEW FILES:
- src-tauri/src/commands/diagnostics.rs (panic hook + 5 Tauri commands)
CHANGES:
- crates/storage/src/file_storage.rs: crashes_dir() + logs_dir() helpers
- crates/storage/src/database.rs: ErrorLogRow + list_recent_errors()
- crates/storage/src/lib.rs: re-exports
- src-tauri/src/commands/mod.rs: + diagnostics module
- src-tauri/src/lib.rs: install_panic_hook() before Builder; 5 new
Tauri commands registered (log_frontend_error,
list_recent_errors_command, list_crash_files,
generate_diagnostic_report, save_diagnostic_report)
- src/routes/+layout.svelte: installGlobalErrorCapture() at mount
- src/lib/pages/SettingsPage.svelte: diagnostic state + buttons +
preview block at the end of the About section
cargo check -p kon-storage clean. Settings.svelte if/each balanced.
LAYER 3 (deferred):
- Optional opt-in remote reporting (self-hosted Sentry on Tartarus or
similar). Not for friends beta. Open up after volume justifies it.
Finally exposes the canonical SQLite store to the frontend. Previously
the transcript/task tables existed but no Tauri command read or wrote
them — the UI lived entirely in localStorage. Closes that gap and adds
the missing commands flagged by both Codex and architecture-review.md
§13.
MIGRATIONS:
- v2 adds:
- transcripts_fts virtual table (FTS5, porter+unicode61 tokeniser,
diacritics-folded) backed by transcripts via content_rowid
- INSERT/UPDATE/DELETE triggers keep FTS in sync automatically
- dictionary table for custom vocabulary (term, note, created_at)
- Append-only as required; never modifies v1
STORAGE FUNCTIONS (crates/storage/src/database.rs):
- list_transcripts_paged(limit, offset) — pagination
- count_transcripts() — for "showing X of N" in UI
- update_transcript(id, text?, title?) — closes the historic
architecture-review.md §13 rename-never-persists TODO
- search_transcripts(query, limit) — FTS5 wrapper
- list_dictionary / add_dictionary_entry / delete_dictionary_entry
- DictionaryEntry struct exported
TAURI COMMANDS (src-tauri/src/commands/transcripts.rs new file):
- add_transcript, list_transcripts, count_transcripts_command,
get_transcript, update_transcript, delete_transcript,
search_transcripts
- list_dictionary_command, add_dictionary_entry_command,
delete_dictionary_entry_command
- TranscriptDto + DictionaryDto for camelCase frontend serialisation
REGISTRATION (src-tauri/src/lib.rs):
- All 10 new commands wired into the invoke_handler
cargo check -p kon-storage passes clean. The Tauri crate cargo check
still requires `sudo dnf install cmake clang-devel` for whisper-rs-sys
(pre-existing infra dep, unrelated to this work).
NEXT (still in this sprint):
- Wire DictationPage / FilesPage to call add_transcript on every save
(dual-write: keep localStorage for now, add SQLite alongside)
- Wire HistoryPage rename to call update_transcript (fixes the TODO)
- Add a History search input that calls search_transcripts
- Add Settings → Dictionary panel
- Inject dictionary terms into the LLM cleanup prompt
- Remove unused chrono and uuid dependencies from kon-storage
- Add schema versioning TODO to run_migrations() for pre-release safety
- Add doc comment and usage example to log_error() (uncalled public API)
- Add TODO to consolidate app_data_dir() with model_manager::dirs_path()
- Add safety comments and #[allow(deprecated)] to keystore set_var usage
- Add TODO for keyring crate migration in cloud-providers keystore
- Apply cargo fmt
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>