返回 CodeWhale
v0.9.2-terminal-matrix.md
根目录 / docs / releases / v0.9.2-terminal-matrix.md
1 # v0.9.2 terminal / key / modal acceptance matrix (#3758)
2
3 Status vocabulary, used literally:
4
5 - **PASS** — an automated cell exists, and this document names the test that
6 covers it. It does **not** claim the test was run in this session; see
7 "How to run" below.
8 - **UNRUN** — no automated coverage. Every `UNRUN` row is a literal physical
9 observation in a real terminal emulator or over a real SSH link, which a
10 pseudo-terminal reproduces the byte protocol of but not the emulator.
11
12 Nothing in this document asserts a test result. It records which cells have
13 automated acceptance and which remain human work.
14
15 ## What is automated, and where
16
17 | Test target | File |
18 | --- | --- |
19 | Provider-free terminal/key/modal matrix | `crates/tui/tests/terminal_matrix_qa.rs` |
20 | Running-turn contract against a loopback mock provider | `crates/tui/tests/release_runtime_qa.rs` |
21 | Shortcut-copy canonical-action audit | `crates/tui/src/tui/keybindings.rs` (unit tests) |
22 | PTY harness, terminal-mode ledger, view-stack trace reader | `crates/tui/tests/support/qa_harness/` |
23
24 Supporting machinery added for this matrix:
25
26 - `support/qa_harness/modes.rs` — replays the raw PTY control stream into a
27 DEC-private-mode ledger. Terminal-mode restoration is invisible on the
28 rendered frame, so the control stream is the only truthful evidence.
29 - `support/qa_harness/view_log.rs` — reads the product's existing
30 `codewhale_tui::view_stack` trace records (push/pop, `ModalKind`, depth).
31 Modal coverage asserts on those records rather than on "the frame changed",
32 because a replaced modal and a closed modal render identically.
33 - `Harness::transcript` / `terminal_modes` / `diagnostics` — every bounded wait
34 fails with the frame **and** the mode ledger.
35
36 ### How to run
37
38 ```
39 cargo test -p codewhale-tui --test terminal_matrix_qa --locked -- --test-threads=1
40 cargo test -p codewhale-tui --test release_runtime_qa --locked -- --test-threads=1
41 cargo test -p codewhale-tui --bin codewhale-tui --locked keybindings
42 ```
43
44 These are Unix-gated (`#![cfg(unix)]`); Windows ConPTY input plumbing is a
45 separate audit and its rows are `UNRUN` below.
46
47 ## Automated cells
48
49 ### Terminal geometry
50
51 | Cell | Status | Covered by |
52 | --- | --- | --- |
53 | 80x24 classic | PASS | `size_matrix_keeps_the_composer_visible_and_inside_the_viewport` |
54 | 120x40 laptop | PASS | same |
55 | 60x20 narrow split | PASS | same |
56 | 48x14 tiny pane | PASS | same |
57 | 200x50 wide | PASS | same |
58 | Live resize across all of the above, composer stays typable | PASS | same |
59 | No row overflows the viewport; cursor stays in bounds | PASS | same |
60
61 ### `TERM` / `COLORTERM` capability tiers
62
63 | Cell | Status | Covered by |
64 | --- | --- | --- |
65 | `xterm-256color` + `COLORTERM=truecolor` | PASS | `capability_matrix_honors_the_advertised_color_tier` |
66 | `xterm-256color`, no truecolor claim → no 24-bit SGR emitted | PASS | same |
67 | `xterm` (unknown tier) → no 24-bit SGR emitted | PASS | same |
68 | `screen-256color` (tmux-style) → no 24-bit SGR emitted | PASS | same |
69 | `NO_COLOR=1` present → still boots and paints | PASS | same |
70 | `CODEWHALE_ASCII_SAFE=1` narrows decorative glyphs; default tier differs | PASS | `ascii_safe_tier_removes_decorative_glyphs_the_default_tier_paints` |
71
72 `NO_COLOR` is deliberately a boot-and-paint row only. The palette does not
73 honor `NO_COLOR` today, and this matrix does not invent a contract for it.
74
75 ### Paste and IME
76
77 | Cell | Status | Covered by |
78 | --- | --- | --- |
79 | Bracketed single line | PASS | `paste_matrix_lands_in_the_composer_without_autosubmitting` |
80 | Bracketed multiline | PASS | same |
81 | Bracketed with trailing newline (no auto-submit) | PASS | same |
82 | Bracketed CJK / wide glyphs | PASS | same |
83 | Raw (unbracketed) multiline | PASS | same |
84 | Large payload (~2 KB) | PASS | same |
85 | IME-style per-character commits are typing, not paste | PASS | `ime_style_commits_are_typing_and_the_following_enter_submits` |
86 | Lone trailing commit + Enter still submits | PASS | same |
87 | Bracketed paste over SSH stays in the composer | PASS | `qa_pty.rs::paste_bracketed_from_macos_client_into_linux_ssh_stays_in_composer` (pre-existing) |
88 | Repeated Enter after a raw paste still submits | PASS | `qa_pty.rs::paste_unbracketed_then_repeated_enter_still_submits` (pre-existing) |
89
90 ### Mouse, resize, focus
91
92 | Cell | Status | Covered by |
93 | --- | --- | --- |
94 | Click / drag / wheel never decode as composer text | PASS | `mouse_resize_and_focus_events_never_reach_the_composer_as_text` |
95 | Focus lost / gained never decode as text | PASS | same |
96 | `FocusGained` re-establishes focus reporting and bracketed paste | PASS | same |
97 | Resize during a focus transition keeps the draft | PASS | same |
98 | Wheel/click ownership on real work-surface rows | PASS | `qa_pty.rs::work_surface_real_rows_own_click_wheel_and_resize` (pre-existing) |
99
100 ### Modals
101
102 | Cell | Status | Covered by |
103 | --- | --- | --- |
104 | Help (F1) opens and Esc closes to depth 0 | PASS | `every_provider_free_modal_opens_and_escape_returns_to_the_composer` |
105 | Command palette (Ctrl+K) | PASS | same |
106 | Context inspector (`/context`) | PASS | same |
107 | Live transcript (`/transcript`) | PASS | same |
108 | Theme picker (`/theme`) | PASS | same |
109 | Skills manager (`/skills`) | PASS | same |
110 | Composer is typable again after the full sweep | PASS | same |
111 | Approval modal review + deny without side effect | PASS | `qa_pty.rs::approval_modal_keeps_wheel_for_review_and_denies_without_side_effect` (pre-existing) |
112 | Help overlay advertises exactly the chords this matrix drives | PASS | `help_overlay_advertises_the_chords_this_matrix_drives` |
113
114 Modals that need a provider, a saved session, or a live Fleet to open
115 deterministically (model picker, provider picker, fleet setup/roster, setup
116 wizard, session picker, worktree manager) are **not** listed as PASS. They are
117 not physical-terminal work either — they are follow-on automation, tracked as a
118 gap rather than claimed.
119
120 ### Running-turn contract
121
122 | Cell | Status | Covered by |
123 | --- | --- | --- |
124 | Enter queues while a turn is running | PASS | `release_runtime_qa.rs::release_queued_follow_ups_dispatch_exactly_once_and_in_order` |
125 | Composer advertises `↵ queue` while busy | PASS | same |
126 | Stash chords are never advertised as send/queue/steer | PASS | same, plus `help_overlay_advertises_the_chords_this_matrix_drives` |
127 | Queued follow-ups dispatch exactly once, in order (no drop, no double send) | PASS | `release_queued_follow_ups_dispatch_exactly_once_and_in_order` |
128 | Ctrl+Enter steers the running turn | PASS | `release_runtime_qa.rs::release_ctrl_enter_steers_running_turn` (pre-existing) |
129 | Shift+Enter / Alt+Enter / Ctrl+J stay newlines during a running turn | PASS | same |
130 | Empty Enter promotes the next queued follow-up | PASS | `release_runtime_qa.rs::release_empty_enter_promotes_queued_follow_up` (pre-existing) |
131 | Explicit queue dispatch (`/queue send <n>`) exists as the named action | PASS | `release_empty_enter_promotes_queued_follow_up` asserts the advertised copy |
132
133 ### Shortcut copy — one canonical action per key
134
135 | Cell | Status | Covered by |
136 | --- | --- | --- |
137 | No key advertises two actions without a context qualifier | PASS | `keybindings.rs::every_advertised_key_names_exactly_one_canonical_action` |
138 | Ctrl+G / Ctrl+S documented once, as stash only | PASS | `keybindings.rs::stash_chords_advertise_stashing_and_nothing_else` |
139 | Enter / Ctrl+Enter / newline chords each own one verb | PASS | `keybindings.rs::running_turn_verbs_belong_to_one_chord_each` |
140 | Bare `v` never advertised; details is Alt+V | PASS | `keybindings.rs::tool_details_documents_alt_v_only_never_bare_v` (pre-existing) |
141 | Alt+? never advertised; F1 / Ctrl+/ are the help paths | PASS | `keybindings.rs::help_advertises_f1_and_ctrl_slash_never_alt_question` (pre-existing) |
142
143 The one intentional double-listing is `Ctrl+C` / `Ctrl+C (selection)`: cancel
144 versus copy, disambiguated in the advertised chord itself. The audit permits
145 exactly that shape and rejects an unqualified second meaning.
146
147 ### Terminal-mode restoration
148
149 | Cell | Status | Covered by |
150 | --- | --- | --- |
151 | Ctrl+D on an empty composer restores every mode | PASS | `terminal_modes_are_restored_on_every_exit_path` |
152 | SIGINT restores every mode | PASS | same |
153 | Exit from a non-256-color terminal restores every mode | PASS | same |
154 | Keyboard-enhancement stack fully unwound (no `^[[>5u` residue, #1583) | PASS | same |
155 | Cursor restored visible after the alternate screen | PASS | same |
156 | The leak guard actually covers the modes the TUI enables | PASS | `the_leak_guard_covers_the_modes_the_tui_enables` |
157
158 ## UNRUN — physical observation only
159
160 Every row here needs a human at a real terminal. A PTY delivers the same bytes,
161 but it is not the emulator: it has no font stack, no GPU renderer, no
162 Option-key mapping, no window manager, and no network path.
163
164 | Cell | Status | Why it cannot be automated here |
165 | --- | --- | --- |
166 | iTerm2 — rendering, colors, wide glyphs | UNRUN | Emulator-specific renderer and font shaping |
167 | iTerm2 — `Cmd+V` paste, `⌥` chords, Cmd-forwarding config | UNRUN | Depends on the emulator's own key remapping |
168 | macOS Terminal.app — 256-color fallback appearance | UNRUN | The visual result of the tier, not the emitted bytes |
169 | macOS Terminal.app — Option-as-Meta behaviour | UNRUN | Emulator preference, not a byte protocol |
170 | WezTerm — kitty keyboard protocol chords (Ctrl+Shift+A/O/T) | UNRUN | Requires the emulator's enhanced-keyboard implementation |
171 | WezTerm — mouse selection and OSC 52 clipboard | UNRUN | Requires a real clipboard and window system |
172 | Real SSH session — paste, latency, resize propagation | UNRUN | Needs a real network path and remote tty |
173 | tmux inside a real emulator — nested capability negotiation | UNRUN | Nested terminal behaviour, not reproducible in a bare PTY |
174 | Windows Terminal / ConPTY — input plumbing | UNRUN | The PTY suite is `#![cfg(unix)]`; ConPTY needs its own audit |
175 | System notification banner appearance | UNRUN | macOS attributes it to the unbundled `osascript` host (#4847) |
176
177 Physical observations, when they are made, belong in a separate record. Do not
178 fold a one-off visual observation into the PASS tables above — a flaky or
179 unrepeatable observation carries different weight than a test that runs on
180 every change, and merging them would make both less trustworthy.
181
182 ## Known gaps
183
184 - Provider-dependent modals (model/provider/fleet/setup/session/worktree) have
185 no automated open/close cell yet.
186 - The matrix is Unix-only.
187 - `NO_COLOR` has boot coverage, not behavioural coverage, because it is not
188 currently a contract.
189
189 lines MARKDOWN