Sitemap
Carlo C.

Carlo C.

AI/ML engineer building production-grade agents, RAG and MLOps systems. I write about architecture, trade-offs, security, and what survives deployment.

A task management and local voice dictation apps for COSMIC DE

5 min readAug 15, 2026

--

Press enter or click to view image in full size

Kan-bone and grunt-bone are local-first Rust applications reveal that COSMIC integration is as much about protocols, process boundaries, and failure modes as UI.

A native desktop application begins where its own process ends.

That boundary connects grunt-bone-rs and kan-bone-rs, two Rust applications developed for COSMIC on Pop!_OS 24.04. Their jobs are unrelated. grunt-bone turns speech into text locally and inserts it into the application that owns focus. KanBone is a keyboard-first kanban system split between a full board and a panel applet.

Both projects become difficult at the same point: when their core logic has to cooperate with the desktop. A correct transcript is not useful if Wayland prevents it from reaching the focused client. A correct task database does not make a panel applet responsive, foreground the main window, or keep notifications alive after every process exits.

Native does not describe one architecture

I would classify KanBone as toolkit-native and grunt-bone as integration-native.

KanBone is a libcosmic workspace with two clients. apps/kanban-app owns the complete board interface; apps/kanban-applet supplies quick capture, deadline views, and notifications from the COSMIC panel. Both depend on a UI-free kanban-domain crate and a SQLite-backed kanban-storage crate. The applet uses COSMIC's anchored popup and activation-token path instead of imitating a panel widget with a free-standing window.

That process split creates a less visible constraint. App and applet can open the same database, while the applet shares the compositor’s frame budget. Its storage calls therefore run through tokio::task::spawn_blocking: rusqlite is synchronous, and a blocking database call in the async UI path could freeze a panel component.

grunt-bone does not use libcosmic. Its windows use GPUI and its tray stack includes GTK. Its COSMIC integration lives below the toolkit: it manages COSMIC shortcut configuration, binds Wayland protocols exposed by cosmic-comp, finds the active toplevel, and injects the paste chord through a virtual keyboard.

The distinction matters because each form of nativeness creates a different maintenance bill. KanBone pins an exact libcosmic Git revision and must absorb later toolkit changes deliberately. grunt-bone owns a wider protocol and backend surface: GPUI, GTK, CPAL, ONNX Runtime, a vendored transcription path, desktop configuration, and optional acceleration providers.

Where the Rust architecture earns its keep

KanBone places invalid persistent state behind typed boundaries. The domain crate defines identifiers such as Id<Card>, parsed titles, SortKey, recurrence, deadlines, and synchronization metadata without depending on UI, SQL, or I/O. Time-sensitive functions accept an explicit time value, which makes month-end, time-zone, daylight-saving, snoozing, and recurrence behavior testable.

Get Carlo C.’s stories in your inbox

Join Medium for free to get updates from this writer.

Its schema is sync-ready, not synchronized. Entities carry UUIDs, revisions, timestamps, device metadata, and tombstones, but no sync engine exists. The ADR rejects a full CRDT for the present scope and records an unresolved collision risk in the sparse integer ordering scheme. The newtype around SortKey leaves room to replace that scheme later.

The storage crate owns migrations through PRAGMA user_version, WAL mode, backups through VACUUM INTO, and concurrent access from the two clients. Tests cover simultaneous migration, concurrent writes, stale revisions, and WAL data that has not yet been checkpointed.

grunt-bone has a different shape because its product is a temporal pipeline. A Tao event loop handles tray and hotkey activity. A dedicated worker thread owns the expensive transcriber and receives explicit messages for transcription, streaming commits, cancellation, replay, and voice-note work. Stale partial requests are collapsed before inference so obsolete UI updates do not accumulate behind expensive model calls.

Its audio path captures through PulseAudio or PipeWire, handles multiple sample formats, downmixes and resamples input, and retains a short tail after stop. Model installation verifies size and SHA-256 before atomic promotion. Optional noise reduction can fall back to the original audio; acceleration initialization can fall back to CPU; unavailable Wayland injection leaves the transcript on the clipboard.

The Wayland injection path came from a recorded failure. An earlier library reported success even though native Wayland clients received neither committed text nor a real Ctrl+V. The replacement binds zwp_virtual_keyboard_v1, uploads a four-key XKB map, sets modifiers explicitly, and reuses the virtual keyboard after the compositor compiles its keymap. Active-window detection combines the standard foreign-toplevel list with COSMIC's toplevel-info protocol so terminals can receive Ctrl+Shift+V instead of Ctrl+V.

Evidence sets the limits

grunt-bone’s documented 15-sample, five-language CPU suite reports mean WER of 6.9% and a real-time factor around 0.04. Those measurements also prevented a premature default. Incremental streaming commit reduced the inference left after a long dictation, but split-and-join experiments moved mean WER from 6.90% to 7.18%. It remains opt-in, and the repository notes that short samples do not represent the long, multi-cut recordings for which the feature matters.

The GPU records are equally bounded. Vulkan was faster than the CPU baseline in one documented Windows RTX 4070 run. CUDA was slower with the quantized export because partial CPU assignment introduced copy overhead. The repository does not turn either result into a general GPU claim, and it does not publish the preferred experimental Vulkan artifact because the external ONNX Vulkan build is not reproducible from that repository alone.

For the working trees reviewed, grunt-bone passed 113 tests with one live-Wayland test ignored. KanBone passed 224 workspace tests with one visible-notification test ignored. Formatting and Clippy with warnings denied also passed. These counts prove neither UI quality nor release readiness.

grunt-bone still needs live compositor and audio-device checks; native macOS paths are untested, and official Linux artifacts are x86–64 only. Its analyzed tree also contains substantial Unreleased work beyond package version 0.3.0.

KanBone has no independent reminder daemon, notification actions, archive browser, attachment garbage collection, or UI for its domain-level WIP limit. Several visual and accessibility checks remain manual and incomplete. More fundamentally, the inspected Git repository has no commits: its files are untracked despite the workspace declaring version 0.1.0. The code builds, but there is no versioned baseline or release artifact.

The maintenance rule is concrete: name every desktop capability the product depends on, decide which thread or process owns blocking work, and specify the fallback when the desktop cannot supply that capability. “Native COSMIC” is useful only when it describes those commitments.

The full article, including the repository caveats and architecture comparison, is available at https://autognosi.substack.com/p/a-task-management-and-local-voice.

--

--

Carlo C.
Carlo C.

Written by Carlo C.

AI/ML engineer building production-grade agents, RAG and MLOps systems. I write about architecture, trade-offs, security, and what survives deployment.