Skip to content

Fix PR checks sticking to a stale linked PR after a terminal branch switch - #8760

Merged
brennanb2025 merged 4 commits into
mainfrom
brennanb2025/pr-checks-stale-branch
Jul 15, 2026
Merged

brennanb2025 merged 4 commits into
mainfrom
brennanb2025/pr-checks-stale-branch

Conversation

@brennanb2025

Copy link
Copy Markdown
Contributor

Problem

When an agent (or any terminal command) switches branches inside a workspace, the PR checks panel keeps showing the previous branch's PR, and the panel's Refresh button does not recover. Reproduced live against demo-project PRs #19/#20.

Root cause

A workspace's linkedPR is a branch-scoped hint (persisted when a PR URL appears in terminal output, e.g. after gh pr create), but the lookup that consumes it is exact-by-number and ignores the branch. Two refresh paths race when a terminal switches branches:

  • the git-status identity path (updateWorktreeGitIdentity) clears branch-scoped review links and tombstones the clear, and
  • the worktree-listing path (fetchWorktrees / fetchAllWorktrees) rehydrates the new branch together with the stale persisted link, clearing nothing.

If the listing merge applies first, the identity path early-returns (branch already matches) and the clear never runs. git worktree list is much faster than git status, so on real repos the listing almost always wins — the wedge is effectively deterministic. From then on every refresh (including the Refresh button) re-fetches the linked old PR by number and caches it under the new branch's cache key, permanently.

Fix

Prevention — new worktree-listing-branch-switch.ts: listing refreshes route observed branch switches through updateWorktreeGitIdentity before merging, so the existing clear/tombstone machinery runs regardless of which path wins the race. Gated on the entry still carrying branch-scoped review context, so a stale listing row cannot roll back a newer branch identity (covered by the existing stale-refetch protection tests). Wired into all three listing-hydration sites; clears all provider links (GitHub/GitLab/Bitbucket/Azure DevOps/Gitea) via the shared machinery.

Recovery — heals wedges persisted by earlier builds: PRInfo now carries headRefName, and a fetch that returns the linked open PR whose head branch matches neither the current branch, nor the workspace push target, nor the worktree HEAD commit clears the durable link and immediately re-resolves by branch. Wired into fetchPRForBranch and the background refresh coordinator, mirroring the existing merged-PR divergence clear. The push-target and HEAD guards keep legitimate links for fork PRs and PR checkouts under renamed local branches.

Verification

  • Live in the dev app: forced the previously-losing ordering (listing applies the branch first) → link now clears and the panel flips to the new branch's PR; manufactured a persisted wedge (workspace on one branch, linked to the other branch's PR) → a single Refresh click unlinks and re-resolves correctly.
  • Unit: new pure-helper tests, a fetchWorktrees regression test (stale link cleared when the listing observes the switch), and predicate tests for the mismatch clear. Full store-slice suite passes (1,721 tests), plus typecheck, oxlint, and the max-lines ratchet.

…witch

A worktree's linked PR is a branch-scoped hint, but two refresh paths race
when a terminal switches branches: the git-status identity path clears
branch-scoped review links, while the worktree-listing path rehydrates the
new branch together with the stale persisted link and clears nothing. When
the listing lands first (the common case — worktree listing is much faster
than git status), the identity path sees no branch change and the stale
link survives. Every subsequent refresh then re-fetches the linked PR by
exact number, which ignores the branch, so Checks stays pinned to the old
branch's PR and the Refresh button cannot recover.

Two-part fix:
- Prevention: listing refreshes now route observed branch switches through
  updateWorktreeGitIdentity before merging, so the existing link clear and
  tombstone machinery runs no matter which refresh path wins. Gated on the
  entry still carrying branch-scoped review context so a stale listing row
  cannot roll back a newer branch identity.
- Recovery: PRInfo now carries headRefName, and a fetch that returns the
  linked OPEN PR whose head branch matches neither the current branch, the
  worktree push target, nor the worktree HEAD clears the durable link and
  re-resolves by branch. Wired into both fetchPRForBranch and the
  background refresh coordinator, mirroring the merged-PR divergence clear.
  This also heals wedged workspaces persisted by earlier builds.
@coderabbitai

coderabbitai Bot commented Jul 14, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 37a54819-d0f9-4047-a2d3-f29d5e3c4348

📥 Commits

Reviewing files that changed from the base of the PR and between 567112f and e93a2af.

📒 Files selected for processing (2)
  • src/renderer/src/store/slices/github.test.ts
  • src/renderer/src/store/slices/github.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/renderer/src/store/slices/github.ts

📝 Walkthrough

Walkthrough

Adds headRefName to PR data and introduces branch-mismatch detection for stale linked open PRs. Direct PR fetches and GitHub refresh events now clear invalid links with validation. Worktree listing refreshes route branch changes through Git identity updates before sanitizing branch-scoped review metadata, including detached-head handling. Tests cover predicate behavior, routing scenarios, and persisted stale-link cleanup.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description covers the issue, root cause, fix, and verification, but it omits several required template sections. Reformat the PR description to match the repository template and add the missing Summary, Screenshots, Testing, AI Review Report, Security Audit, and Notes sections.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main fix: stale linked PR checks after a branch switch.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@brennanb2025
brennanb2025 merged commit a1778d9 into main Jul 15, 2026
1 check passed
enrick-dev pushed a commit to enrick-dev/orca that referenced this pull request Oct 15, 2026
…witch (stablyai#8760)

* Fix PR checks sticking to a stale linked PR after a terminal branch switch

A worktree's linked PR is a branch-scoped hint, but two refresh paths race
when a terminal switches branches: the git-status identity path clears
branch-scoped review links, while the worktree-listing path rehydrates the
new branch together with the stale persisted link and clears nothing. When
the listing lands first (the common case — worktree listing is much faster
than git status), the identity path sees no branch change and the stale
link survives. Every subsequent refresh then re-fetches the linked PR by
exact number, which ignores the branch, so Checks stays pinned to the old
branch's PR and the Refresh button cannot recover.

Two-part fix:
- Prevention: listing refreshes now route observed branch switches through
  updateWorktreeGitIdentity before merging, so the existing link clear and
  tombstone machinery runs no matter which refresh path wins. Gated on the
  entry still carrying branch-scoped review context so a stale listing row
  cannot roll back a newer branch identity.
- Recovery: PRInfo now carries headRefName, and a fetch that returns the
  linked OPEN PR whose head branch matches neither the current branch, the
  worktree push target, nor the worktree HEAD clears the durable link and
  re-resolves by branch. Wired into both fetchPRForBranch and the
  background refresh coordinator, mirroring the merged-PR divergence clear.
  This also heals wedged workspaces persisted by earlier builds.

* Harden stale PR recovery across refresh races

* Avoid duplicate PR recovery refresh work

* Index linked PR refresh aliases once
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.

1 participant