Chapter 10Lesson 04~160 minutes

Protected Branches, Repository Rulesets, Push Rules, Required Checks, and Bypass Governance: Diagnostics, Failure Modes, Security, and Performance

Diagnose repository-policy failures by preserving the rejected action, enumerating every policy source, proving the exact ref/check identity, and correcting the smallest cause without normalizing bypass.

TroubleshootingGH013Rule layeringPolicy drift

Learning objectives

  • Use an evidence-first diagnostic sequence for branch/ruleset failures.
  • Diagnose unexpectedly strict effective policy caused by overlapping rulesets/protection.
  • Diagnose a required check that disappeared or changed source/name.
  • Recognize routine administrator bypass as a governance failure, not a successful operational shortcut.
  • Explain why push rules can block a fork and why target patterns may miss intended release refs.
  • Repair an intentionally broken scenario while keeping original failure evidence visible.
Diagnostic rule: do not change protection merely because a merge/push is blocked. First preserve the exact actor, repository, target ref, head SHA, rejected action, rule/check name, and current policy sources. A block can be correct policy doing its job.

1. Evidence-first diagnostic sequence

  1. Preserve evidence: PR URL/number, head/base OIDs, push stderr, check names/status, and time.
  2. Name scope: user/account, repository, organization, branch/tag, fork network, PR, workflow, environment.
  3. Enumerate policy: classic protection plus repository/parent rulesets.
  4. Inspect evidence producers: reviews, workflows, statuses, deployments, security results.
  5. Choose least destructive correction: fix a target/check/workflow first; do not disable all protection.
  6. Verify: replay the same intended path and prove the ref update succeeds under policy.
  7. Record: if a bypass occurred, capture reason/actor and close the exception.
Shell portability: commands shown with trailing \, $(...), printf, cat <<'EOF', or rm are Git Bash/Bash/zsh syntax. In PowerShell, keep the same Git/gh arguments but use the backtick for line continuation and Set-Content/Add-Content/Remove-Item for file operations. GitHub ruleset and branch-policy semantics do not depend on the shell.

2. Failure mode: overlapping rulesets are stricter than expected

Suppose the repository UI shows a local ruleset requiring one approval, yet the PR says two approvals are required and signed commits are also mandatory. Do not assume GitHub is “ignoring” the local rule. A parent organization ruleset or classic protection may be layering additional requirements.

gh ruleset list -R OWNER/REPO --parents
gh ruleset check main -R OWNER/REPO

gh api -H "Accept: application/vnd.github+json" \
  -H "X-GitHub-Api-Version: 2026-03-10" \
  repos/OWNER/REPO/rules/branches/main

Repair by identifying policy ownership. If both requirements are intentional, document the aggregate. If they are accidental duplicates, remove only the policy copy whose owner agrees it is redundant. Never weaken a central rule just to make a local expectation true.

3. Intentionally broken example: a required check name no longer exists

Initial policy requires policy-gate. A maintainer later renames the workflow job to policy-validation. New PRs produce the new check, but policy still waits for the old identity.

# Before
jobs:
  policy-gate:
    runs-on: ubuntu-latest
    steps:
      - run: echo ok

# After a careless rename
jobs:
  policy-validation:
    runs-on: ubuntu-latest
    steps:
      - run: echo ok

Representative evidence:

$ gh pr checks 42 -R OWNER/REPO
policy-validation   pass   ...

Merge box / rules:
Required status check "policy-gate" is expected.

Interpretation: CI is not generally broken; the evidence producer changed identity while the enforcement contract did not. GitHub also documents that required checks should have been observed recently in the repository, and duplicate names can create ambiguity.

Least destructive repair options:

  1. Rename the job back to the stable contract name and rerun it, or
  2. Through a reviewed policy change, replace the required check with the new exact name/source, then verify a new PR.

Do not press an admin bypass on every PR. That converts a clear configuration defect into silent policy erosion.

4. Failure mode: routine administrator bypass makes policy ineffective

If every release requires “admin override because the checks are slow,” the incident is not that GitHub blocks releases; the governance system is misdesigned. Measure why the gate is too slow or flaky, correct the workflow or risk policy, and narrow bypass access.

Signal Likely governance problem Correction
Bypass every day Exception path became normal path Fix gate/throughput; remove broad bypass
Same admin always bypasses Single-person control concentration PR-only bypass + incident/approval process
No recorded reason Audit trail cannot explain decision Require incident/PR reason and review
Temporary actor never removed Privilege lifecycle failure Time-bound access and post-event cleanup

Ruleset insights/history can help explain configuration and bypass events where available, but operational governance must not depend on someone remembering why an override happened.

5. Failure mode: a push rule unexpectedly blocks a fork

A developer forks an eligible private/internal repository, adds a file that the root push ruleset prohibits, and receives a push rejection in the fork. This is expected: GitHub push rules apply across the entire fork network, and fork users do not gain independent bypass authority.

{
  "root": "ORG/secure-root",
  "fork": "CONTRIBUTOR/secure-root",
  "root_push_rule": "block *.pem",
  "attempt": "push test-key.pem to fork feature branch",
  "expected": "blocked by root fork-network push policy"
}

The repair is not “create a second fork.” Remove/rename the prohibited content or follow the root repository’s governed bypass process. This capability is simulated in the mandatory course because push rulesets are not available on its public personal free repository.

6. Failure mode: target patterns miss release branches or tags

Pattern mistakes are dangerous because policy may look correct in Settings while the important ref is not targeted. GitHub uses fnmatch semantics for branch/tag targeting; * does not cross / path separators under the documented mode.

Intent Risky assumption Safer verification
All one-level release branches release/* covers arbitrary depth Test exact names with gh ruleset check
Nested release refs Visual pattern “looks broad” Use documented pattern such as a recursive form when needed, then test examples
Default branch Hard-code main forever Target default-branch selector where available or govern rename procedure
gh ruleset check release/2026.08 -R OWNER/REPO
gh ruleset check release/2026/08 -R OWNER/REPO
gh ruleset check --default -R OWNER/REPO

7. Interpret a blocked push without hiding the cause

A direct push may return a repository-rule rejection such as:

remote: error: GH013: Repository rule violations found for refs/heads/main.
remote: - Changes must be made through a pull request.
To github.com:OWNER/REPO.git
 ! [remote rejected] main -> main (push declined due to repository rule violations)

This is representative text; preserve your actual server output. The key facts are that authentication and repository discovery worked, GitHub evaluated the target ref, and policy rejected the update. The correct repair is normally “push the commit to a topic branch and open a PR,” not “change credentials” or “disable protection.”

8. Security, reliability, performance, and billing only where causal

Security: bypass and force-push/delete permissions can defeat the branch as an audit boundary. Keep them narrow. Reliability: missing/flaky checks can halt delivery, so required evidence must have an owner and outage runbook. Performance: strict up-to-date requirements can create extra CI reruns on busy branches; merge queues may reduce update churn when available. Billing: additional workflow runs can consume plan resources; that is a consequence of stricter validation, not a reason to silently remove critical gates. Verify current plan limits rather than hard-coding numbers.

9. Compact diagnostic runbook

# 1) exact PR/ref state
gh pr view 42 -R OWNER/REPO --json baseRefName,baseRefOid,headRefName,headRefOid,mergeStateStatus,reviewDecision,statusCheckRollup

# 2) all applicable rules
gh ruleset list -R OWNER/REPO --parents
gh ruleset check BRANCH -R OWNER/REPO

# 3) API-active rules
gh api -H "Accept: application/vnd.github+json" \
  -H "X-GitHub-Api-Version: 2026-03-10" \
  repos/OWNER/REPO/rules/branches/BRANCH

# 4) actual check identities
gh pr checks 42 -R OWNER/REPO

# 5) after correction, verify instead of assuming
gh pr checks 42 -R OWNER/REPO --watch

10. Lesson summary

Most protection incidents become tractable when you preserve the denied action and separate policy sources from evidence producers. Rulesets layer; checks have exact identities; bypass can become an anti-control; push rules cross fork networks; and target patterns can leave gaps. Correct the smallest cause, then replay the compliant path and preserve proof.

Knowledge check

A PR has a passing policy-validation check but policy waits for policy-gate. What is the most likely root cause?

Why can a repository-local rules page look weaker than the actual merge requirement?

A contributor cannot push a prohibited .pem file even to a fork. Is that necessarily a permissions bug?

What is wrong with solving every blocked release through administrator bypass?

How do you verify a pattern against a future branch name without creating that branch?

Next lesson

Integrate policy, failure, compliant recovery, and break-glass governance

Lesson 05 builds a complete disposable policy lab, records a direct-push rejection, merges a compliant PR, and rehearses a narrowly controlled PR-only emergency bypass followed by recovery.

Authoritative references

 About rulesets

 Creating rulesets for a repository

 Available rules for rulesets

 About protected branches

 REST API endpoints for rules

 GitHub CLI: gh ruleset

 Troubleshooting rules

 Troubleshooting required status checks

Keep the academy open

Support free, practical DevOps education.

Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.