Skip to content

watch-pr: stack discovery ends at a fork PR from main and at a branch cycle - #37

Open
J0UH wants to merge 1 commit into
chhoumann:mainfrom
J0UH:fix/watch-pr-stack-and-readiness
Open

J0UH wants to merge 1 commit into
chhoumann:mainfrom
J0UH:fix/watch-pr-stack-and-readiness

Conversation

@J0UH

@J0UH J0UH commented Oct 21, 2026

Copy link
Copy Markdown

watch-pr --stack never returns in a repository that has an open PR from a fork's main into main. This fixes stack discovery and adds a test for each case. I left the release to you: no VERSION, manifest, or CHANGES.md change. The pins stay at 88d4751.

The bug

orderStack in watch-pr/github.ts keys every open PR by its head branch name. gh pr list reports a fork PR's head as the bare branch name, so a PR from someone:main into main is stored under main. Any PR based on main then finds that fork PR as its parent, the fork PR finds itself as its own parent, and the ancestor loop pushes it forever. Watching the fork PR itself loops the same way. Two same-repository PRs whose branches point at each other (a into b, b into a) loop too.

This is common. cursor/plugins has three open PRs from a fork's main right now (#173, #176, #330). On main, against a PR there that is based on main:

$ perl -e 'alarm 30; exec @ARGV' ./watch-pr/watch-pr --owner cursor --repo plugins --pr 744 --stack --status-only --pretty
exit=142 after 30s

It printed nothing and was killed by the alarm. Lauren's orderStack at 88d4751 has the same code. github.ts is already a declared fork here.

The fix

  • The gh reader asks for isCrossRepository, and OpenPullRequest carries it.
  • A PR whose head is in another repository is never a parent, and discovery does not look for children above it. No PR in this repository can be based on a branch in someone's fork.
  • The ancestor walk stops when it reaches the starting PR or one it already took.

The field is optional on the type so that the upstream github.test.ts keeps compiling unchanged. The reader always sets it and fails if gh leaves it out. The tools/forks.json entries for github.ts and types.ts now name the change.

After the fix, the same command:

| PR | CI | Review | Merge |
| --- | --- | --- | --- |
| [#744](https://github.com/cursor/plugins/pull/744) | ➖ no checks | ✅ | ✅ |
exit=0 after 6s

Tests

New port-only file watch-pr/stack.test.ts:

  • "does not take a fork PR from main as the parent of a PR on main"
  • "does not stack the PRs on main above a fork PR from main"
  • "stops at a cycle of branches"
  • "watches the stack without the fork PR": runs the real gh reader against a fake gh that lists a fork PR from main, with --stack --status-only.

On main the three orderStack tests never finish (each was killed by a 25-second alarm, exit 142), and the gh reader test fails:

error: expect(received).toBeNull()
Received: "SIGTERM"
(fail) the gh reader on a repository with an open PR from a fork's main > watches the stack without the fork PR [5050.19ms]

On this branch all four pass.

Checks

  • GIT_CONFIG_GLOBAL=/dev/null bun test tests/ --timeout 30000: 550 pass, 46 skip, 0 fail
  • bun tools/generate.mjs --check: 17 generated files current, all checks pass
  • Scripts: bun run typecheck clean, GIT_CONFIG_GLOBAL=/dev/null bun test orch watch-pr check-plan.test.ts: 123 pass, 0 fail
  • bunx prettier@3.6.2 --check passes on stack.test.ts, and github.ts and types.ts are unchanged by Prettier
  • bun tools/sync.mjs {pstack,cursor-team-kit,thermos} 88d4751 --dry-run: exit 0 for all three. pstack lists github.ts (60 changed lines) and types.ts (58) as declared policy forks and stack.test.ts as port-only

orderStack keyed every open PR by its head branch name. An open PR from
a fork's main into main has the head name main, so it became its own
parent, and the ancestor walk pushed it forever for that PR and for
every PR based on main. watch-pr --stack on cursor/plugins#744 never
printed and was killed by a 30-second alarm. Two PRs whose branches
point at each other looped the same way.

A PR whose head is in another repository is no longer a parent, and
stack discovery does not look for children above it. The ancestor walk
stops when it reaches a PR it has already taken. The gh reader now asks
for isCrossRepository.
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