# Read-only proof audit, pass 1

Issue [#16821](https://git.cafe/pingdotgg/t3code/issues/16821). Branch `fix/issue-16821`; audited [4fc8ee4714](https://git.cafe/pingdotgg/t3code/commit/4fc8ee471464d8ff540d014010424e348e5be392) against [a12d6ed942](https://git.cafe/pingdotgg/t3code/commit/a12d6ed942). No PR. Source inspection only: no browser, server, tests, edits to checkout, or commits. All empirical rows below are REQUIRED / NOT EXECUTED by this reviewer; none is a claim of a passing result.

## Scope and derivation

The entire diff is in [ComposerPendingUserInputPanel.tsx](https://git.cafe/pingdotgg/t3code/blob/4fc8ee4714/apps/web/src/components/chat/ComposerPendingUserInputPanel.tsx): option native buttons become `div role=button`, gain `select-text`, manual Enter/Space handlers, explicit tabIndex/aria-disabled, and a click guard using the current DOM selection. The sole runtime caller is [ChatComposer.tsx](https://git.cafe/pingdotgg/t3code/blob/4fc8ee4714/apps/web/src/components/chat/ChatComposer.tsx), around line 3552. It supplies permission-disabled state, merged responding state, drafts, question index, toggle, advance and dismiss callbacks. Its drawer gives approvals precedence. Settings/command palette have no alternate pending-question renderer. Numeric shortcuts are a separate listener in the same card, so they still need behavioral coverage.

[usePendingThreadRequests.ts](https://git.cafe/pingdotgg/t3code/blob/4fc8ee4714/apps/web/src/components/chat/usePendingThreadRequests.ts) handles draft toggles, displaced custom answers, question progression and final submission. A single option arms a 200ms advance; a multi option only toggles. Question prose uses ChatMarkdown, unchanged but explicitly part of the reported defect. Expanded options, selected/check versus unselected/shortcut, single/multi hint, optional description, disabled/responding styling, and keyboard focus are affected or adjacent observable branches. Empty and collapsed cards render no changed option DOM.

Web served locally, public web connected remotely, and Electron use this shared web renderer; transport adds no different option markup. Do not multiply every provider or connection mode: one real Claude request reaches the changed renderer, while provider normalization/contracts are unchanged. Codex/Cursor/Grok/OpenCode/Antigravity need no duplicate visual fixtures for identical normalized questions. No server, contracts, permissions, scheduler or MCP API changes. Native mobile uses a separate, unchanged [PendingUserInputCard.tsx](https://git.cafe/pingdotgg/t3code/blob/4fc8ee4714/apps/mobile/src/features/threads/PendingUserInputCard.tsx); 390px web coverage is not RN coverage.

## Two reusable fixture shapes

**F-M (multi):** expanded real Claude AskUserQuestion with at least three options: long label plus distinct multiline description containing a harmless ticket/command/path; one missing/empty description; one description equal to its label (suppressed). Put question prose and inline code in the question. Show one selected and one unselected option together. Set an explicit option value unequal to its label on one option if the provider supports it; otherwise cover value fallback by source inspection. Add a tenth short option only for a compact tail check: index 10 has neither digit badge nor numeric shortcut, yet remains clickable/focusable. No separate screenshot for each option.

**F-S (single, two questions):** same mixed content, multiSelect false; first question nonterminal, second terminal. This exposes optimistic check, 200ms next-question advance, reset of selection by question identity, and final response. Reuse it with permission disabled and with response in flight; those are distinct guards even though they share visual styling. F-M covers selected/unselected draft toggles without consuming the request. A custom answer can be added transiently for selection displacement rather than creating a third fixture.

Use a stable viewport height (e.g. 900px), browser zoom 100%, record actual viewport and scroll position. For 390px ensure long text wraps without horizontal overflow and badges/checks stay visible. Scroll to the tenth option if necessary.

## Scaled minimum matrix

A = changed DOM/handler; N = nearby behavior explicitly needed by issue or changed callers; U = unaffected boundary. Every interaction/time row MUST have a recording, including keyboard, copying, blocked actions and state transitions. Clipboard proof means paste into a harmless scratch field and visibly compare the exact selected text; selection highlight or a clipboard read alone is insufficient.

| ID | Affected | Fixture / state and coverage | Minimum placement | Required evidence / acceptance |
|---|---|---|---|---|
| V1 | A+N | F-M expanded; long question/label/description; absent and equal descriptions; hint; selected check + unselected digit | 1440 light, 1440 dark, 390 light, 390 dark | Four matched before/after still pairs. No clipping/overflow, duplicated equal description, lost check/badge, or theme contrast regression. Same content/state on both revisions. |
| I1 | A+N | F-M drag within label and description, then question prose/inline code; cross-option drag and reverse-direction drag | 1440 light | Before/after recording: show original failure and fixed exact copy/paste. Selection must not toggle options, replace custom answer, advance question, or submit. Include pointer release and enough idle time to expose accidental 200ms advance. |
| I2 | A | F-M click label, description and empty row area; toggle on/off; select text then plain click same option and another option; clear selection and retry | 1440 light | Recording. Ordinary click toggles once; drag never toggles. Establish whether stale selection blocks a later ordinary click. Also try a selection wholly outside the option; unrelated selected text must not disable clicks. |
| I3 | A+N | F-S click first option; choose on final question; repeat using Enter and Space on restored/reissued request | 1440 light | Recording through optimistic check, approximately 200ms advance and final response. Exactly one intended question/response transition; no drag-induced submission. Fresh request per terminal run. |
| I4 | A | F-M Tab/Shift+Tab, focus ring, Enter, Space, held Space/key repeat; digit 1/9; option 10; Ctrl+C | 1440 dark | Recording: focusable accessible button role/name, Enter/Space toggle once per intended activation and Space does not scroll. Check held-key repeats do not silently toggle repeatedly; digit shortcuts work outside input and do not fire while typing in composer/input/contenteditable or using modifiers. Tenth option remains keyboard reachable; no digit-0 shortcut. Clipboard chord does not activate. |
| I5 | A | Same card with disabled=true, then isResponding=true, separately | 1440 light | Recording for each state (may be one file): opacity/cursor, aria-disabled=true, tabIndex=-1, Tab skips options; click and already-focused Enter/Space/digit cannot change answer or submit. Drag/copy still works. Use a real permission-limited connection for disabled; capture actual in-flight response for responding. Never infer one guard from the other. |
| I6 | A+N | F-M at narrow viewport: wrap, drag label+description, copy/paste, click; then F-S advance | 390 dark | Recording. Narrow scrolling/selection does not accidentally answer or drag the card; no horizontal overflow; copy exact text and normal click/advance still work. This is responsive web, not native touch or RN proof. |
| I7 | N | Collapse/reopen, change question, custom-answer-to-option replacement; empty/no-active question; not_resumable versus message response capability | 1440 light | One recording assembled from labeled real states: collapsed hides options and disables digit shortcuts; reopen preserves draft; custom answer survives in composer when displaced; question change clears optimistic marker/timer. No card when no active question. For not_resumable, no actual response; message-capable stopped turn remains answerable. Source-only fallback must be labeled unverified. |
| B1 | A, unavailable | Electron on macOS, including native drag and Cmd+C (issue's reported platform) | Desktop width, either theme | UNVERIFIED: Linux VM has no usable Electron/macOS client. Web recordings cannot certify OS clipboard, Electron ancestor CSS or native shortcut behavior. Later desktop drag/copy/keyboard recording required to close this platform gap. |
| B2 | U | Native mobile separate RN component | None | Unchanged; no RN fixture or capture required for this diff. Explicitly outside proof. |
| B3 | U/shared | Other providers, local/relay/tunnel, Settings/command palette | None | No distinct option renderer or changed wire contract. Reuse normalized web evidence, do not claim actual connection-mode/provider testing. Approvals take precedence; avoid an approval obscuring the test question. |

V1 includes both selection branches in one frame, F-M includes both description rendering outcomes, and F-S reuses the markup for single-select. No single/multi × description × theme × width cross product is justified. Disabled/responding share appearance but require separate interaction observations. Existing navigation browser tests exercise hook persistence with mocked ChatMarkdown, not this DOM selection/keyboard change; they do not replace recordings.

## Minimum capture set

- **8 stills:** V1 four viewport/theme combinations on base plus four on head. Take them from synchronized points in recordings if practical. No extra stills for B rows.
- **8 labeled recordings:** one base 1440-light reproduction; one head 1440-light F-M session (I1/I2 and obtainable I7 transitions); three head F-S runs for click/Enter/Space including first and terminal questions (I3); one head 1440-dark keyboard session (I4); one head guard session with separately labeled disabled/responding segments (I5); one head 390-dark responsive session (I6). Segmenting a single underlying recording is acceptable, provided every required observation remains visible. If I7 states cannot be reached in the F-M session, add a recording or explicitly leave them unverified. No time/interaction row is satisfied by a still.
- Capture actual in-flight state rather than artificially holding a spinner and calling it verified. If it is too brief, report that coverage gap; controlled latency can aid a later explicitly authorized real-client test, but is not part of this read-only pass.

## Real-app seeding and current availability

The lead reports an existing real app on port 6226, environment `08883e64-44b3-4439-82b8-5e6e98d81392`, thread `bd3e6c1e-ed9b-4469-947a-6f0f64059e6c`, with a live Claude multiselect request. This reviewer did not connect to or consume it. Reuse it for F-M after checking its actual option shape; its existence does not establish missing/equal descriptions, ten options, disabled state or single-select coverage. Have the real provider emit another harmless AskUserQuestion for missing fixture shapes, without running project commands. Preserve the multiselect request until copy/drag captures finish; single-select terminal actions consume their request and need a reissued question for repeat methods.

Read-only SQLite inspection of `/tmp/issue-16821-evidence/baseline.sqlite` found 16 V2 thread projections and two user-input runtime request projections: one pending and one resolved. Matching user-input turn items include a two-option single question and a two-option multi question, both with distinct descriptions. The snapshot therefore offers real persisted question content/history, but does not cover optional-description branches, disabled/responding or ten-option tail. It contains provider-native request references and response capability references; copied rows cannot recreate an in-memory live provider callback. Do not upload the DB or its payloads.

For a separate isolated test environment, use the repository's migrate-dev-db workflow only with that destination dev server stopped. It copies from a read-only source into worktree `.t3/userdata/statev2.sqlite`, prunes live/recoverable thread families, and clears scheduler/outbox/session/auth execution state. That safety filtering means the desired live request may not survive as an answerable fixture. Never start against live `~/.t3/userdata`, symlink to it, restore the snapshot over the lead's running database, or enable copied pending work. Start the isolated app using the normal single-origin dev configuration and generate a fresh real provider question there. Do not set VITE_HTTP_URL/VITE_WS_URL. A component harness or edited projection can supplement a difficult state, but must be labeled harness evidence rather than real-app proof; it does not fulfill I5's real guard evidence.

## Review risks to resolve with evidence

1. The new click guard tests the global current selection, not whether this particular gesture dragged. A retained selection intersecting the option might suppress a later intended click; I2 must determine actual Chromium behavior.
2. Space now activates on keydown rather than native button keyup; repeated keydown can toggle repeatedly in multiselect. I4 explicitly checks this parity risk. Enter repeat and focus after a single-question transition merit observation in I3.
3. Manual click/key handlers call a helper that checks disabled/responding; aria-disabled alone does not block events. Guard recordings are required. The broader not_resumable distinction predates the diff: numeric listener honors responseDisabled, option helper does not include canRespond, and parent advance refuses final response. Report any observed inconsistency with baseline attribution rather than assuming it is introduced here.
4. Question prose is unchanged. The issue requires selecting it too, so verify actual ancestor CSS and markdown selection rather than treating the option div conversion as proof for the whole report.

FRICTION
coverage|Desktop/Electron unavailable on Linux VM; no browser or dev server authorized for this pass|Keep desktop explicitly unverified and obtain later native recording; do not substitute web proof.
fixtures|Snapshot has persisted questions but cannot prove live provider callbacks or missing conditional states|Use the lead's live request and fresh real AskUserQuestion fixtures; keep copied state isolated.

Pass 1 complete: matrix derived from source and read-only snapshot; behavioral proof remains pending.
