12 KiB
12 KiB
spec, created, status
| spec | created | status |
|---|---|---|
| docs/specs/integrations/requirements/github-pr-merge-queue.md | 2026-08-22 | done |
Implementation Plan: GitHub PR Merge Queue Status
Overview
Extend the existing batched GitHub PR sync to observe and persist merge queue state, position, and estimated merge duration, then carry the queued semantic through the bounded task projection and the metadata through the existing PR status surfaces. Reuse the current task indicator, compact popover/drawer, and review detail compositions; no new polling path, endpoint, or queue-management action is needed.
Backend
GitHub queue observation
- Update
apps/backend/internal/github/graphql.gosobatchedPRResultdecodesmergeQueueEntry { state position estimatedTimeToMerge },prFieldsBlockrequests it for numbered and branch queries, andconvertBatchedPRResultnormalizes the provider enum while retaining GitHub's one-based position and optional estimate in seconds. - Update
apps/backend/internal/github/models.gosoPRStatuscarries the normalized queue state, position, and estimate plus one internal populated guard, andTaskPRcarries the three persisted queue fields. - Keep
newPRStatusinapps/backend/internal/github/client_helpers.goqueue-unaware. Its populated guard remains false, so REST andgh pr viewfeedback reads cannot erase a GraphQL observation.
Persistence and synchronization
- Add
merge_queue_state TEXT NOT NULL DEFAULT '', nullablemerge_queue_position INTEGER, and nullablemerge_queue_estimated_time_to_merge_seconds INTEGERto the freshgithub_task_prsschema and the idempotent existing-database migration inapps/backend/internal/github/store.go. - Thread the field through
taskPRColumns,taskPRColumnsQualified,CreateTaskPR,ReplaceTaskPR,RestoreTaskPR, andUpdateTaskPRso every association path round-trips the complete queue entry and schema replay remains safe. - Extend
taskPRSyncState,prepareTaskPRSyncState,taskPRChangedFields, andSyncTaskPRinapps/backend/internal/github/service_pr_watch.go. A populated GraphQL result atomically replaces the stored queue state, position, and estimate; an unpopulated read preserves all three; and any merged or closed lifecycle state clears all three. - Extend
associateTaskPRRequestandbuildTaskPRFromRequestinapps/backend/internal/github/mock_controller.goso E2E fixtures can seed a queue state, position, and estimate and publish the normalgithub.task_pr.updatedevent.
Bounded task status
- Add merge queue state to the PR observation and rebuild input in
apps/backend/internal/task/statussummary, including live event projection, restart rebuild, and aggregate-state derivation. - Add
queuedto the bounded PR aggregate states with a rank betweenreadyandpassing: a more attention-worthy sibling still wins for multi-PR tasks, while a single queued PR retains the queue color. - Pass
TaskPR.MergeQueueStatethroughapps/backend/internal/backendapp/status_summary_adapter.goso restart hydration and live events converge on the same aggregate state.
Frontend
Types and semantic color
- Add the normalized
MergeQueueStateunion and all three TaskPR queue fields toapps/web/lib/types/github.ts. - Add the shared
queuedsemantic toapps/web/components/integrations/change-request-task-status-color.ts, using the exacttext-[#966600]GitHub merge-queue color. This color is distinct from the current yellow CI-in-progress state. Add the same queued tone to the shared task-summary presentation. Preserve the existing worst-status aggregation for tasks with several open PRs. - Update
apps/web/components/github/pr-task-icon.tsxandapps/web/components/github/pr-status-chip.tsxso an open queued PR exposes the queue color/status and a dedicated glyph before non-terminal review, check, and mergeability colors. Terminal states still win.
Hover, compact status, and review detail
- Extend the shared summary-row data in
apps/web/components/integrations/change-request-task-status-summary.tsxwith an optional localized detail line. Use it from the merge row inapps/web/components/github/pr-task-status-summary.tsxto show queue state, one-based position, and the available estimate. Recognized values distinguish queued, awaiting checks, mergeable, unmergeable, and locked; unknown non-empty values use generic queued copy. - Add a reusable queue-status notice under
apps/web/components/github/that formats position and GitHub's estimate in localized minute/hour units. Values below 60 seconds use a localized sub-minute label; larger values round up to the next whole minute. Render it frompr-mergeability-row.tsxandpr-detail-panel.tsx. This places the same queue information in the compact desktop popover, phone status drawer, and full PR detail panel without creating another data fetch. - Add queue labels, descriptions, position, and pluralized duration copy to all
five
apps/web/src/locales/*/github.jsoncatalogs. Generatezh-hkandzh-twwithpnpm run i18n:zh-hant.
Mobile design contract
- Desktop outcome: task and compact PR indicators use the queue color; hover/popover content and PR detail show translated queue state, position, and the available estimate.
- Mobile entry points: tap the existing composer PR status chip for its drawer, or open the existing full-height Review destination.
- Nearest shipped exemplars:
PRStatusChipDrawercontributes the inset, internally scrolling status drawer; the existing mobile Review surface contributes direct navigation and full-height review geometry. - Information hierarchy: queue status replaces the mergeability line for an active entry and sits below review/check information in compact status; position and estimate form one subordinate metadata line. The detail notice stays below the PR header and above review content.
- Presentation and rationale: retain the current drawer and direct Review navigation because this is content-only status inside already established surfaces, not a new temporary choice or primary destination.
- Geometry and shared logic: no scroll owner, safe-area, breakpoint, or touch-target changes. Queue derivation, copy, and state are shared between viewports; only the existing responsive wrappers differ.
Tests
- What: GraphQL selection and decoding distinguish every known queue enum,
position, a present or absent estimated duration,
null, and an unknown future state. File:apps/backend/internal/github/graphql_merge_queue_status_test.go. How: query snapshot and table-driven conversion tests. - What: sync atomically persists queue entry transitions and metadata,
preserves all fields across queue-unaware feedback reads, clears them on an
authoritative
null, and clears them for terminal PRs. File:apps/backend/internal/github/service_pr_merge_queue_status_test.go. How: focused service tests against the real store. - What: fresh schema, same-database replay, and every TaskPR write/read path
round-trip the complete queue entry. Files:
apps/backend/internal/github/store_taskpr_schema_drift_test.goandapps/backend/internal/github/store_merge_queue_status_test.go. How: real SQLite store tests. - What: live projection and restart rebuild derive the same
queuedaggregate and preserve multi-PR attention ranking. Files:apps/backend/internal/task/statussummary/projector_test.go,apps/backend/internal/task/statussummary/rebuild_test.go, andapps/backend/internal/backendapp/status_summary_adapter_test.go. How: table-driven projection and adapter tests. - What: queue membership maps to
#966600on the task icon and compact chip. The hover merge row, compact queue notice, and detail notice show the translated status and metadata. Terminal and non-queued states retain their existing behavior. Files:apps/web/components/github/pr-task-icon.test.ts,pr-task-status-summary.test.ts,pr-status-chip.test.tsx,pr-merge-queue-status.test.tsx,pr-mergeability-row.test.tsx, andpr-detail-panel.test.tsx. How: pure formatter and focused component tests, including absent, sub-minute, minute, and hour estimate boundaries.
E2E Tests
- Scenario: GIVEN an eligible queue-required PR, WHEN a desktop user
activates
Merge PR, THEN the merge API returns{"status":"queued"}, the success notification appears, and the accepted state suppresses the duplicate action. File: the action test inapps/web/e2e/tests/pr/pr-merge-queue.spec.ts. - Scenario: GIVEN a linked open PR with an active merge queue entry, WHEN a
desktop user views the task and PR detail, THEN the task indicator has queued
semantics, its hover summary and compact popover show queue state, position,
and estimated merge duration, and the detail notice shows the same data.
File: the display test in
apps/web/e2e/tests/pr/pr-merge-queue.spec.ts. - Scenario: GIVEN an eligible queue-required PR on a phone viewport, WHEN
the user opens Review and activates the action, THEN the queued notification
appears, the action target is at least 44px high, and the document has no
horizontal overflow. File: the action test in
apps/web/e2e/tests/pr/mobile-pr-merge-queue.spec.ts. - Scenario: GIVEN the same queued PR on a phone viewport, WHEN the user
opens the PR status drawer and the Review destination, THEN both expose queue
status, position, and the available estimate without hover, and the document
retains no horizontal overflow. File: the display test in
apps/web/e2e/tests/pr/mobile-pr-merge-queue.spec.ts.
Verification Results
Passed:
cd apps/backend && go test -tags fts5 ./internal/github/... ./internal/task/statussummary/... ./internal/backendapppassed 2,193 tests across 3 packages. The focused RED run recorded the expected missing-contract compile failures, and the focused GREEN run passed the queue precedence tests.cd apps/web && pnpm test -- components/github/pr-task-icon.test.ts components/github/pr-task-status-summary.test.ts components/github/pr-status-chip.test.tsx components/github/pr-merge-queue-status.test.tsx components/github/pr-mergeability-row.test.tsx components/github/pr-detail-panel.test.tsxpassed 160 tests across 6 files.cd apps/web && pnpm run typecheckandcd apps/web && pnpm run lintpassed.cd apps/web && pnpm run i18n:checkpassed with 7,223 referenced keys, 8,779 English entries, 48 orphans, and all five required catalogs complete.node scripts/validate-public-docs.test.mjspassed 61 tests, andnode scripts/validate-public-docs.mjsvalidated 41 published pages.docs/public/integrations.mddocuments queue state, position, and estimate surfaces.- A fresh
cd apps/web && pnpm e2e:run tests/pr/pr-merge-queue.spec.tsbuild completed the backend, Vite production bundle, and plugin. The final desktop run passed 2 tests, including the merge-action and queue-display scenarios, andpnpm e2e:run --no-build --project mobile-chrome tests/pr/mobile-pr-merge-queue.spec.tspassed 2 matching tests. - Previously captured display assets remain valid:
apps/web/.pr-assets/pr-merge-queue--desktop-pr-merge-queue-status.pngandapps/web/.pr-assets/mobile-pr-merge-queue--mobile-pr-merge-queue-status.png. Both were inspected, compressed with thepngquant-binfallback, and preserved for PR media publication; action coverage is behavioral and adds no capture asset. cd apps/web && pnpm e2e:cleanremoved E2E results, reports, PR assets, and shard logs. The E2E scenarios use mock GitHub state and created no external GitHub writes.
Implementation Waves And Parallel Candidates
Wave 1:
Wave 2:
Wave 3:
Execution is sequential in the primary conversation unless the user explicitly authorizes subagents.