Repository navigation
Fix PR checks sticking to a stale linked PR after a terminal branch switch - #8760
Conversation
…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.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughAdds 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
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. Comment |
…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
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
linkedPRis a branch-scoped hint (persisted when a PR URL appears in terminal output, e.g. aftergh 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:updateWorktreeGitIdentity) clears branch-scoped review links and tombstones the clear, andfetchWorktrees/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 listis much faster thangit 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 throughupdateWorktreeGitIdentitybefore 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:
PRInfonow carriesheadRefName, 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 intofetchPRForBranchand 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
fetchWorktreesregression 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.