docs(roadmap): revise phases 6-10 after cross-model review
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

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).
This commit is contained in:
2026-04-24 18:03:21 +01:00
parent 3cf3e41899
commit 55b34d8ffc

View File

@@ -135,59 +135,95 @@ MicroSteps is shipped; its "just-start" timer button emits an event that current
---
## Phase 6 — Soft-touch nudging (Margot protocol)
## Phase 6 — Soft-touch nudging (Margot protocol) — **REVISED 2026/04/23**
**Why here.** This is the big differentiator and it's intentionally scheduled late because it depends on the phases before it (it nudges *about* tasks, timers, rituals) and on a careful copy pass to not feel like a push notification. The brief explicitly calls out: reminders must not function as standard push notifications; they must be anticipatory guidance.
**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.
**Scope.**
- Context-aware trigger engine in Rust: watches activity signals (keyboard activity, active window, last-interaction timestamp) and fires `nudge` events when triggers match.
- Trigger set for v1: `inactivity_with_active_timer` (timer running, no UI interaction for 90 s), `pending_morning_triage` (past 10:00 and triage untouched), `micro_step_idle` (micro-step generated, not acted on within 15 min).
- Delivery: OS notification via `tauri-plugin-notification`. Platform sounds: `Glass` on macOS, `message-new-instant` on Linux, `Default` on Windows. Haptic cue on mobile (not yet in scope; desktop first).
- Suppression rules: no nudge if user typed in last 5 s, no nudge during a running timer's first 60 s, hard cap at 3 nudges per hour, instant mute button in settings.
- Rhythmic voice anchoring: piggy-back on Phase 4 TTS. Optional "speak nudges aloud" toggle. Default off. When on, short calm lines ("Time to move on", "Your list is still here"). No branding voice, no personality yet — that's a Phase 9 polish item.
**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.
**Out of scope.** Custom trigger editor (user-facing rule UI is Phase 7). Cross-device delivery. Biometric signals (HRV, fidget detection, etc.). Any Margot-as-character visual.
Phase 6 instead ships a **frontend-owned nudge bus** that consumes signals Corbie already produces:
- Focus-timer state (running / completed / cancelled) — already on `window` events from Phase 1.
- Task-completed events — adds a `kon:task-completed` window event dispatched when `complete_task_cmd` / `complete_subtask_cmd` resolve.
- Micro-step generation — event from the decompose path.
- Ritual state — `ritualsMorning`, `lastTriageDate` from settings/storage.
- App focus / visibility — `document.visibilitychange` + Tauri window `focus`/`blur` events.
**Acceptance.** Nudges fire on each trigger in a dogfood walkthrough. Suppression rules observed. Mute button kills everything immediately.
The bus applies suppression rules, then dispatches via Rust commands for platform-native delivery.
**Estimated effort.** 1 2 days.
**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 calls `requestPermission()` on first use.
- Expose ACL entries in `src-tauri/capabilities/default.json`.
- Windows caveat: notifications only deliver properly for *installed* apps (not `tauri dev` builds). Flag this in release notes and the dev HANDOVER.
- Sound path: Windows expects a `.wav` file path, not a platform sound name. Drop the original spec's `"Default"` string for Windows; ship a tiny custom `.wav` in `src-tauri/sounds/`. macOS `"Glass"` is valid. Linux freedesktop sound-name `message-new-instant` works via `tauri-plugin-notification`'s `sound` option.
**Scope (revised).**
- `nudgeBus.svelte.ts` store — subscribes to the in-app signals above, owns cooldown/suppression logic.
- Rust command `deliver_nudge(title, body, sound?)` — thin wrapper around `tauri-plugin-notification` that also persists a row to a new `nudges` SQLite 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 window `blur` for 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 + no `kon:task-completed` or `kon:step-completed` event 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)
## Phase 7 — Implementation intentions (if-then automation) — **REVISED 2026/04/23**
**Why here.** Leans on Phase 6's trigger engine. The user-facing rule editor is its own thing, but the execution path is the nudge pipeline with user-defined conditions.
**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: ISO8601` and, 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_date` is 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-completed` event from Phase 6. Rule fires once per task id (guarded via `last_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`, `morning triage finishes`.
- Actions for v1: `surface task X`, `start a 5-min timer`, `speak a line aloud`.
- Rules stored in SQLite. On/off per rule. Global mute respected.
- No location triggers (desktop app, no geolocation). No app-running detection in v1 (fragile cross-platform; revisit in v0.2).
- 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 (`rules` table: 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' and surface inbox", save it, and have it fire next morning at 09:00. Delete works.
**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 day.
**Estimated effort.** 1 1.5 days.
---
## Phase 8 — Forgiving gamification
## Phase 8 — Forgiving gamification — **REVISED 2026/04/23**
**Why here.** Last feature phase because it's the lowest-risk and the furthest from make-or-break. Neatly rounds out the spec list.
**Why here.** Low-risk, closes the spec list.
**Scope.**
- Completion count per day (non-punitive: no streaks, no chain-breaking). "You've completed 4 tasks today. Three in the afternoon. Want to call it?"
- Grace days: the badge logic ignores up-to-3 consecutive days of no activity without reset.
- Visual: soft-edged numeric badges on the tasks header, no leaderboards, no social comparison.
- Zero loss language. Never "you lost your streak". Framing is always "look what you did".
**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 → header shows "3 today". Open the app after 4 days off → no negative framing, header reads today's count only.
**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 to 1 day.
**Estimated effort.** Half day.
---
@@ -207,45 +243,63 @@ MicroSteps is shipped; its "just-start" timer button emits an event that current
---
## Phase 10 — QC + rename + release
## Phase 10 — QC + rename + release — **SPLIT 2026/04/23**
**Prerequisite:** Phase 1 Phase 9 complete.
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.
**QC block.**
- Full dogfood walkthrough: record a real brain-dump → clean transcript → task extraction → micro-step one task → run a focus timer → tag energy → complete → check evening shutdown.
- RB-08 macOS power-assertion verification: **Rachmann runs this offline** on his Mac. He runs `pmset -g assertions` while a live session is active; expected: entry for `PreventSystemSleep` attributed to Corbie's bundle id. Once confirmed, mark RB-08 closed and delete `docs/issues/power-assertion-macos-objc2.md` or move to resolved.
- Cross-platform build matrix (CI already runs): Linux / macOS / Windows, ensure all three are green.
- Accessibility regression check: keyboard-only traversal of each new surface.
### Pre-Phase-10: Cargo.lock policy decision
- `.gitignore` currently excludes `Cargo.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 in `HANDOVER.md`.
- **Decision needed before tagging v0.1.0:** commit `Cargo.lock`. Remove the `.gitignore` line in a dedicated commit, run `cargo generate-lockfile` if 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 assertions` during a live session; expected `PreventSystemSleep` attributed to Corbie's bundle id. On confirmation, close RB-08 and move `docs/issues/power-assertion-macos-objc2.md` to `docs/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.
**Kon → Corbie codebase rename sweep.** Runs after QC once Jake has renamed the two repos:
- `package.json` name `kon@0.1.0``corbie@0.1.0`.
- Cargo crate names: `kon`, `kon-audio`, `kon-storage`, `kon-transcription`, `kon-llm`, `kon-ai-formatting`, `kon-core`, `kon-cloud-providers`, `kon-hotkey`, `kon-mcp``corbie-*`. Mass-rename via `Cargo.toml` + `use`-path sweep.
- Binary + product names: `src-tauri/tauri.conf.json`, bundle identifier, `.desktop` file, Windows product name, macOS bundle name.
- Install paths: `~/.local/share/kon/``~/.local/share/corbie/`. **Migration shim required**: first-run check for old dir, move contents, write a sentinel. Document in the release notes.
- Database filename: `kon.db``corbie.db`. Handled by the same migration shim.
- Window titles, tray tooltip, About-dialog text, README body, docs/brief/ references where they refer to the product (leave historical brief content talking about "Kon" as-is — it's a historical document).
- Event names: `kon:start-timer``corbie:start-timer` and similar. Kept `kon:` through Phase 1 Phase 9 so any dogfood doesn't need to re-learn them mid-cycle.
- Logs, error messages, user-facing copy.
- Remotes: `ssh://git.corbel.consulting:2222/jake/kon.git` + `github.com:jakejars/kon.git``…/corbie.git` on both, after Jake has clicked rename in the web UIs. Update `git remote set-url` locally.
Rachmann's Mac slot runs in parallel; not blocking the rest of 10a.
**Release.**
- Bump `Cargo.toml` and `package.json` to `0.1.0`. Tag `v0.1.0` on the commit.
- Write `CHANGELOG.md` (seed it with everything from this roadmap's phases).
- Write release notes in plain language — what it does, who it's for, the data-migration note.
- Push tag to both remotes. GitHub Actions release workflow (already in place for cross-platform CI) should auto-build artefacts for Linux / macOS / Windows.
### Phase 10b — Kon → Corbie rename sweep (estimated half day to 1 day)
**Estimated effort.** 1 day (QC + rename + release ceremony), plus Rachmann's slot on his Mac (parallel, not blocking).
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"`, `description` update. (Version stays at `0.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 via `Cargo.toml` name field + workspace path references + `use` imports.
- Binary + product names: `src-tauri/tauri.conf.json` (productName, identifier), `.desktop` file, Windows product name, macOS bundle name.
- Install paths: `~/.local/share/kon/``~/.local/share/corbie/` (plus the macOS `~/Library/Application Support` and 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-url` locally after web-UI renames.
### Phase 10c — Release (estimated half day)
- Version is already `0.1.0` in `Cargo.toml`, `package.json`, and `tauri.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.0` on 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.5 9.5 days** of focused work
- Phase 1 8 feature build: **6 9 days** of focused work
- Phase 9 polish: **1 2 days**
- Phase 10 QC + rename + release: **1 day + Rachmann's Mac session**
- Phase 10 QC + rename + release (split): **1 2 days** + Rachmann's Mac session
**Total to v0.1.0 feature-complete release:** **~9 13 days of focused work**, depending on how much polish time Jake wants in Phase 9.
**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