# fix\(web\): pending questions read as a question-first menu · PR \#23855 · pingdotgg/t3code

[View on GitCafe](https://git.cafe/pingdotgg/t3code/pull/23855)

Repository: [pingdotgg/t3code](https://git.cafe/pingdotgg/t3code)

Visibility: public

State: open

Source repository: pingdotgg/t3code

Source branch: fix/pending-question-ui

Target branch: main

Head commit: a39c1c2eae37b5a439de0a44f53ae4d9005624fd

[Discussion and reviews](https://git.cafe/pingdotgg/t3code/pull/23855)

[Commits](https://git.cafe/pingdotgg/t3code/pull/23855/commits?format=markdown)

[Files changed](https://git.cafe/pingdotgg/t3code/pull/23855/diffs?format=markdown)

## Description

---

Pending questions read like a form: a heavy title row, divider lines, keycaps everywhere, and "Write my own answer" sat in the list as if it were a pickable option. When an agent like Pi asks several one-question requests at once, the counter only counted inside each request, so it showed 1/1 three times.

This turns the panel into a question-first menu. The header is a quiet eyebrow on a subtle 3% band, the dividers are gone, and the selected row gets a highlight with the check on the right. On wide web, descriptions sit in a second column and stack under the label when the panel is narrow. The `m/n` count now spans question requests that are pending at the same time. Options flagged `customAnswer` show as a bottom hint with a down arrow, pointing at the composer, and can't be selected. Mobile gets the same treatment.

## Before / After

Web, dark theme, ~900px panel unless noted.

| | Before | After |
|---|---|---|
| a. Bare labels, generic header | ![](https://gh-file-drop-api-prod-wb6kniibetdm74b5.davis-benjamin41902.workers.dev/f/8e87292b672b7287/a-bare-labels-before.png) | ![](https://gh-file-drop-api-prod-wb6kniibetdm74b5.davis-benjamin41902.workers.dev/f/c32e5e24ee4b47e8/a-bare-labels-after.png) |
| b. Descriptions, header, selected | ![](https://gh-file-drop-api-prod-wb6kniibetdm74b5.davis-benjamin41902.workers.dev/f/01772f3ce1ce404e/b-descriptions-selected-before.png) | ![](https://gh-file-drop-api-prod-wb6kniibetdm74b5.davis-benjamin41902.workers.dev/f/9885d828cd6a6348/b-descriptions-selected-after.png) |
| c. Multi-select, two picked | ![](https://gh-file-drop-api-prod-wb6kniibetdm74b5.davis-benjamin41902.workers.dev/f/fd58657e533d7fda/c-multi-select-before.png) | ![](https://gh-file-drop-api-prod-wb6kniibetdm74b5.davis-benjamin41902.workers.dev/f/c9999d21ce4b3267/c-multi-select-after.png) |
| d. Queue position (2/3) | ![](https://gh-file-drop-api-prod-wb6kniibetdm74b5.davis-benjamin41902.workers.dev/f/54e08d0e66e39187/d-queue-position-before.png) | ![](https://gh-file-drop-api-prod-wb6kniibetdm74b5.davis-benjamin41902.workers.dev/f/87309e161d87f85c/d-queue-position-after.png) |
| e. Collapsed | ![](https://gh-file-drop-api-prod-wb6kniibetdm74b5.davis-benjamin41902.workers.dev/f/beb8f390a30e921c/e-collapsed-before.png) | ![](https://gh-file-drop-api-prod-wb6kniibetdm74b5.davis-benjamin41902.workers.dev/f/30939266f211ef07/e-collapsed-after.png) |
| f. Narrow (414px) | ![](https://gh-file-drop-api-prod-wb6kniibetdm74b5.davis-benjamin41902.workers.dev/f/f7888aebec9bc934/f-narrow-before.png) | ![](https://gh-file-drop-api-prod-wb6kniibetdm74b5.davis-benjamin41902.workers.dev/f/199cc059679109f4/f-narrow-after.png) |
| g. Overflow (10 options) | ![](https://gh-file-drop-api-prod-wb6kniibetdm74b5.davis-benjamin41902.workers.dev/f/5e637db2c5362ce4/g-overflow-before.png) | ![](https://gh-file-drop-api-prod-wb6kniibetdm74b5.davis-benjamin41902.workers.dev/f/0004c1b8aa281b25/g-overflow-after.png) |
| h. Light theme | ![](https://gh-file-drop-api-prod-wb6kniibetdm74b5.davis-benjamin41902.workers.dev/f/e356ba380d1c454c/h-light-descriptions-before.png) | ![](https://gh-file-drop-api-prod-wb6kniibetdm74b5.davis-benjamin41902.workers.dev/f/57250fce14ad107b/h-light-descriptions-after.png) |
| i. Dismissible | ![](https://gh-file-drop-api-prod-wb6kniibetdm74b5.davis-benjamin41902.workers.dev/f/573d7ce815afbec2/i-dismissible-before.png) | ![](https://gh-file-drop-api-prod-wb6kniibetdm74b5.davis-benjamin41902.workers.dev/f/c0f1e3f33646afb7/i-dismissible-after.png) |

In (d), "before" is three questions in one request. "After" is three simultaneous one-question requests, which used to show 1/1.

## Important files

- `apps/web/src/components/chat/ComposerPendingUserInputPanel.tsx`
  - New question-first layout: eyebrow header band, highlighted selection with a right-side check, a description column that stacks when narrow, and custom-answer options as a non-selectable hint.
- `packages/client-runtime/src/state/threadRequests.ts`
  - Pending user-input requests whose pending intervals overlap form one queue. Each request gets `queueQuestionIndex`/`queueQuestionTotal`. Also exports `isGenericUserInputQuestionHeader` so generic "Question N" headers are hidden.
- `apps/web/src/pendingUserInput.ts`, `apps/mobile/src/lib/threadActivity.ts`
  - Custom-answer options can't be toggled or resolved as a selected answer. The answer comes from the composer.
- `apps/mobile/src/features/threads/PendingUserInputCard.tsx`
  - The same treatment on mobile: header band, quiet selection, custom-answer hint, and the cross-request count.
- `packages/contracts/src/providerRuntime.ts`, `packages/contracts/src/orchestrationV2.ts`, `apps/server/src/mcp/toolkits/thread/tools.ts`
  - New optional `customAnswer` flag on question options, mirrored in the MCP thread tool schema.

## Testing

- `pendingUserInput.test.ts` (17), both `ComposerPendingUserInputPanel.*.browser.test.tsx` (21), `threadRequests.test.ts` (14), mobile `threadActivity.test.ts` (36) and `reactCompilerCoverage.test.ts` (909): all pass.
- Typecheck is clean for web, mobile, and client-runtime. Lint has no errors; the only warnings, in `PendingUserInputCard.tsx`, are already on main. Formatting is clean.

The `customAnswer` contract flag also lands here. The Pi-side tools that set it follow in a separate PR.

Reviewed with sol-loop: 3 rounds with gpt-6.1-sol on high and claude-opus-5-5 on high. Sol verdict: approved. Opus verdict: approved.
Model: Claude Opus 5.5 via Claude Code (T3 Code)

🤖 Generated with [Claude Code](https://claude.com/claude-code)


