merge_freeze: read lists one bounded page per gh call, not --paginate
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
checkreturns 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