Branch-Protection Drift Detector
Your live ruleset matches your spec — or the gate fails. Branch protection as code, with drift detection that catches GUI edits within ≤7 days.
The problem
GUI-edited branch protection drifts silently.
GitHub branch protection is GUI-edited. Anyone with repo admin can toggle off "require status checks" or remove a required check name. The change leaves no PR. There's no diff. The next time the rules matter — a security-relevant PR, an audit — the customer assumes the guarantees are in place. They might not be.
Even with the best intentions, drift accumulates. An incident demands a temporary relaxation. The fix-up commit never lands. Six months later, the relaxation is still live and forgotten.
How it works
Per-PR drift check + weekly cron reconciliation.
tools/branch-protection/spec.yaml declares the desired state for every
branch + ruleset + required check. audit_branch_protection.py reads the
live state via GitHub Ruleset API and diffs against the spec. Any divergence —
missing check, wrong enforcement, modified actor allowlist — fails the audit and the gate.
The audit runs as a required PR status, so drift fails fast on the next PR. A weekly cron handles the case where the repo is quiet — drift is caught within 7 days regardless. The output is a structured diff: which fields drift, what's expected, what's live. Fix the spec or fix the live config — either way, the next PR re-runs the audit.
- spec.yaml is the source of truth — every protection rule + required check listed
- Per-PR drift check runs as a required status — fails closed on any divergence
- Weekly cron reconciles even on quiet weeks; catches drift within 7 days max
- Diff output is human-readable + machine-actionable — fix the spec or fix the live config
- GitHub Ruleset API target; supports Rulesets + legacy Branch Protection Rules
# tools/branch-protection/spec.yaml
branches:
main:
ruleset: main1
enforcement: enforced
required_status_checks:
- Agent Critic
- Cycle Doc Validation
- Branch Protection Audit
- Quality Gate
required_review_thread_resolution: true
required_approving_review_count: 0
allow_force_pushes: false
allow_deletions: false
bypass_actors:
- actor_id: 12345 # @chief-engineer
bypass_mode: pull_request
actor_type: User
# Drift detector output (CI):
# AUDIT FAILED — drift detected on branch 'main':
# - required_status_checks: missing "Branch Protection Audit"
# found: [Agent Critic, Cycle Doc Validation, Quality Gate]
# expected: [Agent Critic, Cycle Doc Validation,
# Branch Protection Audit, Quality Gate]
# - bypass_actors: drift in allowlist
# found: [12345, 67890]
# expected: [12345]
# Fix the spec or fix the live ruleset; re-run. Get Started
Branch protection is code. Drift is a regression.
Stop hoping nobody flipped that checkbox. The drift detector runs on every PR + a weekly cron — closes the silent-drift attack surface for any GitHub repo.