Files
kandev/docs/plans/github-pr-merge-queue/plan.md
T
João Salavisa ad21460007 feat(github): add merge queue actions (#2755)
Co-authored-by: Kandev Agent <agent@kandev.dev>
2026-08-17 23:28:59 +00:00

6.3 KiB

spec, created, status
spec created status
docs/specs/github-pr-merge-queue/spec.md 2026-08-17 complete

Implementation Plan: GitHub PR Merge Queue

Overview

Extend the existing GitHub merge boundary to use GitHub's asynchronous, queue-aware merge request and return a typed merged-or-queued outcome. Then reuse that outcome in the existing PR merge button across the desktop detail, compact status, and mobile Review surfaces, with targeted unit and E2E proof.

Backend

Provider client contract

  • Update apps/backend/internal/github/client.go so the merge operation returns a typed outcome rather than only an error.
  • Update apps/backend/internal/github/gh_client.go and apps/backend/internal/github/pat_client.go to call PUT /repos/:owner/:repo/pulls/:number/merge-async with merge_action=default, preserve the selected merge_method, and normalize GitHub's accepted, already-queued, and already-merged response shapes.
  • Update apps/backend/internal/github/mock_client.go and apps/backend/internal/github/noop_client.go for the same contract so local and E2E behavior remains deterministic.

Service and HTTP response

  • Update apps/backend/internal/github/service_pr.go to propagate the typed outcome while retaining workspace scope checks, personal-write credential routing, merge-method fallback, and cache invalidation.
  • Update apps/backend/internal/github/controller.go to return {"status":"merged"} or {"status":"queued"} and retain the existing operational-auth and provider-error mapping.

Frontend

Queue-aware merge action

  • Update apps/web/lib/api/domains/github-pr-api.ts and the re-exporting API module to type the merged-or-queued response.
  • Update apps/web/components/github/pr-task-icon.tsx with a tested eligibility predicate that permits a queue-required branch only after explicit successful checks and satisfied reviews, while continuing to reject drafts, conflicts, behind branches, changes requested, and incomplete review/check gates.
  • Update apps/web/components/github/pr-merge-button.tsx to render for direct or queued eligibility, show the outcome-specific notification, suppress repeat submission after acceptance, and refresh the PR state.
  • Add the new action and outcome copy to all locale catalogs under apps/web/src/locales/*/github.json; generate the Traditional Chinese pair through the repository script rather than translating them independently.

Mobile design contract

  • Desktop outcome: the existing PR detail header and compact status surface expose one merge action whose result says merged or queued.
  • Mobile entry point: the task's existing Review bottom-navigation destination.
  • Nearest shipped exemplar: the existing full-height mobile Review surface and PRMergeButton; retain its single internal scroll owner and shared PR state.
  • Information hierarchy: PR status and blockers remain above the primary merge action; the action is the terminal operation for an eligible PR.
  • Presentation: retain direct navigation to the full-height Review surface because PR review is dense primary content, not a temporary picker.
  • Geometry: keep the existing dynamic-height/safe-area behavior, avoid document horizontal overflow, and preserve a touch target at least 44px high on phone.
  • Shared logic: eligibility, mutation, result handling, and refresh remain shared; only the existing responsive composition differs.

Tests

  • What: PAT and gh clients send merge-async, merge_action=default, and the chosen merge method, and normalize direct, queued, and idempotent results. Files: apps/backend/internal/github/pat_client_writes_test.go, apps/backend/internal/github/gh_client_commands_test.go. How: focused HTTP/command capture tests with representative GitHub responses.
  • What: service preserves method resolution and cache invalidation while returning the provider outcome. File: apps/backend/internal/github/service_pr_test.go. How: table-driven unit tests with the stub client.
  • What: HTTP validation, outcome JSON, auth routing, and provider errors. File: apps/backend/internal/github/controller_test.go. How: handler tests for merged, queued, malformed method, and rejection paths.
  • What: frontend API response typing and queue eligibility boundaries. Files: apps/web/lib/api/domains/github-api.test.ts, apps/web/components/github/pr-task-icon.test.ts. How: request capture and table-driven predicate tests.
  • What: button notifications, accepted-state suppression, retry behavior, and refresh callback. File: apps/web/components/github/pr-merge-button.test.tsx. How: component tests with mocked API outcomes.

E2E Tests

  • Scenario: eligible direct merge reports merged. File: apps/web/e2e/tests/pr/pr-merge-queue.spec.ts. What to verify: the desktop PR surface exposes the action, submits it, and shows the merged notification.
  • Scenario: queue-required merge reports queued and does not resubmit. File: apps/web/e2e/tests/pr/pr-merge-queue.spec.ts. What to verify: the desktop action remains available for the queue-required state and the accepted queue notification is shown once.
  • Scenario: phone Review surface provides the same queued outcome. File: apps/web/e2e/tests/pr/mobile-pr-merge-queue.spec.ts. What to verify: Review navigation, touch activation, queued notification, a touch-sized action, and no document horizontal overflow.

Verification Results

  • Backend: make -C apps/backend test ARGS='./internal/github/...' passed; the Make target executed the full backend go test -tags fts5 ./... suite.
  • Frontend: 96 focused Vitest tests passed; pnpm run i18n:check and pnpm run typecheck passed.
  • Desktop E2E: pnpm e2e:run --no-build tests/pr/pr-merge-queue.spec.ts passed (1 test).
  • Mobile E2E: pnpm e2e:run --no-build --project mobile-chrome tests/pr/mobile-pr-merge-queue.spec.ts passed (1 test).

Implementation Waves And Parallel Candidates

Wave 1:

Wave 2:

Wave 3:

Execution is sequential in the primary conversation unless the user explicitly authorizes subagents.