feat(web): pull request surfaces — filters & qualifiers, all-server listing, update branch, reactions, in-place editing, smarter diffs - #6039
Conversation
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
ApprovabilityVerdict: Needs human review Diff is too large for automated approval analysis. A human reviewer should evaluate this PR. You can customize Macroscope's approvability policy. Learn more. |
… summary An approval review is a verdict rather than a finding, so its comment no longer offers the fix-in-a-thread hand-off.
GitHub omits hunks for files it will not inline, so the viewer's hunk-sum counts read -0 +0. The host's own additions/deletions now ride the diff slice, and the code tab draws every file's counts itself.
Dismissing prior approval to re-evaluate 4095917
Dismissing prior approval to re-evaluate c4e4edc
Dismissing prior approval to re-evaluate 9b66581
Attribution is the avatar alone with the name on hover, the dot separators go, review states read as words (Changes requested), and a dismissed review no longer offers the fix hand-off.
…tions `reactions` was the one capability a client had to be sent. A page built after this field and a server built before it would fail to decode the whole detail — no title, no checks, no actions — over a flag whose absence only ever meant "this host has none". Optional now, like `updateMethods` and `edit` beside it, and read as no reactions wherever it is missing.
…rs back The rewrite read the pull request first and sent its reviewers back with the new words, which quietly undid whoever had been added or taken off in between. Bitbucket's PUT is a partial update — the reviewer write beside this one has always relied on it — so only the words that changed travel now, and the read is gone with them.
…nother A comment id belongs to its host: Azure DevOps numbers a remark inside its own thread, so "1:1" exists on every pull request. React keeps a component where the same key comes round again, so opening another pull request could hand an already-open editor a different remark — and saving would have written the first one's words onto the second. The editor now starts again whenever the words it was given change, the summary tab names the remark it is rewriting with the pull request it belongs to, and the timeline and diff conversations name theirs the same way.
…il0000/t3code into t3code/feat/pr-edit-description-comments
- Keep collapsed summary sections anchored while scrolling - Clarify actions targeting the current thread - Surface actionable, redacted GitHub rate-limit details
Pressing Update branch, which asks for a merge commit, printed the hint written for a rebase: "a rebase stops at the first commit that does not apply cleanly; updating with a merge commit may still work". The reader had already chosen the merge commit, so the one sentence under the failure told them to do what they had just done. gh reports only that it exited, so this hint is all there is to read. Each way of updating now has its own: the merge commit points at write access on the branch — a fork also needs its author to allow maintainer edits — and at a conflict with the base, and the rebase keeps the sentence about the commit it stopped on. The Merge button also read "Merging..." while the branch update ran, because one flag stood for every action in flight. It now names which action is running, so only the button that was pressed speaks; everything else is still disabled for as long as any of them runs.
## What's Changed * fix(web): align update toast release notes link by @t3-code[bot] in pingdotgg/t3code#6322 * fix(web): render tooltips above dropdowns by @extoci in pingdotgg/t3code#6241 * fix(web): open modified PR clicks in browser by @t3-code[bot] in pingdotgg/t3code#6278 * Fix mobile command popover glass rendering by @juliusmarminge in pingdotgg/t3code#6370 * test(mobile): seed snoozed showcase threads by @PixPMusic in pingdotgg/t3code#5155 * fix(web): preserve appearance mode when changing themes by @extoci in pingdotgg/t3code#6343 * feat(connect): deregister account environments from any client by @StiensWout in pingdotgg/t3code#4844 * feat(web): pull request surfaces — filters & qualifiers, all-server listing, update branch, reactions, in-place editing, smarter diffs by @Bil0000 in pingdotgg/t3code#6039 **Full Changelog**: pingdotgg/t3code@v0.0.34-nightly.20260812.1077...v0.0.34-nightly.20260812.1079 Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.34-nightly.20260812.1079
- PullRequestDetailPanel takes main's #6039 rework wholesale (reactions, update-branch, auto-merge, in-place editing); the thread-details action hooks stay in usePullRequestActions with label maps extended for the new action variants - CommandPalette #6330 provider subtitles adapted to the v2 shell (session -> runtime for provider instance and display name) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… PR panel The round-5 ChatView reconciliation kept the round-3-era PullRequestDetailPanel call, so the panel mounted without chromeVariant="collapse" — the #6039 scroll-condensing chrome never engaged and the description scrolled under a full-height chrome. Restore main's call exactly: collapse chrome, composerDraftTarget for same-thread hand-offs, the isThreadOwnPullRequest context check, and tab statuses keyed by the active surface id via updatePullRequestTabStatus instead of a key rebuilt from the status payload. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ions, in-place editing Ported from t3code b28f9bf (pingdotgg#6039). Two conflicts, both branding only: the unsupported-server copy and a filter test fixture keep Ronin naming. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Upstream 65b005f..9513e62, dominated by pingdotgg#6039 (pull request surfaces). 248 files, +21,639/-2,271. Seven conflicts; two were substantive. apps/server/src/git/GitManager.ts: pingdotgg#6039 edited the exact annotateLogs block that 0012043 rewrote yesterday. Both sides are wanted, so the per-branch dedupe gate stays and upstream's provider/providerOperation/providerCommand/ errorDetail annotations moved inside it. The dedupe KEY deliberately stays errorTag/causeTag: errorDetail carries raw command output that can vary run to run for one standing condition, and keying on it would restore the log storm the fix removed. Upstream's isSourceControlProviderError is a cleaner reader than the fork's tagOf(error.cause) and is now used for the annotations. apps/web/src/components/chat/ComposerPrimaryActions.tsx: the fork had hoisted the send button into a `sendButton` const; upstream restyled the same button inline for the stage-artwork backdrop. The fork's definition sits OUTSIDE the conflict region, so resolving as presented compiles and silently drops the restyle. Upstream's className branch and StageBackdropButtonArt span were applied into the fork's definition instead, keeping the fork's two deliberate deviations: `disabled` checks isSendBlocked (not isEnvironmentUnavailable) so a disconnected environment still queues, and the first aria-label branch reads "Queue message to send on reconnect". SidebarChrome.tsx took upstream's compact icon-row footer wholesale with the fork's two full-width panels (SidebarLocalModels, SidebarResourceQueue) re-inserted above the menu. The rest were unions: culori + diff in apps/web/package.json, @clerk/expo + the FORK-ONLY @effect/platform-node entry in pnpm-workspace.yaml, ComposerPrimaryActions.test.ts keeping both sides' helpers. pnpm-lock.yaml was seeded from upstream and regenerated. Audit over all 31 both-sides-changed files: fork-added lines that did not survive were 2, both intentional replacements above; the invariant-4 sweep for fork-deleted lines coming back returned zero. All six fork README invariants re-verified against the merged tree, including that pingdotgg#6039's three new PR RPC methods all carry write scopes in RpcAuthorization. Note for the next reconcile: BSD sed aborts on ChatComposer.tsx ("Assertion failed: (advance > 0), process.c:462") and truncates its input, so a sed-based audit reports that file's fork lines as missing en masse. The memory entry flags this file for wrapped grep; the failure is wider than grep. Read it in Python. pnpm verify green: EXIT=0, 14 projects, 879 test files, 8,625 tests, 0 failures. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017UxbQTnLQvk48Jvsrf9T6V
The rebased provider port grew reactions, update-branch and in-place editing. Arcanum declares the honest values: no update methods, no edit, and reactions false — the write endpoint is unverified, and that one flag is what offers the picker. The reactions the comments payload already carries are decoded read-only (grouped onto the contract's eight contents, unknown codes dropped the way GitLab's awards are), so pills render without ever being pressable. setReaction is the same never-called refusal the other non-reacting hosts answer with, and the arc CLI folds the new rate-limited exit kind into its command error as GitLab does.
- PullRequestDetailPanel takes main's #6039 rework wholesale (reactions, update-branch, auto-merge, in-place editing); the thread-details action hooks stay in usePullRequestActions with label maps extended for the new action variants - CommandPalette #6330 provider subtitles adapted to the v2 shell (session -> runtime for provider instance and display name) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… PR panel The round-5 ChatView reconciliation kept the round-3-era PullRequestDetailPanel call, so the panel mounted without chromeVariant="collapse" — the #6039 scroll-condensing chrome never engaged and the description scrolled under a full-height chrome. Restore main's call exactly: collapse chrome, composerDraftTarget for same-thread hand-offs, the isThreadOwnPullRequest context check, and tab statuses keyed by the active surface id via updatePullRequestTabStatus instead of a key rebuilt from the status payload. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- PullRequestDetailPanel takes main's #6039 rework wholesale (reactions, update-branch, auto-merge, in-place editing); the thread-details action hooks stay in usePullRequestActions with label maps extended for the new action variants - CommandPalette #6330 provider subtitles adapted to the v2 shell (session -> runtime for provider instance and display name) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… PR panel The round-5 ChatView reconciliation kept the round-3-era PullRequestDetailPanel call, so the panel mounted without chromeVariant="collapse" — the #6039 scroll-condensing chrome never engaged and the description scrolled under a full-height chrome. Restore main's call exactly: collapse chrome, composerDraftTarget for same-thread hand-offs, the isThreadOwnPullRequest context check, and tab statuses keyed by the active surface id via updatePullRequestTabStatus instead of a key rebuilt from the status payload. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
> [!NOTE] > `#123`, `GH-123` and `owner/repo#123` in a pull request body are now links, the way they are on > GitHub. What each number turns out to be decides where it opens: a pull request in a project on > this machine opens as a tab beside the one being read, an issue opens in a browser, and a number > the host has nothing under is underlined in red and still opens. GitHub's autolinked references are its own extension rather than GFM, so remark left them as they were: every `pingdotgg#6039` an agent or a reviewer wrote in a description, a comment or a review stayed plain text, and following one meant retyping it into a browser. Reading them is the easy half. Knowing what one *is* takes asking the host, and a body can name a dozen — so they are asked about together, in one GraphQL document, aliased by repository and by number. `issueOrPullRequest` answers which of the two each number turned out to be, which is what decides where it opens; a cache keeps a panel of many bodies from spawning a process per body for numbers a neighbour just resolved. The link is addressed at `/issues/{n}` before any of that comes back, because the host redirects that to `/pull/{n}` for a pull request — so it is already right in a browser, and resolving only ever improves it. A reference clicked before its answer arrives follows it as written. Everything else follows from one rule: **only an answer marks a link as broken.** A request that failed on the way — rate limited, logged out, offline — leaves every reference exactly as it was, and a null is read as nothing-there only where the host filed `NOT_FOUND` against it, since `FORBIDDEN` is SAML or an IP allowlist standing between the reader and something they can very likely open themselves. A host having a bad minute must not repaint a body full of good references as mistakes. Inert unless a surface passes the repository to read numbers against, so `#2` in a conversation — where it is far more likely to be a step than an issue — stays the plain text it reads as. ## Before / after The description of #126, which cites the stale-stack guard from `#95` and the window it was built for (`#94`). | Before | After | | --- | --- | | <img src="https://raw.githubusercontent.com/yngatech/t3code/assets/github-reference-links/before.png" alt="Reference numbers rendered as plain grey text" width="460"> | <img src="https://raw.githubusercontent.com/yngatech/t3code/assets/github-reference-links/after.png" alt="The same numbers rendered as links" width="460"> | `#95` resolves to a pull request and opens as a tab here; `#94` resolves to an issue and opens in a browser, since there is no issue surface to open it in. A reference the host has nothing under keeps its link colour and takes the mark an unknown word takes. No pull request in this repository happens to cite a number the repository lacks, so the state below was forced on a resolved reference to photograph it: <img src="https://raw.githubusercontent.com/yngatech/t3code/assets/github-reference-links/missing.png" alt="A reference underlined with a red wavy line" width="460"> ## Reading the commits Three layers, each of which stands alone: the plugin that reads references and renders nothing differently on its own, the request that answers them and is called by nobody, then the wiring that turns both on. ## Verification - `vp test run` for the touched suites: `sourceControl` (147), `GitManager`, and the web `markdown-github-references`, `chat` and `pullRequest` suites (469). - Targeted `tsgo --noEmit` for `apps/web`, `apps/server` and `packages/contracts`, at each of the three commits rather than only at the tip. - The matching rules are GitHub's own, each checked against its renderer before being written down: `foo#123` and `#123abc` are not references, `(#123)`, `#123.`, `#123-abc` and `/#123` are, `#0` is not, and code spans and link labels are left alone. - Read live against this repository in `test-t3-app`: references linkified in a real description, `#95` re-addressed itself to `/pull/95` and opened here, `#94` stayed addressed for the browser. That pass is also what found two faults nothing else did — a memoized renderer holding the first render's answers, and a mark cancelled by `.chat-markdown a { text-decoration: none }`. --- Written by an agent (T3 Code, claude-opus-5).
> [!NOTE] > `#123`, `GH-123` and `owner/repo#123` in a pull request body are now links, the way they are on > GitHub. What each number turns out to be decides where it opens: a pull request in a project on > this machine opens as a tab beside the one being read, an issue opens in a browser, and a number > the host has nothing under is underlined in red and still opens. GitHub's autolinked references are its own extension rather than GFM, so remark left them as they were: every `pingdotgg#6039` an agent or a reviewer wrote in a description, a comment or a review stayed plain text, and following one meant retyping it into a browser. Reading them is the easy half. Knowing what one *is* takes asking the host, and a body can name a dozen — so they are asked about together, in one GraphQL document, aliased by repository and by number. `issueOrPullRequest` answers which of the two each number turned out to be, which is what decides where it opens; a cache keeps a panel of many bodies from spawning a process per body for numbers a neighbour just resolved. The link is addressed at `/issues/{n}` before any of that comes back, because the host redirects that to `/pull/{n}` for a pull request — so it is already right in a browser, and resolving only ever improves it. A reference clicked before its answer arrives follows it as written. Everything else follows from one rule: **only an answer marks a link as broken.** A request that failed on the way — rate limited, logged out, offline — leaves every reference exactly as it was, and a null is read as nothing-there only where the host filed `NOT_FOUND` against it, since `FORBIDDEN` is SAML or an IP allowlist standing between the reader and something they can very likely open themselves. A host having a bad minute must not repaint a body full of good references as mistakes. Inert unless a surface passes the repository to read numbers against, so `#2` in a conversation — where it is far more likely to be a step than an issue — stays the plain text it reads as. The description of #126, which cites the stale-stack guard from `#95` and the window it was built for (`#94`). | Before | After | | --- | --- | | <img src="https://raw.githubusercontent.com/yngatech/t3code/assets/github-reference-links/before.png" alt="Reference numbers rendered as plain grey text" width="460"> | <img src="https://raw.githubusercontent.com/yngatech/t3code/assets/github-reference-links/after.png" alt="The same numbers rendered as links" width="460"> | `#95` resolves to a pull request and opens as a tab here; `#94` resolves to an issue and opens in a browser, since there is no issue surface to open it in. A reference the host has nothing under keeps its link colour and takes the mark an unknown word takes. No pull request in this repository happens to cite a number the repository lacks, so the state below was forced on a resolved reference to photograph it: <img src="https://raw.githubusercontent.com/yngatech/t3code/assets/github-reference-links/missing.png" alt="A reference underlined with a red wavy line" width="460"> Three layers, each of which stands alone: the plugin that reads references and renders nothing differently on its own, the request that answers them and is called by nobody, then the wiring that turns both on. - `vp test run` for the touched suites: `sourceControl` (147), `GitManager`, and the web `markdown-github-references`, `chat` and `pullRequest` suites (469). - Targeted `tsgo --noEmit` for `apps/web`, `apps/server` and `packages/contracts`, at each of the three commits rather than only at the tip. - The matching rules are GitHub's own, each checked against its renderer before being written down: `foo#123` and `#123abc` are not references, `(#123)`, `#123.`, `#123-abc` and `/#123` are, `#0` is not, and code spans and link labels are left alone. - Read live against this repository in `test-t3-app`: references linkified in a real description, `#95` re-addressed itself to `/pull/95` and opened here, `#94` stayed addressed for the browser. That pass is also what found two faults nothing else did — a memoized renderer holding the first render's answers, and a mark cancelled by `.chat-markdown a { text-decoration: none }`. --- Written by an agent (T3 Code, claude-opus-5).
- PullRequestDetailPanel takes main's #6039 rework wholesale (reactions, update-branch, auto-merge, in-place editing); the thread-details action hooks stay in usePullRequestActions with label maps extended for the new action variants - CommandPalette #6330 provider subtitles adapted to the v2 shell (session -> runtime for provider instance and display name) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… PR panel The round-5 ChatView reconciliation kept the round-3-era PullRequestDetailPanel call, so the panel mounted without chromeVariant="collapse" — the #6039 scroll-condensing chrome never engaged and the description scrolled under a full-height chrome. Restore main's call exactly: collapse chrome, composerDraftTarget for same-thread hand-offs, the isThreadOwnPullRequest context check, and tab statuses keyed by the active surface id via updatePullRequestTabStatus instead of a key rebuilt from the status payload. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Follow-ups to the pull request surfaces, from Discord/X feedback.
In short: the list now covers every connected server and can be filtered like GitHub, the detail page can update a stale branch and let you rewrite a pull request's own words, diffs read in a sensible order, and the right panel behaves.
Filter and search the list
label:x,-label:x,author:me,draft:true,review:approved,status:failure. Any unknownkey:value(size:XXL) is treated as a label, so each repository's own labels just work.One list, every server
The page used to show a single environment. It now merges all connected servers that support pull requests.
Parts adapted from #6108 — thanks @StiensWout.
Update a stale branch
Diffs in reading order
The Code tab was alphabetical. Now each slice is ordered source → tests → generated (lockfiles, snapshots,
dist/).Source files follow an import graph parsed from the patch itself: dependencies before the code that uses them. Tests sit right after the file they test.
Edit without leaving T3 Code
Rewrite a pull request's title, its description, and your own comments, in place.
Everything else
Verification
tsgo --noEmitclean across contracts, client-runtime, server, web.Note
Add filters, reactions, inline editing, multi-server listing, and update-branch to pull request surfaces
label:,author:me,review:,is:draftqualifiers typed into the search box, applied server-side where supported and client-side as fallback.PullRequestReactionBarrenders emoji pills with optimistic toggle and rollback; reactions are fetched and displayed on PR descriptions, timeline comments, and review thread comments across GitHub, GitLab, and Bitbucket (Bitbucket/Azure explicitly unsupported).canEditPullRequestChangeRequest,canEditPullRequestComment);PullRequestMarkdownEditorprovides a write/preview toggle with Escape-to-cancel.update-branch(merge/rebase) andenable-auto-merge/disable-auto-mergeactions with aPullRequestBaseFreshnessWarningpopover showing how far behind the branch is and offering update buttons.orderDiffFilessorts changed files by import graph topology with source/test/generated tiers; omitted file stats from GitHub are used as fallbacks for per-file addition/deletion counts when hunks are withheld.PullRequestChecksPopovershows check status in list rows and the detail panel; rollup state (passing/failing/pending) deduplicates re-run checks by recency.Macroscope summarized 35d5d2f.