PR #8247: the pull request page, end to end

Everything on branch review-pane-ui as of 2026-10-07, captures retaken on the pushed head (6be4faa04a): the page redesign, the host procedures behind it, the bot review round and what was done with it, and the pane responsiveness pass. All captures are from the dev app over CDP against real pull requests on this repository. Open the PR.

1. Summary layout and info rail

The route and the workspace pane now share one page: a top bar (Summary / Changes tabs with the diff size, copy link, open on GitHub, Send to agent, and a split primary pill), a header (state pill beside "repo #n", a large title, author · age · head → base, labels), the description as prose, and an info rail with merge status, comments, reviewers, and checks. The rail is a sticky right column while the body is at least 52rem wide and folds into label/value rows under the header below that. The switch is a container query on the body itself, so a narrow window and a split pane fold the same way.

Summary with the info rail column
Full-width Summary: header, prose, and the rail as a column.
Summary with the rail folded into rows
Below 52rem the same facts render as rows under the header; Checks collapses to a one-line brief.

2. Loading states

The detail loads behind a page-shaped skeleton; a cold Changes click loads the diff renderer and its worker pool behind a file-card skeleton. Both share one shimmer: a viewport-fixed gradient sweep over the muted fill (background-attachment: fixed), 2s linear, off under reduced motion, so every placeholder on screen sweeps in sync instead of each one running its own loop.

Page skeleton
Page skeleton while the detail loads.
Diff skeleton
Changes while the diff loads, title row already in place.
Shimmer sweep mid-frame
Shimmer mid-sweep.

3. Description as page prose

The body renders through the shared markdown renderer with page-specific element renderers layered on top and an unlayered stylesheet for the scale (the renderer's own sheet is unlayered, so Tailwind utilities could not outrank it). Base 14px at 1.7 line height, 0.8rem block rhythm, headings at 600 weight with no underline rule, inline code as a pill, links in a dedicated blue because the desktop's primary token is near-foreground.

Prose scale
Prose scale: heading weights, list rhythm, inline code pills.
Task list
Task boxes in the gutter (#8238).
Table
Bordered table at natural width (#8234).

4. Changes tab

The Changes tab is lazy: the Summary stays mounted (hidden) and the diff tab mounts on first click. A title row (state glyph + PR title) sits above the files toolbar so the pane keeps its context when the Summary scrolls away. The diff viewer underneath is the shared code tab.

Changes tab
The diff for #8247.
Changes title row
Title row above the files toolbar.

5. Conversation and composer

The Summary ends in a Comments section: reviews and conversation comments sorted oldest first as collapsible rows (the last two open), each with avatar, author, a review-state label (approved / requested changes / reviewed), relative time, and a Reply link to the comment on GitHub. Bot findings that follow the title-plus-severity convention are lifted into a title and a colored severity line. Below the rows, a "Leave a comment" pill posts as the gh user through the host and invalidates the detail so the new comment appears on the refetch.

Conversation
Conversation on #8247: bot reviews and comments as collapsible rows, composer at the end.

6. Draft flow and menus

A draft pull request gets Ready for review as its primary pill, and the header pill becomes a Draft / Ready for review state menu. The split pill's chevron opens a menu in a frosted picker skin (14px radius, 40px blur, 10px rows at 12px) with a glyph on every row:

Draft PR
Draft #8243: Ready for review split pill, Draft state pill in the header, no Merge offered.
Merge menu
The merge menu on #8247: comment field, three methods with descriptions, Force merge while a check is failing, Fix findings, Convert to draft, Close.
Draft state menu
The header pill's Draft / Ready for review menu on #8243, with the rail beside it.
Top bar icons
Top bar: chain-link copy (with a check on success), arrow-out open on GitHub, Send to agent.

7. Color and icon consistency

Every green and red on the page (diff stat, state glyph and pill, check marks, merge status) reads one shared constant that the sidebar list row also uses, so the page and the list are the same tone in light and dark. Draft pull requests now show the dashed draft glyph in the list and in every status chip; before this the shared icon drew open and draft with the same arrow.

Draft glyph in the list
The list beside the page: draft #8243 with the dashed glyph, open rows with the green arrow, the same green and red as the page's diff stat and checks.

8. Host service

Writes go through the host's gh, like merge and close already did, so no GitHub App is needed. Every tRPC change is additive and optional for older clients.

ChangeShape
pullRequests.addCommentNew. One REST request: gh api --method POST repos/o/r/issues/n/comments --input - with the JSON body on stdin. gh pr comment posts over GraphQL and then reads the comment back, and GitHub failed that read once after the comment had landed, so the host reported a failure for a successful write. Stdin also keeps a long body out of argv. Evicts the content cache so the refetch carries the comment.
pullRequests.setDraftNew. gh pr ready [--undo]; the host records the draft flag on linked rows before the refetch lands so sidebar chips agree.
getContent / getContentByRepoSame input; the output gains mergeability, merge-state status, diff counts, review decision, reviewers (requested plus everyone who submitted a review; pending reviews excluded), comments with review state and URL, labels, and merged/closed times. All optional; the desktop treats an older host's answer as "unknown", not "empty".

Tests: add-comment, set-draft, the ready/draft branch of the post-write sync, and a populated-payload normalizer case (ghost author, pending review dropped, team review request without a login, reviewer de-dupe, negative and fractional counts).

9. Review round

Greptile posted 7 inline threads, CodeRabbit 5, and the superset-home review one summary with 8 points. Two independent review passes of my own (host side and renderer) overlapped heavily with them. Verdicts, each checked against the code before acting:

FindingVerdictOutcome
CI Test red: fixtures render English text in an i18n-enforced dirvalidMocks render a test id
Favicon lookups send every external hostname to GooglevalidFavicon from the link's own origin, Globe fallback
Host review decision dropped on the way invalid (×2)Mapped to the values the Reviews section checks
Markdown pre-pass rewrote inline code and turned br into a soft breakvalidPre-pass strips comments only; code spans protected
Page stylesheet only targets the default markdown stylevalidRenderer pinned to the default style
Check rows have no accessible outcome textvalidScreen-reader status label per row
mutateAsync().finally() leaves an unhandled rejectionvalid (×2)mutate with onSettled
Fix findings skips comment findings and quotes approvalsvalid (×2)Comment findings included, approvals filtered; inline review threads still a follow-up
Narrow pane clips: the scroll viewport's display:table wrappervalid, confirmed liveWrapper forced to block; no overflow at 411px
Duplicate React key in the skeletonvalidKeyed by index
Fix findings can open a duplicate workspace before the linked lookup landsvalidMutation resolves the lookup first
commits fetched but unreadvalidDropped from the host read
French / Polish / Russian wordingvalidFixed
Force merge calls the same endpoint as Mergetechnically right, declinedGitHub decides bypass by permissions; the item only adds a warning confirmation when checks fail
"Send to agent" only opens the New Workspace modalnot a bugIntended: it pre-fills a workspace draft on the PR
Folder placement and multi-component files (own reviewer)valid, deferredA mechanical reshuffle of most of the diff; waiting on a call

Extra from the host review: a very long comment body is now piped over stdin, and the pending-review author no longer counts as a reviewer.

10. Pane responsiveness

The workspace pane got its own pass with before/after captures at 320, 400, 520, and 570px: a one-row top bar that compacts in stages (diff stat hidden below 30rem, Merge and Ready labels fold to the glyph below 23rem), the title stepping 1.75rem → 1.5rem → 1.25rem, gutters 24px → 16px, a narrower info-row label column, and now no side gutters at all around the diff below 40rem. The full comparison is its own page: PR pane responsiveness: before and after.

320px pane before
320px pane, before.
320px pane after
320px pane, after.
320px Changes with no side gutters
320px Changes with the gutters removed.

11. Removed for now

Two surfaces were built, verified, and then taken out at your request to keep the page focused. The host already returns what they need, so either can come back as its own PR.

Timeline tab
Timeline tab: commits, reviews, and comments in one feed.removed
Ask composer
"Ask about this pull request" composer with an agent picker.removed

12. Status and verification

GateResult
Desktop typecheckexit 0
Host-service typecheckexit 0
Desktop PR suites and PullRequestPane fixtures51 pass
Host pull-request suites75 pass
check:i18nclean; new strings translated in all 17 locales
Live over CDPSummary wide and folded, skeletons, Changes, prose features, conversation, draft and merge menus, top bar, five pane widths
Not exercised livePosting a comment, Ready for review / Convert to draft, Merge, Close, Fix findings, Resolve conflicts (each writes to a real PR or launches an agent)

Branch state: seven commits after the PR's last push (review follow-ups, host test coverage, the pane pass, the diff gutters). Nothing pushed and no review thread replied to yet; both are waiting on a go-ahead.