Skip to content

ci: fix pr-queue-hygiene mergeable check to use per-PR API fetch - #240

Open
mrbobbytables wants to merge 1 commit into
mainfrom
fix-pr-queue-hygiene-mergeable-unknown
Open

mrbobbytables wants to merge 1 commit into
mainfrom
fix-pr-queue-hygiene-mergeable-unknown

Conversation

@mrbobbytables

Copy link
Copy Markdown
Member

Summary

Fixes #217: the mergeability check for the GOVERNANCE.md merge-queue
hygiene rule (issue #58) needs to re-fetch each PR individually rather
than trust gh pr list's mergeable field, which GitHub computes
asynchronously and frequently returns UNKNOWN on list responses —
silently letting conflicting PRs evade the 48h flagging check.

This PR adds:

  • scripts/pr-queue-hygiene.mjs — fetches all open PRs (paginated past
    gh pr list's default --limit 30), then re-checks mergeability
    per-PR via GET /repos/{owner}/{repo}/pulls/{number} (which forces a
    fresh computation) before deciding a PR is conflicting. Persists a
    first-conflict-observed marker comment so the 48h window is measured
    from when the PR actually started conflicting, not from updatedAt
    (which changes on any commit/label/comment).
  • tests/pr-queue-hygiene.test.mjs — unit tests for the marker
    parsing, age calculation, and conflict-detection logic.
  • pr-queue-hygiene npm script for manual/local runs.
  • A GOVERNANCE.md "Operational status" note under Merge-queue hygiene.

Context: related, still-open PR #115

PR #115 proposed this same feature but shipped it as inline bash in
.github/workflows/pr-queue-hygiene.yml using gh pr list's
mergeable field directly — the exact bug #217 reports. A later
revision to #115 extracted the logic into a script
(scripts/pr-queue-hygiene.mjs) that fixes it correctly, but the
workflow file itself was never updated to call that script, so the
bug remained live in the step that actually runs. The PR's author
noted its fork's credentials lack the workflow OAuth scope needed to
push the one-line wiring change to .github/workflows/*.

This PR is in the same boat: this environment's push credentials
also lack the workflow scope
, so .github/workflows/pr-queue-hygiene.yml
itself could not be pushed here. A maintainer with that scope will
need to add it directly — the full contents are below (also mirrors
what #115 needed):

name: PR queue hygiene

# Operationalizes the GOVERNANCE.md merge-queue hygiene rule (see issue #58):
# "Agent PRs that conflict with the base branch for more than 48 hours should
# be rebased by their author agent or closed as superseded." This workflow
# does not close PRs automatically — deciding whether a conflicting PR is
# superseded requires judgment a scheduled job shouldn't make. Instead it
# flags them (label + comment) so a maintainer or the author agent can act
# quickly, keeping the queue from silently rotting.
#
# The actual check lives in scripts/pr-queue-hygiene.mjs, which re-fetches
# each PR's mergeability individually rather than trusting the list
# endpoint's `mergeable` field (frequently `UNKNOWN`/stale there -- see
# issue #217), and paginates past `gh pr list`'s default 30-PR limit.

on:
  schedule:
    - cron: '0 */6 * * *'
  workflow_dispatch:

permissions:
  contents: read
  pull-requests: write

jobs:
  flag-stale-conflicts:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: 20

      - name: Ensure hygiene label exists
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          REPO: ${{ github.repository }}
        run: |
          gh label create "needs-rebase-or-close" \
            --repo "$REPO" \
            --color "d93f0b" \
            --description "Conflicting with base >48h per GOVERNANCE.md merge-queue hygiene" \
            2>/dev/null || true

      - name: Flag PRs conflicting with base for more than 48h
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          REPO: ${{ github.repository }}
        run: node scripts/pr-queue-hygiene.mjs

Once this workflow file is added (either by a maintainer here, or by
rebasing/merging #115 with its workflow step updated to run: node scripts/pr-queue-hygiene.mjs), #115 could likely be closed as
superseded, since this PR delivers the corrected script + tests it was
trying to add.

Testing

  • node --test — full suite passes (63 tests, including the 8 new
    ones in tests/pr-queue-hygiene.test.mjs).
  • npx prettier --check — passes on all changed files.
  • Manually validated the workflow YAML with js-yaml.

— hive: backend=copilot

Fixes #217: scripts/pr-queue-hygiene.mjs re-fetches each
PR's mergeability individually via GET /repos/{owner}/{repo}/pulls/{number}
instead of relying on the pulls-list endpoint's 'mergeable' field, which
GitHub computes asynchronously and frequently returns as UNKNOWN on list
responses -- silently letting conflicting PRs evade the 48h hygiene check.

Also fixes two related bugs caught in review of the underlying,
still-open PR #115 (which operationalizes this GOVERNANCE.md rule but
could not land the workflow file itself due to workflow-scope
credential limits on its fork):
- Paginates through every open PR instead of relying on 'gh pr list'
  default --limit 30, which silently drops PRs past one page.
- Persists a first-conflict-observed marker comment on each PR and
  measures the 48h window from that timestamp, since 'updatedAt'
  reflects any PR update (commit, label, comment), not how long the
  PR has actually been conflicting.

Adds a pr-queue-hygiene npm script and tests, and documents the
operational status in GOVERNANCE.md. The .github/workflows/ wiring
(.github/workflows/pr-queue-hygiene.yml calling this script on a
schedule) is included in the PR description for a maintainer to apply,
since this environment's push credentials lack the GitHub 'workflow'
OAuth scope required to push changes under .github/workflows/.

Signed-off-by: mrbobbytables <mrbobbytables@users.noreply.github.com>
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.

[scanner] PR#115: pr-queue-hygiene.yml reads mergeable from 'gh pr list' (often UNKNOWN) — conflicting PRs silently skipped

1 participant