diff --git a/HANDOVER-2026-04-19.md b/HANDOVER-2026-04-19.md
new file mode 100644
index 0000000..5759210
--- /dev/null
+++ b/HANDOVER-2026-04-19.md
@@ -0,0 +1,97 @@
+---
+name: handover-2026-04-19
+type: reference
+tags: [handover, session, kon]
+description: Session handover — 2026/04/19 dogfood polish + cross-platform window chrome
+---
+
+# Kon Handover — 2026/04/19
+
+Second dogfood sprint. Four phases: (1) fix bugs surfaced on first real use, (2) redesign History for cognitive-load hygiene, (3) resolve broken window resize/drag on Linux Wayland, (4) clean up microphone picker.
+
+## What shipped this session
+
+### Cross-window preferences sync
+- `preferences.svelte.js` emits `kon:preferences-changed` Tauri event on update.
+- Main / viewer / float layouts listen and call `applyExternalPreferences` without re-emit, so theme and font changes propagate live across sibling windows.
+- Echo suppressed via source window label check.
+
+### Hotkey recorder
+- Root cause of "can't change hotkey": button-level `onkeydown` relied on post-click keyboard focus, which webkit2gtk on Linux does not guarantee.
+- Fix: `document.addEventListener("keydown", ..., { capture: true })` inside a `$effect` gated by `recording`. Beats any descendant handler. Escape now cancels.
+
+### History page redesign (research-backed)
+- Compact row now shows the **title** (or "Untitled"), not body-preview text — metadata already lives in the row columns (date, duration, source icon).
+- Expanded row gets an inline title input (replaces the old Rename prompt modal).
+- **Edit** button opens the viewer window in `edit` mode (editable textarea, debounced save to localStorage + storage-event sync back to main history).
+- **Export .md** copies a full YAML-frontmatter markdown document to the clipboard — paste into Obsidian.
+- **Tags**: `$lib/utils/frontmatter.js` exposes `deriveAutoTags` (currently returns `[]`), `buildFrontmatter`, `serialiseFrontmatter`, `buildMarkdown`. Manual tags stored as `item.manualTags`, rendered as removable chips in the expanded row with `+ add tag` input.
+- Header tag chip bar (cap 7, click to filter, × to clear), plus `tag:xyz` search syntax.
+- Global **Starred** filter toggle in the History header.
+- Research memo found all five previous auto-tag families redundant with existing row UI — kept the derivation hook for the post-Task-7 `topic:*` content tag from kon-llm.
+- Duplicate-transcript render fix: expanded `
` only if compact preview actually truncated.
+
+### Viewer / editor popout
+- `/viewer` route now reads `kon_viewer_mode` from localStorage ("view" | "edit").
+- Edit mode renders a plain textarea bound to `item.text`; 400ms debounced save flushes on input, final flush on `onDestroy`. Segment-specific controls (Compact, Starred) hidden in edit mode.
+- Native title: **"Kon - Transcription Editor"**.
+
+### Platform-aware window chrome (Linux fix)
+**Root cause:** Tauri v2 frameless `decorations: false` on KDE Wayland + webkit2gtk does not honour diagonal corner resize (collapses `NorthEast` etc. to a single axis via GTK's `gtk_window_begin_resize_drag`), and `data-tauri-drag-region` adds noticeable drag latency. Setting `setPointerCapture` ahead of `startResizeDragging` does not help once the compositor has taken over the pointer grab. Verified via Context7 docs + Codex diagnosis — Linux frameless is a known-fragile path.
+
+**Fix:**
+- Linux uses **native KWin/Mutter decorations**. `src-tauri/tauri.linux.conf.json` overlays `decorations: true` + full main window config (title, sizes) — overlays **replace** the windows array, so every field must be present, not just the delta. `src-tauri/src/commands/windows.rs` uses `cfg!(target_os = "linux")` to set decorations per window.
+- macOS / Windows keep custom chrome. `src/lib/utils/osInfo.js` `isLinux()` gates `` and `` via `useCustomChrome = $state(false)`; flips to `!isLinux()` after `loadOsInfo()` resolves.
+- Dueling drag-region handlers removed across Titlebar, float page, viewer page — everywhere a manual `startDragging()` lives, the `data-tauri-drag-region` attribute was deleted (they're alternatives per Tauri docs, not combinable).
+- `ResizeHandles` kept for macOS/Windows frameless: 12 px edges / 20 px corners via CSS vars (`--kon-resize-edge`, `--kon-resize-corner`), `pointerdown` + `setPointerCapture`, corners with explicit higher z-index. Handles rendered as siblings of the animated layout div so `position: fixed` is viewport-relative rather than captured by the transform containing block.
+
+### Window minimum sizes (evidence-backed)
+Research pass cited GNOME HIG (1024×600 desktop / 360×294 mobile floors), WCAG 2.2 SC 1.4.10 Reflow (320 CSS px), Raycast 750×474 as a reference for single-pane working width, and consistent A11y principle that nothing should clip in the default configuration.
+
+| Window | Was | Now | Rationale |
+|---|---|---|---|
+| Main | 1020×540 | **960×600** | Fits 210 px sidebar + ~750 px content; GNOME vertical floor. |
+| Float | 400×400 | **360×480** | 360 = GNOME mobile floor; 480 fits pills + quick-add + sort + ~6 task rows without scroll. |
+| Transcript editor | 450×500 | **560×520** | Exceeds WCAG reflow floor; ~60-70 char measure for editing. |
+
+### Microphone picker cleanup
+- ALSA enumeration was leaking `hw:`, `plughw:`, `front:`, `sysdefault:`, `null` et al into the dropdown.
+- `SettingsPage.svelte` now renders only sentinel devices (`default`, `pipewire`, `pulse`) + one entry per unique sound card, keyed off the `sysdefault:CARD=X` alias.
+- `crates/audio/src/capture.rs` reads `/proc/asound/cards` and populates a new `description` field on `DeviceInfo` with the card's full product string (e.g. "Blue Microphones" for Jake's Yeti). Frontend prefers description → CARD=X short name → raw name.
+
+### GPU reporting
+- `commands/models.rs::get_runtime_capabilities` was hardcoded to `accelerators: vec!["cpu"]` and `supports_gpu: false` for whisper. Updated to `["cpu", "vulkan"]` and whisper `supports_gpu: true`, reflecting that `crates/transcription/Cargo.toml` links transcribe-rs with the `whisper-vulkan` feature unconditionally.
+- Settings now shows the Vulkan option instead of the "This build is CPU-only" notice.
+
+### Desktop shortcut
+- `~/Desktop/Kon.desktop` launcher with the 128×128 icon, `Terminal=true` so logs are visible and Ctrl+C cleanly stops the run.sh wrapper.
+
+## What's deferred
+
+- **Transparent windows (`transparent: true`)** — Tauri issue #13270 reports this smooths drag/resize further on Linux, but it's moot now that Linux uses native decorations.
+- **File-system export (.md save dialog)** — currently clipboard-only. Needs a Rust `write_text_file` command for plugin-less file writes.
+- **Bulk select + bulk export** in History.
+- **LLM-powered content tags** (`topic:*`, `intent:*`) — slots into Task 7 `kon-llm` stub once Phase 3 wires real llama-cpp-2.
+- **Settings UX overhaul** — Jake flagged that current settings feel overwhelming. Proposed: bunch high-traffic settings, hide advanced behind a toggle. Brainstorm + plan deferred to a dedicated session.
+- **Task 7 (MicroSteps end-to-end)** — storage + Tauri CRUD + kon-llm stub + frontend dual-write all landed in an earlier commit chain. The MicroSteps UI was written as the final task 7 step but not yet dogfooded against the stub LLM. Needs manual walkthrough.
+
+## Gotchas discovered today
+
+| Issue | Fix |
+|---|---|
+| `tauri.linux.conf.json` stripped title and min sizes from main window | Overlay **replaces** the windows array — include every field, not just the delta |
+| `data-tauri-drag-region` + manual `startDragging()` on the same node caused drag latency | Pick one — we use manual `startDragging` for the button/input early-return logic |
+| Corner resize collapsed to single axis on KWin Wayland | Native decorations on Linux side-step the whole frameless path |
+| `animate-float-enter` on the viewer/float layout root created a containing block that broke `position: fixed` on ResizeHandles children | Render ResizeHandles as a sibling of the animated div, not a descendant |
+| Kon binary auto-respawned on file-save while a second run.sh was also launching → two visible instances sharing one Vite server | Do not script `./run.sh` while the user has already launched via the desktop icon; rely on HMR |
+| `run.sh` leaves `"beforeDevCommand": ""` in tauri.conf.json if its cleanup trap is bypassed (e.g. SIGKILL) | Cleanup trap restores `"npm run dev"` on graceful exit; SIGTERM (not SIGKILL) is the right kill signal |
+| `/proc/asound/cards` header lines have leading whitespace for 2-digit card ID alignment | Parser trims leading whitespace before checking for leading digit |
+
+## How to resume
+
+```
+Picking up Kon dogfooding from 2026/04/19.
+HANDOVER is at HANDOVER.md in the project root.
+Active priorities: (1) confirm resize/drag/mic cleanup, (2) Task 7 MicroSteps
+dogfood with kon-llm stub, (3) Settings UX brainstorm.
+```
diff --git a/HANDOVER.md b/HANDOVER.md
index 5759210..652641b 100644
--- a/HANDOVER.md
+++ b/HANDOVER.md
@@ -1,97 +1,83 @@
---
-name: handover-2026-04-19
+name: handover-2026-04-23
type: reference
-tags: [handover, session, kon]
-description: Session handover — 2026/04/19 dogfood polish + cross-platform window chrome
+tags: [handover, session, kon, consolidation]
+description: Session handover — 2026/04/23 branch consolidation + main-in-its-best-state pass
---
-# Kon Handover — 2026/04/19
+# Kon Handover — 2026/04/23
-Second dogfood sprint. Four phases: (1) fix bugs surfaced on first real use, (2) redesign History for cognitive-load hygiene, (3) resolve broken window resize/drag on Linux Wayland, (4) clean up microphone picker.
+Consolidation session. Jake asked for "all branches to main and the repo in its best state" ahead of Corbie rebrand. This session brought outstanding Dependabot PRs into `main`, cleaned workspace lints, recovered an orphan stash onto a dedicated branch, and established a clean baseline for the post-rebrand push.
+
+## Rebrand note
+
+The product is in the process of being renamed **Kon → Corbie**. As of 2026/04/23 the canonical product name in any new copy (grant applications, landing pages) is **Corbie** (sentence case). The GitHub repo `jakejars/kon` and the Gitea repo `jake/kon` both still carry the `kon` name, intentionally — repo rename is pending Jake's own hand and is not blocking the rebrand copy-wise. Code paths (`crates/`, `src-tauri/`, package name `kon@0.1.0`) remain as-is until the repo rename lands; renaming the codebase identifiers is a separate coordinated sweep. See `memory/project_corbie_rebrand.md` in CORBEL-Main for the full rebrand state.
## What shipped this session
-### Cross-window preferences sync
-- `preferences.svelte.js` emits `kon:preferences-changed` Tauri event on update.
-- Main / viewer / float layouts listen and call `applyExternalPreferences` without re-emit, so theme and font changes propagate live across sibling windows.
-- Echo suppressed via source window label check.
+### Baseline validation
+- Fresh `cargo build`, `cargo test --workspace`, `cargo fmt --check`, `npm run check`, `npm run build` run against the pre-consolidation tip (`9b0067b`, tagged as `pre-consolidation-2026-04-23` for recovery).
+- `npm run build` initially failed with a missing-dependency error for `@chenglou/pretext`. Root cause: stale partial `node_modules` install dated 2026-04-21 07:45 — the directory for the package existed but was empty. `npm install` recovered it; package is present in lockfile and needed by `src/lib/utils/textMeasure.ts`.
+- `cargo clippy -- -D warnings` initially failed with 3 errors in `crates/storage/src/file_storage.rs` (two doc-list overindentations, one needless return). These were trivial style issues that clippy had never enforced at CI strictness.
-### Hotkey recorder
-- Root cause of "can't change hotkey": button-level `onkeydown` relied on post-click keyboard focus, which webkit2gtk on Linux does not guarantee.
-- Fix: `document.addEventListener("keydown", ..., { capture: true })` inside a `$effect` gated by `recording`. Beats any descendant handler. Escape now cancels.
+### Workspace clippy cleanup (commit `fe61661`)
+- Applied `cargo clippy --fix` across 11 files: `crates/audio`, `crates/hotkey`, `crates/storage`, `crates/transcription`, `src-tauri`.
+- Net -2 lines. No behavioural changes — pure lint cleanup (needless returns, unnecessary casts, `iter().any()` → `contains()`, `repeat().take()` → `repeat_n`, lifetime elision, `map_or` simplification, reference-immediately-dereferenced).
+- One remaining warning left untouched: `needless_range_loop` at `src-tauri/src/commands/live.rs:1089` — clippy's suggested iterator rewrite would make it less readable. Earmarked for a focused refactor session.
+- Build + 245 workspace tests remain green post-fix.
-### History page redesign (research-backed)
-- Compact row now shows the **title** (or "Untitled"), not body-preview text — metadata already lives in the row columns (date, duration, source icon).
-- Expanded row gets an inline title input (replaces the old Rename prompt modal).
-- **Edit** button opens the viewer window in `edit` mode (editable textarea, debounced save to localStorage + storage-event sync back to main history).
-- **Export .md** copies a full YAML-frontmatter markdown document to the clipboard — paste into Obsidian.
-- **Tags**: `$lib/utils/frontmatter.js` exposes `deriveAutoTags` (currently returns `[]`), `buildFrontmatter`, `serialiseFrontmatter`, `buildMarkdown`. Manual tags stored as `item.manualTags`, rendered as removable chips in the expanded row with `+ add tag` input.
-- Header tag chip bar (cap 7, click to filter, × to clear), plus `tag:xyz` search syntax.
-- Global **Starred** filter toggle in the History header.
-- Research memo found all five previous auto-tag families redundant with existing row UI — kept the derivation hook for the post-Task-7 `topic:*` content tag from kon-llm.
-- Duplicate-transcript render fix: expanded `` only if compact preview actually truncated.
+### Dependabot merges (commits `6579c5f`, `0b1c492`, `509b983`)
+Three dev-dep bumps landed as `--no-ff` merge commits:
-### Viewer / editor popout
-- `/viewer` route now reads `kon_viewer_mode` from localStorage ("view" | "edit").
-- Edit mode renders a plain textarea bound to `item.text`; 400ms debounced save flushes on input, final flush on `onDestroy`. Segment-specific controls (Compact, Starred) hidden in edit mode.
-- Native title: **"Kon - Transcription Editor"**.
+| # | Package | File changes |
+|---|---|---|
+| 1 | `picomatch` | `package-lock.json` only |
+| 2 | `@sveltejs/kit` | `package.json` + `package-lock.json` |
+| 3 | `vite` (npm_and_yarn group) | `package.json` + `package-lock.json` |
-### Platform-aware window chrome (Linux fix)
-**Root cause:** Tauri v2 frameless `decorations: false` on KDE Wayland + webkit2gtk does not honour diagonal corner resize (collapses `NorthEast` etc. to a single axis via GTK's `gtk_window_begin_resize_drag`), and `data-tauri-drag-region` adds noticeable drag latency. Setting `setPointerCapture` ahead of `startResizeDragging` does not help once the compositor has taken over the pointer grab. Verified via Context7 docs + Codex diagnosis — Linux frameless is a known-fragile path.
+After the three merges: `npm install` clean, `npm run build` green, `npm run check` 0 errors / 0 warnings. Vulnerability count went from 6 (1 low / 2 moderate / 3 high) to 5 (3 low / 2 moderate) — the three highs cleared.
-**Fix:**
-- Linux uses **native KWin/Mutter decorations**. `src-tauri/tauri.linux.conf.json` overlays `decorations: true` + full main window config (title, sizes) — overlays **replace** the windows array, so every field must be present, not just the delta. `src-tauri/src/commands/windows.rs` uses `cfg!(target_os = "linux")` to set decorations per window.
-- macOS / Windows keep custom chrome. `src/lib/utils/osInfo.js` `isLinux()` gates `` and `` via `useCustomChrome = $state(false)`; flips to `!isLinux()` after `loadOsInfo()` resolves.
-- Dueling drag-region handlers removed across Titlebar, float page, viewer page — everywhere a manual `startDragging()` lives, the `data-tauri-drag-region` attribute was deleted (they're alternatives per Tauri docs, not combinable).
-- `ResizeHandles` kept for macOS/Windows frameless: 12 px edges / 20 px corners via CSS vars (`--kon-resize-edge`, `--kon-resize-corner`), `pointerdown` + `setPointerCapture`, corners with explicit higher z-index. Handles rendered as siblings of the animated layout div so `position: fixed` is viewport-relative rather than captured by the transform containing block.
+### Orphan-stash recovery (branch `feat/design-system-recover`, commit `8855db8`)
+- A WIP stash sat on `stash@{0}`, pinned to parent commit `1296173` — the tip of the deleted `feat/design-system` branch. ~1000 LOC across 24 files: `ai-formatting/rule_based.rs` rewrite, `transcription/local_engine.rs`, core `hardware.rs` + `recommendation.rs`, `audio/resample.rs`, and a sweep across Svelte UI (`TaskSidebar`, `FilesPage`, `TasksPage`, `WipTaskList`, `ModelDownloader`, `Titlebar`, `viewer`, `float`).
+- Recovered using `git stash branch`, which creates a branch from the stash's parent commit and applies the stash content. Parent commit still existed in the reflog even though no branch referenced it.
+- Committed the full recovery as `wip(design-system): recover orphan stash` on the new branch. **Not merged to main.** Needs a focused rebase session: heavy conflicts expected in `TasksPage`, `FilesPage`, `ai-formatting/rule_based.rs`, and `transcription/local_engine.rs` against current main since those all saw release-blocker fixes after the stash was taken. Also missing the 2026-04-23 clippy cleanup.
+- Branch pushed to `github` for remote preservation.
-### Window minimum sizes (evidence-backed)
-Research pass cited GNOME HIG (1024×600 desktop / 360×294 mobile floors), WCAG 2.2 SC 1.4.10 Reflow (320 CSS px), Raycast 750×474 as a reference for single-pane working width, and consistent A11y principle that nothing should clip in the default configuration.
+## Release-blocker state (unchanged this session)
-| Window | Was | Now | Rationale |
-|---|---|---|---|
-| Main | 1020×540 | **960×600** | Fits 210 px sidebar + ~750 px content; GNOME vertical floor. |
-| Float | 400×400 | **360×480** | 360 = GNOME mobile floor; 480 fits pills + quick-add + sort + ~6 task rows without scroll. |
-| Transcript editor | 450×500 | **560×520** | Exceeds WCAG reflow floor; ~60-70 char measure for editing. |
+From `docs/issues/README.md`:
-### Microphone picker cleanup
-- ALSA enumeration was leaking `hw:`, `plughw:`, `front:`, `sysdefault:`, `null` et al into the dropdown.
-- `SettingsPage.svelte` now renders only sentinel devices (`default`, `pipewire`, `pulse`) + one entry per unique sound card, keyed off the `sysdefault:CARD=X` alias.
-- `crates/audio/src/capture.rs` reads `/proc/asound/cards` and populates a new `description` field on `DeviceInfo` with the card's full product string (e.g. "Blue Microphones" for Jake's Yeti). Frontend prefers description → CARD=X short name → raw name.
+- **0 open CRITICAL**
+- **1 open MAJOR** — RB-08 `power-assertion-macos-objc2` (awaits manual runtime verification on a real macOS machine: `pmset -g assertions` during a background live-session)
+- **11 RBs resolved**
+- **11 CR items resolved** from the 2026-04-22 code review
-### GPU reporting
-- `commands/models.rs::get_runtime_capabilities` was hardcoded to `accelerators: vec!["cpu"]` and `supports_gpu: false` for whisper. Updated to `["cpu", "vulkan"]` and whisper `supports_gpu: true`, reflecting that `crates/transcription/Cargo.toml` links transcribe-rs with the `whisper-vulkan` feature unconditionally.
-- Settings now shows the Vulkan option instead of the "This build is CPU-only" notice.
+RB-08 continues to gate v0.1 tagging.
-### Desktop shortcut
-- `~/Desktop/Kon.desktop` launcher with the 128×128 icon, `Terminal=true` so logs are visible and Ctrl+C cleanly stops the run.sh wrapper.
+## Deferred (unchanged from 2026/04/19 handover unless noted)
-## What's deferred
+- **RB-08 verification** — needs macOS hardware access
+- **Design-system work** — parked on `feat/design-system-recover`; revisit in a dedicated session
+- **`live.rs:1089` needless_range_loop refactor** — clippy suggestion is ugly, deserves a real rewrite
+- **Kon → Corbie codebase rename** — package name, crate prefixes, repo name on Gitea + GitHub. Coordinated sweep, not yet scheduled
+- **Transparent windows**, **file-system export**, **bulk select/export**, **LLM content tags**, **Settings UX overhaul**, **Task 7 walk-through** — all carried over from 2026-04-19 handover
+- **CI clippy enforcement** — 1 warning remains on main; turning on `-D warnings` in CI means either fixing it or explicit `#[allow]`
-- **Transparent windows (`transparent: true`)** — Tauri issue #13270 reports this smooths drag/resize further on Linux, but it's moot now that Linux uses native decorations.
-- **File-system export (.md save dialog)** — currently clipboard-only. Needs a Rust `write_text_file` command for plugin-less file writes.
-- **Bulk select + bulk export** in History.
-- **LLM-powered content tags** (`topic:*`, `intent:*`) — slots into Task 7 `kon-llm` stub once Phase 3 wires real llama-cpp-2.
-- **Settings UX overhaul** — Jake flagged that current settings feel overwhelming. Proposed: bunch high-traffic settings, hide advanced behind a toggle. Brainstorm + plan deferred to a dedicated session.
-- **Task 7 (MicroSteps end-to-end)** — storage + Tauri CRUD + kon-llm stub + frontend dual-write all landed in an earlier commit chain. The MicroSteps UI was written as the final task 7 step but not yet dogfooded against the stub LLM. Needs manual walkthrough.
+## Repo state at session end
-## Gotchas discovered today
+- `main` at `509b983` — 7 commits ahead of origin at session start (1 clippy + 3 dependabot original commits + 3 merge commits)
+- `feat/design-system-recover` at `8855db8` — WIP recovery, pushed to `github` only
+- Tags: `pre-consolidation-2026-04-23` points at `9b0067b` (restore anchor)
+- Stashes: none
+- Dependabot branches: still present locally; can be pruned now that merges landed
-| Issue | Fix |
-|---|---|
-| `tauri.linux.conf.json` stripped title and min sizes from main window | Overlay **replaces** the windows array — include every field, not just the delta |
-| `data-tauri-drag-region` + manual `startDragging()` on the same node caused drag latency | Pick one — we use manual `startDragging` for the button/input early-return logic |
-| Corner resize collapsed to single axis on KWin Wayland | Native decorations on Linux side-step the whole frameless path |
-| `animate-float-enter` on the viewer/float layout root created a containing block that broke `position: fixed` on ResizeHandles children | Render ResizeHandles as a sibling of the animated div, not a descendant |
-| Kon binary auto-respawned on file-save while a second run.sh was also launching → two visible instances sharing one Vite server | Do not script `./run.sh` while the user has already launched via the desktop icon; rely on HMR |
-| `run.sh` leaves `"beforeDevCommand": ""` in tauri.conf.json if its cleanup trap is bypassed (e.g. SIGKILL) | Cleanup trap restores `"npm run dev"` on graceful exit; SIGTERM (not SIGKILL) is the right kill signal |
-| `/proc/asound/cards` header lines have leading whitespace for 2-digit card ID alignment | Parser trims leading whitespace before checking for leading digit |
+## Full pre-push verification (Phase 5)
-## How to resume
+Run at end of session — see commit log after `509b983` for the verification commit, and `outputs/` for the full log.
-```
-Picking up Kon dogfooding from 2026/04/19.
-HANDOVER is at HANDOVER.md in the project root.
-Active priorities: (1) confirm resize/drag/mic cleanup, (2) Task 7 MicroSteps
-dogfood with kon-llm stub, (3) Settings UX brainstorm.
-```
+## Anchors
+
+- Previous handover: [HANDOVER-2026-04-19.md](HANDOVER-2026-04-19.md)
+- Release-blocker index: [docs/issues/README.md](docs/issues/README.md)
+- Rebrand memory: `~/.claude/projects/-home-jake-Documents-CORBEL-Main/memory/project_corbie_rebrand.md`
+- Active-focus upstream: `context/active-focus.md` in CORBEL-Main