Skip to content

fix(server): restore merge actions on hosts without the stacks API - #12645

Open
bquenin wants to merge 3 commits into
pingdotgg:mainfrom
bquenin:fix/github-stack-404
Open

bquenin wants to merge 3 commits into
pingdotgg:mainfrom
bquenin:fix/github-stack-404

Conversation

@bquenin

@bquenin bquenin commented Sep 19, 2026 •

Copy link
Copy Markdown

What Changed

Recognize HTTP 404 in gh stderr as not-found in VcsProcess, so hosts without the stacks API can reach the existing no-stack fallback.

Limit that fallback to the stacks listing and verify access before returning no stack. A listing 404 triggers a read-only request for one commit on the same PR, using the same host and credentials. The commit-list endpoint requires the same pull-request read permission as the stacks API. If the probe fails, its error propagates; a 404 while reading an already discovered stack's details also stays an error.

Replace the pre-classified fallback mock with raw process-output tests through VcsProcess, GitHubCli, and GitHubPullRequestCli. Cover both observed 404 formats, denied/missing PR access, invalid credentials, rate limits, and service failures.

Why

On GitHub Enterprise Server, the stacks endpoint returns gh: Not Found (HTTP 404). The process boundary currently classifies this as command-failed, so the existing not-found fallback never runs. An otherwise mergeable PR stays stuck on Retry stack lookup, with the normal merge action hidden.

GitHub also uses 404 for inaccessible resources. Verifying PR-read access prevents an authorization-scoped 404 from being silently treated as an unstacked PR. A repository-metadata or PR-detail probe would be weaker: those can succeed without pull-request read permission. The client-side stack guard and merge-permission checks are unchanged.

Validation

  • Regression tests fail before their corresponding fixes: raw 404 classification, failed access probes, and known-stack detail failures.
  • All 312 tests pass across VcsProcess.test.ts, GitHubCli.test.ts, GitHubPullRequestCli.test.ts, PullRequestSyncReactor.test.ts, and pullRequestDetail.logic.test.ts.
  • Server TypeScript check, targeted lint, formatting, and git diff --check pass.
  • Read-only GHES check: the stacks listing returns 404 while the PR commit-list probe succeeds.
  • Initial fix was tested in an isolated local UI: reproduced Retry stack lookup, then verified Squash and merge and its confirmation dialog. A rebuilt macOS app successfully squash-merged a real enterprise PR and displayed Merged.

No UI components or layouts change. Screenshots from the local validation contain private enterprise repository details, so they are not attached.

Checklist

  • This PR is small and focused.
  • I explained what changed and why.
  • Regression tests cover the actual CLI failure rather than only a pre-classified mock.
  • Existing UI behavior was verified locally, including a completed merge.

Summary by CodeRabbit

  • Bug Fixes
    • Pull requests are marked as not stacked only after a 404 stack-preview response is successfully verified.
    • Existing stack information is preserved when retrieving additional pull request details fails.
    • GitHub CLI errors now provide more accurate handling for missing pull requests, authentication failures, rate limits, permission issues, service outages, and network errors.

@github-actions github-actions Bot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:XS 0-9 changed lines (additions + deletions). labels Sep 19, 2026
(normalized.includes("could not resolve to a pullrequest") ||
normalized.includes("repository.pullrequest") ||
normalized.includes("no pull requests found for branch") ||
normalized.includes("http 404") ||

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟠 High vcs/VcsProcess.ts:96

An inaccessible GitHub resource is classified as not-found, so an expired or insufficiently scoped token causes GitHubCli to raise GitHubPullRequestNotFoundError and getPullRequestStack to return null, discarding the existing stack and entering the non-stack merge flow. The generic http 404 match also covers GitHub's authorization-scoped 404 responses; remove it or restrict it to an explicit pull-request-not-found message so authentication failures remain actionable.

-        normalized.includes("http 404") ||
🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/server/src/vcs/VcsProcess.ts around line 96:

An inaccessible GitHub resource is classified as `not-found`, so an expired or insufficiently scoped token causes `GitHubCli` to raise `GitHubPullRequestNotFoundError` and `getPullRequestStack` to return `null`, discarding the existing stack and entering the non-stack merge flow. The generic `http 404` match also covers GitHub's authorization-scoped 404 responses; remove it or restrict it to an explicit pull-request-not-found message so authentication failures remain actionable.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed in bedb2a1. The fallback now applies only to the stacks listing and verifies PR-read access before returning null. It probes GET /repos/{owner}/{repo}/pulls/{number}/commits?per_page=1 with the same host and credentials; every probe failure propagates. Unlike the basic repository/PR endpoints, listing PR commits requires the same pull-request read permission as listing stacks.

A not-found error fetching an already discovered stack's details is no longer caught by the fallback either. This keeps access failures actionable and preserves the previous stack instead of clearing it.

Added regression coverage for access-probe 404/401/403, signed-out credentials, 429/503, and known-stack detail failures. All 312 focused tests pass, along with server typecheck and targeted lint. The read-only GHES check still returns a stacks 404 with a successful permission probe, preserving the original fix.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Sorry, I'm unable to act on this request because you do not have permissions within this repository.

@macroscopeapp

macroscopeapp Bot commented Sep 19, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Would Approve

Macroscope's review found this PR approvable — This is a focused two-file bug fix that restores the existing no-stacks fallback through one gh error-classification change, with regression tests covering the raw CLI path. The generic HTTP 404 match carries an unresolved risk of treating authentication-scoped 404 responses as missing pull requests, which remains a significant correctness concern.

Not approved because:

  • 1 blocking correctness issue found at or above your repo's Minimum Blocking Severity

Adjust the Minimum Blocking Severity for this repo — including turning it Off — in Settings. You can add or adjust custom eligibility rules. Learn more.

@coderabbitai

coderabbitai Bot commented Sep 19, 2026 •

Copy link
Copy Markdown

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: pingdotgg/t3code/.coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: 5b7875e8-0ce3-4153-ad67-bcd9671f01bc

📥 Commits

Reviewing files that changed from the base of the PR and between 3bfbe59 and bedb2a1.

📒 Files selected for processing (2)
  • apps/server/src/pullRequest/GitHubPullRequestCli.test.ts
  • apps/server/src/pullRequest/GitHubPullRequestCli.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 8 remain after this review.


📝 Walkthrough

Walkthrough

GitHub CLI HTTP 404 failures now use the not-found classification. A stacks preview 404 now triggers a pull request access check. The stack read returns null only when that check succeeds, and related failures propagate.

Changes

GitHub CLI stack handling

Layer / File(s) Summary
Classify HTTP 404 failures
apps/server/src/vcs/VcsProcess.ts
GitHub command failures containing http 404 now use the not-found classification.
Verify stack access after 404
apps/server/src/pullRequest/GitHubPullRequestCli.ts, apps/server/src/pullRequest/GitHubPullRequestCli.test.ts
A stacks preview 404 triggers a commits endpoint check. A successful check returns null; verification and detail failures propagate. Tests cover the typed error mappings and mocked process calls.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Bug fix

Sequence Diagram(s)

sequenceDiagram
  participant GitHubPullRequestCli
  participant VcsProcess
  participant GitHubAPI
  GitHubPullRequestCli->>VcsProcess: Read stacks preview
  VcsProcess->>GitHubAPI: Request stacks data
  GitHubAPI-->>VcsProcess: Return 404
  GitHubPullRequestCli->>VcsProcess: Check pull request commits
  VcsProcess->>GitHubAPI: Request commits with --silent
  GitHubAPI-->>VcsProcess: Return success or typed failure
  VcsProcess-->>GitHubPullRequestCli: Return null or propagate failure
Loading

Suggested reviewers: bil0000

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 3…
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.
Title check ✅ Passed The title clearly and concisely describes restoring merge actions for hosts without the stacks API, which matches the primary change.
Description check ✅ Passed The description includes the required What Changed, Why, and Checklist sections. It explains the implementation, rationale, validation, and lack of UI changes. The UI Changes section is appropriately …
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR

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

@github-actions github-actions Bot added size:S 10-29 changed lines (additions + deletions). and removed size:XS 0-9 changed lines (additions + deletions). labels Sep 19, 2026
incognitojam added a commit to incognitojam/styal that referenced this pull request Sep 24, 2026
The tracked upstream PR list held bare numbers. The reports showed each
PR's intake status, but not why the fork was waiting on it or when the
entry could be removed.

Each entry in `.github/upstream-tracked-prs.json` is now `{ "pr": 123,
"reason": "..." }`, and the decoder rejects an entry without a reason.
`upstream-queue.ts status` prints the reason under each PR, and the
tracked PR report in the Upstream lag report and promotion summaries has
a new "Why tracked" column.

List changes:

- Removed `pingdotgg#9511`, `pingdotgg#9753`, `pingdotgg#9773` and `pingdotgg#9807`, which
are already recorded as imported.
- Added the GitHub stack merge chain: `pingdotgg#10839`,
`pingdotgg#10870`, `pingdotgg#10875` and `pingdotgg#11486`, plus the open follow-up `pingdotgg#12645`. The
fork's merge button uses GitHub's legacy merge endpoint, which GitHub
documents as unable to merge stacked PRs; `pingdotgg#10875` adds a merge stack
action and replaces the fork's stack section.
- Wrote reasons for the other existing entries from the investigations
that added them.

The runbook now says to remove an entry once the report shows it
recorded or once its reason no longer applies, and that a reason writes
a fork PR as "fork #123" while a bare number means an upstream PR.

## Validation

- Ran `node scripts/upstream-queue.ts status` and `node
scripts/upstream-tracked-prs-report.ts` against freshly fetched fork and
upstream refs. All 13 entries show their reason; 11 are pending and
`pingdotgg#10845` and `pingdotgg#12645` are open upstream.
- Decoder tests cover a valid entry, a bare number, a string PR number,
a duplicate PR, and a missing or blank reason. The tracked PR and intake
tests pass (16), along with the scripts typecheck and targeted lint.

---
Written by an agent (Claude Code, claude-opus-5-5).

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:S 10-29 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant