diff --git a/PLAN.md b/PLAN.md index b9988a8d..ce0f25ed 100644 --- a/PLAN.md +++ b/PLAN.md @@ -6,189 +6,55 @@ > source of truth for individual tasks — this file points at the current > wave and explains how to resume. > -> **Last updated:** 2026-05-19 (Session 389 — #447 intermediate landed (PR #495), full migration filed as #493. TUI convergence: #475 closed, #479/#480/#481 unblocked. quadraui pipeline: #222 TextInput shipped, #227 drop overlay shipped, #230 link advance shipped. Remaining quadraui gaps: #223 ButtonBar, #224 Palette dual-mode, #225 Dialog table.) +> **Last updated:** 2026-09-01 — no wave in flight. Both `ShellApp` migrations +> (#448 GTK, #595 TUI) are closed and `event_loop()` is deleted. Read +> [`GOALS.md`](GOALS.md) for what to work on next; this file is history until +> the next multi-stage wave opens. --- -## 🧭 Current wave (2026-07-23) — TUI → `ShellApp`/`run_with_shell` (vimcode#595) - -**Status:** Stage 0 (`TuiShellApp` scaffold), Stage 1 (#600, paint -centralization), and Stage 2 (#601, `render_content` paints for real) are -landed. Still dormant — not wired to `main.rs`/`tui_bin.rs`. **This is -genuinely multi-session** — the 2026-07-21 note below undersold the coupling -depth by roughly an order of magnitude (see "What this session found" -below). Do not re-attempt the discovery work below; it's done. Pick up at -"Staged plan," Stage 3 (#602). - -**Stage 2 scoping note (2026-07-23):** confirmed structural, not just -unwired — `render_content(&self, backend: &mut dyn Backend, ...)` can -*never* get a raw `ratatui::Frame`/`Buffer`, in any future stage: -`TuiBackend`'s frame pointer (`current_frame_ptr`) is a **private** field -with no public accessor, and `render_content` runs inside quadraui's own -`enter_frame_scope` (`shell_adapter.rs::ShellAdapter::render` → -`tui/run.rs::render_frame`) — so `Backend::draw_*` trait calls work (they -reach the smuggled pointer internally), but nothing needing raw buffer -access ever can, from this signature, period. #601 wires everything that -*is* reachable that way (editor windows, tab/breadcrumb bars, per-window -status lines, the 4 editor-anchored popup kinds) and splits out the true -raw-buffer holdouts as three follow-on issues, all filed and added to the -epic's Work order (`after: 601`) and to #605's dependencies (cutover can't -drop `event_loop()` while these stay unpainted): -[#607](https://github.com/JDonaghy/vimcode/issues/607) sidebar panel -content, [#608](https://github.com/JDonaghy/vimcode/issues/608) -quickfix/bottom panel, [#609](https://github.com/JDonaghy/vimcode/issues/609) -window/group dividers + tab-drag overlay + tab-hover tooltip. Note for -whoever picks up #608: `render_quickfix_panel`, `render_bottom_panel_tabs`, -and `render_terminal_toolbar` (unlike `render_terminal_panel`'s actual PTY -grid content and the debug-output `TextDisplay`'s surrounding chrome) turned -out to already be trait-only, no raw buffer needed — worth checking before -assuming the whole panel needs new plumbing; #601 did not paint them (kept -to its originally-approved scope) but they may be a quick win. - -**All findings below are also recorded as pinned `coord context` notes on -vimcode#595** (ids 260-262) — this section is the human-readable expansion. - -### What this session found (bigger than the original scoping assumed) - -The GTK precedent (#493) took **9 stages** (B.5) **+ 13 more stages** (B.5b) to -go from "trait compiles" to "runtime actually uses it" — see this file's -"Phase B.5"/"Phase B.5b" sections below for the full history. TUI's equivalent -is at least that size, for the same reason GTK's was: `ShellApp::render_content(&self, -backend: &mut dyn Backend, ...)` and `handle(&mut self, event, backend: &mut dyn -Backend, ...)` **only ever get a trait object** — never a raw `ratatui::Frame`, -never the concrete `TuiBackend`. Three concrete places TUI's current code -depends on one of those two things: - -1. **Paint layer.** `src/tui_main/render_impl.rs` (2,427 lines) + - `src/tui_main/panels.rs` (1,570 lines) call - `backend.enter_frame_scope(frame, |b| {...})` at **~30 separate call - sites** (each re-threading the raw `Frame` and re-calling - `backend.set_current_theme(...)` per panel) instead of entering scope - once at the top the way quadraui's own runner does - (`quadraui/src/tui/run.rs::render_frame`). Several sites also call - quadraui's *free* rasteriser functions directly on `frame.buffer_mut()` - (`quadraui::tui::draw_editor`, `draw_toast_stack`, `draw_drop_overlay`; - `super::quadraui_tui::draw_tooltip`, `draw_find_replace`, - `draw_context_menu`, `draw_dialog`) instead of the equivalent - `Backend::draw_*` trait method that **already exists** and would work - through `&mut dyn Backend` — likely a historical artifact of the trait - methods being added after these call sites were written. Fixing this is - mechanical (same underlying function either way — swap the call site, - not the logic) but touches ~30 sites across two large, currently-live - files. A handful of raw `set_cell(frame.buffer_mut(), ...)` writes for - decorative separators have no primitive/trait equivalent at all yet. -2. **Mouse handling.** `src/tui_main/mouse.rs::handle_mouse` (~3,066 lines, - the bulk of the file's 4,124) takes `&mut quadraui::DragState` + - `&mut quadraui::ModalStack` directly via `TuiBackend::drag_and_modal_mut()` - — a concrete-only method the `Backend` trait deliberately does not - expose (by design — see the method's own doc comment). It cannot be - called from `ShellApp::handle` as written. Needs either a new - trait-level accessor in quadraui, or `handle_mouse` rewritten onto the - newer `quadraui::dispatch_mouse_down/drag/up()` free-function pattern - GTK increasingly uses (see "Hit-test glue" rows in the cross-backend - coverage table in `PROJECT_STATE.md`). -3. **Editor cursor placement (quadraui-side gap, not vimcode's to fix).** - `Backend::draw_editor`'s `EditorPaintResult::cursor_position` is - documented "host applies via `Frame::set_cursor_position`" — but - **no consumer of it exists anywhere in quadraui's `shell_adapter.rs` or - `tui/run.rs`** (verified by grep). `render_content` has no Frame to call - `set_cursor_position` on. The fix belongs in quadraui: cache the last - `cursor_position` on `TuiBackend`, apply it in - `tui/run.rs::render_frame` after `terminal.draw(...)` returns — the - exact same shape `apply_selection_highlight(frame.buffer_mut())` already - uses for the same class of problem (buffer-only paint can't carry a - Frame-level side effect). **Filed as - [quadraui#466](https://github.com/JDonaghy/quadraui/issues/466)** — per - `CLAUDE.md`'s Platform-Neutrality Rule, wait for it to land rather than - working around it inside vimcode. - -### What landed this session (Stage 0) - -`src/tui_main/shell_app.rs` (new, `mod shell_app;` added to `mod.rs`): -- `TuiShellApp` struct — every local `mut` variable `event_loop()` declares - (`mod.rs:793`-`:911`), moved onto the struct. Render-time-mutated fields - (`last_layout`, hover/completion/context-menu/dialog layout caches, etc.) - wrapped in `Cell`/`RefCell`, mirroring GTK's `App` (`menu_row_rect: - Cell`) and `Engine`'s own render-time caches. -- `ShellApp::setup` — fully ported (nerd-font sync, panel-key accelerator - registration, menu defs). Required widening TUI's - `register_panel_accelerators` from `&mut backend::TuiBackend` (concrete) - to `&mut dyn quadraui::Backend` (mirrors GTK's own copy of this function, - which already took the trait object) — safe, since it only calls - `Backend::register_accelerator`/`unregister_accelerator`, both trait - methods. -- `ShellApp::tick` — fully ported: the per-frame viewport sync - (`mod.rs:916`-`:967`, using `backend.viewport()` in place of - `terminal.size()`) + all the idle-loop background work (`mod.rs:1157`- - `:1247`: `poll_idle`, format-on-save deferred quit, sidebar/SC - auto-refresh, settings reload, pending terminal command, startup - message, ext-panel focus request, yank-highlight expiry, tab-switcher - auto-confirm). -- `ShellApp::handle` — only the two dispatch layers that don't touch - Frame/DragState/ModalStack: panel-key accelerators (via a - `dispatch_panel_accelerator_sizeless` wrapper — same logic, `terminal: - &Terminal<...>` replaced with `screen_w: u16` from `backend.viewport()`) - and the `MenuSystem` intercept. Key/mouse dispatch bodies are explicit - `// TODO(#595)` stubs, not guesses. -- `ShellApp::render_content` — stub (computes nothing yet; gap 1 above - blocks real painting). -- Tests: `TuiShellApp::setup`/`tick` are exercised directly against a real - `TuiBackend` (quadraui's `driver_with_shell`/`TuiDriver` wraps the app in - a `pub(crate)`-fielded `ShellAdapter` with no accessor back to the - concrete app and no exposed `tick()` passthrough, so it can't be used for - field-level assertions) + one `driver_with_shell` end-to-end smoke - (constructs + paints a first frame without panicking, proving the - `ShellConfig`/`PanelDefinition` wiring). - -### Staged plan for follow-up sessions - -Mirrors how GTK's B.5/B.5b actually shipped — many small, independently -buildable/testable stages, not one PR: - -- ✅ **Stage 1 — paint centralization** (#600, landed). Swept - `render_impl.rs` + `panels.rs`: (a) converted the free-function-on- - `frame.buffer_mut()` calls with a `Backend::draw_*` trait equivalent; - (b) collapsed the ~30 `enter_frame_scope`/`set_current_theme` call sites - to one entry per `terminal.draw(|frame| ...)` closure via a new - `with_frame_scope` helper. No behavior change, pure threading. -- ✅ **Stage 2 — `render_content` for real** (#601, landed). Wired - `render_content` to paint the trait-portable subset — editor windows - (`render_all_windows`, `Frame` param now `Option`), tab bars, breadcrumb - bars, per-window status lines, and the completion/hover/editor-hover/ - diff-peek/signature-help popups (extracted into a shared - `paint_editor_popups` so `draw_frame` and `render_content` can't drift) — - via a new `build_screen_for_shell_content` (mirrors `build_screen_for_tui`'s - row-accounting tail without re-subtracting activity-bar/sidebar width, - since `AppShellLayout::main_content_bounds` already excludes that chrome). - `render_tab_bar`/`draw_breadcrumb_bar`/`render_window_status_line`/ - `render_editor_hover_popup` widened from concrete `&mut TuiBackend` to - `&mut dyn quadraui::Backend`, same technique Stage 0 used for - `register_panel_accelerators`. 2 new `driver_with_shell` `screen_contains` - assertions (single-window text, and a vertical-split proving multi-window - painting). What's *not* painted this stage — and structurally can't be, - from `render_content`'s `&mut dyn Backend`-only signature, without raw - `Frame`/`Buffer` access `TuiBackend` doesn't expose — split into three - follow-on issues, see the "Stage 2 scoping note" above: #607 (sidebar - content), #608 (quickfix/bottom panel), #609 (dividers/drag-overlay/ - tab-tooltip). All three now block #605 (cutover) in the epic's Work order. -- **Stage 3 — mouse handling** (#602). Resolve gap 2 (new quadraui trait accessor, - or `handle_mouse` rewritten onto `dispatch_mouse_down/drag/up`), then wire - `TuiShellApp::handle`'s mouse arms. -- **Stage 4 — key handling** (#603). Wire the remaining `KeyPressed` dispatch - (dialog/palette/completion/context-menu intercepts, `Engine::handle_key`) - into `handle()`. -- **Stage 5 — quadraui cursor-placement fix** (#604). Filed as - [quadraui#466](https://github.com/JDonaghy/quadraui/issues/466); wait for - it to land before cutover, since without it the live TUI would lose its - blinking cursor. -- **Stage 6 — parity + cutover** (#605). Once Stages 1-5 *and* 2a/2b/2c - (#607/#608/#609) land and `driver_with_shell` coverage is solid, swap - `main.rs`/`tui_bin.rs` to `quadraui::tui::shell_runner::run_with_shell`, - delete `event_loop()`, do the full manual smoke pass, then land. - -Not blocked on quadraui#465 (macOS `ShellApp` support) — independent, -parallel supply-side item; TUI already runs on macOS via crossterm -regardless. +## 🧭 Current wave — **none in flight** + +_As of 2026-09-01._ There is no multi-stage feature mid-flight. Both `ShellApp` +migrations are closed, so this file has no live pickup instructions; it is +history plus the course-correction notes below. + +**Plan against [`GOALS.md`](GOALS.md), not this file.** It holds the north star +(eliminate platform-specific code from vimcode, lift it into quadraui) and +sequences what is actually left. `PROJECT_STATE.md` holds current status. + +When a multi-stage wave next starts, re-open this section with its stage table +and pickup instructions — that is the only thing PLAN.md is for. + +--- + +## ✅ Completed wave — TUI → `ShellApp` / `run_with_shell` (vimcode#595, closed 2026-08-26) + +All ten stages landed. `TuiShellApp` lives at `src/tui_main/shell_app.rs:1251` +(`impl ShellApp for TuiShellApp`), the runner is +`quadraui::tui::shell_runner::run_with_shell`, and **`fn event_loop` no longer +exists anywhere in `src/`** (#634). Its GTK counterpart, #448, closed the same +week — both backends now run the same quadraui-owned loop. + +Stages, for the record: #600 paint centralization → #601 `render_content` paints +for real → #607/#608/#609 the raw-`Buffer` holdouts (sidebar content, quickfix / +bottom panel, dividers + drag overlay + tab tooltip) → #602 mouse → #603 keys → +#604 cursor placement (needed quadraui#466) → #605 parity + cutover → #634 +`event_loop()` deletion. + +**The scoping lesson, kept because it generalises.** The original estimate +undersold the coupling depth by roughly an order of magnitude. The cause: +`ShellApp::render_content(&self, backend: &mut dyn Backend, ...)` and +`handle(&mut self, event, backend: &mut dyn Backend, ...)` only ever receive a +**trait object** — never a raw `ratatui::Frame`, never the concrete +`TuiBackend`. `TuiBackend`'s frame pointer (`current_frame_ptr`) is private with +no accessor, and `render_content` runs inside quadraui's own +`enter_frame_scope`. So `Backend::draw_*` calls work (they reach the smuggled +pointer internally) and anything needing raw buffer access structurally cannot, +from that signature, in any stage. GTK's equivalent (#493) took 9 + 13 stages +for the same reason. **Assume any future backend-runner migration is this +shape**: enumerate what needs raw buffer access *first*, and file those as +separate blocking issues before estimating. --- diff --git a/PROJECT_STATE.md b/PROJECT_STATE.md index b0bdd60a..9e9d3b50 100644 --- a/PROJECT_STATE.md +++ b/PROJECT_STATE.md @@ -1,27 +1,89 @@ # VimCode Project State -**Last updated:** May 19, 2026 (Session 389 — **Coordinator-driven 3-agent sprint.** 13 issues closed, 10 PRs merged. vimcode: #447 GTK AppShell intermediate (PR #495), #475 TUI key dedup (PR #492), #467 completion popup filtering (PR #498), #483/#485 TUI ext panel fixes (PR #501), #465 breadcrumb symbol picker (PR #502). quadraui: #206 unicode-width (PR #220), #209 rounded chrome (PR #228), #227 drop overlay (PR #229), #230 rich text link advance (PR #231), #222 TextInput primitive (PR #232). Rolled back PR #496 (platform-neutrality violation) → fixed properly via quadraui #230. Filed 7 quadraui pipeline issues (#221–#227), 2 already-implemented, 3 shipped. Unblocked vimcode #479/#480/#481/#488. Next: Server #479 (Settings FormController), Desktop A #488 (hover links — zero-code retest after quadraui pull), Quadraui #223 (ButtonBar). 1994+ lib tests passing.) - -## Active milestone: Cross-Platform UI Crate - -**This is the current top priority.** All quadraui primitive migrations must complete before moving to other milestones. The goal is zero bespoke per-backend code — every UI surface paints, scrolls, and handles clicks through quadraui's shared API. A native Windows backend will be re-added as a thin wrapper when the quadraui Win backend ships (quadraui#19–#31). - -**All bespoke paint surfaces are now eliminated.** Every UI surface in both TUI and GTK paints through quadraui primitives. **Scroll dispatch consolidation (#307) is complete** — all scrollable surfaces route through `dispatch_scroll`/`dispatch_click`. - -**Vimcode-side dedup work added to milestone** (#429, #428, #395, #274, #225, #233) **+ TUI convergence backlog** from Session 388 audit (#477, #478, #479, #480, #481 — #475 closed Session 389). **Session 389 unblocked:** #479 (Settings FormController — Form cursor already exists in quadraui), #480 (partially — TextInput primitive shipped as quadraui#222), #481 (scrollbar + drop overlay — both primitives exist). Remaining quadraui pipeline: #223 (ButtonBar), #224 (Palette dual-mode), #225 (Dialog table). **GTK convergence chain:** #447 intermediate step landed (PR #495) → full `run_with_shell()` migration filed as #493 (blocked on quadraui stages 3+4) → #448 (event dispatch → UiEvent) → #449 (click dispatch → FrameHitMap). - -**#505 landed (ff-merge `ee502f9`):** SC action-button row migrated to `quadraui::Toolbar` (unblocked by quadraui#257/#259). Both hand-painters (TUI `set_cell` + GTK Cairo/Pango) and the bespoke `sc_button_hit_test` deleted; shared `render::sc_button_toolbar` + `draw_sc_button_toolbar` builder, cached `ToolbarLayout` for click/hover hit-test. Commit button now dims + no-ops when the message is empty. Carved out of #480's four chunks. Follow-ups filed: #509 (adopt `SidebarPanel` to consolidate the SC button-row + sections Y-geometry, recomputed across 4 files) and #510 (debug toolbar → `Toolbar`, replacing the faked `StatusBar`). +**Last updated:** September 1, 2026 — **audit session.** Verified the platform-neutrality effort against the code rather than against issue-closure state. #592's four children (#669–#672) all landed and `src/gtk/draw.rs` is deleted, but **`screen.ai_panel` is still unpainted on GTK** (`src/gtk/mod.rs:9364`), so #592 stays open. Found a second orphan pocket the epic could not have seen: **22 Relm4-era widget handles on `App` are initialised `None` and assigned nowhere**, guarding ~103 unreachable arms — which is why #723's GTK half (`e02a824`) cannot run. #593 re-scoped (unblocked by #672; #646's `GtkDriver` replaces its stale "needs live smoke" plan). Three issue bodies drafted: the orphan sweep, the `Msg`-bus retirement, and mouse-router convergence. + +## Active milestone: #7 Platform-Neutral + +**The north star is [`GOALS.md`](GOALS.md): eliminate all platform-specific code from +vimcode and lift it into quadraui.** Milestone **#7 Platform-Neutral** is the consume +side (vimcode adopts a shipped quadraui API and *deletes* its bespoke per-backend code); +milestone **#5 Cross-Platform UI Crate** is the supply side (building quadraui itself). +Don't conflate them. A native Windows/macOS backend gets re-added as a thin wrapper once +there is no feature logic left in the existing backends to re-implement. + +### Done + +- **Both structural migrations closed 2026-08-26.** #448 (GTK → `ShellApp::handle`) and + #595 (TUI → `ShellApp` + `run_with_shell`). `fn event_loop` no longer exists in `src/`. + `impl ShellApp for App` at `src/gtk/mod.rs:8123`; `TuiShellApp` at + `src/tui_main/shell_app.rs:1251`. +- **The orphaned GTK paint path is gone.** #669/#670/#671/#672 painted 13 of the 14 + dropped `ScreenLayout` fields on GTK's live path and deleted `src/gtk/draw.rs` + (−2,327 production lines). #676 recovered the Command Center, the one sibling the + epic's `draw.rs`-shaped method could not have found. +- **Dedup sweep landed:** #621 (`fuzzy_score` → `quadraui::text_util`), #659 (driver tab + geometry + `SidebarSystem::reveal`), #660 (four duplicates retired, incl. `SplitTree`), + #536 (activity-bar keyboard nav → `AppShell` cursor). +- Milestone #7 stands at **29 closed / 4 open**; quadraui milestone #9 ("vimcode + Platform-Neutral blockers") is closed out. + +### Open, and what actually gates each + +| Issue | State | +|---|---| +| **#592** (epic) | 13 of 14 fields done. **`ai_panel` still unpainted on GTK** — #670 deferred it to a follow-up that was never filed (`src/gtk/mod.rs:9364-9369`, click holdout at `:7325`). Closes when that lands. | +| **#593** Ctrl+V on GTK | **Unblocked** — the #672 hold is lifted, and #646's `GtkDriver` supersedes the "no GTK harness, needs live smoke" plan in its body. Re-scoped 2026-09-01. | +| **#658** preview tier | The only genuinely supply-blocked item: quadraui#596 + #597 both OPEN. Two live copies of vimcode's own preview policy until it lands. | +| **#146** Lua → quadraui | Weakest fit of the original #7 seeding. Re-triage or drop. | + +### Untracked residual — the actual remaining mass + +Production lines (`#[cfg(test)]` blocks excluded): + +| | 2026-05-01 | 2026-07-01 | 2026-09-01 | +|---|---|---|---| +| `src/gtk/` | 18,979 | 13,388 | **12,588** | +| `src/tui_main/` | 14,657 | 10,305 | **11,135** | +| `src/render.rs` (shared) | 10,547 | 12,690 | **15,110** | + +The May→July drop was real. **Since July 1 the two backends are flat** — 23,693 → 23,723 +combined; `draw.rs`'s −2,327 was cancelled by new growth. Shared code grew +2,420 over +the same window, so *new* features are going shared (the Platform-Neutrality Rule is +working) while the *existing* per-backend mass has stopped coming down. + +Where it sits, none of it issue-shaped before 2026-09-01: + +- **Mouse/click routing, ~4,800 lines.** GTK `handle_mouse_click_msg` (`mod.rs:3785`, + 1,071) + `handle_mouse_drag_msg` (356) + `try_route_sidebar_mouse_event` (176) ↔ TUI + `handle_mouse` (`mouse.rs:157`, **3,027 lines in one function**) + `handle_mouse_event` + (364). Same precedence ladder, written twice, with rungs each backend has and the other + doesn't. +- **The GTK `Msg` bus.** 124 variants (`mod.rs:1096-1413`), 301 `Msg::` sites, 684-line + `fn dispatch` (`:1799`), 16 `handle_*_msg` methods. `ShellApp::handle` re-encodes a + `UiEvent` it already holds into one of 17 `Msg` variants so `dispatch` can decode it + again. TUI has no equivalent. +- **Frame composition, ~4,500 lines.** GTK `render_content` (1,533) ↔ TUI + `render_content` (708) + `draw_frame` (749) + `panels.rs` (1,555). +- **22 orphaned Relm4-era widget handles** on `App` — initialised `None`, assigned + nowhere in the crate, guarding ~103 arms that never execute. Same class as `draw.rs`, + but invisible to #672's "no file-level `allow(dead_code)`" criterion because they are + *read*, just never *written*. Live cost: #723's GTK half (`e02a824`) targets a + `gtk4::Scrollbar` that is never constructed, so that fix cannot run. + +### Trust gate + +**#657** (promote `gtk`/`render`/`tui_main` into `vimcode_core`, seal a +`tests/acceptance/` suite) remains the gate under all of the above — today's tests are +written by the same worker that writes the fix. #553 is the in-repo proof: it shipped +`GtkDriver` tests that stayed green with the bug reinstated. Note the false blocker in +#657's body — "vimcode needs a GTK acceptance driver first" is **wrong** (#646 shipped +one) and chasing it costs a large piece of work that isn't needed. --- -Vimcode at 1994+ lib tests passing. - -> Sessions 388 and earlier in **SESSION_HISTORY.md**. - -> Feature documentation lives in **README.md**. -> **Active multi-stage wave:** `quadraui` cross-platform UI crate extraction — see **PLAN.md** for pickup-on-another-machine instructions. - - +> Feature documentation lives in **README.md**. Sessions 389 and earlier in +> **SESSION_HISTORY.md**. No multi-stage wave is in flight — **PLAN.md** is history until +> the next one opens. --- @@ -49,14 +111,19 @@ TUI was the reference implementation through Phase C; GTK caught up. Numbers update with each Path-A landing — read this to find the next slice. -**Status (post #296, 2026-05-02):** **TUI/GTK paint duplication is -done.** Every entry in the cross-backend coverage table below is ✅ -on both backends. Debug sidebar migrated to `MultiSectionView` -(#296) — both paint and click consume one cached layout per frame. -**No bespoke section-walk paint code remains.** Residual convergence -work (#210/#211/#288-style hit-test/click items) plus -intrinsic-to-surface divergences (Cairo painter order vs ratatui -cell coalescence) remain but are tracked separately. +**Status (2026-09-01):** **Paint duplication is done for every +surface in the table below** — all ✅ on both backends. The +GTK-side regression that #540 introduced (surfaces painted only +by the since-deleted `draw.rs`) was swept by #669–#672; the one +holdout is `ai_panel`, which has no row here because it was never +migrated to a primitive on GTK at all. + +No bespoke section-walk paint code remains (debug sidebar moved to +`MultiSectionView` in #296 — both paint and click consume one cached +layout per frame). What remains cross-backend is **not paint**: it is +the mouse-routing and event-dispatch duplication listed under +"Untracked residual" above, plus intrinsic-to-surface divergences +(Cairo painter order vs ratatui cell coalescence). | Surface | Primitive | TUI | GTK | Notes | |---|---|---|---|---| @@ -132,3 +199,20 @@ cell coalescence) remain but are tracked separately. ## Recent Work > Sessions 389 and earlier in **SESSION_HISTORY.md**. + +**2026-09-01 — platform-neutrality audit (docs only).** Measured the effort against the +code. Findings and their disposition are in "Active milestone" above; the three drafted +issue bodies (orphan-handle sweep, `Msg`-bus retirement, mouse-router convergence) plus +the missing `ai_panel` child are the queue that comes out of it. #592 given an audit +comment and deliberately **left open**; #593 re-scoped. + +**2026-08-26 → 09-01 — the #592 epic and the dedup sweep cleared.** #669/#670/#671/#672 +(GTK live-path paint + `draw.rs` deletion), #676 (Command Center), #673/#674/#677 (tab +MRU, jump-list pane identity, vacuous-test rewrites), #621/#659/#660/#536 (dedup), +#691 (quadraui pinned as a git rev instead of a sibling path dep), #693/#694/#695 +(menu-bar paint + hamburger), #699–#705 (VS Code chrome-metrics parity), #35 (minimap +primitive, both backends), #710/#712 (omnibar + dropdown fonts), #715/#716/#719/#720 +(WM identity, titlebar glyphs, app icon), #722/#723 (per-pane minimap, scroll thumb). + +**2026-08-26 — both `ShellApp` migrations closed.** #448 (GTK) and #595 (TUI). +`fn event_loop` deleted from `src/` (#634).