merge-compile-guard: its gh reads skip the gh guard's repeat throttle; a slow GitHub says so; a missing compile names the fleet compile-check
What
scripts/ci/merge_compile_guard.py (the merge-compile-guard PreToolUse hook):
- Its gh reads skip the gh PATH guard's repeat throttle. Every
ghcall the guard makes setsCMUX_GH_GUARD_DISABLE=1. The cmux-ci client calls are unchanged. - A slow or failing read says so. Each call keeps its 15 s timeout, inside the guard's 100 s budget. The refusal now names the read that timed out or failed, for example
could not read #N from GitHub (\gh api repos/...` did not answer within 15 s). Before, it blamed a rate limit every time.CMUX_MERGE_GUARD_GH_TIMEOUT` can only lower the timeout; tests use it. - A missing compile result names the fleet compile-check first. When the head has no checks, its Release compile is queued or running, or routing hasn't created it yet, the refusal leads with
cmux-ci compile-check --ref <full head sha> --appon the fleet dev-build workers, as COMMON.md says. The Release compile state follows in parentheses.
Why
Lanes were refused merges of #23611 (head f397fb85) three times with "could not read #23611 from GitHub", while the REST quota was fine. palette-audit measured the cause:
- The hook runs with the session's
CLAUDE_SESSION_ID. - The
ghon cosmic-gray's PATH is~/.local/lib/cmux-local-build-guards/gh, which sleeps up to 30 s when the same caller repeats an identical read within 30 s. - A lane's
gh pr view Nfollowed by the merge made the hook repeat that read, the wrapper slept 11-30 s, and the 15 s timeout ran out.
The guard reads once per merge and never polls, so the anti-polling throttle doesn't apply to it (deputy-2's decision). The wrapper stays as it is for agents. HQ's require-cmux-guards got the same bypass in hq#2830.
Tests
tests/test_merge_compile_guard.py, written first (a187dbac, red on the old guard: 2 failures, 1 error):
- A fake
ghsleeps 30 s unlessCMUX_GH_GUARD_DISABLE=1. A merge after a green compile must go ahead in under 10 s. On the old guard this reproduced the exact refusal the lanes saw. - A fake
ghthat is really slow, with the timeout lowered to 1 s: the refusal says it "did not answer within 1 s" and doesn't mention a rate limit. - No checks, a queued compile and routing not yet done each name
cmux-ci compile-check --ref <full head> --appand "fleet dev-build workers" before the Release compile.
On cmux16 at 68a0d520, 47 tests pass under Python 3.13 and under the system /usr/bin/python3 3.9.
Agent session: cosmic-gray
๐ค Generated with Claude Code
![View with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v3-3x.png)
![Autofix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark-3x.png)
Need help on this PR? Tag @codesmith-bot with what you need.
Summary by cubic
Fixes the merge-compile-guard hook so slow GitHub reads no longer block merges with a misleading rate-limit error, and a missing compile result points to the fleet compile-check.
Changes
- The guard's
ghreads setCMUX_GH_GUARD_DISABLE=1to skip the PATH guard's 30 s repeat throttle; a lane's owngh pr view Npreviously made that wrapper sleep and timeout, refusing merges of #23611 despite a fine REST quota. - Refusals now name the read that timed out or failed โ
`gh api repos/...` did not answer within 15 sโ instead of always blaming a rate limit. - Each read clears the previous failure cause, so a later unreadable read isn't blamed for an earlier one.
- When a head has no compile result, the refusal leads with
cmux-ci compile-check --ref <full sha> --appon the fleet dev-build workers, built from its parts so it reads correctly with multiple Swift files or paths containing " and ". CMUX_MERGE_GUARD_GH_TIMEOUTlowers the per-call timeout; the 100 s budget stays.