feat: Implement merge queue feature with per-repo opt-in and UI - #121
Merged
Conversation
Contributor
📸 App QA screenshotsRe-ran the 4 flows whose spec this PR adds or modifies — 17 captures. Other specs in the library were not run. merge-queue-flag-off
merge-queue-setting
merge-queue-tab
merge-queue
Each bullet under a capture is what the spec claims that image should show — open the PNG and check it. commit |
Drives the real merge queue UI (real jj repo, real Rust dispatch, real React tree) through each state the backend can emit, capturing the header queue button and sidebar status dot. Only the Supabase boundary and the gh subprocess are stubbed, since neither is reachable from the desktop harness. Covers: not queued, enqueue via the real button, testing, merging, merged, and the no-open-PR error path.
…uring polling Enable the mergeQueue feature flag and make the whole feature respect it: useMergeQueueStatus, the sidebar's per-repo query and the GitHub panel's queue tab were all polling Supabase regardless of the flag, and the panel's tab is now hidden entirely when it is off. Add a cloud-stored per-repo opt-in (007_merge_queue_enabled.sql) exposed as get/set_merge_queue_enabled RPCs. A repo with no config row reads as OFF, so the merge queue has to be switched on from the GitHub panel's Merge Queue tab before 'Add to Queue' appears on a workspace or anything can be enqueued. Merge Queue tab: - toggle is a Switch rather than a button - entries are listed as PRs and grouped into stacks, each stack rendered as a block that states it merges bottom-up into its target - entries and whole stacks can be removed from the queue; removing a branch mid-stack takes everything above it so nothing is left stranded Also fixes defects found in QA: QueueEntryStatus was missing the 'merging' enum value and carried a 'passed' value the entry enum never emits, leaving merging branches with an unstyled grey sidebar dot; enqueue only invalidated the workspace's own status query, leaving sidebar dots and the queue tab stale until their next 30s poll; and the header no longer surfaces a 'Testing…' label. Screenshot specs cover the queue states, the tab with multiple stacked PRs, removal, and a flag-off regression guard.
…ntegrations The per-repo merge queue toggle now lives in the settings page as a GitHub setting alongside the repositories card, rather than in the GitHub panel's Merge Queue tab. The tab keeps the disabled state but only reports it and links to Settings; it no longer owns the control. Also shortens the workspace header's queued label to 'Queued' instead of 'Queue #N'.
… continuous queue rail Settings > Integrations now uses one header per integration with its settings listed directly beneath, instead of bordered cards. The merge queue sits under the GitHub header alongside the connected-repositories row. The disabled state is a CTA rather than a prompt: an eligible repo (Pro plan, GitHub App installed) gets a primary 'Enable merge queue' button, and the switch only appears once the queue is on. An ineligible repo gets the reason -- upgrade to Pro, or install the App on this repo -- so the control is never offered where it could only fail. The Merge Queue tab draws the queue as one continuous vertical rail with a status-coloured node per entry, following the stack panel's idiom. The line runs unbroken across stack groupings down to the target branch, showing that the whole queue is a single merge sequence; stacks are marked by inline headers and an accent on their entries rather than by separate cards. Keys GitHubIntegrationSettings' repository-loading effect on user.id instead of the auth objects, so a caller whose useAuth returns fresh objects per render no longer loops.
Rebased onto main (Storybook setup, sparse checkout, font update, and the new max-3-capture-document-expectations / no-inline-comments-in-tests / no-multiline-inline-comments lint rules landed there since this branch was cut). Rebase itself was conflict-free; this addresses what the newer lint rules and CI now catch: - Extract GitHubPanel's merge-queue rendering into github-panel/MergeQueueTab.tsx, bringing GitHubPanel.tsx under the 500-line cap. - Rewrite useDequeueBranches' sequential dequeue loop without an await-in-loop, using a promise-chained reduce (ordering is still strictly top-down, which is why it can't be Promise.all). - Fix max-params/max-nested-callbacks in merge-queue-stacks.test.ts by giving entry() an options object and extracting branchNames()/stackBranches() helpers instead of nesting .map() calls three deep. - Use array destructuring instead of indexing in buildQueueStacks. - Remove comments from the two merge-queue .test.ts files (banned there) and collapse a few multi-line comments elsewhere to one line. - Split two captureDocument() calls whose expectations grew past 3 into separate captures with their own names. Also rebuilt the NAPI addon, which was stale against main's sparse-checkout Rust changes and was failing test/integration/workspace/sparse.test.ts. No behavioral changes: full suite (281 tests) and the screenshot suite (25 captures across 14 spec files) pass, and I re-verified the merge-queue screenshots pixel-by-pixel against their (now split) expectations.
Ziinc
force-pushed
the
claude/merge-queue-qa-9b2gmc
branch
from
July 29, 2026 00:44
a2c3612 to
9fe3af7
Compare
…ks.test.ts npm run check:format (biome format ./src ./test) was failing CI on wrapping that hadn't been run through the formatter after the file extraction/rewrite in the previous commit.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR implements a complete merge queue feature for Treq, allowing users to automatically merge branches once CI passes. The feature includes per-repo opt-in controls, a dedicated merge queue tab in the GitHub panel, queue status visualization in workspaces, and comprehensive screenshot tests.
Key Changes
Core Merge Queue Infrastructure
useMergeQueueEnabled(),useSetMergeQueueEnabled(), anduseDequeueBranches()hooks to manage queue state and operationsbuildQueueStacks()utility to group flat queue entries into logical stacks, preserving merge order and handling stacked PRs (branches targeting other queued branches)007_merge_queue_enabled.sql) with RPCs forget_merge_queue_enabled()andset_merge_queue_enabled()UI Components
MergeQueueSettingcomponent with eligibility checks (Pro plan + GitHub App installed) and toggle/enable controlsuseEnqueueWorkspace()hook for enqueueing operationsFeature Flag & Build Configuration
mergeQueuefeature flag inpackage.json(was previously disabled)FEATURES.mergeQueueto prevent unnecessary Supabase polling when disabledTesting
merge-queue.spec.tsx: Full workflow from workspace creation through queue statesmerge-queue-tab.spec.tsx: Queue tab visualization with stacked PRsmerge-queue-setting.spec.tsx: Settings page opt-in controlsmerge-queue-flag-off.spec.tsx: Regression guard ensuring no polling when flag is offNotable Implementation Details
QueueEntryStatustype to use "merging" instead of "passed" to better reflect queue state semanticshttps://claude.ai/code/session_013HAvkSFAyYFkHMAsorqZMc