Sanctuary Branch Protection Setup — runbook

Purpose. This runbook walks the repository owner through the exact GitHub branch-protection settings required to make the test-baseline guard a hard merge gate, rather than a check that runs but can be ignored. Claude Code cannot set branch protection rules through code; this is a manual click-through Erik executes in the GitHub UI.

Prerequisites.

Estimated time. 5 minutes.


Step 1 — Confirm the workflow has run at least once

Before you can name a status check as “required”, GitHub must have seen it complete at least once. Verify:

  1. Go to https://github.com/eriknewton/sanctuary-framework/actions
  2. Confirm there is at least one green Test Baseline Guard run on main. (It should show up automatically on the next push after commit 3035a47 lands.)
  3. If no run is visible yet, push an empty commit to trigger it:
    git commit --allow-empty -m "ci: trigger test-baseline-guard registration"
    git push
    
  4. Wait for the workflow to complete (≈ 1–2 minutes).

Do not proceed to step 2 until you have at least one successful test-baseline-guard run recorded in the Actions tab.


Step 2 — Open branch protection settings

  1. Go to https://github.com/eriknewton/sanctuary-framework/settings/branches
  2. Under Branch protection rules, click Add rule (or Edit if a rule for main already exists).

Step 3 — Configure the rule

Fill in the form with these exact settings. Items marked (required) are hard requirements from the hardening plan; items marked (recommended) are best-practice additions that make the guard harder to accidentally bypass.

Branch name pattern

Protect matching branches

Rules applied to everyone including administrators


Step 4 — Save

Click Create (or Save changes). GitHub will confirm the rule is active.


Step 5 — Verify

Test the protection by attempting a direct push to main from a local clone with a trivial change:

echo "# verify branch protection" >> README.md
git add README.md
git commit -m "test: verify branch protection blocks direct push to main"
git push origin main

Expected result: GitHub rejects the push with an error like:

remote: error: GH006: Protected branch update failed for refs/heads/main.
remote: error: Changes must be made through a pull request.

If the push succeeds, branch protection is NOT active. Return to step 3 and verify the “Do not allow bypassing” checkbox is set.

After verifying rejection, revert the local change:

git reset --hard HEAD~1

Rollback

To temporarily relax the rule for an emergency:

  1. Settings → Branches → main rule → Edit
  2. Uncheck Do not allow bypassing (allows admin bypass)
  3. Save
  4. Perform the emergency action
  5. Immediately re-check Do not allow bypassing and save again

Full removal (NOT recommended under any normal circumstance):

  1. Settings → Branches → main rule → Delete

Document any rollback action in docs/audit/branch-protection-overrides.log (create on first use) with timestamp, actor, and one-sentence reason.


Why these exact settings

Setting Why
Require PR before merging Blocks direct pushes to main. Every change goes through a PR where the status checks can run, review can happen, and history is auditable. The commit 4ac95830 postmortem calls out “direct pushes to main from any environment” as one of the four specific failure modes the CI layer must catch.
Require branches to be up to date before merging Ensures the status checks ran against the actual merge commit, not a stale commit from N hours ago. Without this, a PR can sit on a stale base, get approved and merged, and the merge commit’s behaviour can differ from what CI actually ran against.
test-baseline-guard required The CI job that mirrors the pre-commit hook. Fails on typecheck errors, transform/collection errors, silent test file drops, and baseline regressions. This is the authoritative structural defense.
test (22) and test (24) required The existing CI test matrix. Keeps compatibility coverage across supported Node versions. These already exist in ci.yml and are already running — making them required just closes the loophole where a PR could merge while one matrix job was red.
Dismiss stale PR approvals on new commits A reviewer approves at commit A; author pushes commit B that breaks something; without dismissal, the approval from A still counts. This setting forces re-review on every push.
Require signed commits Makes it harder for agents (or attackers) to commit as Erik without his signing key. Not strictly part of the test-baseline hardening but good hygiene for the same reason: structural over conventional.
Do not allow bypassing (applies to admins) The load-bearing setting. Without it, all of the above is a convention that admins can override silently. With it, the protection is structural.


Runbook for Erik Newton. Sanctuary Framework. Apache-2.0.