1---2name: tui-observe-first3description: Use when building, fixing, polishing, or comparing any terminal UI — ratatui/crossterm REPLs, agent CLI chrome — or when a TUI shows a frozen spinner, dead keys, duplicated responses, stale cells, flicker, resume failures on dangling tool_use, or when you need side-by-side screenshots against reference CLIs.4---56# TUI observe-first78A TUI is judged **rendered in a real terminal**, never from its source. Render9it, screenshot it, compare it against strong CLIs at the **same terminal grid**,10and cite a screenshot for every finding and fix. Terminal sibling of11`ui-fix-observe-first`.1213## The comparative loop14151. **Reference set**: spawn the CLIs you must beat (e.g. claude, codex, droid)16 and your TUI in the **same** rendering stack and viewport. Reuse the same17 browser/PTY tab for every capture — a resized tab makes an unfair comparison.182. **Capture the same CUJ moments**: welcome, empty composer, slash palette,19 streaming/working, tool receipts, permission dialog, error, interrupt, quit.203. **Teardown**: every finding cites a screenshot. No screenshot = not a finding.21 Rank by impact; fix one issue-class at a time.224. **Re-render after every rebuild**, then dogfood real turns (tools, interrupts).23 Many worst bugs only appear live.2425## What a strong agent TUI usually has2627| Element | Shape |28|---|---|29| Welcome header | Logo + name/version, model · permission-mode, cwd · session — few lines |30| Bottom bar | Mode + key hints left; model · session · cost · ctx% right |31| Working line | Spinner + verb + elapsed + interrupt hint + queued count |32| Turn / tool receipts | Compact status; collapsed summaries, not raw dumps |33| Text | Markdown-lite: bold, inline code, tinted fences |34| Palette | Ranked search; clear selected row; dim aliases |35| Dialogs | Clear selection + quick keys + hint row |36| Input | Distinct prompt; status lives outside the input line |37| Flow | Type-to-queue while running; Esc interrupts; Ctrl+C arms quit |3839## Render-loop laws4041- **Never await a shared engine/session lock in the render loop.** Clone handles42 out and poll with `try_lock`, or the spinner, timer, and keyboard freeze.43- **Every new-turn path goes through one `begin_turn()`** that stamps start time.44 Setting `Running` without a stamp freezes a 0s spinner.45- **Drain completion channels before finalizing** a turn, or leftover deltas46 duplicate response blocks.47- **`Clear` the full frame before drawing** — otherwise stale cells bleed through.48- **Set the terminal cursor explicitly** from the edit column.49- **Repair histories at session load**: dangling `tool_use` without `tool_result`50 breaks resume (API 400). Insert synthetic error tool_results at load.51- **TestBackend tests gate layouts** — they are not proof; screenshots are.5253## Capture traps5455- Compare only at the same font/grid/renderer as your references.56- Browser attach pages can swallow Escape / chords — reproduce keyboard bugs57 against the raw PTY before changing code.58- Scrollback / ready-regex must match the **current** UI string after redesigns.59- `cargo test | tail` can eat exit codes — capture status yourself.6061## Common mistakes6263| Mistake | Reality |64|---|---|65| "It compiles / tests pass, ship it" | Many TUI bugs have green tests. Render + dogfood. |66| Diagnosing look/flow from source | Freeze, bleed-through, grid width, timing do not live in code. |67| Different tabs/sizes for comparisons | PTY grid follows viewport — same tab, same size. |68| Restyling without a reference set | Capture references first, then rank gaps. |6970## Cross-references7172- `ui-fix-observe-first` — web sibling (render → measure → cite → fix)