fix(pi): show an extension dialog's title once#22463
Problem
A Pi extension that asks with ctx.ui.select(title, options) or ctx.ui.editor(title) shows the whole title twice in the pending question panel, once in the header row and again as the question. An input dialog without a placeholder does the same. A long or multi-line title, like the ones rpiv-ask-user-question sends, also fills the header row, runs past the card edge and leaves no room for the collapsed preview.
Pi's RPC select and editor requests carry only a title (plus options or prefill), with no message or placeholder. piQuestion set header: title and fell back to question = title, so both fields got the same text.
Change
When a Pi dialog has no message or placeholder, piQuestion now shows the title once, as the question, under the "Question" header that the Codex, Grok and Antigravity adapters already use when a question has no header of its own. An input dialog with a non-blank placeholder keeps its title as the header, as before. A blank placeholder counts as none, as main does since #23235, so that dialog also shows its title once under "Question". Answers are keyed by question id, so the responses sent to Pi do not change.
This covers the duplicated title in #17895. On current main the expanded question already keeps its line breaks (screenshots below), so this PR does not touch the renderers.
Scope and approval
Fixes the duplicated-title part of #17895, which has no maintainer triage yet. I'm submitting it as a very small, focused fix for an obvious bug. The panel showed the same text twice, and the issue's expected behavior is that the title appears once. The change touches only the line the issue's diagnosis names. It doesn't change any default, setting or answer sent to Pi. Prior PRs: none. No open or closed PR references #17895 or changes piQuestion.
Verification
- Focused tests, from
packages/provider-pi:vp test run src/server/adapter.test.ts -t "input dialog|empty select option|editor prefill". The existing select and editor tests now also assert the header and question, and a new test pins an input dialog with a placeholder. With main'sadapter.ts, the two title-only cases fail because the header is the title, and the placeholder case passes. With this change all three pass. - Full
packages/provider-pisuite: 128 of 128 pass.tsc-rs --noEmitexits 0.vp fmt --checkpasses on both files.vp lintexits 0 with main's existing unusedlayerwarning. - Real Pi in the web app. I ran Pi 1.3.0 in RPC mode with one extension command,
/merge-target, that callsctx.ui.selectwith a five-line title and the optionsmainanddev, plus a placeholder local model entry so T3 lists Pi as available. No model request is made. Pi sends{"method":"select","title":"Merge target\n\nWhich branch should this change merge into?\n\n...","options":["main","dev"]}with nomessageorplaceholder. The T3 server and web dev server ran from source with a fresh state dir, once with main'sadapter.tsand once with this branch, in Chrome 155 at 1920x1081. Each time I picked/merge-targetfrom the composer menu, sent it and captured the panel as it opened, then clicked the header row to collapse it.
Main, panel open: the whole title is the header row, running past the card edge over the toggle icon, and it repeats as the question.
Main: the full title fills the header row and repeats as the question
This branch, panel open: the header reads "Question" and the title appears once, with its line breaks.
Branch: the header reads Question and the title appears once
Main, collapsed: the header row still holds the full title, so the preview has no room.
Main collapsed: the header row holds the full title
This branch, collapsed: "Question" followed by a truncated preview of the title.
Branch collapsed: Question followed by a truncated preview
This branch, after clicking main: the panel closes and the extension's own notice, "Merge target: main", shows that Pi received the answer.
Branch: after answering, the thread shows the extension's notice Merge target: main
Focused tests with main's adapter.ts, captured before #22557 deleted the bare input test: three failures, each with the title where "Question" was expected.
Focused tests on main: three header assertions fail
Focused tests on this branch, same earlier run: all four pass.
Focused tests on this branch: all four pass
Not checked:
- Mobile.
PendingUserInputCardrenders the sameheaderandquestionfields, so I expect the same result there, but I did not run a simulator or emulator. - Pi
editorand bareinputdialogs in the app. A unit test coverseditor; bareinputtakes the same no-message, no-placeholderbranch. - The
rpiv-ask-user-questionextension itself. The test extension sends the same request shape, a multi-linetitlewithoptions. - The desktop app, Windows and macOS.
Generated with Claude Opus 5.5 in T3 Code Nightly
Checks
Added 1 commit
- fix(pi): show an extension dialog's title onceeb2deca
Approvability
Verdict: Approved at eb2deca
Macroscope's review found this PR approvable — This narrowly fixes duplicated extension-dialog titles in the Pi provider by separating titles from messages/placeholders while preserving request and response behavior. Focused tests cover title-only dialogs and placeholder-based inputs.
You can add or adjust custom eligibility rules. Learn more.
No actionable comments were generated in the recent review.
ℹ️ Recent review info
⚙️ Run configuration
- Source mode: Auto
- Repository: pingdotgg/t3code
- Branch: main
- File: .coderabbit.config.ts
- Commit: 24c063a
- Layers: Repository TS config
- Review profile: CHILL
- Plan: Advanced
- Run ID:
bc5157b8-bf6e-479d-b169-b49d689dd024
📥 Commits
Reviewing files that changed from the base of the PR and between f64f2b2525341a2dcdc07af8f820119d036444ce and 6d68bed0d3cd7598af52c15f17cb612ed2774a9c.
📒 Files selected for processing: 2
packages/provider-pi/src/server/adapter.test.tspackages/provider-pi/src/server/adapter.ts
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 5 remain after this review.
Walkthrough
The Pi adapter now maps dialog titles and nonblank detail text to question headers and question text. Tests cover input, select, and editor dialogs.
Changes: Pi dialog question labels
Layer / File(s) Summary Dialog question mapping
packages/provider-pi/src/server/adapter.ts,packages/provider-pi/src/server/adapter.test.tspiQuestionuses the title as the header and the first nonblank message or placeholder as the question when detail text exists. Otherwise, it uses "Question" as the header and the title as the question. Tests cover input, select, and editor dialogs.Estimated code review effort: 2 (Simple) | ~10 minutes
Change: Bug fix
Suggested reviewers:
juliusmarminge
Merge Risk: External image Minimal · up to 6d68b
The dialog presentation change is ready to merge with no identified user-facing or operational risk.
Architecture risk: External image Low · up to f64f2
The change affects 1 system.
Changed systems:
packages/provider-piArchitecture concerns No architecture-level concerns identified.
Review details
Systems and components
- observed — packages/provider-pi (library) was modified; 2 changed files map to changed impact.
Before / after behavior
- observed — Modified behavior in packages/provider-pi/src/server/adapter.ts:
piQuestionnow avoids repeating the title: dialog message or placeholder text becomes the question, the header uses the title, and dialogs without detail use"Question"as the header while retaining the title as the question. - observed — Modified behavior in packages/provider-pi/src/server/adapter.test.ts: Added a test asserting that an input dialog maps
titleto the question header andplaceholderto the question text. - observed — Modified behavior in packages/provider-pi/src/server/adapter.test.ts: Updated the select-dialog test to expect the default
"Question"header while retaining"Pick a tag"as the question text. - observed — Modified behavior in packages/provider-pi/src/server/adapter.test.ts: Updated the editor-dialog test to expect the default
"Question"header.
Pre-merge checks: Passed 4
Check name Status Explanation Title check Passed The title is concise, specific, and accurately summarizes the primary change: preventing a Pi extension dialog title from appearing twice. Linked Issues check Passed Check skipped because no linked issues were found for this pull request. Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request. Description check Passed The description includes all required sections: Problem, Change, Scope and approval, and Verification. It explains the bug, the implementation, the approval rationale for the focused fix, focused and … Full details: Description check
Explanation
The description includes all required sections: Problem, Change, Scope and approval, and Verification. It explains the bug, the implementation, the approval rationale for the focused fix, focused and broader test results, manual web-app verification, screenshots, and unverified platforms or scenarios.
Finishing Touches
🧪 Generate unit tests (beta)
- Create a new PR
- Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Added 2 commits
Note
Written by gpt-6.1-sol on behalf of Maria
Verified in a real dev environment
Verified unchanged head 6d68bed0d3cd7598af52c15f17cb612ed2774a9c on this Linux Boat VM with repository Playwright and /usr/bin/google-chrome at 1440x1000. Projects and threads were created through the app against a real git repository. No mocks, injected protocol events, or demo fixtures.
I reproduced the duplicate on fetched github/main (15fe283272) in /home/user/main, stopped that server, then ran the exact PR head. Both used the same prompt, question, options, repository and viewport. The older PR head has unrelated UI differences from current main.
Real Pi 1.4.0 ran the published @juicesharp/rpiv-ask-user-question 2.15.0 extension with gpt-6.1-sol through the VM proxy. Main repeated [Merge target] Which branch should this change merge into? in the pending panel header and body. This head shows Question as the header and the title once as the question. Collapse and expand preserve the question and options. Selecting main reached the extension and the model replied main.
Neighboring behavior passed: a custom answer in the select panel returned release-candidate; choosing Type something. opened the real blank-placeholder input with its multiline title shown once, and submitting release-candidate-2 returned that exact answer. Separate Codex gpt-6.1-sol turns completed on both revisions with codex verification ready.
Runtime/check transcript:
| 1 | git worktree add ../main github/main |
| 2 | (main) timeout 600 vp i |
| 3 | (main) background vp run dev: server 16733, web 8693 |
| 4 | main: duplicate pending-panel title reproduced; answer main returned |
| 5 | stop captured main process group before starting PR |
| 6 | (PR) timeout 600 vp i |
| 7 | (PR) background vp run dev: server 13773, web 5733 |
| 8 | PR: Question header; main, release-candidate, release-candidate-2 returned |
| 9 | blacksmith-check run: vp i && cd packages/provider-pi && |
| 10 | vp test run src/server/adapter.test.ts -t "input dialog|empty select option|editor prefill" |
| 11 | 4 passed, 84 skipped by the name filter |
| 12 | latest head CI: no failing or pending checks; some jobs skipped |
Before, current main:
main duplicates the pending question title
After, exact PR head:
pr shows Question and the title once
Collapse, expand, answer selection and the returned answer:
real pi question interaction and returned main answer
Real extension input with a blank placeholder:
blank-placeholder input preserves the multiline question without duplicating its title
I inspected the captures and checked uploaded video playback through its end and seeking.
Not verified
Native iOS/Android, Electron, macOS/Windows, remote/relay/tunnel connections, other provider adapters, Pi editor dialogs and nonblank-placeholder inputs in the running app. Focused adapter checks cover editor prefill and placeholder input mapping, but do not replace runtime checks.
The initial Pi Responses API configuration failed before any dialog with an incomplete empty upstream response. Configuring the proxy with its working chat-completions API resolved it; successful Pi checks used that configuration. No application fix or push was needed.