merge_freeze: read lists one bounded page per gh call, not --paginate

Merged
TE
9d ago
+81-8

What

scripts/ci/merge_freeze.py read its lists (open freeze issues, label events, the PR's changed files) with gh api --paginate. The gh quota wrapper refuses that call because one invocation can make any number of requests, so it can't be charged. The read failed, check exited 2, and scripts/gh-merge-green refused merges it had already verified green with "freeze state unknown ... merge_freeze.py check exited 2".

gh_items now makes one gh api call per page, with an explicit per_page=100&page=N, and stops at the first short page. It reads at most 30 pages (3000 items, which is also the most changed files GitHub's files API returns). If a list goes past the cap, it raises, and check exits 2 with "refusing to read a list cut short as complete". A capped list is never treated as "no freeze".

Before / after

Same PR, same base, run through the quota wrapper:

before: cannot read merge freezes for manaflow-ai/cmux#26543: gh-quota: refused ... `gh api --paginate` makes any number of requests, so it can't be charged.
before exit=2
after exit=0

Tests

tests/test_merge_freeze.py adds PaginationTests:

  • each page is a separate bounded call, and a short page ends the list
  • a list past the page cap raises
  • check returns status 2 when the cap is hit
  • the script never passes --paginate

20/20 pass.

Related

#26072 makes the same change but has no page cap (while True). This PR adds the cap and fail-closed behaviour on top of that approach. If one of the two lands, the other should close.

scripts/gh-merge-green also calls gh api --paginate in six places (changed files, check-runs, statuses). Each of those already falls back to GraphQL or git when the call is refused, so none of them blocks a merge. They are left for a follow-up.

Risk

Low. Only the freeze gate's reads change, and every failure path still exits 2 (blocked), never 0.

Agent session: agent session

šŸ¤– Generated with Claude Code