| 1 | # crates/tui — agent guidance |
| 2 | |
| 3 | Scope: the TUI, the runtime engine embedded in it, and everything a user sees. |
| 4 | Read the repo-root `AGENTS.md` first. Current flakes and known debt are in |
| 5 | `docs/ops/CURRENT.md`, not here. |
| 6 | |
| 7 | ## The shell grammar (do not regress it) |
| 8 | |
| 9 | The default shell is the underwater system (`src/tui/underwater.rs`, `ocean.rs`, |
| 10 | `widgets/`, `views/`). Its contract: |
| 11 | |
| 12 | - **One owner per fact.** Route/mode/permission/context live in the header; |
| 13 | Tasks/To-do in the top strip; receipts and the single live row in the |
| 14 | transcript; phase/cost/detail keys in the footer. Never restate a fact in a |
| 15 | second place. |
| 16 | - **One live row.** Settled receipts are still; only the active row and the |
| 17 | footer phase mark move. Decorative motion exists only in empty idle water and |
| 18 | stops the instant the user types or anything needs attention. |
| 19 | - **Phase is typed.** `ShellPhase::from_app` derives idle/typing/working/ |
| 20 | waiting/approval/done/failed from real app state. Never invent state in a |
| 21 | renderer; never compare English strings to detect state — use the enums. |
| 22 | - **Treatment is typed.** `OceanTreatment` (ombre/flat/classic) parses once from |
| 23 | settings. Every treatment keeps ambient life; appearance and motion |
| 24 | (`low_motion`, `fancy_animations`) are independent axes. |
| 25 | - **Footer notices go through the toast system** (`push_status_toast` / |
| 26 | `active_status_toast`), never the legacy `status_message` sink: toasts carry |
| 27 | level + TTL, errors hold sticky, acknowledgements expire. |
| 28 | - **Compact tiers shed chrome, not content.** At small sizes a room drops |
| 29 | titles/captions/spacers before the object the user opened it to manipulate, |
| 30 | and bodies budget from the footer's *wrapped* height (`wrapped_footer_lines` / |
| 31 | `action_footer_lines`). |
| 32 | - **Rows are objects.** Anything selectable has a hitbox recorded at render |
| 33 | time, keyboard + mouse parity, and visible focus. Destructive controls arm |
| 34 | before they fire. |
| 35 | |
| 36 | ## Localization |
| 37 | |
| 38 | Every user-visible string goes through `tr(locale, MessageId::…)` — no hardcoded |
| 39 | English in render paths. Glyphs (`▸ · ▾ ─`), key names (`Enter`, `Alt+?`), and |
| 40 | commands (`/fleet setup`) are composed in code, never embedded in translations. |
| 41 | Adding a string is a four-part change: see `locales/AGENTS.md`. |
| 42 | |
| 43 | ## Verification |
| 44 | |
| 45 | ```sh |
| 46 | cargo test -p codewhale-tui --bins --locked # unit suite (bin targets only) |
| 47 | cargo test -p codewhale-tui --tests --locked # every crates/tui/tests/ target |
| 48 | cargo clippy --workspace --all-targets --locked -- -D warnings |
| 49 | ``` |
| 50 | |
| 51 | Narrower reruns of the slow acceptance targets, once `--tests` has told you |
| 52 | which one moved: |
| 53 | |
| 54 | ```sh |
| 55 | cargo test -p codewhale-tui --test qa_pty --locked # PTY snapshots |
| 56 | cargo test -p codewhale-tui --test release_runtime_qa --locked |
| 57 | cargo test -p codewhale-tui --test terminal_matrix_qa --locked |
| 58 | ``` |
| 59 | |
| 60 | **`--bins` and `--tests` are disjoint target sets.** `crates/tui/tests/` holds |
| 61 | two dozen process-level acceptance targets that a `--bins` run never compiles, |
| 62 | let alone executes, so a green `cargo test -p codewhale-tui --bin codewhale-tui` |
| 63 | says nothing about them. `adaptive_evidence_acceptance` sat red across two |
| 64 | releases for exactly that reason: every routine command anyone ran was a `--bins` |
| 65 | run, and only `cargo test --workspace` reached it. Run both, or run the |
| 66 | workspace gate. |
| 67 | |
| 68 | Run clippy with `--all-targets`: `--bin` alone skips test targets and lets lints |
| 69 | reach CI. |
| 70 | |
| 71 | Real-terminal QA gotchas, learned the hard way: |
| 72 | |
| 73 | - The local tmux **server** may carry `NO_COLOR=1` and `TERM=dumb` from old VHS |
| 74 | runs — launch panes with `env -u NO_COLOR` or all color QA silently lies. tmux |
| 75 | also force-enables the low-motion overlay; prove full motion with |
| 76 | `TMUX`/`TMUX_PANE` removed. |
| 77 | - Scripted PTY input: one Enter on the slash menu both accepts the highlighted |
| 78 | match and runs it. A scripted second Enter lands *inside* whatever modal just |
| 79 | opened. Send one key, wait, capture. |
| 80 | - Judge motion from repeated captures diffed over time, never single |
| 81 | screenshots. Layout gates: 40x12, 60x16, 80x24, 100x32, 140x40. |
| 82 | - `CODEWHALE_TUI_DEBUG=1` writes per-frame diff sizes to |
| 83 | `~/.codewhale/logs/tui-render.log`. Streaming should be tens of cells per |
| 84 | frame; a multi-thousand-cell frame is only acceptable on a genuine layout |
| 85 | transition. |
| 86 |