BRANCH PROTECTION DRIFT CHECK · RUNS IN YOUR BROWSER

Does your branch protection still match your own policy?

Someone disables a rule for one urgent hotfix — required reviews, admin enforcement, the force-push block — and nothing reminds anyone to turn it back on. It stays invisible until the rule is actually needed: a merge lands with no review, or a force push overwrites history nobody meant to lose. Paste your repo's actual settings and your declared policy below to find every rule that has quietly drifted weaker.

Nothing is uploaded. What you paste is read in this browser and never leaves your device, and this tool makes no network calls of its own — not even to read your branch protection settings for you.

1. Actual settings

Run gh api repos/:owner/:repo/branches/:branch/protection and paste the JSON output here.

2. Declared policy

JSON, or plain key: value lines — see the field list below the results.

Paste your actual settings and declared policy above, then click Check.

Declared policy field reference

Every field is optional — a field you leave out is reported not policed, never assumed. Booleans: required_pull_request_reviews is entered as a plain required_approving_review_count number; the rest (require_code_owner_reviews, require_last_push_approval, enforce_admins, allow_force_pushes, allow_deletions, required_conversation_resolution, required_linear_history, required_signatures, strict_status_checks) are true/false. required_status_check_contexts is a comma-separated list (or a JSON array) of check names that must all be present — extra actual checks beyond the list are fine.

Prefer a CLI, or want this in your own CI?

Same logic, zero dependencies, one file:

gh api repos/:owner/:repo/branches/:branch/protection > actual.json
node cli.mjs --actual actual.json --policy policy.txt
node cli.mjs --actual actual.json --policy policy.txt --format json
node cli.mjs --actual actual.json --policy policy.txt --fail-on-drift   # exit 2 if anything drifted

Get cli.mjs — Node.js only, nothing to install, no network calls. Run it in CI right after a branch-protection or policy change to catch drift before it is needed.

Common questions

How does branch protection drift from what was intended?

Someone disables a rule for one urgent hotfix — turns off required reviews, or enables force pushes to fix a bad merge — and the settings page gives no reminder to turn it back on. Nothing breaks visibly until the next force push overwrites history, or the next merge lands with no review, on a rule everyone still believed was enforced.

Does this tool read my branch protection settings for me?

No. It makes no network calls at all. Fetch your own settings with gh api repos/:owner/:repo/branches/:branch/protection, paste the JSON in, write your declared policy in the second box, and the comparison happens entirely in this page.

What if my policy doesn't mention a rule?

It is reported as "not policed", never silently treated as a pass or a fail. A policy that only cares about force pushes says nothing about signed commits, and this tool will not invent an opinion your policy never stated.

What if the actual settings are stricter than the policy requires?

That is reported as "stronger than policy", not drift. Drift specifically means weaker than declared — more required reviewers than the policy demands, or an extra required status check, is never flagged as a problem.

Does a clean result mean my branch protection is fully correct?

No. It checks only the rules your own policy declares, against the specific fields GitHub's branch-protection API exposes. It is not a security audit, a compliance attestation, or a review of whether the policy itself is any good.

More: why branch protection drifts silently, and how to stop finding out the hard way.