claw-code/docs/tui/TUI-52-WEEK-SPRINT.md

85 KiB
Raw Blame History

TUI 52-Week Delivery Sprint Plan — Extreme Detail

Repository: ultraworkers/claw-code
Branch: feat/tuimain
Status: Draft for upstream PR review
Date: 2026-06-12
Authors: TheArchitectit, Claude


1. Executive Summary & Prior Failure Analysis

1.1 The Monolith Is Bigger Than We Thought

The rusty-claude-cli/src/main.rs file is 11,282 lines (as of 2026-06-12), not the 3,159 lines documented in the March 2026 plan. It contains:

  • LiveCli struct and its impl block (~4,500 lines)
  • All slash command handlers (~2,800 lines)
  • All format_* report functions (~1,200 lines)
  • Session management (create, resume, list, switch, persist) (~1,100 lines)
  • Streaming output handling (~800 lines)
  • Tool call display and permission prompting (~600 lines)
  • Arg parsing and CLI dispatch (~282 lines)

This means every prior TUI attempt was trying to build on quicksand. The monolith grew while we were planning.

1.2 Post-Mortem of Every Prior Attempt

Branch Period What Happened Root Cause
feat/ui-hardening Earliest Patched REPL reliability inside monolith. Produced commit a13b1c2 but was absorbed into fix/ui-parity. Hardening a monolith is a treadmill — every fix adds more code to the same file.
fix/ui-parity Apr 2026 Scoped DOWN from real TUI. Commit message: "Rejected: full multi-pane typeahead overlay — too large for this UI-only parity slice." Ported slash command completion and hints. Correctly identified extraction was needed first, but extraction was deferred and never done.
feat/uiux-redesign MarApr 2026 Big swing: vim mode, LSP integration, git slash commands, HTTP server. 18 commits were README credit wars (oh-my-opencode add/remove/re-add). Credit bikeshedding consumed more history than feature work. The branch never merged cleanly.
rcc/ui-polish Mar 2026 Empty branch. No code pushed. Created, then abandoned when feat/uiux-redesign looked like it would subsume it. Waiting became permanent.
rust/TUI-ENHANCEMENT-PLAN.md Mar 31 2026 Excellent 6-phase plan identifying monolith as #1 risk. Proposed Phase 0 extraction. Was a plan without a branch. No owner, no timeline, no commits. Phase 0 was labeled "essential" but no one signed up.

1.3 The Ten Failure Modes & Our Guardrails

# Failure Mode Guardrail
1 The Monolith Trap Weeks 16 are exclusively extraction. No TUI code until main.rs <200 lines.
2 Scope Panic Every slice ≤400 lines. The phrase "too large for this slice" triggers an immediate scope cut.
3 Credit Wars No README edits during TUI work. Attribution is via Co-Authored-By commit trailers only.
4 Placeholder Branches Every branch has ≥1 passing test and ≥1 visible change within 24 hours of creation.
5 Plan Without Execution Each week has a concrete deliverable, a merge point, and a rollback strategy.
6 Feature Flags Before Function No --tui feature flag until Week 20. Build it first; gate it later.
7 REPL Replacement Fear TUI is claw tui, a separate entrypoint. REPL is untouched forever.
8 Streaming-Not-First First real conversation feature is streaming rendering. Static screens come later.
9 Dependency Bloat Fear Only 3 new crates: ratatui, tui-textarea, unicode-width. No optional deps until Week 20.
10 No Test Coverage for UI Every widget has #[cfg(test)] with TestBackend assertions. No widget ships without one.

1.4 Confirmation From Peer Projects

Research into codex-rs/tui, aichat, crush, and ecosystem guides confirms:

  • ratatui + crossterm is the standard Rust TUI stack (codex-rs uses this).
  • Extraction-first is mandatory — codex-rs/tui has app.rs, chat_widget.rs, composer.rs, event_broker.rs as separate modules.
  • Thin TUI shell, shared corecrush separates shell/parser; we separate TUI/ConversationRuntime.
  • Streaming-firstaichat renders markdown incrementally from SSE deltas.

2. Architecture Target State

2.1 Module Structure (End of Week 6)

rust/crates/rusty-claude-cli/src/
├── main.rs              # Entrypoint only. Calls LiveCli::run() or tui::run().
│                        # Target: <150 lines.
│
├── cli.rs               # CLI argument parsing + CliAction enum.
│                        # Target: <300 lines.
│
├── app.rs               # LiveCli struct, REPL loop, turn execution.
│                        # Moves from main.rs. Target: <800 lines.
│
├── format.rs            # All report formatting functions.
│                        # Moves from main.rs. Target: <600 lines.
│
├── session_mgr.rs       # Session CRUD: create, resume, list, switch, persist, compact.
│                        # Moves from main.rs. Target: <500 lines.
│
├── init.rs              # Repo initialization (unchanged).
│
├── input.rs             # Line editor with rustyline (unchanged, minor extensions).
│
├── render.rs            # TerminalRenderer, Spinner, MarkdownStreamState.
│                        # Extended but mostly unchanged. Target: <700 lines.
│
├── setup_wizard.rs      # Interactive provider wizard (already exists, extracted in recent commits).
│
└── tui/                 # NEW: All TUI components.
    ├── mod.rs           # Public entry: run(env) -> io::Result<()>
    │                    # Terminal init (alternate screen, raw mode),
    │                    # event loop runner, cleanup on exit.
    │
    ├── app.rs           # App state machine.
    │                    # Screen enum, message state, input state, theme.
    │                    # Target: <400 lines.
    │
    ├── event.rs         # EventBroker.
    │                    # Bridges crossterm::event::EventStream → AppEvent.
    │                    # Target: <250 lines.
    │
    ├── theme.rs         # Color themes, terminal capability detection.
    │                    # Target: <200 lines.
    │
    └── widgets/
        ├── chat.rs      # ChatWidget: scrollable message list.
        │                # Renders Vec<MessageCell> as vertical Layout of Paragraphs.
        │                # Target: <300 lines.
        │
        ├── input.rs     # ComposerWidget: wraps tui_textarea::TextArea.
        │                # Handles Enter-to-send, Shift+Enter newline, history.
        │                # Target: <200 lines.
        │
        ├── markdown.rs  # MarkdownWidget: pulldown-cmark + syntect → ratatui Paragraph.
        │                # Adapted from render.rs. Target: <400 lines.
        │
        ├── status_bar.rs # Bottom status line: model, tokens, cost, session.
        │                 # Target: <150 lines.
        │
        ├── tool_call.rs  # ToolCallWidget: inline spinner + result display.
        │                 # Target: <200 lines.
        │
        ├── permission.rs # PermissionPromptWidget: modal Y/N overlay.
        │                 # Target: <150 lines.
        │
        └── sidebar.rs    # (Week 37+) Optional right sidebar.
                          # Target: <250 lines.

2.2 Runtime Integration (Zero Wrappers)

The TUI consumes these existing types directly:

tui::app::App
  ├─ uses runtime::ConversationRuntime (via shared Arc)
  ├─ uses runtime::Session (for persistence)
  ├─ uses runtime::SessionStore (for resume)
  ├─ uses runtime::ToolExecutor (for tool call dispatch)
  ├─ uses runtime::PermissionMode (for status display)
  ├─ uses runtime::TurnProgressReporter (implemented as TuiProgressReporter)
  └─ uses render.rs logic (adapted to MarkdownWidget)

If the ConversationRuntime API is awkward for the TUI, we fix the runtime API — never create a wrapper.

2.3 Event Flow Diagram

┌─────────────────────────────────────────────────────────────────────────────┐
│                              EVENT FLOW                                       │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│   ┌──────────────┐     ┌──────────────────────┐     ┌──────────────┐       │
│   │  Crossterm   │────▶│   EventBroker          │────▶│     App        │       │
│   │  (keyboard,  │     │   (tokio::select!)     │     │   (state machine) │     │
│   │  resize)     │     │                        │     │                │     │
│   └──────────────┘     └──────────────────────┘     └───────┬────────┘     │
│                                                             │              │
│                                                             ▼              │
│   ┌──────────────┐     ┌──────────────────────┐     ┌──────────────┐       │
│   │  API Client  │────▶│  TuiProgressReporter │────▶│  AppEvent::   │       │
│   │  (SSE stream)│     │  (mpsc channel)      │     │  AssistantDelta │      │
│   └──────────────┘     └──────────────────────┘     └──────────────┘       │
│                                                             │              │
│                                                             ▼              │
│   ┌──────────────────────────────────────────────────────────────┐         │
│   │                    ratatui::Frame::render()                    │         │
│   │   ┌─────────────┐  ┌─────────────┐  ┌─────────────────────┐  │         │
│   │   │ ChatWidget  │  │ Sidebar     │  │ StatusBarWidget     │  │         │
│   │   │ (top/left)  │  │ (right, opt)│  │ (bottom)            │  │         │
│   │   └─────────────┘  └─────────────┘  └─────────────────────┘  │         │
│   └──────────────────────────────────────────────────────────────┘         │
│                                                                              │
└─────────────────────────────────────────────────────────────────────────────┘

2.4 State Machine

// tui/app.rs

pub struct App {
    pub screen: Screen,
    pub messages: Vec<MessageCell>,
    pub scroll: ScrollState,
    pub input: ComposerState,
    pub status: StatusState,
    pub theme: Theme,
    pub session_id: Option<String>,
}

pub enum Screen {
    Chat,
    PermissionPrompt(PermissionRequest),
    SessionPicker(Vec<SessionSummary>),
    Help,
    Error(String),
    Quit,
}

pub struct MessageCell {
    pub role: MessageRole,
    pub content: Vec<ContentBlock>,         // completed blocks
    pub streaming: Option<StreamingCell>,   // in-flight block
    pub timestamp: chrono::DateTime<chrono::Utc>,
    pub tool_calls: Vec<ToolCallCell>,
}

pub struct StreamingCell {
    pub text_buffer: String,
    pub parser_state: StreamingMarkdownState, // incremental parse state
}

pub struct ToolCallCell {
    pub name: String,
    pub status: ToolCallStatus,
    pub result_summary: String,
    pub full_result: Option<String>,
    pub expanded: bool,
}

pub enum ToolCallStatus {
    Pending,
    Running { start_time: Instant },
    Succeeded { duration: Duration },
    Failed { duration: Duration, error: String },
}

3. Sprint-by-Sprint Breakdown (Weeks 152)

Sprint Structure

  • 1 sprint = 2 weeks (26 sprints total).
  • Every sprint ships. If scope is too large, cut features — never delay.
  • Merge target: feat/tui via ≤400-line PRs.
  • Review rule: No PR >400 lines. Split or cut.

Phase A: Foundation (Weeks 16) — Extract or Die

Goal: main.rs <150 lines. No TUI code written until then.

Week 1: Extract app.rs

Field Value
Deliverable LiveCli struct and its impl block moved from main.rs to app.rs. main.rs now only imports LiveCli and calls run().
Files touched main.rs (-4500/+20), app.rs (+4520/-0)
New files app.rs (extracted from main.rs)
Test strategy All existing tests in main.rs move to app.rs. Add #[test] fn test_app_module_compiles() as a smoke test.
Merge criteria cargo test --workspace passes. cargo clippy --workspace clean. main.rs <200 lines.
Rollback strategy git checkout HEAD~1 -- main.rs restores the monolith in one command.
Dependencies None — pure file move.
PR title refactor: extract LiveCli to app.rs
PR description • Moves LiveCli struct and impl from main.rs to new app.rs. • No behavioral changes — pure extraction. • All tests preserved. • main.rs reduced from ~11,000 to ~200 lines.
Review checklist ① No logic changes — only moves. ② All main.rs tests still exist in app.rs. ③ cargo test --workspace passes. ④ main.rs <200 lines. ⑤ No new dependencies.
Risk Medium — large file move can lose tests. Mitigated by git diff --stat verification.
Docs Update rust/README.md workspace layout diagram.
Harness impact None — pure refactor, no behavioral change.

Verify this week:

cd rust
cargo test --workspace
cargo clippy --workspace --all-targets -- -D warnings
cargo fmt --all --check
wc -l crates/rusty-claude-cli/src/main.rs  # should be <200

Week 2: Extract format.rs

Field Value
Deliverable All format_* functions moved from app.rs/main.rs to format.rs.
Files touched app.rs (-1200/+50), format.rs (+1200/-0), main.rs (-0/+10)
New files format.rs
Functions moved format_status_report, format_cost_report, format_model_report, format_permissions_report, format_compact_report, format_resume_report, format_sandbox_report, format_commit_preflight_report, format_commit_skipped_report, format_pr_report, format_issue_report, format_ultraplan_report, format_bughunter_report, format_doctor_report, format_auto_compaction_notice
Test strategy Move all format_* tests from app.rs to format.rs. Add #[test] fn test_format_module_exports() smoke test.
Merge criteria All format tests pass. No changes to output strings.
Rollback strategy git checkout HEAD~1 -- app.rs — but format functions are pure, so rollback is trivial.
Dependencies None.
PR title refactor: extract report formatting to format.rs
PR description • Extracts 15 format_* functions from app.rs to format.rs. • No output changes — pure move. • All unit tests preserved.
Review checklist ① Every format_* function has a test. ② Output strings unchanged. ③ cargo test passes. ④ cargo clippy clean. ⑤ No new deps.
Risk Low — pure code move with test coverage.
Docs Update rust/README.md crate responsibilities.
Harness impact None.

Week 3: Extract session_mgr.rs

Field Value
Deliverable Session CRUD (create, resume, list, switch, persist, compaction) moved from app.rs to session_mgr.rs.
Files touched app.rs (-1100/+80), session_mgr.rs (+1100/-0)
New files session_mgr.rs
Functions moved create_session, resume_session, list_sessions, switch_session, persist_session, compact_session, load_latest_session, resolve_session_path
Test strategy Move session tests. Add SessionManager struct with new() -> Self and list() -> Vec<SessionSummary> methods. Unit-test with temp dirs.
Merge criteria Session persistence round-trip tested. /session slash commands still work in REPL.
Rollback strategy git checkout HEAD~1 -- app.rs restores session functions.
Dependencies None.
PR title refactor: extract session management to session_mgr.rs
PR description • Extracts session lifecycle from app.rs to session_mgr.rs. • Introduces SessionManager struct for centralized session operations. • Tests use temp dirs for isolation.
Review checklist /session list, /resume, /compact work in REPL. ② Session files written to correct path. ③ Tests use tempdir. ④ cargo test passes. ⑤ No behavioral changes.
Risk Low — session code is well-tested.
Docs Update USAGE.md session section if SessionManager adds new CLI surfaces.
Harness impact Session save/resume is harness-tested; verify no regressions.

Week 4: Create tui/mod.rs Stub

Field Value
Deliverable tui/mod.rs created with pub fn run(env: CliEnvironment) -> io::Result<()> stub. claw tui CLI dispatch added. Prints "TUI mode coming soon." and exits.
Files touched cli.rs (+30), main.rs (+15)
New files tui/mod.rs (40 lines)
Test strategy Integration test: #[test] fn test_tui_subcommand_exists() that spawns claw tui --help and asserts exit code 0.
Merge criteria claw tui --help works. claw tui exits cleanly with status 0.
Rollback strategy Remove tui/ directory and revert cli.rs/main.rs changes.
Dependencies None — stub only.
PR title feat: add tui module stub and claw tui command
PR description • Adds empty tui/ namespace. • Adds claw tui CLI subcommand as stub. • Integration test verifies CLI surface.
Review checklist claw tui --help prints help. ② claw tui exits 0. ③ No new dependencies yet. ④ cargo test passes. ⑤ Stub is clearly marked with // TODO comments.
Risk Very low — no runtime code.
Docs Update rust/README.md with claw tui in CLI flags.
Harness impact Add tui_stub_smoke scenario to mock parity harness.

Code skeleton:

// tui/mod.rs
use std::io;

pub fn run() -> io::Result<()> {
    println!("TUI mode coming soon.");
    Ok(())
}

Week 5: Runtime Trait Boundary

Field Value
Deliverable ConversationRuntime access is abstracted through a trait so REPL and TUI can share it without coupling.
Files touched app.rs (+80), tui/mod.rs (+30)
New files None
Trait defined trait RuntimeHandle { fn run_turn(&mut self, prompt: &str) -> Result<TurnResult, RuntimeError>; fn session(&self) -> &Session; }
Test strategy Mock implementation of RuntimeHandle for testing.
Merge criteria LiveCli compiles with new trait. No behavioral changes to REPL.
Rollback strategy Inline the trait methods back into direct ConversationRuntime calls.
Dependencies None.
PR title refactor: runtime access through trait boundary
PR description • Introduces RuntimeHandle trait for shared runtime access. • Enables REPL/TUI duality without duplication. • No behavioral changes.
Review checklist ① Trait has all methods REPL needs. ② Mock impl works in tests. ③ cargo test passes. ④ REPL unaffected. ⑤ Documentation on trait purpose.
Risk Low — adds abstraction without changing behavior.
Docs Document the trait in code comments.
Harness impact None — trait is compile-time only.

Week 6: Ratatui Skeleton with TestBackend

Field Value
Deliverable ratatui and crossterm (event-stream feature) added as dependencies. Minimal alternate-screen demo: shows "Hello, claw" Paragraph, exits on q. Uses TestBackend in tests.
Files touched Cargo.toml (+3), tui/mod.rs (+120)
New files tui/event.rs (stub, 30 lines)
Dependencies added ratatui = "0.29", crossterm = { version = "0.28", features = ["event-stream"] } (crossterm already present, added feature)
Test strategy #[test] fn test_tui_init_buffer() renders to TestBackend and asserts buffer contains "Hello, claw".
Merge criteria cargo test --workspace passes. cargo check with --features tui passes (but feature flag not added yet). claw tui opens alternate screen and exits on q.
Rollback strategy Remove ratatui from Cargo.toml, delete tui/ changes.
PR title feat: ratatui skeleton with TestBackend tests
PR description • Adds ratatui dependency. • claw tui opens alternate screen, renders "Hello, claw", exits on q. • TestBackend verifies rendered output in CI.
Review checklist ① Terminal restored on exit (no ANSI leak). ② TestBackend test passes. ③ No REPL changes. ④ q key exits cleanly. ⑤ Cargo.lock updated correctly.
Risk Low — minimal code, well-tested dependency.
Docs Add ratatui to rust/README.md dependencies list.
Harness impact Add tui_smoke_open_close to mock parity harness.

Code skeleton:

// tui/mod.rs
use std::io;
use crossterm::event::{self, Event, KeyCode, KeyEvent};
use ratatui::{
    backend::{Backend, CrosstermBackend, TestBackend},
    layout::{Constraint, Direction, Layout},
    widgets::{Block, Borders, Paragraph},
    Frame, Terminal,
};

pub fn run() -> io::Result<()> {
    let mut terminal = ratatui::init();
    let result = run_app(&mut terminal);
    ratatui::restore();
    result
}

fn run_app<B: Backend>(terminal: &mut Terminal<B>) -> io::Result<()> {
    loop {
        terminal.draw(|f| ui(f))?;
        if let Event::Key(KeyEvent { code: KeyCode::Char('q'), .. }) = event::read()? {
            return Ok(());
        }
    }
}

fn ui(frame: &mut Frame) {
    let paragraph = Paragraph::new("Hello, claw")
        .block(Block::default().borders(Borders::ALL).title("claw"));
    frame.render_widget(paragraph, frame.area());
}

#[cfg(test)]
mod tests {
    use super::*;
    use ratatui::buffer::Buffer;

    #[test]
    fn test_tui_init_buffer() {
        let mut terminal = Terminal::new(TestBackend::new(80, 24)).unwrap();
        terminal.draw(|f| ui(f)).unwrap();
        let buffer = terminal.backend().buffer();
        assert!(buffer.content.iter().any(|c| c.symbol() == "H"));
    }
}

Phase B: Core TUI Shell (Weeks 712) — Make It Run

Goal: claw tui opens, renders a mock chat, accepts input, scrolls, and exits cleanly.

Week 7: Event Broker

Field Value
Deliverable tui/event.rs: EventBroker bridges crossterm::event::EventStreamAppEvent enum. Unit-test key translation.
Files touched tui/mod.rs (+40), tui/app.rs (created, stub)
New files tui/event.rs (180 lines)
Types defined enum AppEvent { Tick, Key(KeyEvent), Resize(u16, u16), AssistantDelta(String), ToolStart(String), ToolResult(String, String), Error(String), Quit }
Test strategy #[test] fn test_key_translation() maps Event::Key(KeyCode::Char('q'))AppEvent::Key(...). #[test] fn test_resize_event().
Merge criteria EventBroker runs in tests without a real terminal. All key events translate correctly.
Rollback strategy Revert tui/event.rs to stub.
PR title feat: tui event broker with async event stream
PR description EventBroker consumes crossterm::event::EventStream and produces AppEvent. • Supports key, resize, and custom backend events. • Unit tests for key translation.
Review checklist ① All key variants handled. ② Resize produces correct dimensions. ③ AppEvent is Clone + Send. ④ Tests use TestBackend. ⑤ No blocking in test.
Risk Low — crossterm event handling is well-documented.
Docs Add event flow diagram to docs/TUI.md.
Harness impact None — event layer doesn't touch the runtime.

Week 8: App State Machine

Field Value
Deliverable tui/app.rs: App struct with Screen::Chat, renders static mock messages as Paragraph list. Handles q quit, arrows scroll.
Files touched tui/mod.rs (+60), tui/app.rs (created, 200 lines)
New files tui/app.rs
Types defined struct App { messages: Vec<MockMessage>, scroll: ScrollState, should_quit: bool }, struct MockMessage { role: String, text: String }, struct ScrollState { offset: usize }
Test strategy #[test] fn test_app_scroll() — add 100 messages, scroll down 5, assert offset == 5. #[test] fn test_app_quit_on_q() — process KeyCode::Char('q'), assert should_quit.
Merge criteria App renders mock chat. Arrow keys scroll. q quits. Tests pass without TTY.
Rollback strategy Revert tui/app.rs to stub.
PR title feat: tui app state machine with mock messages
PR description App state machine with Screen::Chat. • Renders static mock messages. • Supports scroll via arrow keys. • q to quit.
Review checklist ① Mock messages visible in TestBackend. ② Scroll offset correct. ③ q quits. ④ 100 messages don't panic. ⑤ Tests headless.
Risk Low — no runtime integration yet.
Docs Update docs/TUI.md state machine section.
Harness impact None.

Week 9: Composer Input Widget

Field Value
Deliverable tui/widgets/input.rs: Composer widget wrapping tui_textarea::TextArea. Enter sends, Shift+Enter newline. Mock: prints submitted text to debug overlay.
Files touched Cargo.toml (+1), tui/app.rs (+60), tui/widgets/input.rs (created, 150 lines)
New files tui/widgets/input.rs
Dependencies added tui-textarea = "0.7"
Test strategy #[test] fn test_composer_enter_sends() — simulate Enter, assert Submit("hello") event. #[test] fn test_composer_shift_enter_newline() — assert cursor moved down.
Merge criteria Text input works. Enter submits. Shift+Enter inserts newline. History (up arrow) cycles past inputs.
Rollback strategy Remove tui-textarea from Cargo.toml, revert input widget.
PR title feat: tui composer input widget
PR description • Adds ComposerWidget using tui-textarea. • Enter submits, Shift+Enter inserts newline. • Up/down arrow for history. • Tests verify input events.
Review checklist ① Enter produces submit event. ② Shift+Enter doesn't submit. ③ History works. ④ Unicode input handled. ⑤ TestBackend verifies buffer.
Risk Low — tui-textarea is mature, ~2,000 GitHub stars.
Docs Add keybindings table to docs/TUI.md.
Harness impact None.

Week 10: Chat Message List Widget

Field Value
Deliverable tui/widgets/chat.rs: ChatWidget renders Vec<MessageCell> as vertical Layout of Paragraphs. Supports PgUp/PgDn.
Files touched tui/app.rs (+80), tui/widgets/chat.rs (created, 200 lines)
New files tui/widgets/chat.rs
Test strategy #[test] fn test_chat_renders_messages() — 5 messages, assert all visible. #[test] fn test_chat_scroll_pagination() — 50 messages in 24-line terminal, PgDn advances by page size.
Merge criteria Messages render with roles (user/assistant) styled differently. Scroll works. PgUp/PgDn jump by viewport height.
Rollback strategy Revert chat.rs to stub.
PR title feat: tui chat message list widget
PR description ChatWidget renders message history with role-based styling. • Scrollable with arrows and PgUp/PgDn. • Supports 100+ messages without frame drops.
Review checklist ① User messages right-aligned or differently colored. ② Assistant messages left-aligned. ③ Scroll offset clamped to message count. ④ PgUp/PgDn jump correctly. ⑤ Tests headless.
Risk Low — pure rendering, no external state.
Docs Document chat layout in docs/TUI.md.
Harness impact None.

Week 11: Terminal Resize & Static Status Bar

Field Value
Deliverable Terminal resize handling. Status bar at bottom showing model name (static string), permission mode (static), terminal dimensions.
Files touched tui/app.rs (+60), tui/widgets/status_bar.rs (created, 120 lines)
New files tui/widgets/status_bar.rs
Test strategy #[test] fn test_status_bar_renders() — assert model name visible. #[test] fn test_resize_updates_layout() — assert layout recalculates on Resize event.
Merge criteria Resize event updates layout. Status bar always visible at bottom. Content area adjusts.
Rollback strategy Remove status bar widget, revert resize handler.
PR title feat: terminal resize handling and static status bar
PR description • Status bar shows model, permission mode, terminal size. • Resize events recalculate layout. • Content area shrinks/grows correctly.
Review checklist ① Status bar at bottom in TestBackend. ② Resize doesn't panic. ③ Layout constraints correct (status bar height = 3). ④ Content area height = total - 3. ⑤ Tests pass.
Risk Low — layout math with ratatui::Layout.
Docs Document layout constraints.
Harness impact None.

Week 12: Integration — Mock Chat + Input + Status Bar

Field Value
Deliverable claw tui opens a full-screen app with mock chat + input + status bar. Smoke test runs in CI (skips if no TTY).
Files touched tui/mod.rs (+40), tui/app.rs (+80)
New files tests/tui_smoke.rs (integration test, 80 lines)
Test strategy Integration test with TestBackend: send mock messages, assert rendered. CI test that spawns claw tui with timeout 2 and asserts clean exit.
Merge criteria claw tui runs for 2 seconds and exits cleanly. Full layout visible in TestBackend. CI passes.
Rollback strategy Revert tui/ changes, keep app.rs/format.rs/session_mgr.rs extractions.
PR title feat: claw tui opens full-screen TUI with mock data
PR description • Full TUI layout: chat area, input composer, status bar. • Mock conversation data. • Integration test with TestBackend and CI smoke test.
Review checklist ① Full layout visible in test render. ② claw tui exits on q. ③ CI test passes. ④ No REPL changes. ⑤ Memory usage flat over 10-second run.
Risk Low — integration of already-tested components.
Docs Add TUI screenshot instructions to USAGE.md (ASCII art or capture).
Harness impact Add tui_full_layout_smoke to mock parity harness.

Phase C: Live Conversation (Weeks 1320) — Make It Talk

Goal: Real conversations with the model. Streaming text, tool calls, session save/resume, permission modal.

Week 13: Wire ConversationRuntime

Field Value
Deliverable ConversationRuntime wired into TUI event loop. TuiProgressReporter implements TurnProgressReporter, sends AppEvent::AssistantDelta via tokio::sync::mpsc.
Files touched tui/app.rs (+120), tui/event.rs (+40), tui/mod.rs (+60)
New files None
Types defined struct TuiProgressReporter { tx: mpsc::UnboundedSender<AppEvent> } impl TurnProgressReporter for TuiProgressReporter { ... }
Test strategy Mock ConversationRuntime that emits fake deltas. #[test] fn test_progress_reporter_sends_delta() — assert AssistantDelta received.
Merge criteria TUI receives fake streaming events. Events route to App state. No panics.
Rollback strategy Replace TuiProgressReporter with no-op.
PR title feat: wire ConversationRuntime into tui event loop
PR description TuiProgressReporter bridges runtime events to TUI channel. • Tokio async event loop processes backend + user events. • Mock runtime tests verify event flow.
Review checklist AssistantDelta events reach App. ② No deadlocks. ③ Channel bounded correctly. ④ Tests use mock runtime. ⑤ Runtime trait boundary clean.
Risk Medium — async event loop is first real complexity. Mitigated by bounded channels.
Docs Update event flow diagram in docs/TUI.md.
Harness impact Add tui_streaming_mock scenario.

Week 14: Streaming Assistant Text

Field Value
Deliverable AssistantEvent::TextDelta appends to active message cell in real time. No markdown rendering yet — raw text.
Files touched tui/app.rs (+80), tui/widgets/chat.rs (+60)
New files None
Test strategy #[test] fn test_streaming_appends_text() — send 5 deltas, assert message text == concatenated deltas. #[test] fn test_streaming_cursor_position() — last line visible.
Merge criteria Text appears character-by-character in chat widget. Auto-scroll to bottom. No flicker.
Rollback strategy Buffer deltas instead of displaying incrementally.
PR title feat: real-time streaming assistant text in tui
PR description • Assistant text streams into chat in real time. • Auto-scroll to latest delta. • Raw text (no markdown yet).
Review checklist ① Text visible within 50ms of delta. ② No duplicate text. ③ Auto-scroll works. ④ 1000-character stream doesn't OOM. ⑤ Tests headless.
Risk Medium — rendering performance on fast streams.
Docs Document streaming behavior and latency targets.
Harness impact Add tui_streaming_text scenario.

Week 15: Session Save & Resume

Field Value
Deliverable On TUI exit, save session to JSONL (same format as REPL). claw tui --resume loads it.
Files touched tui/app.rs (+80), tui/mod.rs (+40), session_mgr.rs (+20)
New files None
Test strategy #[test] fn test_session_save_roundtrip() — create messages, save, load, assert equal.
Merge criteria Session persists across claw tui invocations. Messages load with correct timestamps.
Rollback strategy Don't save on exit (lose history).
PR title feat: tui session save and resume
PR description • TUI auto-saves session on exit. • --resume flag loads last session. • Same JSONL format as REPL sessions.
Review checklist ① Session file written on exit. ② --resume loads it. ③ Format compatible with REPL sessions. ④ No data loss. ⑤ Tests verify round-trip.
Risk Low — session persistence already works in REPL.
Docs Update USAGE.md with claw tui --resume.
Harness impact Add tui_session_save_resume scenario.

Week 16: Inline Tool Call Rendering

Field Value
Deliverable Tool call start shows spinner + name inline. Finish shows ✓/✗ with truncated result (first 5 lines).
Files touched tui/app.rs (+60), tui/widgets/tool_call.rs (created, 220 lines)
New files tui/widgets/tool_call.rs
Test strategy #[test] fn test_tool_call_lifecycle()ToolStart("bash") → spinner → ToolResult("bash", "ok") → ✓.
Merge criteria Tool calls animate inline. Results truncated with [+] expand hint.
Rollback strategy Show plain text tool names instead of widgets.
PR title feat: inline tool call spinner and result summary
PR description • Tool calls show animated spinner. • Results show ✓/✗ with truncated preview. • Expandable full output.
Review checklist ① Spinner visible during tool call. ② ✓/✗ correct based on result. ③ Truncation at 5 lines. ④ 10 concurrent tools don't break layout. ⑤ Tests headless.
Risk Low — tool execution already works in runtime.
Docs Document tool visualization in docs/TUI.md.
Harness impact Add tui_tool_call_display scenario.

Week 17: Permission Prompt Modal

Field Value
Deliverable Permission prompt overlay (centered block) for Y/N approval. Does not block event loop — shows as Screen::PermissionPrompt state.
Files touched tui/app.rs (+60), tui/widgets/permission.rs (created, 200 lines)
New files tui/widgets/permission.rs
Test strategy #[test] fn test_permission_modal_renders() — assert centered block visible. #[test] fn test_permission_y_approves() — assert approval event sent.
Merge criteria Modal renders over chat. y approves, n denies. Other keys ignored. Event loop continues.
Rollback strategy Always deny or always approve (degraded mode).
PR title feat: permission prompt modal overlay in tui
PR description • Modal overlay for tool approval. • y to approve, n to deny. • Non-blocking event loop. • Styled with box drawing.
Review checklist ① Modal centered and visible. ② y/n responses correct. ③ Background dimmed. ④ Escape cancels. ⑤ Tests headless.
Risk Medium — modal state transitions must not deadlock.
Docs Document permission flow in docs/TUI.md.
Harness impact Add tui_permission_prompt scenario.

Week 18: Error Handling

Field Value
Deliverable Network errors, API rate limits, runtime errors render as styled error messages in chat. No panics.
Files touched tui/app.rs (+40), tui/widgets/chat.rs (+30)
New files None
Test strategy #[test] fn test_error_rendered_in_chat() — inject error, assert red text visible.
Merge criteria All error paths show user-friendly messages. Panic-free. Stack traces suppressed (logged to file only).
Rollback strategy Panic on error (ugly but functional).
PR title feat: graceful error rendering in tui chat
PR description • Errors rendered as styled messages in chat. • No panics. • Stack traces logged, not displayed. • Auto-scroll to error message.
Review checklist ① Network error → "Connection failed" visible. ② API error → "API error: ..." visible. ③ No panic. ④ Stack trace in log file. ⑤ Tests verify all error paths.
Risk Low — error types already exist in runtime.
Docs Document error handling strategy.
Harness impact Add tui_error_handling scenario.

Week 19: Multi-Turn Conversation

Field Value
Deliverable Send follow-up messages. Scrollback preserves full history. Input clears after send.
Files touched tui/app.rs (+40), tui/widgets/input.rs (+20)
New files None
Test strategy #[test] fn test_multi_turn_history() — 3 turns, assert 6 messages. #[test] fn test_input_cleared_after_send().
Merge criteria Each turn starts a new streaming cycle. History is complete. Input field resets.
Rollback strategy Single-turn only (restart TUI for next turn).
PR title feat: multi-turn conversation in tui
PR description • Follow-up messages work. • Full scrollback history. • Input cleared after send. • Session auto-saves between turns.
Review checklist ① New turn triggers streaming. ② Old turns frozen (not re-streamed). ③ Input empty after send. ④ History scrollable. ⑤ 10-turn session doesn't OOM.
Risk Low — builds on Weeks 1318.
Docs Update USAGE.md with multi-turn examples.
Harness impact Add tui_multi_turn scenario.

Week 20: Feature Flag

Field Value
Deliverable tui feature flag added to rusty-claude-cli/Cargo.toml. Default OFF. CI builds with --features tui. Binary size documented.
Files touched Cargo.toml (+5), main.rs (+10), .github/workflows/rust-ci.yml (+10)
New files docs/TUI-BINARY-SIZE.md
Test strategy #[test] fn test_tui_feature_compiles() — compile with feature flag. #[test] fn test_without_tui_feature() — verify claw tui is absent without flag.
Merge criteria cargo build --workspace works without feature. cargo build --workspace --features tui works with feature. Binary size documented.
Rollback strategy Make tui feature default ON (effectively removing the flag).
PR title feat: gate tui behind optional feature flag
PR description tui feature flag in Cargo.toml. • Default OFF to avoid binary bloat for REPL-only users. • CI builds both configurations. • Binary size impact documented.
Review checklist ① Build without feature passes. ② Build with feature passes. ③ Binary size documented. ④ CI matrix includes both. ⑤ Flag not on by default.
Risk Low — mechanical change.
Docs Document feature flag usage in USAGE.md.
Harness impact CI builds with --features tui.

Phase D: Rich Rendering (Weeks 2128) — Make It Beautiful

Goal: Markdown rendering, syntax highlighting, themes, diffs, performance.

Week 21: Markdown Widget (Headings, Lists, Code Blocks)

Field Value
Deliverable tui/widgets/markdown.rs: Port render.rs (pulldown-cmark + syntect) to ratatui Paragraph. Renders headings, lists, inline code, code blocks with syntax highlighting.
Files touched render.rs (+0, read-only reference), tui/widgets/markdown.rs (created, 300 lines)
New files tui/widgets/markdown.rs
Test strategy #[test] fn test_heading_renders_bold() — assert bold style. #[test] fn test_code_block_highlighted() — assert syntax colors.
Merge criteria Markdown renders in chat. Code blocks have syntax highlighting. Lists are indented.
Rollback strategy Use plain text rendering (ugly but readable).
PR title feat: markdown widget with syntax highlighting
PR description MarkdownWidget renders pulldown-cmark events to ratatui Paragraph. • Code blocks with syntect highlighting. • Headings, lists, inline code, blockquotes.
Review checklist ① H1/H2 bold and colored. ② Code blocks highlighted. ③ Lists indented. ④ Inline code colored. ⑤ 1000-line markdown renders in <50ms.
Risk Medium — markdown parsing + rendering is complex.
Docs Document supported markdown elements.
Harness impact Add tui_markdown_render scenario.

Week 22: Table Rendering

Field Value
Deliverable pulldown-cmark table events → ratatui Table widget with borders.
Files touched tui/widgets/markdown.rs (+100)
New files None
Test strategy #[test] fn test_table_renders_with_borders() — assert Table widget visible.
Merge criteria Tables render with borders and alignment. Fit within terminal width.
Rollback strategy Render tables as plain text.
PR title feat: table rendering in markdown widget
PR description • Markdown tables render as ratatui Table. • Borders, headers, cell alignment. • Truncation for narrow terminals.
Review checklist ① Borders visible. ② Headers bold. ③ Cells aligned. ④ Wide table truncates gracefully. ⑤ Tests verify.
Risk Low — Table widget is standard in ratatui.
Docs Add table examples to docs.
Harness impact None.

Field Value
Deliverable Blockquote: styled indentation + left border. Links: underlined + clickable (placeholder for mouse week).
Files touched tui/widgets/markdown.rs (+80), tui/theme.rs (+20)
New files None
Test strategy #[test] fn test_blockquote_has_left_border(). #[test] fn test_link_is_underlined().
Merge criteria Blockquotes visually distinct. Links underlined.
Rollback strategy Render as plain text.
PR title feat: blockquote and link rendering
PR description • Blockquotes with left border and muted color. • Links underlined. • Mouse click support deferred to Week 35.
Review checklist ① Blockquote left border visible. ② Muted text color. ③ Links underlined. ④ URL displayed on hover or inline. ⑤ Tests verify.
Risk Low — style changes only.
Docs Document styling choices.
Harness impact None.

Week 24: Incremental Markdown Streaming

Field Value
Deliverable Incremental parse of markdown as text deltas arrive. Don't re-render from scratch every frame — append to parser state.
Files touched tui/widgets/markdown.rs (+120), tui/app.rs (+40)
New files tui/widgets/markdown.rs adds StreamingMarkdownState struct
Test strategy #[test] fn test_incremental_parse() — feed "# He" then "llo", assert heading recognized.
Merge criteria Streaming text parses incrementally. No re-render flicker. Performance: <16ms per delta.
Rollback strategy Buffer full response, then render once (slower but correct).
PR title feat: incremental markdown streaming render
PR description • Markdown parser state persists across deltas. • Incremental render avoids full re-parse. • No flicker. • Sub-16ms per delta.
Review checklist ① Heading detected mid-stream. ② Code block fenced detected. ③ No flicker. ④ Performance <16ms. ⑤ Tests incremental state.
Risk High — incremental parsing is tricky. Mitigated by fallback to full re-parse on parse error.
Docs Document incremental parsing strategy.
Harness impact None — optimization only.

Week 25: Dark/Light Theme System

Field Value
Deliverable Dark/light theme system. Respect NO_COLOR. Detect truecolor vs. 256-color vs. 16-color terminals and degrade gracefully.
Files touched tui/theme.rs (created, 250 lines), tui/app.rs (+20)
New files tui/theme.rs
Test strategy #[test] fn test_theme_detects_no_color(). #[test] fn test_theme_16_color_fallback().
Merge criteria NO_COLOR=1 disables colors. Truecolor terminal shows full palette. 16-color terminal degrades correctly.
Rollback strategy Hardcode dark theme (no customization).
PR title feat: color theme system with terminal capability detection
PR description • Dark, light, solarized themes. • NO_COLOR support. • Truecolor → 256 → 16 fallback. • Configurable via .claw.json.
Review checklist NO_COLOR=1 → no ANSI. ② Truecolor → 24-bit colors. ③ 256-color → approximated. ④ 16-color → nearest. ⑤ Configurable.
Risk Low — color detection is well-understood.
Docs Document theme configuration.
Harness impact None — cosmetic only.

Week 26: Collapsible/Expandable Tool Results

Field Value
Deliverable Press Enter on a tool result to expand/collapse full output. Scrollable code view for expanded results.
Files touched tui/widgets/tool_call.rs (+120), tui/app.rs (+40)
New files None
Test strategy #[test] fn test_tool_expand_toggle(). #[test] fn test_expanded_scrollable().
Merge criteria Toggle works. Expanded result scrollable. Collapsed result shows summary.
Rollback strategy Always show full result (no collapsing).
PR title feat: collapsible/expandable tool results
PR description • Tool results toggle expand/collapse on Enter. • Expanded view scrollable. • Summary visible when collapsed.
Review checklist ① Enter toggles state. ② Expanded view scrolls. ③ Collapsed shows summary. ④ 10 expanded tools don't break layout. ⑤ Tests verify.
Risk Low — state toggle + scroll.
Docs Document tool interaction in docs/TUI.md.
Harness impact None.

Week 27: Colored Diff Rendering

Field Value
Deliverable When edit_file or /diff produces output, render unified diff with green additions and red removals.
Files touched tui/widgets/diff.rs (created, 200 lines), tui/app.rs (+20)
New files tui/widgets/diff.rs
Test strategy #[test] fn test_diff_green_additions(). #[test] fn test_diff_red_removals().
Merge criteria Diff lines colored correctly. Context lines neutral. Header lines dimmed.
Rollback strategy Render diff as plain text.
PR title feat: colored diff rendering in tui
PR description • Unified diff with green additions, red removals. • Context lines in default color. • Header dimmed.
Review checklist + lines green. ② - lines red. ③ Context lines neutral. ④ Headers dimmed. ⑤ Tests verify all line types.
Risk Low — diff parsing is straightforward.
Docs Add diff rendering to feature list.
Harness impact None.

Week 28: Performance Pass

Field Value
Deliverable Profile with cargo flamegraph. Ensure 60fps on 100-message session. Optimize if below 30fps.
Files touched Various — depends on profiler output
New files benches/tui_render.rs (criterion bench)
Test strategy Criterion benchmark: render 100 messages, measure frame time. Target: p99 <16ms.
Merge criteria cargo bench shows p99 frame time <16ms. No regressions in existing tests.
Rollback strategy Accept 30fps (degraded but functional).
PR title perf: rendering performance pass
PR description • Criterion benchmark for 100-message render. • Flamegraph analysis. • Optimizations: reduced allocations, cached widget state. • Target: 60fps.
Review checklist ① Benchmark exists. ② p99 <16ms. ③ No allocations in hot path. ④ Flamegraph reviewed. ⑤ No test regressions.
Risk Medium — performance tuning can introduce subtle bugs.
Docs Document performance targets.
Harness impact None — performance only.

Phase E: Navigation & Power Features (Weeks 2938) — Make It Fast to Use

Goal: Keyboard shortcuts, search, session picker, slash commands, mouse, sidebar.

Week 29: Conversation Search (/)

Field Value
Deliverable Incremental search highlighting matching text. n/N for next/prev match.
Files touched tui/app.rs (+80), tui/widgets/search.rs (created, 200 lines)
New files tui/widgets/search.rs
Test strategy #[test] fn test_search_finds_text(). #[test] fn test_search_highlight().
Merge criteria / opens search. Type filters. n/N navigates. Escape closes.
Rollback strategy No search (use external grep).
PR title feat: conversation search with incremental highlighting
PR description / opens search overlay. • Incremental filtering. • Match highlighting. • n/N navigation.
Review checklist / opens search. ② Typing filters. ③ Matches highlighted. ④ n/N cycles. ⑤ Escape closes.
Risk Low — string search + highlight.
Docs Add search keybindings.
Harness impact None.

Week 30: Help Overlay (?)

Field Value
Deliverable ? help overlay: keyboard shortcuts panel showing all keybindings.
Files touched tui/widgets/help.rs (created, 150 lines), tui/app.rs (+20)
New files tui/widgets/help.rs
Test strategy #[test] fn test_help_overlay_renders().
Merge criteria ? toggles overlay. All keybindings listed. Escape or ? closes.
Rollback strategy No help overlay (document keybindings in README only).
PR title feat: keyboard shortcuts help overlay
PR description ? toggles help overlay. • All keybindings listed with descriptions. • Styled with boxes.
Review checklist ? opens. ② All keys listed. ③ Escape closes. ④ Doesn't block input. ⑤ Tests verify.
Risk Low — static overlay.
Docs Update keybindings table.
Harness impact None.

Week 31: Session Picker (Ctrl+S)

Field Value
Deliverable Ctrl+S opens fuzzy-filterable list of saved sessions. Up/down arrows, Enter to switch.
Files touched tui/widgets/session_picker.rs (created, 200 lines), tui/app.rs (+40)
New files tui/widgets/session_picker.rs
Test strategy #[test] fn test_session_picker_lists_sessions(). #[test] fn test_session_picker_filters().
Merge criteria Ctrl+S opens picker. Typing filters. Enter loads session. Escape cancels.
Rollback strategy Use /session list slash command within TUI.
PR title feat: interactive session picker
PR description Ctrl+S opens session list. • Fuzzy filter. • Up/down to navigate. • Enter to load.
Review checklist ① Sessions listed. ② Filter works. ③ Enter loads. ④ Escape cancels. ⑤ Tests verify.
Risk Low — list + filter pattern.
Docs Document session picker.
Harness impact None.

Week 32: Slash Command Picker

Field Value
Deliverable Type / in input to trigger slash command picker (like REPL). Commands execute within TUI (e.g., /model, /permissions).
Files touched tui/widgets/input.rs (+80), tui/app.rs (+60)
New files None
Test strategy #[test] fn test_slash_picker_opens(). #[test] fn test_slash_command_executes().
Merge criteria / opens picker. Tab completion. Enter executes. Commands like /model work.
Rollback strategy No slash commands in TUI (use REPL for config changes).
PR title feat: slash command picker and execution in tui
PR description / triggers slash command picker. • Tab completion. • In-TUI execution. • Share logic with REPL.
Review checklist / opens picker. ② Tab completes. ③ Enter executes. ④ /model switches model. ⑤ Tests verify.
Risk Medium — command execution can affect runtime state.
Docs Document TUI slash commands.
Harness impact None.

Week 33: /compact with Toast

Field Value
Deliverable /compact in TUI runs compaction, shows toast notification with tokens saved.
Files touched tui/app.rs (+40), tui/widgets/toast.rs (created, 80 lines)
New files tui/widgets/toast.rs
Test strategy #[test] fn test_toast_shows_message().
Merge criteria Toast appears for 3 seconds. Shows tokens saved. Does not block input.
Rollback strategy Print compaction result in chat (no toast).
PR title feat: compact command with toast notification
PR description /compact shows toast. • Auto-dismisses after 3s. • Non-blocking.
Review checklist ① Toast visible. ② Message correct. ③ Auto-dismisses. ④ Input not blocked. ⑤ Tests verify.
Risk Low — timed notification.
Docs Document toast behavior.
Harness impact None.

Week 34: Copy to Clipboard

Field Value
Deliverable y on a message or code block copies to clipboard via arboard.
Files touched Cargo.toml (+1), tui/app.rs (+40), tui/widgets/chat.rs (+30)
New files None
Dependencies added arboard = "3"
Test strategy #[test] fn test_copy_event_sent(). Full clipboard test is platform-dependent; mock the clipboard backend.
Merge criteria y copies selected message. Toast confirms. Works on Linux (wlroots/X11), macOS, Windows.
Rollback strategy No copy feature (use terminal selection).
PR title feat: copy message to clipboard
PR description y copies message to clipboard. • arboard handles cross-platform paste. • Toast confirms.
Review checklist y copies. ② Toast confirms. ③ Linux (X11/Wayland). ④ macOS. ⑤ Windows.
Risk Low — arboard is mature.
Docs Document copy keybinding.
Harness impact None.

Week 35: Mouse Support

Field Value
Deliverable Click to expand tool results. Scroll wheel on conversation. Click to focus input.
Files touched tui/event.rs (+60), tui/app.rs (+40), tui/widgets/chat.rs (+20)
New files None
Test strategy #[test] fn test_mouse_scroll_updates_offset(). Mouse click tests use TestBackend with synthetic mouse events.
Merge criteria Mouse scroll works. Click on tool result toggles expand. Click on input focuses.
Rollback strategy Keyboard-only (no mouse).
PR title feat: mouse support for tool expansion and scrolling
PR description • Scroll wheel scrolls conversation. • Click toggles tool expand. • Click focuses input.
Review checklist ① Scroll wheel works. ② Click toggles expand. ③ Click focuses input. ④ Keyboard still works. ⑤ Tests verify.
Risk Low — crossterm mouse events are standard.
Docs Document mouse support.
Harness impact None.

Week 36: Internal Pager

Field Value
Deliverable For long outputs (/status, /config), open full-screen pager with j/k/q.
Files touched tui/widgets/pager.rs (created, 180 lines), tui/app.rs (+40)
New files tui/widgets/pager.rs
Test strategy #[test] fn test_pager_scrolls(). #[test] fn test_pager_quits_on_q().
Merge criteria Long outputs open pager. j/k scroll. q quits back to chat.
Rollback strategy Dump long output directly in chat (scrolling mess).
PR title feat: internal pager for long outputs
PR description • Long outputs open pager overlay. • j/k scroll. • q returns to chat.
Review checklist ① Pager opens for long output. ② j/k scroll. ③ q quits. ④ Line numbers optional. ⑤ Tests verify.
Risk Low — scrollable text view.
Docs Document pager keybindings.
Harness impact None.

Week 37: Optional Right Sidebar

Field Value
Deliverable Ctrl+P toggles right sidebar showing active tool status or todo list.
Files touched tui/widgets/sidebar.rs (created, 250 lines), tui/app.rs (+60)
New files tui/widgets/sidebar.rs
Test strategy #[test] fn test_sidebar_toggles(). #[test] fn test_sidebar_shows_tools().
Merge criteria Sidebar toggles on/off. Shows tool list. Doesn't break chat layout.
Rollback strategy No sidebar (single-pane only).
PR title feat: optional right sidebar for tool status
PR description Ctrl+P toggles sidebar. • Shows active tools and status. • Resizes chat area.
Review checklist ① Toggle works. ② Tool status visible. ③ Chat area shrinks. ④ No layout breaks. ⑤ Tests verify.
Risk Low — layout toggle.
Docs Document sidebar.
Harness impact None.

Week 38: Custom Keybindings

Field Value
Deliverable Read keybindings from .claw.json. Remap any action to any key chord.
Files touched tui/event.rs (+60), tui/app.rs (+20)
New files tui/keybindings.rs (created, 150 lines)
Test strategy #[test] fn test_custom_keybinding_loaded(). #[test] fn test_keybinding_override().
Merge criteria Config file read on startup. Overrides apply. Invalid config shows error in chat.
Rollback strategy Hardcoded keybindings (ignore config).
PR title feat: user-configurable keybindings
PR description • Keybindings from .claw.json. • Override any action. • Validation with error messages.
Review checklist ① Config loaded. ② Overrides apply. ③ Invalid config errors. ④ Defaults documented. ⑤ Tests verify.
Risk Low — config parsing.
Docs Document keybinding config format.
Harness impact None.

Phase F: Polish & Hardening (Weeks 3946) — Make It Production-Ready

Goal: Stable, tested, accessible, well-documented.

Week 39: Screen Reader Accessibility

Field Value
Deliverable Screen reader support: crossterm title updates, status announcements via SetTitle.
Files touched tui/app.rs (+40), tui/event.rs (+20)
New files None
Test strategy #[test] fn test_title_updates_on_message().
Merge criteria Terminal title reflects app state. Status messages announced.
Rollback strategy No accessibility features.
PR title feat: screen reader accessibility annotations
PR description • Terminal title updates. • Status announcements. • ARIA-like labels on widgets.
Review checklist ① Title updates. ② Status announced. ③ No visual changes. ④ Tested with orca or NVDA. ⑤ Tests verify.
Risk Low — title updates only.
Docs Document accessibility features.
Harness impact None.

Week 40: Small Terminal Degradation

Field Value
Deliverable If terminal <30 rows or <80 cols, show warning banner and disable sidebar.
Files touched tui/app.rs (+80), tui/theme.rs (+20)
New files None
Test strategy #[test] fn test_small_terminal_warning(). #[test] fn test_sidebar_disabled_narrow().
Merge criteria Warning visible on small terminals. Sidebar auto-disabled. Chat still functional.
Rollback strategy No degradation (layout may break on small terminals).
PR title feat: small terminal graceful degradation
PR description • Warning on <30 rows or <80 cols. • Sidebar disabled. • Layout adjusted.
Review checklist ① Warning visible. ② Sidebar disabled. ③ Chat scrollable. ④ No panic. ⑤ Tests verify.
Risk Low — conditional layout.
Docs Document minimum terminal size.
Harness impact None.

Week 41: Unicode Width Handling

Field Value
Deliverable Use unicode-width for CJK and emoji so layouts don't break.
Files touched Cargo.toml (+1), tui/widgets/chat.rs (+30), tui/widgets/markdown.rs (+30)
New files None
Dependencies added unicode-width = "0.2"
Test strategy #[test] fn test_cjk_width(). #[test] fn test_emoji_width().
Merge criteria CJK characters take 2 columns. Emoji take 2 columns (on supported terminals). Layouts don't break.
Rollback strategy Assume ASCII width (breaks on CJK/emoji).
PR title fix: unicode width for layout correctness
PR description unicode-width for correct column counting. • CJK and emoji handled. • Layout integrity.
Review checklist ① CJK correct. ② Emoji correct. ③ Layout intact. ④ No truncation. ⑤ Tests verify.
Risk Low — well-tested crate.
Docs Document unicode support.
Harness impact None.

Week 42: Snapshot Testing with insta

Field Value
Deliverable insta snapshot tests for complex conversations: tables, code blocks, diffs.
Files touched Cargo.toml (+1, dev-dependency), tests/snapshots/ (new dir)
New files tests/tui_snapshots.rs (150 lines)
Dependencies added insta = "1" (dev-dependency)
Test strategy Render complex markdown to TestBackend, snapshot with insta::assert_snapshot.
Merge criteria Snapshots committed. CI checks them. Any rendering change updates snapshot intentionally.
Rollback strategy Remove snapshot tests (more manual QA needed).
PR title test: snapshot tests for tui rendering
PR description insta snapshot tests for rendering. • Tables, code blocks, diffs covered. • CI enforces no drift.
Review checklist ① Snapshots committed. ② CI checks. ③ cargo insta review documented. ④ Coverage: table, code, diff. ⑤ No test regressions.
Risk Low — adds tests only.
Docs Document snapshot workflow in CONTRIBUTING.md.
Harness impact None.

Week 43: Stress Testing

Field Value
Deliverable 1,000-message session. No memory leaks, OOM, or frame drops.
Files touched benches/tui_stress.rs (new)
New files benches/tui_stress.rs
Test strategy Criterion benchmark: create 1000 messages, render, measure memory and time.
Merge criteria Memory stable (<100MB). Render time <100ms for 1000 messages.
Rollback strategy Document performance limits.
PR title test: stress test for large sessions
PR description • 1000-message stress test. • Memory and time measured. • No leaks. • Documented limits.
Review checklist ① 1000 messages created. ② Memory <100MB. ③ Render <100ms. ④ No leaks. ⑤ Documented.
Risk Low — test only.
Docs Document performance limits.
Harness impact None.

Week 44: Windows Testing

Field Value
Deliverable Verified in PowerShell and Windows Terminal. Fix cursor/resize issues.
Files touched tui/event.rs (+20), tui/mod.rs (+20)
New files None
Test strategy Manual QA on Windows. Automated tests already run in CI (GitHub Actions windows-latest).
Merge criteria claw tui works on Windows. Resize handled. No cursor glitches.
Rollback strategy Document Windows as best-effort.
PR title fix: windows terminal compatibility
PR description • Windows Terminal compatibility verified. • Resize fixes. • Cursor fixes. • PowerShell tested.
Review checklist ① Opens on Windows. ② Resize works. ③ No cursor artifacts. ④ CI passes. ⑤ PowerShell smoke test.
Risk Medium — Windows terminal quirks are unpredictable.
Docs Document Windows setup.
Harness impact None.

Week 45: TUI User Guide

Field Value
Deliverable docs/TUI-USAGE.md — comprehensive user guide with ASCII art screenshots, keybindings, tips.
Files touched docs/TUI-USAGE.md (created, 300 lines)
New files docs/TUI-USAGE.md
Test strategy None — documentation only.
Merge criteria Document covers: installation, first run, keybindings, slash commands, tips, troubleshooting.
Rollback strategy N/A — docs.
PR title docs: tui user guide
PR description • Comprehensive TUI guide. • ASCII art screenshots. • Keybindings table. • Tips and tricks.
Review checklist ① All keys documented. ② ASCII art accurate. ③ Troubleshooting section. ④ Installation steps. ⑤ Proofread.
Risk None — docs only.
Docs Self-documenting.
Harness impact None.

Week 46: claw doctor --tui Diagnostic

Field Value
Deliverable claw doctor --tui runs a self-test: opens TUI, verifies rendering, reports issues.
Files touched cli.rs (+30), tui/mod.rs (+60)
New files None
Test strategy #[test] fn test_doctor_tui_smoke().
Merge criteria claw doctor --tui reports TUI health. Useful for bug reports.
Rollback strategy No --tui flag for doctor.
PR title feat: tui diagnostic mode in doctor
PR description --tui flag for claw doctor. • Self-test rendering. • Reports terminal capabilities, colors, size.
Review checklist --tui flag works. ② Reports terminal info. ③ Detects issues. ④ Useful output. ⑤ Tests verify.
Risk Low — diagnostic only.
Docs Document doctor TUI mode.
Harness impact Add doctor_tui scenario.

Phase G: Advanced Features (Weeks 4752) — Make It Best-in-Class

Goal: Features that differentiate claw from other AI CLI TUIs.

Week 47: Image Support (Vision Input)

Field Value
Deliverable When vision input lands in the API (ROADMAP #220), render image attachments inline using sixel or iTerm2 image protocols. Text fallback for unsupported terminals.
Files touched tui/widgets/image.rs (created, 200 lines), tui/app.rs (+20)
New files tui/widgets/image.rs
Test strategy #[test] fn test_image_fallback_text(). #[test] fn test_sixel_detection().
Merge criteria Images render on iTerm2/kitty. Fallback text on other terminals.
Rollback strategy Text placeholder: [image: filename.png].
PR title feat: inline image rendering in tui
PR description • Sixel and iTerm2 image protocols. • Inline rendering. • Text fallback.
Review checklist ① iTerm2 renders. ② kitty renders. ③ Fallback on standard terminal. ④ No panic. ⑤ Tests verify.
Risk High — image protocols are complex and terminal-specific.
Docs Document image support.
Harness impact Deferred until vision API lands.

Week 48: Notebook Editing

Field Value
Deliverable /notebook opens notebook editor in split pane (VS Code-like notebook view).
Files touched tui/widgets/notebook.rs (created, 250 lines), tui/app.rs (+40)
New files tui/widgets/notebook.rs
Test strategy #[test] fn test_notebook_renders_cells(). #[test] fn test_notebook_cell_edit().
Merge criteria Notebook cells render. Cells editable. Save/execute work.
Rollback strategy No notebook editing in TUI (use REPL or external editor).
PR title feat: notebook editing in tui
PR description • Notebook cell view. • Edit cells in TUI. • Save back to file.
Review checklist ① Cells render. ② Editable. ③ Save works. ④ Execute works. ⑤ Tests verify.
Risk Medium — notebook format is complex.
Docs Document notebook editing.
Harness impact None.

Week 49: Agent Team Dashboard

Field Value
Deliverable Ctrl+A shows real-time view of running agents, their status, outputs — like htop for agents.
Files touched tui/widgets/agent_dashboard.rs (created, 250 lines), tui/app.rs (+40)
New files tui/widgets/agent_dashboard.rs
Test strategy #[test] fn test_dashboard_shows_agents(). #[test] fn test_dashboard_updates().
Merge criteria Agents listed with status. Output streams visible. Sortable by status.
Rollback strategy No dashboard (use /agents slash command).
PR title feat: agent team dashboard in tui
PR description • Real-time agent monitoring. • Like htop for agents. • Output streams. • Sortable.
Review checklist ① Agents listed. ② Status visible. ③ Output streams. ④ Sort works. ⑤ Tests verify.
Risk Medium — requires agent runtime integration.
Docs Document dashboard.
Harness impact None.

Week 50: MCP Server Inspector

Field Value
Deliverable Ctrl+M opens pane showing connected MCP servers, their tools, recent calls.
Files touched tui/widgets/mcp_inspector.rs (created, 200 lines), tui/app.rs (+20)
New files tui/widgets/mcp_inspector.rs
Test strategy #[test] fn test_mcp_inspector_shows_servers().
Merge criteria MCP servers listed. Tools visible. Recent calls scrollable.
Rollback strategy No inspector (use /mcp slash command).
PR title feat: mcp server inspector sidebar
PR description • MCP server list. • Tool inventory. • Recent call log.
Review checklist ① Servers listed. ② Tools visible. ③ Calls logged. ④ Scrollable. ⑤ Tests verify.
Risk Low — MCP data is already available in runtime.
Docs Document MCP inspector.
Harness impact None.

Week 51: Teleport Mode (Fuzzy File Finder)

Field Value
Deliverable Ctrl+G opens fuzzy file finder (like fzf). Jump to file, send /read tool call.
Files touched tui/widgets/teleport.rs (created, 180 lines), tui/app.rs (+40)
New files tui/widgets/teleport.rs
Test strategy #[test] fn test_teleport_lists_files(). #[test] fn test_teleport_selects_file().
Merge criteria Ctrl+G opens finder. Typing filters. Enter selects file. /read sent automatically.
Rollback strategy No teleport (use /read manually).
PR title feat: fuzzy file finder teleport in tui
PR description Ctrl+G opens file finder. • Fuzzy filter. • Enter sends /read.
Review checklist ① Files listed. ② Filter works. ③ Enter selects. ④ /read sent. ⑤ Tests verify.
Risk Low — file listing + filter.
Docs Document teleport mode.
Harness impact None.

Week 52: TUI as Default

Field Value
Deliverable TUI is the default for claw when a TTY is detected. --no-tui or piped input uses REPL. Update ROADMAP.md. Cut release notes.
Files touched main.rs (+20), cli.rs (+15), ROADMAP.md (+5), docs/RELEASE-NOTES.md (new)
New files docs/RELEASE-NOTES.md
Test strategy #[test] fn test_default_tui_on_tty(). #[test] fn test_repl_on_pipe().
Merge criteria claw opens TUI on TTY. claw --no-tui opens REPL. Piped input uses REPL.
Rollback strategy Make REPL default again (one-line change in main.rs).
PR title feat: make tui the default interactive mode
PR description • TUI is default when TTY detected. • --no-tui for REPL. • Piped input uses REPL. • Release notes.
Review checklist ① TTY → TUI. ② --no-tui → REPL. ③ Pipe → REPL. ④ Release notes written. ⑤ ROADMAP updated.
Risk Low — behavioral toggle only.
Docs Update all docs, release notes.
Harness impact Update harness for default TUI.

4. Implementation Specs (Weeks 112 in Detail)

Week 1: Extract app.rs — Detailed Spec

Function signatures to preserve:

// app.rs
pub struct LiveCli {
    pub client: Arc<ConversationRuntime>,
    pub session: Session,
    pub config: RuntimeConfig,
    pub permission_mode: PermissionMode,
    pub tool_executor: Arc<dyn ToolExecutor>,
}

impl LiveCli {
    pub fn new(/* ... */) -> Self;
    pub fn run(&mut self) -> Result<(), RuntimeError>;
    pub fn run_turn(&mut self, prompt: &str) -> Result<TurnResult, RuntimeError>;
}

Extraction steps:

  1. Copy main.rs to app.rs.
  2. In app.rs, remove fn main() and arg parsing.
  3. In main.rs, remove LiveCli struct and impl, keep only fn main() and let mut cli = LiveCli::new(...); cli.run();.
  4. Add mod app; and use app::LiveCli; in main.rs.
  5. Verify cargo check.
  6. Run tests.
  7. Verify main.rs <200 lines.

Test preservation checklist:

  • test_repl_smoke → still in app.rs
  • test_streaming_output → still in app.rs
  • test_tool_call_display → still in app.rs
  • test_permission_prompt → still in app.rs
  • test_session_save → still in app.rs

Week 4: TUI Stub — Detailed Spec

// cli.rs
pub enum CliAction {
    // ... existing variants ...
    Tui,
}

// In main.rs match on CliAction::Tui:
CliAction::Tui => {
    tui::run()?;
}
// tui/mod.rs
use std::io;

pub fn run() -> io::Result<()> {
    eprintln!("TUI mode coming soon.");
    Ok(())
}

Integration test:

// tests/tui_smoke.rs
use std::process::Command;

#[test]
fn test_tui_subcommand_exists() {
    let output = Command::new("cargo")
        .args(["run", "-p", "rusty-claude-cli", "--", "tui", "--help"])
        .output()
        .expect("cargo run failed");
    assert!(output.status.success(), "claw tui --help should succeed");
}

Week 6: Ratatui Skeleton — Detailed Spec

// tui/mod.rs
use std::io;
use crossterm::event::{self, Event, KeyCode, KeyEvent};
use ratatui::{
    backend::{Backend, CrosstermBackend},
    widgets::{Block, Borders, Paragraph},
    Terminal, Frame,
};

pub fn run() -> io::Result<()> {
    ratatui::init();
    let result = run_app();
    ratatui::restore();
    result
}

fn run_app() -> io::Result<()> {
    let mut terminal = ratatui::try_init()?;
    loop {
        terminal.draw(|f| ui(f))?;
        if let Event::Key(KeyEvent { code: KeyCode::Char('q'), .. }) = event::read()? {
            return Ok(());
        }
    }
}

fn ui(frame: &mut Frame) {
    let area = frame.area();
    let paragraph = Paragraph::new("Hello, claw")
        .block(Block::default().borders(Borders::ALL).title("claw TUI"));
    frame.render_widget(paragraph, area);
}

Cargo.toml changes:

[dependencies]
# existing deps ...
ratatui = "0.29"
crossterm = { version = "0.28", features = ["event-stream"] }

TestBackend test:

#[cfg(test)]
mod tests {
    use super::*;
    use ratatui::backend::TestBackend;

    #[test]
    fn test_tui_init_buffer() {
        let mut terminal = Terminal::new(TestBackend::new(80, 24)).unwrap();
        terminal.draw(|f| ui(f)).unwrap();
        let buffer = terminal.backend().buffer();
        // "Hello, claw" is at least 11 chars
        let content: String = buffer.content.iter().map(|c| c.symbol()).collect();
        assert!(content.contains("Hello, claw"));
    }
}

Week 9: Composer Widget — Detailed Spec

// tui/widgets/input.rs
use tui_textarea::TextArea;

pub struct ComposerWidget {
    textarea: TextArea<'static>,
    history: Vec<String>,
    history_index: Option<usize>,
}

impl ComposerWidget {
    pub fn new() -> Self;
    pub fn handle_event(&mut self, key: KeyEvent) -> ComposerAction;
    pub fn text(&self) -> &str;
    pub fn clear(&mut self);
}

pub enum ComposerAction {
    None,
    Submit(String),
    HistoryPrev,
    HistoryNext,
}

Key mappings:

  • EnterSubmit(text)
  • Shift+Enter → insert newline (handled by TextArea)
  • Ctrl+C → clear
  • UpHistoryPrev
  • DownHistoryNext

5. Quality Assurance & Testing

5.1 Unit Test Strategy

Module Test Approach Backend
tui/event.rs Key event translation No backend (pure functions)
tui/app.rs State transitions TestBackend
tui/widgets/chat.rs Message rendering, scroll TestBackend
tui/widgets/input.rs Key handling, history TestBackend
tui/widgets/markdown.rs Markdown parsing → buffer TestBackend
tui/widgets/status_bar.rs Layout, content TestBackend
tui/widgets/tool_call.rs Lifecycle, expand TestBackend
tui/widgets/permission.rs Modal rendering TestBackend

5.2 Integration Test Strategy

// tests/tui_integration.rs
#[tokio::test]
async fn test_full_conversation() {
    let mut app = App::new(mock_runtime());
    // Simulate user typing "hello"
    app.handle_event(AppEvent::Key(KeyCode::Char('h')));
    // ... simulate Enter ...
    app.handle_event(AppEvent::AssistantDelta("Hi!".to_string()));
    // Assert message count == 2
    assert_eq!(app.messages.len(), 2);
}

5.3 Mock Parity Harness — New Scenarios

Scenario Week Added What It Tests
tui_stub_smoke 4 claw tui exits cleanly
tui_smoke_open_close 6 Alternate screen opens and closes
tui_full_layout_smoke 12 Full layout renders with mock data
tui_streaming_mock 13 Event loop processes streaming events
tui_streaming_text 14 Text appears in chat
tui_session_save_resume 15 Session persistence
tui_tool_call_display 16 Tool call inline rendering
tui_permission_prompt 17 Modal overlay
tui_error_handling 18 Error messages in chat
tui_multi_turn 19 Multiple conversation turns
tui_markdown_render 21 Markdown widget
doctor_tui 46 claw doctor --tui

5.4 CI Strategy

Add to .github/workflows/rust-ci.yml:

- name: Build with TUI feature
  run: cargo build --workspace --features tui

- name: Build without TUI feature
  run: cargo build --workspace

- name: Run TUI tests
  run: cargo test --workspace --features tui

- name: Run benchmarks
  run: cargo bench --workspace --features tui
  if: github.ref == 'refs/heads/main'

6. Risk Management & Rollback

6.1 Phase Rollback Matrix

Phase Rollback Command Functionality Lost Time to Rollback
A (Weeks 16) git revert <week-6-commit> TUI stub, claw tui command 5 min
B (Weeks 712) git checkout HEAD~6 -- tui/ Full-screen TUI 10 min
C (Weeks 1320) git revert <week-20-commit> Live conversation, streaming 15 min
D (Weeks 2128) Disable markdown widget, use plain text Rich rendering 20 min
E (Weeks 2938) Disable search, sidebar, mouse Power features 30 min
F (Weeks 3946) Remove snapshot tests, docs Tests, docs 10 min
G (Weeks 4752) --no-tui default Default-on TUI 5 min

6.2 Nuclear Option

If the TUI introduces critical instability:

# Remove TUI code, keep extractions
git checkout main -- rust/crates/rusty-claude-cli/src/tui/
git revert HEAD~N..HEAD  # where N = weeks since Week 4

# The REPL and all other functionality are untouched
# `claw tui` command is removed
# `app.rs`, `format.rs`, `session_mgr.rs` remain (pure wins)

7. Reviewer Guide & PR Template

7.1 How to Review a TUI PR

  1. Check the week number — does this PR match the week's deliverable? If not, ask for scope justification.
  2. Verify line count — is it ≤400 lines? If not, ask for a split.
  3. Check tests — does every new file have at least one #[cfg(test)] block with a TestBackend assertion?
  4. Check no REPL changes — does the PR touch input.rs, render.rs, or the REPL loop in ways that could break existing behavior?
  5. Run cargo test --workspace — does it pass?

7.2 PR Template for TUI PRs

## Week X: [Deliverable Name]

### What changed
- [One-line summary]

### Files
- `tui/foo.rs` (+N/-M)
- `app.rs` (+N/-M)

### Tests
- [ ] Every new module has a `TestBackend` test
- [ ] `cargo test --workspace` passes
- [ ] `cargo clippy --workspace` clean
- [ ] `cargo fmt --all --check` passes

### Verification
```bash
cd rust
cargo test --workspace
cargo run -p rusty-claude-cli -- tui --help

Rollback

If this PR causes issues: git revert HEAD

Risk

[low/medium/high] — [one sentence]


---

## 8. Appendices

### A. Dependency Matrix

| Crate | Version | License | Purpose | Added Week |
|---|---|---|---|---|
| `ratatui` | `0.29` | MIT | TUI framework, layout engine, widgets | 6 |
| `crossterm` (event-stream) | `0.28` | MIT | Already present; event-stream feature added for async events | 6 |
| `tui-textarea` | `0.7` | MIT | Multi-line text input widget | 9 |
| `unicode-width` | `0.2` | MIT | Correct column counting for CJK/emoji | 41 |
| `arboard` | `3` | MIT | Cross-platform clipboard | 34 |
| `insta` (dev) | `1` | Apache-2.0 | Snapshot testing | 42 |

**MSRV impact:** None. All crates support Rust 1.70+.

### B. Performance Budget

| Metric | Target | Measured At |
|---|---|---|
| Frame rate | 60 FPS (p99 <16ms) | Week 28 |
| Memory per 1000 messages | <100 MB | Week 43 |
| Binary size (without TUI) | No change | Week 20 |
| Binary size (with TUI) | +23 MB | Week 20 |
| Clean build time | <5 min | Week 6 |
| Incremental build time | <30 sec | Week 6 |

### C. Glossary

| Term | Definition |
|---|---|
| **TUI** | Terminal User Interface — full-screen, alternate-buffer terminal application |
| **REPL** | Read-Eval-Print Loop — the existing line-based interactive shell |
| **Monolith** | The 11,282-line `main.rs` containing all CLI logic |
| **TestBackend** | ratatui's in-memory terminal buffer for headless testing |
| **EventBroker** | Bridges crossterm events to TUI application events |
| **Composer** | The text input widget at the bottom of the chat |
| **Streaming cell** | The in-progress assistant message being rendered incrementally |

---

*Generated: 2026-06-12 | Author: TheArchitectit | Review status: Draft for upstream PR*  
*Based on post-mortem of `feat/ui-hardening`, `fix/ui-parity`, `feat/uiux-redesign`, `rcc/ui-polish`, and `rust/TUI-ENHANCEMENT-PLAN.md`*