Context
A branch can be governed by GitHub’s classic branch protection API or by a repository ruleset —
two different, non-overlapping APIs with different capabilities. repo-policy’s directory layout
already separates policies/branch_protection.py and policies/rulesets.py; something has to
decide which one governs a given branch.
Decision
Add an explicit enforcement: branch_protection | ruleset field to BranchPolicy, defaulting to
branch_protection, set per branch in policy.yml.
Alternatives Considered
- Auto-detect from current repo state — inspect whether a branch currently has classic
protection or a ruleset, and manage whichever is found. Rejected: makes “which backend am I
governing” implicit, harder to reason about in a
plandiff, and ambiguous on first-ever creation (which one shouldapplycreate?). - Classic branch protection only for v1 — drop ruleset support entirely. Rejected: contradicts the explicit v1 scope of supporting both, and GitHub’s own direction is toward rulesets.
Consequences
- Deterministic: reading
policy.ymlalone tells you which API a branch will be managed through, with no live-state lookup required. - The two backends can diverge in behavior without ambiguity — see
0004-ruleset-ownership-via-naming-convention.mdfor how ruleset ownership and deletion differ from branch protection’s. - A team migrating a branch from one backend to the other does so by editing one field, at the
cost that repo-policy does not automatically clean up the other backend’s leftover state (out
of scope for v1 — see
ROADMAP.md).
Links
src/repo_policy/models.py(BranchPolicy.enforcement)ROADMAP.md