ci(next): refuse a hand merge of a Swift PR before its Release compile passed

Merged
TE
16d ago
+353-0

Tonight #22835, #21480 and #21487 were merged by hand (gh pr merge) while their Release compile was still pending. Before that, #22764 and #22778 broke the base compile the same way. next-autoland now waits for the compile (leo-workspace #662), but hand merges skipped that check.

This PR adds a Claude Code PreToolUse hook, scripts/ci/merge-compile-guard-hook.sh โ†’ merge_compile_guard.py, registered in .claude/settings.json next to the base-merge warning from #22888. It refuses gh pr merge [N|URL|#N] (including --auto) and gh api .../pulls/N/merge when all of these hold:

  • the PR is a manaflow-ai/cmux PR into feat-cmux-next;
  • it changes Swift, a C/ObjC header or source, a module map, an .xcconfig, an Xcode project or a package manifest;
  • its head's cmux-next Release compile (Xcode 26) is queued or running, not created yet while cmux-next routing or push preflight still runs, or finished red or cancelled.

A rerun that passed counts. A compile the router skipped (iOS, packages outside the CmuxNext graph, forks) does not hold the merge, so those PRs aren't stranded. When GitHub can't be read, it refuses and says so. Any command that doesn't merge a PR exits in bash without starting Python.

Together with #22914 (a Release compile on every feat-cmux-next push, superseded pushes included), this covers both of the coordinator's asks.

Scope: the hook fires for Claude sessions whose project is a cmux checkout. A session started elsewhere that merges with -R manaflow-ai/cmux isn't covered; that would need a user-level hook in leo-workspace (follow-up).

Changelog

none

Observed / Not run

Observed, on cmux16:

  • python3 tests/test_merge_compile_guard.py fails at the test commit (no guard module) and passes at the head (11 tests). The hook tests run the real bash wrapper against a fake gh on PATH.
  • tests/test_base_merge_warning.py still passes.
  • prepush-gate: no new violations.

Not run: a live merge attempt through the hook.

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

Blocks hand merges of feat-cmux-next PRs that change Swift (or related build files) until their Release compile passes. Previously gh pr merge could land such PRs while the compile was pending, breaking the base compile (#22835, #21480, #21487).

Adds a Claude Code PreToolUse hook (scripts/ci/merge-compile-guard-hook.sh โ†’ merge_compile_guard.py) that refuses gh pr merge and gh api .../pulls/N/merge when the head's cmux-next Release compile (Xcode 26) is queued, running, not yet created while cmux-next routes, or finished red or cancelled.

  • Compiles the router skipped (iOS, packages outside the CmuxNext graph, forks) don't hold the merge; PRs with no Swift changes aren't affected.
  • If GitHub can't be read, the merge is refused.
  • Any command that doesn't merge a PR exits without starting Python.