diff --git a/apps/desktop/plans/20260413-1600-v2-review-tab.md b/apps/desktop/plans/20260413-1600-v2-review-tab.md new file mode 100644 index 00000000000..a620fab261c --- /dev/null +++ b/apps/desktop/plans/20260413-1600-v2-review-tab.md @@ -0,0 +1,488 @@ +# V2 Workspace Sidebar: Review Tab (PR Info, Checks, Comments) + +This ExecPlan is a living document. The sections `Progress`, `Surprises & Discoveries`, `Decision Log`, and `Outcomes & Retrospective` must be kept up to date as work proceeds. + +Reference: This plan follows conventions from AGENTS.md and this template. + + +## Purpose / Big Picture + +Today the v2 workspace sidebar has three tabs: "All files", "Changes", and "Checks". The Checks tab is a placeholder that reads "Coming soon." There is no way for a user in the v2 workspace to see their pull request status, CI check results, or PR review comments. In the v1 workspace, all of this lives in a fully-featured "Review" tab inside the right sidebar's ChangesView component. That v1 tab shows the PR title and state, the review decision badge, requested reviewers, a collapsible list of CI checks with pass/fail/pending icons and links, and a full list of PR comments with avatar, author, age, preview text, copy-to-clipboard, resolve/unresolve, and a "mark all done" batch action. + +After this plan is implemented, a v2 workspace user will be able to click the "Review" tab in the right sidebar and see all of that information. They will be able to resolve and unresolve comment threads, copy individual comments or all comments to the clipboard, and click through to GitHub for checks and comments. The existing "Checks — Coming soon" stub will be replaced by this richer "Review" tab. + + +## Assumptions + +1. The v1 `electronTrpc` endpoints (`workspaces.getGitHubStatus`, `workspaces.getGitHubPRComments`, `workspaces.resolveReviewThread`) are the correct data sources. The v2 host-service has `git.getPullRequest` and `git.getPullRequestThreads`, but these return different shapes and the resolve-thread mutation only exists on the electron router. Using the v1 endpoints avoids needing to add a new host-service mutation and means the data shapes match the proven v1 UI exactly. + +2. The review tab replaces the "Checks" stub entirely (the tab ID changes from `"checks"` to `"review"`). Checks are shown inside the review tab as a collapsible section, matching v1 behavior. + +3. The `PRIcon` component at `renderer/screens/main/components/PRIcon/PRIcon.tsx` and the ReviewPanel utility functions at `renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/components/ReviewPanel/utils.ts` can be imported directly from their current locations. No need to move them into the v2 directory tree since they are general-purpose and already used by v1. + +4. The v2 sidebar tab architecture (the `useChangesTab` hook pattern that returns a `SidebarTabDefinition`) is the correct pattern to follow. The review tab will be built as a `useReviewTab` hook. + + +## Open Questions + +1. **Should we use the v1 electron endpoints or the v2 host-service endpoints for PR data?** Using v1 endpoints is simpler and proven. Using v2 endpoints would be more architecturally consistent but requires adding a resolve-thread mutation to the host-service and adapting the data shapes. Pre-linked to Decision Log entry D1. + +2. **Should the badge on the Review tab show open comment count, total comment count, or checks status?** V1 shows open comment count on the "Review" sub-tab. Pre-linked to Decision Log entry D2. + + +## Progress + +- [ ] Plan drafted and awaiting approval. +- [ ] Milestone 1: useReviewTab hook + ReviewTabContent shell. +- [ ] Milestone 2: PR header section (title, state, decision, reviewers). +- [ ] Milestone 3: Checks section (collapsible, icons, links). +- [ ] Milestone 4: Comments section (list, copy, resolve, batch resolve). +- [ ] Milestone 5: Wire into WorkspaceSidebar, remove Checks stub. +- [ ] Milestone 6: Validation and cleanup. + + +## Surprises & Discoveries + +(None yet.) + + +## Decision Log + +- **D1 — Use v1 electron endpoints for PR data.** + Rationale: The v1 endpoints (`electronTrpc.workspaces.getGitHubStatus`, `.getGitHubPRComments`, `.resolveReviewThread`) are battle-tested, return the exact shapes the v1 ReviewPanel already consumes, and include the resolve-thread mutation. The v2 host-service endpoints (`workspaceTrpc.git.getPullRequest`, `.getPullRequestThreads`) return different shapes (e.g., `CheckRun` with `conclusion` field vs. `CheckItem` with `status` field, and threads as nested objects vs. flat `PullRequestComment[]`). Using v1 endpoints lets us reuse the v1 utility functions directly and avoids adding a new mutation to the host-service. A future migration to host-service endpoints can happen independently. + Date: 2026-04-13 / Plan author. + +- **D2 — Badge shows open (unresolved) comment count, matching v1.** + Rationale: This is what v1 does and it is the most actionable number for a reviewer. + Date: 2026-04-13 / Plan author. + + +## Outcomes & Retrospective + +(To be filled at completion.) + + +## Context and Orientation + +This section explains the relevant parts of the codebase for someone who has never seen it. + +### The v2 workspace sidebar + +The v2 workspace lives at `apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/`. Its right sidebar is rendered by the `WorkspaceSidebar` component at `components/WorkspaceSidebar/WorkspaceSidebar.tsx`. The sidebar shows tabs defined by the `SidebarTabDefinition` interface (from `components/WorkspaceSidebar/types.ts`): + + export interface SidebarTabDefinition { + id: string; + label: string; + badge?: number; + actions?: ReactNode; + content: ReactNode; + } + +Currently three tabs are assembled in `WorkspaceSidebar.tsx`: + + const tabs = [filesTab, changesTab, checksTab]; + +The `changesTab` is built by a hook, `useChangesTab`, which lives at `components/WorkspaceSidebar/hooks/useChangesTab/useChangesTab.tsx`. It returns a `SidebarTabDefinition` with the "Changes" label, a badge showing the number of changed files, and a `ChangesTabContent` component as its `content`. This hook-returns-tab-definition pattern is the model we will follow. + +The `checksTab` is an inline stub: + + const checksTab: SidebarTabDefinition = useMemo( + () => ({ + id: "checks", + label: "Checks", + content: ( +
+ {getPreviewText(comment.body)} +
+
+ {children}
+
+ );
+ }
+
+ return (
+