Skip to content

Fix PR status flicker when GitHub returns UNKNOWN mergeable - #539

Merged
onevcat merged 1 commit into
onevcat:mainfrom
Alex-ai-future:fix/block-state
Jul 8, 2026
Merged

Fix PR status flicker when GitHub returns UNKNOWN mergeable#539
onevcat merged 1 commit into
onevcat:mainfrom
Alex-ai-future:fix/block-state

Conversation

@Alex-ai-future

Copy link
Copy Markdown

Problem

When refreshing PR status in the sidebar, the PR badge briefly flashes red "Blocked" before settling on the correct state (e.g. green "Mergeable").

Root Cause

GitHub GraphQL API asynchronously calculates the mergeable field. When the calculation hasn't finished yet, it returns "UNKNOWN". The existing PullRequestMergeReadiness code falls through to .blocked for any value that isn't "MERGEABLE" or "CONFLICTING":

if mergeable == "MERGEABLE" {
  self.blockingReason = nil
  return
}
self.blockingReason = .blocked  // ← UNKNOWN lands here

This means every refresh where GitHub hasn't finished computing mergeable temporarily shows "Blocked".

PR #538 fixed a separate flicker issue (confirmed-no-PR clearing), but did not address the UNKNOWN.blocked path.

Fix

Preserve the last-known mergeable and mergeStateStatus values at the reducer level when the new value is UNKNOWN. All other fields (title, checks, commits, etc.) update normally — only the mergeable state is carried over from the previous PR data.

This is applied in repositoryPullRequestsLoaded before the equality check, so the UI never sees the intermediate UNKNOWN state.

Tests

4 new test cases in RepositoriesFeatureTests:

Test Scenario
PreservesMergeableWhenNewIsUnknown New PR has UNKNOWN, old has MERGEABLE → noop (preserved)
PreservesMergeableButUpdatesOtherFields New PR has UNKNOWN + new title → mergeable preserved, title updated
KeepsUnknownWhenNoPreviousPR First load with UNKNOWN → stays UNKNOWN (no old value to preserve)
KeepsUnknownWhenPreviousAlsoUnknown Both old and new are UNKNOWN → stays UNKNOWN (no known value to preserve)

GitHub GraphQL API asynchronously calculates the mergeable field and
may return "UNKNOWN" when the calculation is not yet complete. The
previous code treated UNKNOWN as .blocked in PullRequestMergeReadiness,
causing the sidebar to briefly flash red "Blocked" during PR refresh.

Fix: preserve the last-known mergeable/mergeStateStatus values in the
reducer when the new value is UNKNOWN, so the UI never sees the
intermediate state.

Signed-off-by: Alex <alex.tech.lab@outlook.com>
@onevcat

onevcat commented Jul 8, 2026

Copy link
Copy Markdown
Owner

Reviewed — confirmed the flicker path on main: GitHub GraphQL returns mergeable: UNKNOWN while recomputing (exactly after pushes, when Prowl refreshes), and PullRequestMergeReadiness falls through to .blocked for it. Preserving the previous known value before the equality check is the right layer, and it composes cleanly with the tri-state work from #538 rather than overlapping it. Tests look great too.

Two optional follow-ups if you're interested (both non-blocking):

  • The workspace-children path (applyWorkspaceChildrenInfo in RepositoriesFeature+WorkspaceChildren.swift) still writes UNKNOWN directly, so child rows can flicker on their (slower) refresh cadence.
  • A previous.number == pullRequest.number guard would avoid grafting the old PR's mergeable state onto a brand-new PR for one cycle in the closed-then-reopened edge case.

Thanks for the careful work! 🙌

@onevcat
onevcat merged commit c0085be into onevcat:main Jul 8, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants