Branch Protections
main is the only long-lived code branch. Work happens on short-lived branches, merges through PR review, and promotes through tags and values files instead of environment branches.
Rules that matter
| Rule | Why it exists |
|---|---|
Protect main | Keeps the deployment baseline controlled |
| Block direct pushes | Forces review, checks, and audit metadata |
| Require at least one approval | Creates separation of duties |
| Dismiss stale reviews | Prevents old approvals from covering new code |
| Require conversation resolution | Keeps review concerns visible |
| Enforce squash-only linear history | Makes one PR equal one revertible commit |
| Enforce branch-name and commit conventions | Keeps automation and audit search reliable |
| Require signed commits where policy demands it | Ties sensitive changes to verified actors |
Branch names
Use prefix/short-kebab-slug.
| Prefix | Use for | Example |
|---|---|---|
feat/ | New user-visible behavior | feat/language-selector |
fix/ | Bug fixes and remediation | fix/duplicate-submission |
security/ | Auth, secrets, hardening, vulnerability fixes | security/rotate-signing-key |
infra/ | Helm, ArgoCD, Kubernetes, cloud, runners | infra/preview-namespaces |
ci/ | GitHub Actions, checks, release automation | ci/add-commitlint-check |
build/ | Build system and packaging | build/container-cache |
docs/ | Documentation-only changes | docs/gitops-branch-rules |
test/ | Test-only changes | test/add-profile-regression |
refactor/ | Behavior-preserving structure changes | refactor/form-state |
perf/ | Performance improvements | perf/query-cache |
style/ | Formatting and style-only changes | style/lint-cleanup |
revert/ | Reverting a prior change | revert/bad-cache-change |
chore/ | Routine maintenance | chore/update-dependencies |
Emergency work still uses fix/ or revert/ plus an incident reference in the PR. There is no hotfix/ or emergency/ prefix because those imply a second path around normal controls.
What to avoid
| Avoid | Use instead |
|---|---|
Long-lived staging or prod branches | Promote tags or values-file changes |
| Week-long WIP branches | Split the work and merge behind a flag |
Direct emergency pushes to main | Open an emergency PR and use the override path if needed |
Branch names like updates or misc | Use a prefix that says what kind of review is needed |
| Hiding broken code behind a flag | Fix the code before merge |
Feature flags
Use a feature flag when code is safe to deploy but behavior is not ready to expose. The hidden path still needs tests, review, and a cleanup owner. The detailed flag standard lives in Deployments.
Enforcement
GitHub governance is declarative state. The scripts under templates/governance/ define the desired organization and repository policy; validation reports drift when GitHub no longer matches those scripts.
Branch policy is enforced by organization rulesets:
branch-protectionstargets default branches, blocks deletion and force-pushes, requires linear history, and allows only squash merges.conventional-branch-namestargets all branches and requires the approved prefix plus lowercase slug.code-reviewrequires PRs, code-owner review where configured, stale review dismissal, and conversation resolution.release-tagsallows only approved SemVer, beta, and release-candidate tag shapes.
Run apply, then validate. Do not repair drift by clicking through the GitHub UI unless the script is updated in the same change.
Run or inspect the scripts:
ORG=mdds bash templates/governance/enforce-branch-protections.shORG=mdds bash templates/governance/enforce-branch-names.shtask governance:apply:org ORG=mddstask governance:validate ORG=mddstask governance:apply:repo ORG=mdds REPO=my-repotask governance:validate ORG=mdds REPO=my-repoGovernance apply order
- Create governance teams.
- Apply org settings and Actions defaults.
- Define custom properties.
- Apply code-review rules.
- Apply branch protections.
- Apply optional deploy gates.
- Enable reusable workflow sharing.
- Deploy and require the admin governance workflow.
- Apply restricted-data controls.
- Apply branch-name, commit-message, and release-tag rules.
- Apply repo-scoped controls where required.
Teams run first because rulesets and production environments reference team IDs. Mainline branch protections have no bypass actors; convention rules have no bypass actors.
Governance controls
| Control | Scope | What it prevents |
|---|---|---|
| Required PRs and code-owner review | Default branches | Direct mainline writes and unreviewed owned-path changes |
| Stale review dismissal and thread resolution | Default branches | Old approvals carrying unrelated later changes |
| Squash-only merges and linear history | Default branches | Noisy release history and hard-to-revert merge bubbles |
| Branch deletion, force-push, and signed-commit rules | Default branches | Loss of audit history and unverified authorship |
| Central governance workflow | Default branches | PR titles or shared checks bypassing the release grammar |
| Conventional branch names and commits | Branches and commits | Ambiguous branch and changelog metadata |
| Release tag naming | Tags | Ambiguous production promotion targets |
| Restricted-data rules | Restricted repositories | Baseline-only controls on high-risk repositories |
| Production environment approval | Repository prod environment | One-person production promotion |
| Incident priority labels | Repository labels | Inconsistent incident language |
Runtime knobs
| Variable | Use |
|---|---|
ORG / GITHUB_ORG | Target organization for every governance task |
REPO | Target repository for repo-scoped tasks |
ADMIN_REPO | Repository that hosts the central governance workflow |
GOVERNANCE_WORKFLOW_PATH / GOVERNANCE_WORKFLOW_REF | Required workflow location |
REQUIRED_STATUS_CHECKS | Optional deploy-gate checks |
RULESET_BYPASS_TEAM | Emergency bypass team, normally technical-owners |
PROD_REVIEWER_TEAMS | Teams that approve protected production deployments |
COMPLIANCE_REPORT_PATH | JSON evidence export from validation |
Bypass policy
technical-owners may bypass only selected review, workflow, restricted-data, deploy-gate, and release-tag rules for emergency unblock. They cannot bypass direct mainline write protections, branch naming, or commit-message rules. Every bypass needs an incident or ticket reference.
ATO evidence
For ATO, retain the default-branch ruleset export, required-review settings, CODEOWNERS routing, governance script output, validation logs, custom-property schema, a representative PR, and any bypass audit log. These support separation of duties, controlled configuration changes, least privilege, and auditability.