# QA Plan: TUI Bubble Tea v2 + native cursor — `d247b19` `85af2c6`

**Status:** DRAFT
**Run date / operator:**
**BACKLOG item:** none — operator-requested TUI work, already archived in `RETRO.md`
(2026-06-22). This plan does not gate a BACKLOG→RETRO move; it gates the *behavioral*
claims that `go test` can't prove (Shift+Enter, the real cursor, no render regressions),
which need a real kitty-protocol terminal.

---

## Preconditions

- [ ] **terminal:** kitty (or any kitty-keyboard-protocol terminal: ghostty / foot / wezterm / recent iTerm2) — Shift+Enter disambiguation depends on it.
- [ ] **tui build/branch:** `feat/backlog-burndown` at `85af2c6` — `cd apps/tui-go && GOTOOLCHAIN=auto go build -o /tmp/correx-tui .`
- [ ] **server up:** a correx server reachable at `--host/--port` (default `localhost:8080`) with at least one resumable session, so in-session views render. _Rebuild the server only while it's stopped (lazy classloading)._
- [ ] **config synced:** `~/.config/correx` matches `examples/*` (stale copies cause silent bugs).
- [ ] _(optional)_ `CORREX_TUI_LOG=/tmp/tui.log` to capture the `KEY type=… alt=… ` debug lines as corroborating evidence for the key checks.

## Acceptance gate (one sentence)

> The TUI is correct on v2 **iff** Shift+Enter inserts a newline in the composer without
> sending, a real blinking cursor tracks the caret in insert mode (and vanishes in normal
> mode / behind modals), and nothing in the render layer regressed from the v1 build.

## Checks

Evidence is the live TUI render (an allowed source) plus, where noted, the `CORREX_TUI_LOG` key lines.

| # | Action | Expected observable evidence | Result |
|---|--------|------------------------------|--------|
| 1 | Enter a session, press `i`, type a word | A blinking vertical-bar cursor sits at the caret; typing extends it. Log shows `KEY … type=keyRunes`. | |
| 2 | With text in the composer, press **Shift+Enter** | A newline is inserted; the input grows to a second row; nothing is submitted. Log line shows the Enter key with `shift` (no `submit`). | |
| 3 | Press **Enter** (bare) | The message submits (composer clears / turn appears). Confirms bare Enter still sends. | |
| 4 | Press **Ctrl+J**, then **Alt+Enter** (fallback paths) | Each inserts a newline without sending (parity with Shift+Enter). | |
| 5 | From insert mode press `esc` to normal mode | The blinking cursor disappears entirely (no stray bar left on screen). | |
| 6 | In insert mode, open the palette (`/` or `p`) | The modal's own filter caret blinks; **no** second cursor shows behind/around the modal. Close it → composer cursor returns. | |
| 7 | Leave the composer in insert mode and idle ~5s, then drag-select text with the mouse | Selection holds (screen isn't being redrawn out from under it) — confirms insert mode no longer forces the frame loop. | |
| 8 | Observe the terminal tab/title while in a session vs. idle | Title reads `correx — <session name>` in-session; `correx — <model>` (or `correx`) when idle. | |
| 9 | Visual regression sweep: open `?` help, stats, an `e` event inspector, a `^x` diff, the `t` artifacts viewer; resize the terminal narrow↔wide | Colors/borders/opaque panels look as before; modals scroll (`PgUp/PgDn`, `g/G`); diff viewer pages; no garbled ANSI, no off-by-one panel widths, alt-screen restores the shell cleanly on `q`. | |
| 10 | Walk the approval band (trigger a gate), `↑/↓` the queue, approve/reject/steer | Band behaves exactly as on v1 (single-key low tiers, arm→confirm T3+); steering caret still draws. | |

## Out of scope (explicitly NOT covered this pass)

- Mouse-wheel scroll (deliberately not added — would fight native text selection).
- Native cursor for the palette/files/steering/config carets (still frame-drawn `▏`; only the main composer moved to `View.Cursor`).
- Behavior on non-kitty terminals beyond the documented Ctrl+J/Alt+Enter fallback.

## Disposition

- **PASS** → set Status: PASSED with the run date; the RETRO 2026-06-22 rows for `d247b19`/`85af2c6` stand as live-verified.
- **FAIL** → file each failure as a numbered finding in `BACKLOG.md` (action + exact repro + the render/log signal that was wrong), fix, then re-run only the failed checks. Set Status: FAILED until green.
