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

Merged
TE
16d ago
+101-16

What

scripts/ci/merge_compile_guard.py (the merge-compile-guard PreToolUse hook):

  1. Its gh reads skip the gh PATH guard's repeat throttle. Every gh call the guard makes sets CMUX_GH_GUARD_DISABLE=1. The cmux-ci client calls are unchanged.
  2. 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.
  3. 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> --app on 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 gh on 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 N followed 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 gh sleeps 30 s unless CMUX_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 gh that 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> --app and "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 Autofix with [code]smith
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 gh reads set CMUX_GH_GUARD_DISABLE=1 to skip the PATH guard's 30 s repeat throttle; a lane's own gh pr view N previously 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> --app on 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_TIMEOUT lowers the per-call timeout; the 100 s budget stays.