Review flagged five issues; all addressed: 1. Phase 6 cross-platform activity detection was fragile (Wayland has no sanctioned global-keyboard API, macOS needs accessibility permission, Windows needs a message-loop hook). Rewritten as a frontend-owned nudge-bus hybrid: consumes in-app signals Corbie already produces (focus-timer state, task-completed events, visibility/focus), dispatches via Rust for notification + TTS. OS-wide activity detection deferred post-v0.1. 2. Notification plugin setup was missing entirely. Added as cross-cutting Phase 6 prerequisite: tauri-plugin-notification in Cargo.toml + package.json, ACL entries, permission-request flow, Windows installed-app caveat, .wav sound path instead of the invalid 'Default' string. 3. Phase 7 idempotency nailed down: last_fired_at + last_fired_local_date per rule, catch_up_on_resume toggle for sleep/resume, task-completed event bridge spec'd, skip-counts-as- finish decision for morning triage, surface-action semantic clarified (specific task by id, not 'inbox'). 4. Phase 8 grace days dropped — without streaks they were solving a problem that doesn't exist. Replaced with an optional non-punitive recent-momentum sparkline. 5. Phase 10 split into 10a (QC), 10b (rename sweep), 10c (release). Pre-10 Cargo.lock decision added as gating item. Removed the no-op 'bump to 0.1.0' step — version already matches across package.json, Cargo.toml, tauri.conf.json. Totals adjusted: 8 – 13 days (was 9 – 13).
24 KiB
name, description, type, tags, created, status, author
| name | description | type | tags | created | status | author | ||||
|---|---|---|---|---|---|---|---|---|---|---|
| Corbie — feature-complete roadmap | Build plan from 2026-04-23 baseline to full feature-complete v0.1 release | roadmap |
|
2026/04/23 | active | Wren (CORBEL's resident agent) on behalf of Jake Sames |
Corbie — Feature-Complete Roadmap
What Corbie is. A local-first, cognitive-load-aware dictation + task-capture desktop app. Vulkan-accelerated Whisper / Parakeet speech-to-text, a local LLM (Qwen3 tiers) for transcript cleanup and task extraction, an MCP server for integration with Claude Desktop / Cline / Cursor, and a UI designed around ADHD / executive-dysfunction needs. Tauri 2 + Svelte 5 + Rust. Zero telemetry.
Formerly known as Kon. Rebrand in flight; repo names at
jakejars/kon+git.corbel.consulting/jake/konstill carry the Kon name and will rename together with the codebase sweep in the final phase.
Baseline — where we are (2026/04/23)
Core MVP (from docs/brief/feature-set.md): 9 of 10 complete.
One gap: visual time representation (the spec's "#1 community-requested feature" — shrinking colour disks / progress rings, externalising time passage). The rest — local transcription, auto-populating tasks, WIP limits, history + search, light/dark theming, templates, vocabulary profiles, file upload, open-format markdown export — all shipped.
Post-MVP (designed, not yet prioritised): 1 of 9 complete.
MicroSteps is shipped; its "just-start" timer button emits an event that currently has no listener anywhere in the codebase. The differentiating ADHD-specific features (Margot nudges, energy-aware sequencing, rituals, if-then intentions, forgiving gamification, TTS, human-in-the-loop feedback) are all documented in the brief but not started.
Release-blockers: 1 — RB-08 macOS power-assertion, pending manual verification on a real Mac (Jake's friend Rachmann has a Mac and can run this offline).
Workspace state: main is clippy-zero-warnings, 245/245 tests passing, fmt clean, svelte-check clean, npm build clean. Three dependabot bumps landed this session plus a clippy cleanup pass and a needless_range_loop refactor. One orphan design-system WIP branch parked on github as archive.
Approach
Layer 1 first (per Jake's standing rule): build the features roughly, in series, through Phase 1 – Phase 8. Do polish passes in Phase 9 and QC + release in Phase 10. Do not mix make-it-work and make-it-neat passes — every phase ships end-to-end (event wired, UI rendered, store state committed, tests updated) but does not chase aesthetic polish until Phase 9.
Ordering rationale. Phase 1 closes the Core MVP gap and unblocks the already-half-wired just-start timer. Phase 2 enables model improvement by collecting human feedback — useful even while later phases are being built. Phases 3–8 build the differentiating ADHD-specific features from highest-utility-per-effort to lowest. Phase 9 is polish debt. Phase 10 is release prep (including Rachmann's RB-08 verification and the Kon → Corbie codebase rename).
Phase 1 — Visual countdown + Just-Start timer
Why now. Closes the one remaining Core MVP gap. Unblocks the dangling kon:start-timer emit from MicroSteps. Directly combats time blindness, which the brief names as the single biggest lever for the target audience.
Scope.
FocusTimer.svelte— a progress-ring countdown component. Shrinking colour ring (not digital). Subtle colour shift across the last 15%. Remaining time label inside the ring for users who want the number; small enough that the ring dominates the visual field.focusTimer.svelte.tsstore — single active timer, running / paused / completed state, elapsed / remaining computed, event dispatch on state transitions.- Mount in
+layout.svelteso the timer persists across page navigation (dictation → tasks → settings). - Listener for
windoweventkon:start-timerwith{ durationSeconds, label }payload. - Trigger from MicroSteps row (already exists). Trigger from task-row context button (new).
- Floating position top-right of main content, above everything, not intrusive when idle (hidden until a timer is running).
- On completion: gentle chime + 3-second ring flourish + OS notification via
tauri-plugin-notification. No modal. No guilt copy. - Store survives window close/reopen via localStorage.
Out of scope for Phase 1. Rhythmic voice anchoring (part of Margot, Phase 6). Custom-duration picker (fixed 2 / 5 / 10 / 15 min presets for now). Multi-timer UI.
Acceptance. From a MicroStep row, clicking the timer button (a) shows the ring visible somewhere on screen, (b) it counts down, (c) at 0 it fires completion + notification, (d) works across page switches, (e) survives a window close + reopen mid-countdown.
Estimated effort. Half day.
Phase 2 — Human-in-the-loop feedback
Why here. AI output quality is the single biggest determinant of whether Corbie feels useful. Thumbs-up / thumbs-down on AI-generated micro-steps and task extractions costs almost nothing to add and gives us a feedback corpus the moment anyone uses it. Enables later retraining / prompt tuning.
Scope.
- Thumbs-up / thumbs-down buttons on every AI-generated item (micro-step row, extracted task, cleanup paragraph).
- "Edit" path already exists for text; the feedback is additive.
feedbacktable in SQLite:item_id,item_type,rating,timestamp, optionalcorrection_text.- Rust command
record_feedback+list_feedback(for later export). - No UI surface for viewing feedback yet. Just capture. A future export pass to JSONL feeds prompt-engineering or fine-tuning.
Out of scope. Retraining loop, per-user profile adjustment, any UI to view feedback history.
Acceptance. Thumbs visible on AI-generated items. Clicking records to SQLite. cargo test on storage covers migration + insert + list.
Estimated effort. Half day to 1 day.
Phase 3 — Energy-aware task sequencing
Why here. Next-highest-utility post-MVP feature. Replaces the cut-for-OS-reasons temptation-bundling feature. Small surface, clear user-facing outcome.
Scope.
energycolumn on tasks: nullable enumHigh | Medium | BrainDead.- Migration + Rust CRUD.
- Tag chip on task rows; tap to cycle / set.
- Sort / filter option on the tasks page: "Match my energy" → AI surfaces tasks matching a user-set current energy, falls back to
Mediumif unset. - No automatic energy detection. Pure user input.
Out of scope. Time-of-day heuristics, calendar integration, AI-predicted user energy.
Acceptance. User tags a task, sets their current energy via a header control, tasks page filter respects it.
Estimated effort. 1 day.
Phase 4 — Read Page Aloud (TTS)
Why here. Small and self-contained. Engages auditory processing which the brief specifically calls out as a retention lever for the target audience. Uses OS-native TTS (no new dependencies, no model download). Clean single-tap affordance.
Scope.
- Rust command
tts_speak(text: String, rate: f32, voice: Option<String>)— platform dispatch:- Linux:
spd-say(speech-dispatcher is available on most distros; graceful fallback toespeakif missing). - macOS:
say(built in). - Windows: PowerShell
System.Speech.Synthesis.SpeechSynthesizer.
- Linux:
- Small "speaker" icon on any text view (transcript viewer, micro-step list, cleanup result).
- Single-tap play; second tap stops. No pause/resume in v1.
- Settings: voice picker (populated from OS), rate slider (0.5–2.0).
Out of scope. Premium voices. Cloud TTS. Concurrent-speaking queue. SSML.
Acceptance. Tap speaker icon on a transcript → hear it read on Linux + expected-to-work-on macOS+Windows (test matrix in Phase 10).
Estimated effort. Half day.
Phase 5 — Start / shutdown rituals
Why here. Meaningful UX but larger surface. Needs calm copy, gentle flow, and a default-off toggle because rituals can feel parental if not optional.
Scope.
- Morning triage: on first launch after 06:00, show a modal / dedicated page: "yesterday's incomplete tasks" (from SQLite query:
completed = false AND created_at < today), with checkbox pick-list, and "pick 1–3 for today" constraint that refuses selections > 3. - Evening shutdown: user-triggerable (not scheduled) review: "what got done today", "open loops to close", "separate work from rest" copy. No automation; ritual as reflection.
- Both off by default in settings. When off, no modal, no pressure.
- Skip-for-today button on morning triage; never shows guilt copy.
Out of scope. Calendar integration, automatic sleep detection, weekly / monthly reviews.
Acceptance. Morning modal shows correct tasks. Selecting > 3 is prevented with a gentle message. Skip works and doesn't re-prompt same day. Evening shutdown opens a reflective page, doesn't block closing the app.
Estimated effort. 1 – 2 days.
Phase 6 — Soft-touch nudging (Margot protocol) — REVISED 2026/04/23
Why here. Big differentiator. Scheduled late because it nudges about the things the earlier phases built (tasks, timers, rituals), and needs careful copy so it doesn't collapse into a push-notification daemon. Spec is explicit: not push notifications — anticipatory guidance.
Revised architecture — "nudge bus" hybrid. Earlier drafts proposed a Rust-side OS-activity watcher (keyboard, active window). Cross-platform review flagged this as fragile: Wayland offers no sanctioned global-keyboard API, macOS needs accessibility permission, Windows needs a message-loop hook, and the signal is low quality everywhere. Deferred to post-v0.1.
Phase 6 instead ships a frontend-owned nudge bus that consumes signals Corbie already produces:
- Focus-timer state (running / completed / cancelled) — already on
windowevents from Phase 1. - Task-completed events — adds a
kon:task-completedwindow event dispatched whencomplete_task_cmd/complete_subtask_cmdresolve. - Micro-step generation — event from the decompose path.
- Ritual state —
ritualsMorning,lastTriageDatefrom settings/storage. - App focus / visibility —
document.visibilitychange+ Tauri windowfocus/blurevents.
The bus applies suppression rules, then dispatches via Rust commands for platform-native delivery.
Notification plugin prerequisites (new cross-cutting work, done in Phase 6).
- Add
tauri-plugin-notification = "2"(Cargo.toml) +@tauri-apps/plugin-notification(package.json). - Register the plugin in
lib.rs. - Frontend checks
isPermissionGranted()and callsrequestPermission()on first use. - Expose ACL entries in
src-tauri/capabilities/default.json. - Windows caveat: notifications only deliver properly for installed apps (not
tauri devbuilds). Flag this in release notes and the dev HANDOVER. - Sound path: Windows expects a
.wavfile path, not a platform sound name. Drop the original spec's"Default"string for Windows; ship a tiny custom.wavinsrc-tauri/sounds/. macOS"Glass"is valid. Linux freedesktop sound-namemessage-new-instantworks viatauri-plugin-notification'ssoundoption.
Scope (revised).
nudgeBus.svelte.tsstore — subscribes to the in-app signals above, owns cooldown/suppression logic.- Rust command
deliver_nudge(title, body, sound?)— thin wrapper aroundtauri-plugin-notificationthat also persists a row to a newnudgesSQLite table (for debugging + future analytics). - Trigger set for v1 (all in-app, no OS-wide detection):
inactivity_with_active_timer— timer running +document.visibilitystate === 'hidden'OR windowblurfor 90 s continuous. (We know the user has switched away; we don't need to know what they switched to.)pending_morning_triage— past 10:00 local + triage enabled + last-shown ≠ today. Fires once per day; gets suppressed forever if user later skips or completes.micro_step_idle— micro-step created + nokon:task-completedorkon:step-completedevent for that parent-task-id within 15 min.
- Suppression rules:
- Global mute in settings (on/off).
- Hard cap 3 nudges per rolling hour.
- No nudge in first 60 s of a timer.
- No nudge during app focus (the user is already looking).
- Rhythmic voice anchoring: piggy-back on Phase 4 TTS. Optional "speak nudges aloud" toggle. Default off. British-English calm lines: "Time to move on", "Your list is still here". No personality yet — Phase 9.
Out of scope.
- OS-wide activity detection (keyboard hooks, active-window polling). Deferred post-v0.1 as a separate phase, if a real need emerges.
- Custom trigger editor (owned by Phase 7).
- Biometric signals. Any Margot-as-character visual.
Acceptance. Each trigger fires on its defined condition in a dogfood walkthrough. Suppression observed (hidden-for-90 s → nudge; return-to-focus → no nudge). Global mute kills everything immediately. Notification permission request appears on first trigger; denial is respected.
Estimated effort. 1 – 2 days (including notification-plugin setup and the nudges table).
Phase 7 — Implementation intentions (if-then automation) — REVISED 2026/04/23
Why here. Leans on Phase 6's nudge bus. User-defined rules reuse the same delivery path.
Rule idempotency (new explicit requirement).
- Each rule stores
last_fired_at: ISO8601and, for daily rules,last_fired_local_date: YYYY-MM-DD. Without this, a poll-driven "at 09:00" fires on every tick. - On sleep/resume: on app focus after > 10 minutes away, check each time-of-day rule; if today's fire time has passed and
last_fired_local_dateis not today, fire once and update. Configurable per-rule toggle:catch_up_on_resume(default ON for time-of-day rules). - "After a task completes" rule: subscribes to the
kon:task-completedevent from Phase 6. Rule fires once per task id (guarded vialast_fired_task_ids). - "Morning triage finishes" rule: fires on either "Start the day" or "Skip for today" — skip counts as finishing. Fires once per
last_fired_local_date.
Scope.
- Rule editor UI. Minimal:
if [when-condition], then [action]. - When-conditions for v1:
time of day = HH:MM,after a task completes(pick a specific task from list),morning triage finishes. - Actions for v1:
surface a specific task(jump to Tasks page + highlight),start a 5-min timer,speak a line aloud(reuses Phase 4 TTS). - Rules stored in SQLite (
rulestable: id, name, when_json, then_json, enabled, last_fired_at, last_fired_local_date, last_fired_task_ids). Global mute respected. - No location triggers (desktop app, no geolocation). No app-running detection (fragile cross-platform — revisit post-v0.1).
Out of scope. Calendar triggers, cross-app automation, macro-style action chains, shared/community rules.
Acceptance. User can write "at 09:00, speak 'time to plan the day' aloud and surface my 'daily standup' task", save it, have it fire next morning at 09:00. Polling tick does not re-fire it. Delete works. Sleep the machine through 09:00; on resume the rule catches up once, then goes quiet until tomorrow.
Estimated effort. 1 – 1.5 days.
Phase 8 — Forgiving gamification — REVISED 2026/04/23
Why here. Low-risk, closes the spec list.
Scope (revised — grace days dropped).
- Completion count per day (non-punitive). "You've finished 3 today."
- No streaks, no chains — so the original "grace days" logic was solving a problem that doesn't exist in this design. Dropped.
- Optional "recent momentum" sparkline: last 7 days' daily completion counts as a tiny inline chart on the Tasks header. Always additive; empty days render as baseline, never as gaps.
- Visual: soft-edged numeric badges on the Tasks header. No leaderboards, no social comparison.
- Zero loss language. Always "look what you did".
Out of scope. Leaderboards. Shared challenges. Streak repair purchases. XP systems.
Acceptance. Complete 3 tasks today → header shows "3 today". Open the app after 4 days off → no "you were away" framing; header reads today's count only; sparkline simply shows flat zero bars for the away days.
Estimated effort. Half day.
Phase 9 — Polish debt
All Phase 9 work is paused until Phase 1 – Phase 8 are closed. Per Jake's rule: features first, polish second.
Contents.
- File-system
.mdsave dialog (replace clipboard-only export). Rustwrite_text_filecommand; platform dialog viatauri-plugin-dialog. - Bulk select + bulk export in History.
- LLM-powered content tags (
topic:*,intent:*). Slot into the existingkon-llmstub. - Settings UX overhaul: bundle high-traffic settings into a "Start here" group; hide advanced behind a disclosure.
- Visual polish pass on all Phase 1 – Phase 8 surfaces: spacing, typography, motion curves, colour, dark-mode parity.
- Accessibility pass: keyboard navigation, screen reader labels, focus order, colour contrast audit against WCAG AA.
Estimated effort. 1 – 2 days.
Phase 10 — QC + rename + release — SPLIT 2026/04/23
Earlier draft rolled QC, a full codebase rename, an app-data migration shim, and the release ceremony into one day. Review flagged this as unrealistic and split it. The rename sweep in particular crosses every surface in the app; rushing it is how you end up with kon.db on half the users' machines and corbie.db on the rest.
Pre-Phase-10: Cargo.lock policy decision
.gitignorecurrently excludesCargo.lock. For a Tauri binary workspace this is the wrong default — CI resolves dependencies fresh each run, which is the leading theory for the 2026-04-24 CI red-state noted inHANDOVER.md.- Decision needed before tagging v0.1.0: commit
Cargo.lock. Remove the.gitignoreline in a dedicated commit, runcargo generate-lockfileif needed, commit the lockfile, watch CI for one green cycle. - Captures the dep set users actually get from the release artefacts rather than whatever crates.io happened to resolve at build time.
Phase 10a — QC (estimated half day)
Prerequisite: Phase 1 – Phase 9 complete. Cargo.lock committed.
- Full dogfood walkthrough: record a real brain-dump → clean transcript → task extraction → micro-step one task → run a focus timer → tag energy → complete → open evening wind-down → skip morning triage → re-check no re-prompt.
- RB-08 macOS power-assertion verification: Rachmann runs this offline on his Mac.
pmset -g assertionsduring a live session; expectedPreventSystemSleepattributed to Corbie's bundle id. On confirmation, close RB-08 and movedocs/issues/power-assertion-macos-objc2.mdtodocs/issues/resolved/. - Cross-platform build matrix green across Linux / macOS / Windows.
- Accessibility regression check: keyboard-only traversal of every new Phase 5 – Phase 8 surface.
- Freshly-clean install test on a spare user account: no stray data leaks from dev.
Rachmann's Mac slot runs in parallel; not blocking the rest of 10a.
Phase 10b — Kon → Corbie rename sweep (estimated half day to 1 day)
Runs after Phase 10a QC, after Jake has renamed the two repos in GitHub + Gitea web UIs. The rename only starts here, not earlier, so in-flight Phase 1 – Phase 9 work doesn't have to re-learn event names mid-cycle.
package.json→name: "corbie",descriptionupdate. (Version stays at0.1.0.)- Cargo crates:
kon,kon-audio,kon-storage,kon-transcription,kon-llm,kon-ai-formatting,kon-core,kon-cloud-providers,kon-hotkey,kon-mcp→corbie-*. Mass-rename viaCargo.tomlname field + workspace path references +useimports. - Binary + product names:
src-tauri/tauri.conf.json(productName, identifier),.desktopfile, Windows product name, macOS bundle name. - Install paths:
~/.local/share/kon/→~/.local/share/corbie/(plus the macOS~/Library/Application Supportand Windows%APPDATA%equivalents). App-data migration shim required — first-run checks for the old dir, moves contents, writes a sentinel.migrated-from-kon. Shim must handle the case where both dirs exist (prefer new, log the duplicate). - Database filename:
kon.db→corbie.db. Handled by the same shim. - Window titles, tray tooltip, About-dialog, README body, docs references where they name the product (leave historical brief content talking about "Kon" — it's a historical document).
- Event names:
kon:start-timer,kon:task-completed,kon:open-wind-down,kon:preferences-changed,kon:hotkey-pressed,kon:llm-download-progress→corbie:*. Single commit; one find-replace; both emitter and listener in the same diff. - Logs, error messages, user-facing copy (including toast strings that mention "Kon").
- Settings SQLite key:
kon_preferences→corbie_preferences. Migration reads old key on first launch, writes new key, deletes old. - Remotes:
ssh://git.corbel.consulting:2222/jake/kon.git+github.com:jakejars/kon.git→…/corbie.git.git remote set-urllocally after web-UI renames.
Phase 10c — Release (estimated half day)
- Version is already
0.1.0inCargo.toml,package.json, andtauri.conf.json— no bump needed. Confirm the three match. - Write
CHANGELOG.md. Seed from this roadmap's phases. Entries are written to end-users, not engineers — "You can now read transcripts aloud" not "Added tts_speak command". - Write release notes in plain language: what it does, who it's for, the Kon-data migration note, the Windows notifications caveat (installed app only).
- Tag
v0.1.0on the head commit. - Push tag to both remotes. GitHub Actions release workflow auto-builds artefacts for Linux / macOS / Windows.
- Smoke-test at least one artefact per platform (ideally Rachmann covers macOS) before the release is made public.
Estimated effort (Phase 10 total). 1 – 2 days across 10a / 10b / 10c plus Rachmann's parallel Mac session.
Totals
- Phase 1 – 8 feature build: 6 – 9 days of focused work
- Phase 9 polish: 1 – 2 days
- Phase 10 QC + rename + release (split): 1 – 2 days + Rachmann's Mac session
Total to v0.1.0 feature-complete release: ~8 – 13 days of focused work, depending on how much polish time Jake wants in Phase 9. Revised downward at the lower end after the Phase 8 grace-day drop and Phase 10 split clarified actual scope.
Explicit non-goals
- Mobile apps. Corbie is desktop-first; a mobile companion is post-v0.1.
- Cloud sync. Local-first is the floor, not a feature. Sync is out of scope through v0.1.
- Premium voices, paid tiers, subscription. Licensing + monetisation is a separate track tracked in memory
project_marketplace_creem. - AI body doubling (low-fi focus rooms) — validated but parked to post-v0.1.
- Temptation bundling — cut (OS-integration impossible cross-platform; replaced by Phase 3 energy-aware sequencing).
Post-v0.1 ideas (captured, not scheduled)
Ideas worth keeping warm once the v0.1 release is out. Not tracked as phases until the core release is done.
- Calendar integration. Read-only first pass could parse a local ICS file (Thunderbird / Evolution export) and surface day events alongside the tasks list — stays local-first. A cloud-sync pass (Google / iCloud / CalDAV) is a v0.2+ conversation because it re-opens credential handling and refresh-token plumbing that v0.1 deliberately avoids.
- Right-click highlighted text → capture as task. Two flavours: (a) In-Corbie — context menu on a selected transcript range or viewer segment, routes to
create_task_cmdwith the selection as text. Small — lives naturally in Phase 9 polish or a bolt-on. (b) System-wide — highlight anywhere (browser, Slack, IDE) and call Corbie. Platform-painful: macOS Services API, Windows shell-extension, Linux desktop-env-specific context menus. Scope as a separate post-v0.1 phase.
Anchors
- Spec: docs/brief/feature-set.md + docs/brief/design-principles.md
- Current baseline: this session's HANDOVER.md
- Rebrand memory:
~/.claude/projects/-home-jake-Documents-CORBEL-Main/memory/project_corbie_rebrand.md - Release-blocker index: docs/issues/README.md
This roadmap is a living document. Update it at the end of each phase with actuals vs estimates and any scope revisions.