Skip to content

GitHub PR Approve Helper v1.3.0: approve a whole stack, fix vanishing button and stale state - #9

Merged
MishaKav merged 7 commits into
mainfrom
claude/tampermonkey-review-disappear-oye1sd
Sep 4, 2026
Merged

GitHub PR Approve Helper v1.3.0: approve a whole stack, fix vanishing button and stale state#9
MishaKav merged 7 commits into
mainfrom
claude/tampermonkey-review-disappear-oye1sd

Conversation

@MishaKav

@MishaKav MishaKav commented Sep 3, 2026

Copy link
Copy Markdown
Owner

Summary

  • Approve a whole GitHub stack. On a PR with the stack badge (2/3 next to the state label), the right-click menu of the quick-approve button gains "🥞 Approve whole stack (N PRs)". It approves bottom to top with the default comment, skips merged/closed/own/already-approved PRs, and shows the tally on the button. The plain click still approves only the current PR.
  • Fix: button vanished on the files page. The PR state scan took any "Merged"/"Closed" badge in the fetched conversation page, including timeline cross-references and linked issues. Now only the header badge decides.
  • Fix: wrong "Already approved" after jumping between PRs. Soft navigation (e.g. through the stack popover) left the previous PR's sidebar in the DOM while the URL already named the next PR, and that stale read was cached. The conversation page is now the only cached source; the live page is an uncached hint used only once the tab title names the PR.
  • Simplification pass: one approvePr primitive for both paths, one cache-aware loadPrState, one page fetch per stack member for both its state and its csrf token.

Stack members are read from the page's embedded JSON payload (GraphQL shape stack.entries[].pullRequest.number), falling back to the stack map popover opened through the badge.

Test plan

Verified in headless Chromium against mocked GitHub pages:

  • open PR whose timeline mentions a merged PR: button stays
  • merged / closed header, classic and React markup: button hidden
  • already approved by me: "Already approved" indicator
  • soft navigation #254 (approved) to #255 (not approved): #255 shows the button
  • stack approve via payload and via popover: approves #255 and #256, skips #254
  • plain click still approves only the current PR

Manually tested by @MishaKav on a linear-b stack (#254-#256).

🤖 Generated with Claude Code

https://claude.ai/code/session_01MM2QDa5SMgF9CRr94AZzyW


Generated by Claude Code

On the files page the quick-approve button appears, then vanishes a few
seconds later once the conversation-page fetch resolves: detectPrStateInDoc
took any state badge in the document, so a "Merged"/"Closed" badge on a
timeline cross-reference or linked issue was read as the PR's own state.
Now the first badge that reads as a PR state decides, and an open/draft
header ends the scan. The state log also names the badge it matched.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MM2QDa5SMgF9CRr94AZzyW
The plain click still approves only the current PR. On a PR that is part
of a stack (the N/M badge next to the state label), the right-click menu
gains "Approve whole stack", which approves every PR bottom to top with
the default comment, skipping merged/closed/own/already-approved ones and
reporting the tally on the button.

Stack members are read from the page's embedded JSON payload (graphql
shape: stack.entries[].pullRequest.number), falling back to the stack
map popover opened through the badge.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MM2QDa5SMgF9CRr94AZzyW
Jumping between PRs through the stack popover is a soft navigation: the
url already names the next PR while the previous PR's sidebar is still
in the dom, so the live detection read the old "approved" and cached it
on the new PR. The fetched conversation page is now the only cached
source; the live page is an uncached hint, used only once the tab title
names the current PR.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MM2QDa5SMgF9CRr94AZzyW
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MM2QDa5SMgF9CRr94AZzyW
One approvePr primitive serves the plain click and the stack run, one
cache-aware loadPrState serves the button and the stack run, and a stack
member's conversation page is fetched once for both its state and its
csrf token. The stack payload walker now only accepts the documented
stack.entries shape, the popover is found from its pr links, and the
badge lookup does the cheap text test first.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MM2QDa5SMgF9CRr94AZzyW

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

On conversation pages the quick-approve button can briefly render for merged/closed/approved PRs due to async-only state caching in getPrDisplayState.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This PR updates the GitHub PR Approve Helper userscript to support approving an entire LinearB/GitHub PR stack, and fixes state-detection/caching issues that could incorrectly hide the quick-approve button or show an “Already approved” state after soft navigation.

Changes:

  • Add a “Approve whole stack (N PRs)” action to the quick-approve context menu and implement bottom-to-top stack approval with skipping logic.
  • Tighten PR state detection to rely on the header badge (avoiding false “Merged/Closed” from timeline cross-references) and rework state loading/caching.
  • Update README metadata (last-updated date) and bump userscript version to 1.3.0.
File summaries
File Description
README.md Updates the “Updated” date for the GitHub PR Approve Helper entry.
github-pr-approve-helper.user.js Implements stack approval + refactors PR state/token discovery and caching to fix UI/state issues.
Review details
  • Files reviewed: 2/2 changed files
  • Comments generated: 1
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread github-pr-approve-helper.user.js Outdated
On the conversation page the state was loaded through the async path
even though the document was already there, so the quick-approve button
could render for one frame on a merged or already-approved PR before the
scan re-ran. Detect from the live document synchronously there.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MM2QDa5SMgF9CRr94AZzyW

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Verified edge cases can (1) throw during JSON payload scanning when script.textContent is null and (2) incorrectly show “🎉 Approved” when a single-PR action was actually skipped.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (1)

Previously missed (1) — in code that hasn't changed since the last review.

github-pr-approve-helper.user.js:326

  • jsonPayloads assumes script.textContent is always a string. In the DOM API it can be null (e.g., an empty script node), which would throw at .includes(...) and break CSRF/stack parsing.
  • Files reviewed: 2/2 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment thread github-pr-approve-helper.user.js Outdated
jsonPayloads treats a null textContent as empty instead of throwing.
When every pr of a run was skipped (merged, closed, own or approved
meanwhile) the button now says so rather than showing the success label.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MM2QDa5SMgF9CRr94AZzyW
@MishaKav
MishaKav requested a lite review from Copilot September 4, 2026 15:04
@MishaKav
MishaKav merged commit 0119829 into main Sep 4, 2026
1 check passed
@MishaKav
MishaKav deleted the claude/tampermonkey-review-disappear-oye1sd branch September 4, 2026 15:04

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The stack-run path can treat the current PR as open while its authoritative conversation-page state is still loading, which can bypass the intended skip logic and cause incorrect approvals or run failures.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details
  • Files reviewed: 2/2 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment on lines +949 to +952
const isCurrent = pr.number === parsePrPath()?.number;
const doc = isCurrent ? null : await fetchPrDoc(pr);
const state = isCurrent ? getPrDisplayState(pr) : await loadPrState(pr, doc);
if (state !== 'open') {
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.

3 participants