Skip to content

ci: add required aggregate check#281

Merged
ThomasK33 merged 1 commit into
mainfrom
repo-protection-ps81
Jun 15, 2026
Merged

ci: add required aggregate check#281
ThomasK33 merged 1 commit into
mainfrom
repo-protection-ps81

Conversation

@ThomasK33

Copy link
Copy Markdown
Member

Summary

  • add a merge_group trigger so CI runs for merge queue entries
  • add a GitHub Actions aggregate check named Required
  • make Required fail unless both existing required test jobs succeed

Validation

  • PATH=/home/coder/.local/bin:$PATH mise run all

Notes

This PR implements Phase 1 of the repo-protection rollout. Repository merge settings have already been updated separately to mux parity. The active ruleset and classic branch protection removal should wait until this PR is merged and Required exists on main.


📋 Implementation Plan

Plan: Make coder/claudecode.nvim resemble coder/mux repo protection

Goal

Move coder/claudecode.nvim toward the repository/branch protection posture observed in coder/mux: squash-only merges, auto-merge/update/delete-branch enabled, an active default-branch ruleset, a merge queue, linear-history enforcement, default-branch creation/deletion/non-fast-forward protection, and a single aggregate required GitHub Actions check named Required.

Evidence gathered

  • Current repo: origin is https://github.com/coder/claudecode.nvim; default branch is main.
  • Authenticated GitHub API access: ThomasK33 has maintain on coder/mux and admin on coder/claudecode.nvim.
  • coder/mux has no classic main branch protection returned by the branch-protection endpoint; protection comes from repository ruleset main (id: 8733738) targeting ~DEFAULT_BRANCH, enforcement active.
  • coder/mux ruleset rules: creation, deletion, non_fast_forward, required_linear_history, required_status_checks, and merge_queue.
  • coder/mux required status check: context Required, observed integration id 15368 (GitHub Actions), strict/up-to-date requirement disabled. Treat the id as evidence from mux, not guaranteed portability until GitHub accepts it for claudecode.nvim.
  • coder/mux merge queue parameters: squash merge, ALLGREEN, max build entries 5, min merge entries 1, max merge entries 5, min wait 5 minutes, check response timeout 60 minutes.
  • coder/claudecode.nvim currently has classic branch protection on main, no repository rulesets, no active branch rules, no required checks, one required PR review, force pushes allowed, and branch deletion allowed.
  • coder/claudecode.nvim current workflow .github/workflows/test.yml exposes unit-tests and integration-tests (stable), but no Required aggregate check.

Key differences to close

Area coder/mux target Current coder/claudecode.nvim Required change
Required check shape One aggregate Required check unit-tests, integration-tests (stable) only Add Required aggregate job first
Protection mechanism Repository ruleset on ~DEFAULT_BRANCH Classic branch protection on main Add mux-like ruleset; remove/disable classic protection for strict parity
Merge queue Enabled, squash None Enable merge queue in ruleset
Linear history Required Not required Add required_linear_history rule
Force pushes Blocked Allowed Add non_fast_forward rule
Default branch deletion Blocked Allowed Add deletion rule
Matching default-branch ref creation Prevent creation of the matching default-branch ref except by bypass actors Not present Add creation rule
Status checks Required, non-strict No required checks Require Required, strict_required_status_checks_policy: false
PR review requirement Not present in observed mux ruleset 1 approving review, ThomasK33 bypass Remove if strict mux parity is desired; keep only if intentionally stricter
Merge methods Squash only Squash, merge commit, rebase Disable merge commit and rebase
Repo automation Auto-merge/update/delete branches enabled Disabled Enable all three
Squash defaults PR title + PR body Commit-or-PR title + commit messages Change to PR title + PR body

Recommended rollout

Phase 1 — Add the Required aggregate CI check

  1. Edit .github/workflows/test.yml to add merge_group to the workflow triggers, because merge queues create merge-group check runs and required checks must run there too.
  2. Add a final job named exactly Required.
  3. Make it depend on the current CI jobs:
    • unit-tests
    • integration-tests
  4. Use if: ${{ always() }} so the aggregate check still runs when dependencies fail, are cancelled, or are skipped by upstream failures.
  5. Fail the job unless every dependency result is exactly success.
    • In this repo both dependencies are required; there are no intentionally optional skipped jobs, so skipped should fail.
    • integration-tests currently depends on unit-tests, so if unit-tests fails, integration-tests may be skipped. The aggregate job must still fail.
    • Prefer explicit checks such as ${{ needs.unit-tests.result }} and ${{ needs.integration-tests.result }} (or toJSON(needs) for debugging) over a permissive success() expression so failures are visible and easy to audit.
  6. Keep the change minimal: do not rename existing jobs unless absolutely necessary.

Quality gate after Phase 1:

  • Open a PR with only this workflow change.
  • Verify the PR reports a check named exactly Required from GitHub Actions.
  • Verify a merge queue / merge-group run also reports Required once merge queue is enabled.
  • Verify Required succeeds when both existing jobs succeed.
  • Verify, if feasible with a temporary branch or intentionally failing draft PR, that Required fails when either required underlying job fails.
  • Merge this PR before requiring Required in repository settings.

Acceptance criteria for Phase 1:

  • Required exists on pull requests and on main pushes.
  • Required is produced by GitHub Actions.
  • Existing unit-tests and integration-tests behavior remains unchanged.

Phase 2 — Update repository merge settings to mux parity

Use GitHub repository settings or gh api to set:

  • allow_squash_merge: true
  • allow_merge_commit: false
  • allow_rebase_merge: false
  • allow_auto_merge: true
  • allow_update_branch: true
  • delete_branch_on_merge: true
  • squash_merge_commit_title: PR_TITLE
  • squash_merge_commit_message: PR_BODY

Quality gate after Phase 2:

  • Re-read repos/coder/claudecode.nvim via GitHub API and compare these fields with coder/mux.
  • Confirm the PR merge UI offers squash merge only.
  • Confirm auto-merge is available on a test PR.

Acceptance criteria for Phase 2:

  • Repository merge settings match mux for the fields above.
  • No source code or workflow changes are coupled to the settings update.

Phase 3 — Create a mux-like default-branch repository ruleset

Create repository ruleset main targeting branches and applying to ~DEFAULT_BRANCH. The payload below is illustrative; keep the Required context exact, but validate or adjust the GitHub Actions integration id for claudecode.nvim before applying.

{
  "name": "main",
  "target": "branch",
  "enforcement": "active",
  "conditions": {
    "ref_name": {
      "include": ["~DEFAULT_BRANCH"],
      "exclude": []
    }
  },
  "rules": [
    { "type": "deletion" },
    { "type": "non_fast_forward" },
    { "type": "creation" },
    { "type": "required_linear_history" },
    {
      "type": "required_status_checks",
      "parameters": {
        "strict_required_status_checks_policy": false,
        "do_not_enforce_on_create": false,
        "required_status_checks": [
          { "context": "Required", "integration_id": 15368 }
        ]
      }
    },
    {
      "type": "merge_queue",
      "parameters": {
        "merge_method": "SQUASH",
        "max_entries_to_build": 5,
        "min_entries_to_merge": 1,
        "max_entries_to_merge": 5,
        "min_entries_to_merge_wait_minutes": 5,
        "grouping_strategy": "ALLGREEN",
        "check_response_timeout_minutes": 60
      }
    }
  ]
}

Implementation notes:

  • Prefer using GitHub UI or gh api repos/coder/claudecode.nvim/rulesets with the equivalent payload.
  • Use integration_id: 15368 only if GitHub accepts it for claudecode.nvim; otherwise select GitHub Actions / Required in the ruleset UI, omit the integration id if the API accepts that shape, or use the app id GitHub exposes for this repo's Required check. Do not change the context name away from Required without re-evaluating parity.
  • Apply Phase 3 while logged in as an admin, and confirm at least one admin/bypass path can recover from a misconfigured ruleset before removing classic protection.
  • Ensure the ruleset is active before removing classic protection; this avoids a protection gap.

Quality gate after Phase 3:

  • Re-read repos/coder/claudecode.nvim/rulesets?includes_parents=true and the created ruleset by id.
  • Re-read repos/coder/claudecode.nvim/rules/branches/main and confirm the active rule list is: creation, deletion, non_fast_forward, required_linear_history, required_status_checks, merge_queue.
  • Confirm required check parameters show Required, non-strict checks, and GitHub Actions as the integration.
  • Confirm merge queue parameters match mux.

Acceptance criteria for Phase 3:

  • main is protected by an active repository ruleset equivalent to mux.
  • The ruleset applies to ~DEFAULT_BRANCH, not a hard-coded branch name unless GitHub UI forces equivalent behavior.
  • Direct force-push and branch deletion are blocked for non-bypass actors.

Phase 4 — Remove or intentionally retain classic branch protection

Make an explicit maintainer decision before changing classic branch protection:

  • Strict mux parity: remove/disable classic branch protection on main after the ruleset is active and verified.
  • Intentionally stricter claudecode policy: keep the 1-review classic protection and document that this repo intentionally differs from mux.

Recommended default for this plan is strict mux parity, because the user asked for claudecode.nvim to resemble mux.

Rationale:

  • Mux did not expose classic branch protection for main; its effective protections came from the ruleset.
  • GitHub combines applicable protection mechanisms, and stricter classic protection can keep claudecode.nvim different even after the mux-like ruleset is added.
  • Keeping the existing classic protection would preserve the 1-review requirement and ThomasK33 PR-review bypass, which may be desirable but is not strict mux parity.

Quality gate after Phase 4:

  • Re-read repos/coder/claudecode.nvim/branches/main/protection.
    • For strict mux parity, it should return 404/not found or otherwise show no classic branch protection.
  • Re-read repos/coder/claudecode.nvim/rules/branches/main; the mux-like rules should still be active.

Acceptance criteria for Phase 4:

  • Strict parity path: classic protection is removed/disabled, and ruleset protection remains active.
  • Intentionally stricter path: classic protection remains, and the difference is documented for maintainers.

Phase 5 — End-to-end verification and dogfooding

Use a disposable test branch and PR after settings are changed.

Dogfooding setup:

  1. Create a branch from current main with a harmless documentation-only commit.
  2. Open a draft PR.
  3. Capture a terminal recording of:
    • gh pr create / PR URL creation,
    • gh pr checks --watch,
    • gh api repos/coder/claudecode.nvim/rules/branches/main,
    • gh api repos/coder/claudecode.nvim filtered to merge settings.
  4. Capture screenshots of:
    • PR checks showing Required,
    • PR merge box showing merge queue / auto-merge behavior,
    • repository ruleset UI showing the main ruleset and active rules,
    • repository merge settings showing squash-only and delete-branch enabled.
  5. If safe, enable auto-merge or add the test PR to the merge queue; otherwise stop before merging and document why.
  6. Close the disposable PR/branch after verification unless maintainers want to merge the harmless change.

Functional checks:

  • Required passes only after underlying test jobs pass.
  • PR cannot be merged by merge commit or rebase.
  • PR merge path uses squash and merge queue.
  • Direct push/force-push to main is blocked for ordinary contributors, verified through API/UI and only through a controlled expected-failure attempt from a non-admin/non-bypass account if maintainers explicitly approve that test.
  • Default branch deletion is blocked, verified through API/UI; do not perform a destructive deletion attempt on main.
  • Branch is deleted after merge when using normal PR flow.
  • Auto-merge/update-branch controls are available.

Acceptance criteria for Phase 5:

  • API comparison between coder/mux and coder/claudecode.nvim shows matching values for the intended settings/rules.
  • Any intentional differences, especially required review policy or bypass actors, are documented explicitly.
  • Screenshots and terminal recording are attached to the implementation PR or linked in the follow-up settings-change issue/comment.

Final verification commands

Use direct tool paths if local shims trigger mise trust errors:

GH=/usr/local/bin/gh
JQ=/usr/bin/jq

$GH api repos/coder/mux > /tmp/mux.repo.json
$GH api repos/coder/claudecode.nvim > /tmp/claudecode.repo.json

$JQ '{allow_auto_merge,allow_update_branch,delete_branch_on_merge,allow_squash_merge,allow_merge_commit,allow_rebase_merge,squash_merge_commit_title,squash_merge_commit_message}' /tmp/mux.repo.json
$JQ '{allow_auto_merge,allow_update_branch,delete_branch_on_merge,allow_squash_merge,allow_merge_commit,allow_rebase_merge,squash_merge_commit_title,squash_merge_commit_message}' /tmp/claudecode.repo.json

$GH api repos/coder/mux/rules/branches/main --jq '.[] | {type, parameters}'
$GH api repos/coder/claudecode.nvim/rules/branches/main --jq '.[] | {type, parameters}'

$GH api repos/coder/claudecode.nvim/rulesets?includes_parents=true --jq '.[] | {id,name,target,source_type,source,enforcement}'

Rollback / emergency recovery

If the ruleset or merge queue blocks normal work unexpectedly:

  1. Use an admin account with confirmed settings access.
  2. Disable the new main ruleset or temporarily change enforcement from active to evaluate/disabled.
  3. If the blocker is the required check, remove Required from the ruleset's required status checks or correct the Required workflow trigger/check context.
  4. If needed, temporarily restore the previous classic branch protection while debugging, with the known prior settings: 1 required approving review, no required checks, force pushes allowed, deletions allowed.
  5. Re-run the API comparison and dogfood PR before re-enabling active ruleset enforcement.

Rollback acceptance criteria:

  • Maintainers can merge urgent fixes again through an understood path.
  • The repo is not left unprotected accidentally; either classic protection or the corrected ruleset is active.
  • The cause of rollback is documented before another activation attempt.

Risks and mitigations

  • Missing Required check blocks merging: add and merge the aggregate CI job before requiring it in the ruleset, and include merge_group so the check appears for merge queue runs.
  • Classic branch protection stacks with ruleset: remove classic protection for strict parity, or document the intentional stricter review requirement.
  • Bypass actor parity is not fully visible: mux ruleset details did not expose a bypass actor list; confirm with a mux repository admin if exact bypass behavior matters.
  • Merge queue changes maintainer workflow: dogfood with a disposable PR and document the new merge path before announcing the policy change.
  • Integration id mismatch: keep context Required; if 15368 is rejected, select the check through GitHub UI or use the integration id GitHub exposes for the repo's GitHub Actions check.

Advisor review status

  • Round 1 advisor review requested changes around merge_group, rollback, portable integration id handling, skipped-job semantics, Phase 4 decision clarity, and safe dogfooding.
  • Those changes were incorporated.
  • Final advisor review: approved; no blocking changes remain.

Generated with mux • Model: openai:gpt-5.5 • Thinking: xhigh

@ThomasK33 ThomasK33 merged commit 453920f into main Jun 15, 2026
3 checks passed
@ThomasK33 ThomasK33 deleted the repo-protection-ps81 branch June 15, 2026 14:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant