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

22 KiB
Raw Blame History

TUI Failure Analysis & 52-Week Delivery Plan

Status: Draft | Date: 2026-06-12 | Branch: feat/tui

This document is a post-mortem of every prior TUI attempt in this repository, followed by a week-by-week plan designed specifically to avoid those failure modes. It is meant to be read as a working agreement: if you deviate from this plan, you should write down why.


Part 1: Post-Mortem — Every Prior TUI Branch

Branch: feat/ui-hardening (earliest attempt)

What happened:

  • Built on top of a 3,159-line main.rs that combined REPL loop, arg parsing, slash command handlers, streaming output, tool call display, permission prompting, and session management.
  • Tried to make the REPL "feel more reliable and discoverable" by patching behavior inside the monolith.
  • No structural extraction happened. The REPL code grew fatter, not cleaner.

Why it failed:

  • Hardening a monolith is a treadmill: every fix adds more code to the same file, increasing the surface area of the next bug.
  • No alternate-screen mode was attempted; the work was constrained to inline scrolling, which limits what a "TUI" can even mean.
  • The branch produced useful commits (a13b1c2) but they were absorbed into fix/ui-parity rather than shipping independently.

Lesson: Do not patch the monolith. Extract first, then improve.


Branch: fix/ui-parity (the "parity pass")

What happened:

  • Explicitly scoped DOWN from a real TUI. Commit bcaf6e0 literally says: "Rejected: Rework the REPL into a full multi-pane typeahead overlay | too large for this UI-only parity slice."
  • Instead, it ported REPL affordances to match TypeScript patterns: ranked slash suggestions, alias completion, trailing-space acceptance, argument hints.
  • This was good work (merged), but it was a REPL polish pass, not a TUI.

Why the TUI part failed:

  • The author correctly identified that a full TUI needed structural extraction first, but that extraction was never done.
  • Without Phase 0 (breaking main.rs into modules), any TUI work would have meant adding ratatui code directly into main.rs, creating an unmaintainable 4,000+ line file.
  • The plan became "ship the parity pass now, do TUI later" — and later never came because the monolith became even harder to refactor.

Lesson: If you need extraction first, do the extraction first. Do not defer it.


Branch: feat/uiux-redesign (the big swing)

What happened:

  • The most ambitious branch. It added:
    • Vim keybinding mode (normal/insert/visual/command)
    • LSP client integration (diagnostics, definitions, references)
    • Git slash commands (branch, commit, commit-push-pr, worktree)
    • An HTTP/SSE server crate with axum
  • But it also produced 18 commits that were purely README credit warfare: adding, removing, and re-adding oh-my-opencode and Jobdori credits.

Why it failed:

  • Credit wars consumed more branch history than feature work. This is a classic community/coordination failure: when contributors can't agree on attribution, the actual product stalls.
  • The LSP integration and vim mode were large features that required review cycles, but the branch was mired in docs disputes.
  • The branch never merged cleanly. Its useful features (git slash commands, LSP) were absorbed piecemeal into other branches, while the TUI part was abandoned.

Lesson: Establish a CONTRIBUTING.md attribution policy upfront and enforce it. Do not let credit bikeshedding block the build.


Branch: rcc/ui-polish

What happened:

  • Empty diff against main. This was likely a tracking or experiment branch that never got code.

Why it failed:

  • Branch created, no work pushed, then abandoned. Probably because the author saw how much was already on feat/uiux-redesign and decided to wait.
  • Waiting for another branch is a form of coordination failure.

Lesson: If you start a branch, write a singleline commit to it within 24 hours, or delete it.


Document: rust/TUI-ENHANCEMENT-PLAN.md

What happened:

  • A very good, comprehensive plan written 2026-03-31 by someone who clearly understood the codebase.
  • It correctly identified the 3,159-line main.rs as the #1 risk.
  • It proposed 6 phases (0=structural cleanup, 1=status bar, 2=streaming, 3=tool viz, 4=slash commands, 5=themes, 6=full-screen).

Why it failed:

  • It was a plan, not a commitment. No branch was ever created to execute it.
  • Phases 14 were all "moderate effort" or "large effort" tasks bundled together. Phase 6 (ratatui) was explicitly labeled a "stretch" that requires Phase 0 first — but Phase 0 itself was a multi-file refactor that no one signed up to do.
  • The plan said "Feature-gate heavy dependencies — ratatui should be behind a full-tui feature flag." This is a premature optimization: adding a feature flag before you even know if the code works adds build-system friction that kills momentum.

Lesson: A 6-phase plan with no owner and no timeline is a wishlist. Convert it into weekly tasks with a single owner.


Root Failure Modes (Summary)

# Failure Mode How We Avoid It
1 The Monolith Trap Week 13 of the plan below are exclusively main.rs extraction. No TUI code is written until the monolith is gone.
2 Scope Panic "Too large for this slice" is the killer phrase. Every slice below is ≤200 lines and ships independently.
3 Credit Wars Attribution is handled by the single AUTHOR/CO-AUTHOR commit trailers. No README edits during TUI work.
4 Placeholder Branches No empty branches. Every branch has at least one passing test and one visible change.
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 12. Build the code first; gate it later.
7 REPL Replacement Fear The TUI is claw tui, a separate binary entrypoint. The REPL (claw with no args) is untouched forever.
8 Streaming-Not-First The TUI's first real conversation feature is streaming rendering. Static screens come later.
9 Dependency Bloat Fear Only 3 new crates: ratatui, tui-textarea, crossterm event-stream feature. No optional deps until Week 20.
10 No Test Coverage for UI Every widget module has a #[cfg(test)] block that renders to a TestBackend (ratatui's in-memory buffer).

Part 2: The 52-Week Plan

Sprint Structure

  • Sprints are 2 weeks (26 sprints total).
  • Every sprint ships. If a sprint can't ship, you cut scope, not delay.
  • Merge target: feat/tuimain via PR at end of each sprint.
  • Review rule: No PR >400 lines. If a task is bigger, split it.

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

Goal: Kill the monolith. No TUI code is written until main.rs is <200 lines.

Week Deliverable Lines (est) How It Ships
1 Extract app.rs from main.rs: move LiveCli struct and its impl to app.rs. main.rs now only imports and calls LiveCli::run(). ~300 PR: refactor: extract LiveCli to app.rs
2 Extract format.rs: move all format_* functions (status, cost, model, permissions, compact, etc.) out of main.rs and app.rs. ~400 PR: refactor: extract report formatting to format.rs
3 Extract session_mgr.rs: move session CRUD (create, resume, list, switch, persist, compaction) out of app.rs. ~350 PR: refactor: extract session management to session_mgr.rs
4 Create tui/mod.rs as an empty namespace with a pub fn run() -> io::Result<()> stub. Add claw tui CLI dispatch that calls it and prints "TUI mode coming soon." ~50 PR: feat: add tui module stub and claw tui command
5 Wire ConversationRuntime into app.rs through a trait boundary. This is prep for sharing the runtime between REPL and TUI without duplication. ~200 PR: refactor: runtime access through trait boundary
6 Add ratatui dependency, crossterm event-stream feature. Create a minimal alternate-screen demo in tui/mod.rs that shows a "Hello, claw" Paragraph and exits on q. Tests use TestBackend. ~150 PR: feat: ratatui skeleton with TestBackend tests

Sprint 13 merge checkpoint: main.rs is <200 lines. All tests pass. No behavioral changes.


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

Goal: The TUI opens, renders, and exits cleanly. No conversation yet.

Week Deliverable Lines (est) How It Ships
7 tui/event.rs: EventBroker that bridges crossterm::event::EventStreamAppEvent enum (Tick, Key, Resize, Quit). Unit-test key translation. ~180 PR: feat: tui event broker with async event stream
8 tui/app.rs: App state machine with one screen. Renders a static list of mock messages (using Paragraph). Handles q to quit, arrow keys to scroll. ~200 PR: feat: tui app state machine with mock messages
9 tui/widgets/input.rs: Composer widget wrapping tui_textarea::TextArea. Enter sends, Shift+Enter inserts newline. Mock: prints submitted text to a debug overlay. ~150 PR: feat: tui composer input widget
10 tui/widgets/chat.rs: ChatWidget renders a scrollable Vec<MessageCell> as vertical Layout of Paragraphs. Supports PgUp/PgDn. ~200 PR: feat: tui chat message list widget
11 Terminal resize handling. Status bar at bottom showing model name (static), permission mode (static), terminal dimensions. ~120 PR: feat: terminal resize handling and static status bar
12 Integration week: claw tui opens a full-screen app with mock chat + input + status bar. Add smoke test in tests/ that runs in CI (skips if no TTY). Add --tui feature flag? No. Feature flag deferred until we have a reason. ~100 PR: feat: claw tui opens full-screen TUI with mock data

Sprint 46 merge checkpoint: claw tui runs. It looks like a chat app with fake data. It exits cleanly. CI passes.


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

Goal: The TUI has real conversations with the model, streaming text, tool calls, and session persistence.

Week Deliverable Lines (est) How It Ships
13 Wire ConversationRuntime into TUI event loop. Create TuiProgressReporter that sends AssistantEvent deltas into the TUI channel. ~250 PR: feat: wire ConversationRuntime into tui event loop
14 Streaming assistant text: AssistantEvent::TextDelta appends to the active message cell in real time. No markdown rendering yet — raw text. ~180 PR: feat: real-time streaming assistant text in tui
15 Message persistence: on exit, save the session to the same JSONL format as the REPL. On claw tui --resume, load it. ~200 PR: feat: tui session save and resume
16 Tool call rendering: when a tool call starts, show a spinner + tool name inline. When it finishes, show ✓/✗ with truncated result (first 5 lines). ~220 PR: feat: inline tool call spinner and result summary
17 Permission prompt overlay: modal widget (centered block) for Y/N approval when a tool needs permission. Does not block the event loop — shows up as a Screen::PermissionPrompt state. ~200 PR: feat: permission prompt modal overlay in tui
18 Error handling: network errors, API rate limits, and runtime errors render as styled error messages in the chat, not panics. ~150 PR: feat: graceful error rendering in tui chat
19 Multi-turn conversation: send follow-up messages. Scrollback preserves full history. Input clears after send. ~100 PR: feat: multi-turn conversation in tui
20 Feature flag week: Add tui feature to rusty-claude-cli/Cargo.toml. Default OFF. CI builds with --features tui. Binary size documented. ~50 PR: feat: gate tui behind optional feature flag

Sprint 710 merge checkpoint: cargo run -p rusty-claude-cli -- tui --resume loads a session and chats with the model. Tool calls show inline. Errors are graceful. CI passes.


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

Goal: Markdown rendering, syntax highlighting, themes, and tool visualization.

Week Deliverable Lines (est) How It Ships
21 Port existing render.rs (pulldown-cmark + syntect) to a tui/widgets/markdown.rs widget. Renders headings, lists, inline code, code blocks with syntax highlighting in ratatui Paragraphs. ~300 PR: feat: markdown widget with syntax highlighting
22 Table rendering: pulldown-cmark table events → ratatui Table widget with borders. ~150 PR: feat: table rendering in markdown widget
23 Blockquote rendering: styled indentation + left border. Link rendering: underlined + clickable (if mouse support enabled later). ~100 PR: feat: blockquote and link rendering
24 Streaming markdown: incremental parse of markdown as text deltas arrive. Don't re-render from scratch every frame — append to parser state. ~200 PR: feat: incremental markdown streaming render
25 Dark/light theme system. Respect NO_COLOR. Detect truecolor vs. 256-color vs. 16-color terminals and degrade gracefully. ~250 PR: feat: color theme system with terminal capability detection
26 Tool result expansion: press Enter on a tool result to expand/collapse full output. Use tui-textarea or custom widget for scrollable code view. ~180 PR: feat: collapsible/expandable tool results
27 Colored /diff display: when edit_file or /diff produces output, render unified diff with green additions and red removals in the chat. ~200 PR: feat: colored diff rendering in tui
28 Performance pass: profile rendering with cargo flamegraph. Ensure 60fps on a 100-message session. Optimize if below 30fps. ~100 PR: perf: rendering performance pass

Sprint 1114 merge checkpoint: The TUI renders markdown, tables, code blocks with syntax highlighting, and colored diffs. Themes work. Performance is smooth.


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

Goal: Keyboard shortcuts, search, session picker, and slash command integration.

Week Deliverable Lines (est) How It Ships
29 / search within conversation: incremental search highlighting matching text. n/N for next/prev match. ~200 PR: feat: conversation search with incremental highlighting
30 ? help overlay: keyboard shortcuts panel showing all keybindings. ~150 PR: feat: keyboard shortcuts help overlay
31 Session picker: Ctrl+S opens a fuzzy-filterable list of saved sessions (up/down arrows, Enter to switch). ~200 PR: feat: interactive session picker
32 Slash command integration: type / in the input to trigger slash command picker (like the REPL). Commands execute within the TUI (e.g., /model, /permissions). ~250 PR: feat: slash command picker and execution in tui
33 /compact in TUI: runs compaction, shows a toast notification with tokens saved. ~100 PR: feat: compact command with toast notification
34 Copy to clipboard: y on a message or code block copies it to clipboard via arboard. ~120 PR: feat: copy message to clipboard
35 Mouse support: click to expand tool results, scroll wheel on conversation, click to focus input. ~150 PR: feat: mouse support for tool expansion and scrolling
36 Pager mode: for very long outputs (e.g., /status, /config), open a full-screen pager with j/k/q. ~180 PR: feat: internal pager for long outputs
37 Multi-pane layout (optional): Ctrl+P toggles a right sidebar showing active tool status or todo list. This is the first "full TUI" power feature. ~250 PR: feat: optional right sidebar for tool status
38 Custom keybindings: read keybindings from .claw.json (e.g., remap Ctrl+S to something else). ~150 PR: feat: user-configurable keybindings

Sprint 1519 merge checkpoint: The TUI is a power-user tool. Keyboard shortcuts, search, mouse, session picker, and slash commands all work.


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

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

Week Deliverable Lines (est) How It Ships
39 Accessibility: screen reader support via crossterm title updates and status announcements. ~100 PR: feat: screen reader accessibility annotations
40 Small-terminal graceful degradation: if terminal is <30 rows or <80 cols, show a warning banner and disable sidebar. ~120 PR: feat: small terminal graceful degradation
41 Unicode width handling: use unicode-width to prevent CJK and emoji from breaking layouts. ~100 PR: fix: unicode width for layout correctness
42 Snapshot testing: use insta to snapshot-render complex conversations (tables, code blocks, diffs) and catch regressions. ~150 PR: test: snapshot tests for tui rendering
43 Stress testing: 1,000-message session. Ensure no memory leaks, OOM, or frame drops. ~50 (test-only) PR: test: stress test for large sessions
44 Windows testing: verify in PowerShell and Windows Terminal. Fix cursor/resize issues. ~100 PR: fix: windows terminal compatibility
45 Documentation: docs/TUI-USAGE.md — comprehensive user guide with screenshots (ASCII art), keybindings, tips. ~200 (docs only) PR: docs: tui user guide
46 claw doctor TUI diagnostic: claw doctor --tui runs a self-test that opens the TUI, verifies rendering, and reports any issues. ~150 PR: feat: tui diagnostic mode in doctor

Sprint 2023 merge checkpoint: The TUI is production-ready. Tests cover rendering, large sessions, and cross-platform compatibility. Docs are complete.


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

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

Week Deliverable Lines (est) How It Ships
47 Image support: when vision input lands in the API (see ROADMAP #220), the TUI renders image attachments inline using sixel or iTerm2 image protocols, with a text fallback. ~200 PR: feat: inline image rendering in tui
48 Notebook editing: /notebook opens the notebook editor in a split pane (like VS Code's notebook view). ~250 PR: feat: notebook editing in tui
49 Team/agent dashboard: Ctrl+A shows a real-time view of running agents, their status, and their outputs (like htop for agents). ~250 PR: feat: agent team dashboard in tui
50 MCP server inspector: Ctrl+M opens a pane showing connected MCP servers, their tools, and recent calls. ~200 PR: feat: mcp server inspector sidebar
51 Teleport mode: Ctrl+G opens a fuzzy file finder (like fzf) to jump to any file in the workspace, then sends a /read tool call. ~180 PR: feat: fuzzy file finder teleport in tui
52 Release polish: final pass of the whole plan. Update ROADMAP.md. Cut a release note. The TUI is now the default for claw when a TTY is detected (with --no-tui to opt out). ~100 PR: feat: make tui the default interactive mode

Final merge checkpoint: claw with no args opens the TUI by default (when TTY detected). The TUI supports images, notebooks, agent dashboards, MCP inspection, and fuzzy file search. This is a 52-week execution of the vision.


Part 3: Rules of Engagement

To avoid repeating the failures above, these rules are binding for anyone working on the TUI:

  1. No README edits during TUI work. Attribution is via Co-Authored-By trailers in commits only. README credit updates happen in separate PRs, never mixed with TUI feature PRs.

  2. No empty branches. If you create a branch, commit something testable within 24 hours. If you haven't, delete the branch.

  3. No PR >400 lines. Any task that produces more than 400 lines must be split. This is enforced by reviewer policy, not tooling.

  4. No feature flags before Week 20. The tui feature flag is added only after the TUI is demonstrably working. Before that, ratatui is a normal dependency. This removes the "feature flag complexity" tax that killed momentum in prior plans.

  5. Every widget has a #[cfg(test)] block. Use ratatui::backend::TestBackend to assert rendered buffer state. No widget ships without at least one buffer assertion.

  6. Every sprint ends with cargo test --workspace passing. Not "mostly passing." Green CI is the definition of done.

  7. The REPL is sacred. claw with no args may eventually default to TUI (Week 52), but the REPL mode (claw --repl or piped input) is supported forever. No TUI change may break the REPL.

  8. Streaming is the default UX. The TUI should never buffer an entire response before rendering it. Every message cell supports incremental appending from the first week it exists.

  9. Runtime types are reused directly. No wrapper structs around ConversationRuntime, Session, or ToolExecutor. If the runtime API is awkward for the TUI, fix the runtime API, not the TUI.

  10. Defer, don't abandon. If a week is running long, cut scope and merge what works. Move the cut items to a "stretch" backlog. Never let a week slip into the next week.


Part 4: Emergency Rollback Strategy

If the TUI work introduces instability:

  1. Week 16 (extraction): Each extraction is a pure refactor. git revert of any single PR restores the old code in main.rs because the old code was moved, not deleted, during the transition.

  2. Week 712 (skeleton): The TUI is opt-in via claw tui. Reverting the tui/ directory and the CLI dispatch removes the feature entirely without affecting the REPL.

  3. Week 13+ (live conversation): If the streaming integration breaks runtime stability, the TuiProgressReporter can be swapped for a no-op reporter in a single-line revert.

  4. Nuclear option: git checkout main -- rust/crates/rusty-claude-cli/src/tui/ and remove the claw tui dispatch. The REPL and all other functionality are untouched.


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