Branch Protection Rules
Recommended GitHub branch protection rules to encode ADR-0001 (solo operator with disciplined review). GitHub doesn't store these declaratively by default — this doc captures what to configure via the UI or `gh api`.
Branch protection rules don’t ship as YAML in the repo (by default —
safe-settingsetc. exist but add operational burden). This doc captures what to set up via GitHub UI orgh apiper-repo, encoded so future-you doesn’t relitigate the choices.
What these rules do
Branch protection rules enforce the ADR-0001 workflow at the platform level: PR required, CI green before merge, code-owner review required for protected paths. The rules below are the recommended baseline for every Abukix repo.
Where rules live
Per repo, on the main branch. GitHub doesn’t reconcile branch protection from repo files by default — you configure them once per repo via:
- GitHub UI: Settings → Branches → Branch protection rules → Add rule
gh api: programmatic, scripted (recipe below)safe-settingsGitHub app: declarative YAML reconciliation — adds operational burden; skip until you have multiple repos to maintain
Recommended rules for Abukix repos
Public OSS repo (basecamp, triage, terralabs, platform-ctl, data-tier, llm-gateway, mcp-servers, aiops, studio, rxp, pulse)
Branch name pattern: main
✅ Require a pull request before merging
✅ Require approvals: 1
✅ Dismiss stale pull request approvals when new commits are pushed
✅ Require review from Code Owners
⬜ Restrict approvals to users with write access (off — keeps it open for external)
⬜ Allow specified actors to bypass required pull requests
✅ Require status checks to pass before merging
✅ Require branches to be up to date before merging
Required status checks:
- ci-check / yaml-lint
- ci-check / gitleaks
- ci-check / kustomize-build (basecamp only)
- ci-check / flux-validate (basecamp only)
- ci-check / kyverno-test (basecamp only)
- ci-check / go-test (Go projects only)
✅ Require conversation resolution before merging
✅ Require signed commits
✅ Require linear history (cleaner Git history; rebase or squash)
⬜ Require deployments to succeed before merging (skip — Flux handles)
✅ Lock branch (off — only enable if archiving the repo)
⬜ Do not allow bypassing the above settings (you'll need to bypass occasionally)
⬜ Restrict who can push to matching branches (off for solo; on if co-maintainers join)
✅ Allow force pushes — Specify who can force push: (limited to admin: yourself)
✅ Allow deletions — Specify who can delete: (limited to admin: yourself)
Private repo (ops-handbook)
Same as public, BUT:
- Skip the
kustomize-build/flux-validate/kyverno-testchecks (ops-handbook isn’t a K8s repo). - Skip the
go-testcheck. - Keep gitleaks + yaml-lint +
/pre-publish-checkif any content might eventually be sanitized + published.
Solo-operator caveat
GitHub will not let the same account that opened a PR be the approving reviewer. With one human operator, the “Require approvals: 1” rule cannot be satisfied by you alone on PRs you open. Three workable resolutions:
- Bypass yourself for routine work (the box “Do not allow bypassing the above settings” stays unchecked). The CI green + overnight wait + (consequential cases) external review carries the safety load instead. This is the ADR-0001 recommendation.
- Use a GitHub App or bot for approvals on routine work (e.g., a static-analysis bot that approves when its checks pass). Requires setup; useful at Y3+ scale.
- Drop the “Require approvals” rule entirely and rely on CI + CODEOWNERS-driven “Require review from Code Owners” for paths that truly need it. CI does the load-bearing work.
The /root recommendation is option 1 — keep the rule visible as aspirational (it’ll be satisfied when contributors join), and rely on CI + overnight + external-review-when-consequential for now.
gh api recipe for the recommended rules
Apply via the GitHub CLI (rerun is idempotent — it PUTs the full rule):
REPO="<your-handle>/basecamp" # or any other Abukix repo
gh api -X PUT \
"/repos/${REPO}/branches/main/protection" \
-F required_status_checks[strict]=true \
-f required_status_checks[contexts][]="ci-check / yaml-lint" \
-f required_status_checks[contexts][]="ci-check / gitleaks" \
-f required_status_checks[contexts][]="ci-check / kustomize-build" \
-f required_status_checks[contexts][]="ci-check / flux-validate" \
-f required_status_checks[contexts][]="ci-check / kyverno-test" \
-F enforce_admins=false \
-F required_pull_request_reviews[required_approving_review_count]=1 \
-F required_pull_request_reviews[dismiss_stale_reviews]=true \
-F required_pull_request_reviews[require_code_owner_reviews]=true \
-F required_linear_history=true \
-F required_signatures=true \
-F allow_force_pushes=true \
-F allow_deletions=true \
-F required_conversation_resolution=true \
-F restrictions=null
Adjust the contexts[] list per-repo: drop the K8s-specific ones for non-basecamp repos; add go-test for Go projects.
Verification
After applying, confirm:
gh api "/repos/${REPO}/branches/main/protection" | jq '.required_status_checks, .required_pull_request_reviews'
You should see the rules you set.
Then open a test PR (in a draft branch) and verify CI fires.
Re-verification cadence
Re-verify these rules on:
- Every new Abukix repo bootstrap (apply the recipe above).
- Annual ADR review (revisit ADR-0001’s annual-revisit follow-up).
- When a co-maintainer joins — likely supersedes ADR-0001 and tightens these rules.
Anti-patterns
| Anti-pattern | Why |
|---|---|
| Disabling branch protection “temporarily” to land a hotfix | The discipline is what catches the bad hotfix. Use a --admin push as the explicit, audited bypass instead. |
| Adding bypass actors silently | Each bypass is a future debugging session asking “who pushed past the gates and why.” Make it a PR comment + ADR if you make it permanent. |
| Setting required reviewers but allowing self-approval via app token | Theater. CI does the load-bearing work; don’t pretend the human gate is real if it isn’t. |
| Letting the required-status-checks list rot | When CI workflows change names (rename a job), update the required list. Stale required-checks list = “always failing” PRs that confuse contributors. |
Cross-references
- ADR-0001 — the decision these rules encode
- CODEOWNERS — the file the “Require review from Code Owners” rule uses
ci-check.yml— the workflow that produces the required status checkspull-request-template.md— what the author sees when opening a PRhomelab/dev-machine— Solo PR workflow — the operator workflow