You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The PR Panel is where review and merge happen — the operational endpoint of the entire MVP loop. The merge gate (reviewStatus === 'approved' AND checks === 'passing') must be enforced inside MergePullRequestUseCase, not only in the template. Hiding the button is a usability hint; the use case is the safety rail.
Solution
PRPanel.vue — right-panel content when right === 'pr'
No header bar — the topbar breadcrumb + sidebar active state already communicate context (per the design brief).
Sections (vertical scroll):
A. Linked Issues (conditional — only if pr.issueIds.length > 0):
⊙ icon · Issue number · truncated title · status dot (Open = green)
Click → useAppStateStore.goToIssue(num) (US-029)
B. CI Checks (collapsible — collapsed by default):
Header summary: ✓ 4/4 passing (green) / ✕ N failing (red) / ○ N pending (amber)
› chevron flips to ▾ on expand
Expanded: row per check (icon + name + status text); click shows status-detail toast
C. Review & Merge (unified — one continuous workflow):
Approve button (visible when reviewStatus !== 'approved' and PR is not a draft): click → ApprovePullRequestUseCase; on success: pr.reviewStatus = 'approved', panel re-renders, merge unlocks (US-046)
Merge button (visible only after approve): full-width purple; enabled only when pr.checks === 'passing'; click → MergePullRequestUseCase (which re-checks the gate internally); 800ms transition; on success: pr.status = 'merged', sidebar pill → purple ✓, panel re-renders (US-047)
Before approval: dimmed "Approve this PR to unlock merge" message (US-047)
Draft state: shows ◐ Mark as Ready button instead of Merge; click → MarkDraftReadyUseCase; converts draft to open; re-renders both panels (US-048)
PR AI input at the bottom: prompt "Ask about PR #N…"; context-button label "PR context ▾"
Use cases (application layer)
GetPullRequestUseCase (already in V4)
ApprovePullRequestUseCase
MergePullRequestUseCase — gate re-check: returns Result.err(MergeBlocked) if reviewStatus !== 'approved' OR checks !== 'passing', regardless of what the UI thinks
MarkDraftReadyUseCase
Acceptance criteria
Linked Issues section appears only when pr.issueIds.length > 0; row click navigates to the linked issue (US-029)
CI Checks section collapsed by default; header summary text matches counts; expand shows per-check rows
CI row click shows a contextual toast with the check status
Approve button visible only when not approved + not draft; click dispatches ApprovePullRequestUseCase; on success, panel re-renders and Merge button becomes visible (US-046)
Merge button enabled only when checks === 'passing'; click dispatches MergePullRequestUseCase; 800ms delay; on success, status = 'merged' and sidebar pill updates (US-047)
Gate enforcement test: a unit test calls MergePullRequestUseCase directly with reviewStatus === 'review-required' and asserts it returns Result.err(MergeBlocked) — without involving the UI. Same with checks === 'failing'.
Before approval, the dimmed gate message is shown and the Merge button is hidden (US-047)
Draft PRs show ◐ Mark as Ready instead of Merge; click converts; re-renders sidebar row + panel (US-048)
Parent epic: #326 · Depends on: F2, F3, F4 · Stories: US-029, US-046, US-047, US-048
Problem
The PR Panel is where review and merge happen — the operational endpoint of the entire MVP loop. The merge gate (
reviewStatus === 'approved'ANDchecks === 'passing') must be enforced insideMergePullRequestUseCase, not only in the template. Hiding the button is a usability hint; the use case is the safety rail.Solution
PRPanel.vue— right-panel content whenright === 'pr'No header bar — the topbar breadcrumb + sidebar active state already communicate context (per the design brief).
Sections (vertical scroll):
A. Linked Issues (conditional — only if
pr.issueIds.length > 0):⊙icon · Issue number · truncated title · status dot (Open = green)useAppStateStore.goToIssue(num)(US-029)B. CI Checks (collapsible — collapsed by default):
✓ 4/4 passing(green) /✕ N failing(red) /○ N pending(amber)›chevron flips to▾on expandC. Review & Merge (unified — one continuous workflow):
reviewStatus !== 'approved'and PR is not a draft): click →ApprovePullRequestUseCase; on success:pr.reviewStatus = 'approved', panel re-renders, merge unlocks (US-046)pr.checks === 'passing'; click →MergePullRequestUseCase(which re-checks the gate internally); 800ms transition; on success:pr.status = 'merged', sidebar pill → purple ✓, panel re-renders (US-047)◐ Mark as Readybutton instead of Merge; click →MarkDraftReadyUseCase; converts draft to open; re-renders both panels (US-048)Use cases (application layer)
GetPullRequestUseCase(already in V4)ApprovePullRequestUseCaseMergePullRequestUseCase— gate re-check: returnsResult.err(MergeBlocked)ifreviewStatus !== 'approved'ORchecks !== 'passing', regardless of what the UI thinksMarkDraftReadyUseCaseAcceptance criteria
pr.issueIds.length > 0; row click navigates to the linked issue (US-029)ApprovePullRequestUseCase; on success, panel re-renders and Merge button becomes visible (US-046)checks === 'passing'; click dispatchesMergePullRequestUseCase; 800ms delay; on success,status = 'merged'and sidebar pill updates (US-047)MergePullRequestUseCasedirectly withreviewStatus === 'review-required'and asserts it returnsResult.err(MergeBlocked)— without involving the UI. Same withchecks === 'failing'.◐ Mark as Readyinstead of Merge; click converts; re-renders sidebar row + panel (US-048)npm run typecheck,npm run lint,npm run test,npm run test:storybookpassOut of scope
ApprovePullRequestUseCaseonly handles approval)Affected files
src/ui/components/right/PRPanel.vuesrc/ui/components/right/LinkedIssues.vuesrc/ui/components/right/CIChecks.vuesrc/ui/components/right/ReviewAndMerge.vuesrc/application/pullrequest/ApprovePullRequestUseCase.tssrc/application/pullrequest/MergePullRequestUseCase.tssrc/application/pullrequest/MarkDraftReadyUseCase.tssrc/application/pullrequest/MergeGateError.tstests/application/pullrequest/MergePullRequestUseCase.test.tstests/ui/components/right/PRPanel.test.ts+.po.tsstories/right/PRPanel.stories.tsReferences
inputs/specorator-mvp-design-2026-05/Specorator_Design_Brief.html§"PR Panel" + §"Review & Merge" (gated workflow flow diagram)inputs/specorator-mvp-design-2026-05/Specorator_Handoff.html§"PR Merge Workflow (Gated)"inputs/specorator-mvp-design-2026-05/SYNTHESIS.md§"PR merge gate enforcement"