Skip to content

fix(stack): discover a stack's pull requests without the Search API - #1862

Merged
mergify[bot] merged 1 commit into
mainfrom
devs/jd/jd/stack-discovery-without-search/discover-stack-s-pull-reqs-without-search-api--bd22adea
Oct 1, 2026
Merged

mergify[bot] merged 1 commit into
mainfrom
devs/jd/jd/stack-discovery-without-search/discover-stack-s-pull-reqs-without-search-api--bd22adea

Conversation

@jd

@jd jd commented Sep 30, 2026

Copy link
Copy Markdown
Member

stack push, list, sync and checkout found a stack's pull
requests with GET /search/issues. Environments that only allow
repository-scoped API paths answer that with a 403, so every remote
stack command failed there. Claude Code on the web's GitHub proxy is
one of them.

Discovery now uses only /repos/{owner}/{repo}/... endpoints:

  1. GET /git/matching-refs/heads/<prefix>/ lists every branch under
    the stack prefix in one call, with no pagination. I checked this
    against a repository holding 1,308 matching branches. Only
    branches one segment below the prefix whose name ends in a
    Change-Id are kept, which is the shape stack push creates.
  2. For each local commit whose Change-Id is on no live branch, the
    branch it would be pushed to (<prefix>/<slug>) is added. This is
    how a merged PR is still found after GitHub deleted its branch.
    stack sync needs that PR to drop the commit. The merged state only
    counts when the PR's head.sha equals the local commit, and an
    unchanged commit has an unchanged title, so its slug is the branch
    it was pushed under.
  3. GET /pulls?head=owner:<branch>&state=all runs once per branch.
    That filter reads the PR's recorded head, so it also returns closed
    and merged PRs whose branch is gone. I checked this on a merged PR
    whose branch had been deleted.
  4. Results are sorted by updated_at, newest first, the same order
    the search used, then grouped by Change-Id as before. The author
    filter runs locally on user.login, case-insensitively like
    author: was.

Why not one paginated GET /pulls listing filtered by head prefix?
Its cost grows with the number of open PRs in the repository, not
with the size of the stack. It also misses merged PRs, and those
would still need step 2. The approach above costs one call plus one
per branch. The old one cost one search call plus one per PR.

Search is not kept as a fallback. The only thing it could still find
is a merged PR whose branch is deleted and whose Change-Id is no
longer in the local stack. Nothing reads that PR: orphans are
reported only when open. Search also has its own small rate limit and
an index that lags behind new PRs, which the repository endpoints do
not.

The payloads now come from the list endpoint. It carries everything
downstream reads, including the native stack object, except
mergeable. stack list therefore fetches each unmerged PR itself
for its (conflicting) marker, as part of its CI and review fan-out.
stack open skips that, as it skips the fan-out.

Fixes #1861

Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com

`stack push`, `list`, `sync` and `checkout` found a stack's pull
requests with `GET /search/issues`. Environments that only allow
repository-scoped API paths answer that with a 403, so every remote
stack command failed there. Claude Code on the web's GitHub proxy is
one of them.

Discovery now uses only `/repos/{owner}/{repo}/...` endpoints:

1. `GET /git/matching-refs/heads/<prefix>/` lists every branch under
   the stack prefix in one call, with no pagination. I checked this
   against a repository holding 1,308 matching branches. Only
   branches one segment below the prefix whose name ends in a
   Change-Id are kept, which is the shape `stack push` creates.
2. For each local commit whose Change-Id is on no live branch, the
   branch it would be pushed to (`<prefix>/<slug>`) is added. This is
   how a merged PR is still found after GitHub deleted its branch.
   `stack sync` needs that PR to drop the commit. The merged state only
   counts when the PR's `head.sha` equals the local commit, and an
   unchanged commit has an unchanged title, so its slug is the branch
   it was pushed under.
3. `GET /pulls?head=owner:<branch>&state=all` runs once per branch.
   That filter reads the PR's recorded head, so it also returns closed
   and merged PRs whose branch is gone. I checked this on a merged PR
   whose branch had been deleted.
4. Results are sorted by `updated_at`, newest first, the same order
   the search used, then grouped by Change-Id as before. The author
   filter runs locally on `user.login`, case-insensitively like
   `author:` was.

Why not one paginated `GET /pulls` listing filtered by head prefix?
Its cost grows with the number of open PRs in the repository, not
with the size of the stack. It also misses merged PRs, and those
would still need step 2. The approach above costs one call plus one
per branch. The old one cost one search call plus one per PR.

Search is not kept as a fallback. The only thing it could still find
is a merged PR whose branch is deleted and whose Change-Id is no
longer in the local stack. Nothing reads that PR: orphans are
reported only when open. Search also has its own small rate limit and
an index that lags behind new PRs, which the repository endpoints do
not.

The payloads now come from the list endpoint. It carries everything
downstream reads, including the native `stack` object, except
`mergeable`. `stack list` therefore fetches each unmerged PR itself
for its `(conflicting)` marker, as part of its CI and review fan-out.
`stack open` skips that, as it skips the fan-out.

Fixes #1861

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: Ibd22adea3084f027d64718be8fa6f331a7cb9b9b
Copilot AI balanced review requested due to automatic review settings September 30, 2026 14:40
@mergify
mergify Bot deployed to Mergify Merge Protections September 30, 2026 14:40 Active
@jd
jd deployed to func-tests-live September 30, 2026 14:40 — with GitHub Actions Active
@mergify

mergify Bot commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Merge Protections

🟢 All 6 merge protections satisfied — ready to merge.

Show 6 satisfied protections

🟢 🤖 Continuous Integration

  • all of:
    • check-success=ci-gate

🟢 👀 Review Requirements

  • any of:
    • #approved-reviews-by>=2
    • author = dependabot[bot]
    • author = mergify-ci-bot
    • author = renovate[bot]

🟢 Enforce conventional commit

Make sure that we follow https://www.conventionalcommits.org/en/v1.0.0/

  • title ~= ^(fix|feat|internal|docs|style|refactor|perf|test|build|ci|chore|revert|ui)(?:\(.+\))?!?:

🟢 🔎 Reviews

  • #changes-requested-reviews-by = 0
  • #review-requested = 0
  • #review-threads-unresolved = 0

🟢 📕 PR description

  • body ~= (?ms:.{48,})

🟢 🚦 Auto-queue

When all merge protections are satisfied, this pull request will be queued automatically.

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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@mergify
mergify Bot requested a review from a team September 30, 2026 14:48
@jd
jd marked this pull request as ready for review October 1, 2026 14:13
@mergify
mergify Bot requested a review from a team October 1, 2026 15:27
@mergify

mergify Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Merge Queue Status

This pull request spent 8 minutes in the queue, including 7 minutes 30 seconds running CI.

Required conditions to merge

@mergify mergify Bot added the queued label Oct 1, 2026
@mergify mergify Bot mentioned this pull request Oct 1, 2026
34 of 40 tasks
@mergify
mergify Bot merged commit aef6ef2 into main Oct 1, 2026
22 of 23 checks passed
@mergify
mergify Bot deleted the devs/jd/jd/stack-discovery-without-search/discover-stack-s-pull-reqs-without-search-api--bd22adea branch October 1, 2026 16:22
@mergify mergify Bot removed the queued label Oct 1, 2026

This branch was successfully deployed

2 active deployments
func-tests-live — 21c06c00 Deployed Sep 30, 2026 by jd via live-tests #1951
Mergify Merge Protections — 21c06c00 Deployed Sep 30, 2026 by mergify[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

stack push, list, sync and checkout fail where GitHub API access is limited to /repos/{owner}/{repo}/… paths, because PR discovery uses the Search API

4 participants