diff --git a/docs/governance/codeowners-branch-protection.md b/docs/governance/codeowners-branch-protection.md index f5aa30f..676cae2 100644 --- a/docs/governance/codeowners-branch-protection.md +++ b/docs/governance/codeowners-branch-protection.md @@ -6,6 +6,64 @@ - Keep review load on project teams, not the organizers — each project owns its own merge gates. - Provide consistent guardrails (branch protection, required checks) that match CivicTechWR's volunteer-friendly workflow. +## Team-Based Access Model + +**All repository access must be granted through teams, not directly to individual users.** Direct user-to-repo assignments bypass the team governance model, are invisible in team audit views, and create stale access when contributors rotate off. + +### Permission Levels + +| GitHub Permission | Plain-English meaning | Who gets it | +|------------------|----------------------|-------------| +| **Read** | You can see everything, open issues, leave comments — but not touch code | Anyone browsing or giving feedback | +| **Triage** | Read + you can label/close issues and PRs, but still can't push code | Community managers, issue shepherds | +| **Write** | You can push branches, open PRs, review and merge PRs (if branch protection allows) | All active project contributors | +| **Maintain** | Everything Write + you can change branch protection settings, close stale issues, push to protected branches in an emergency | Project leads | +| **Admin** | Full control: settings, delete the repo, manage who has access, change billing | `@CivicTechWR/organizers` only, on governance repos | + +**Maintain is the right permission for project leads.** It covers all day-to-day lead responsibilities without the nuclear options — deleting the repo, changing billing, or modifying org-level settings. Any team named `*-leads` or `*-admins` should use Maintain, not Admin, unless there is a specific documented reason otherwise. + +### Which permission should I assign? (Decision Guide) + +```mermaid +flowchart TD + A[New person joining a project] --> B{Are they a project lead or maintainer?} + B -- No --> C[Add to contributor team: Write] + B -- Yes --> D{Do they need to delete repos or manage billing?} + D -- No --> E[Add to leads team: Maintain] + D -- Yes, organizer role --> F[Add to organizers team: Admin on governance repos only] + + G[New team being created] --> H{What does this team need to do?} + H -- Contribute code / review PRs --> C + H -- Manage branch rules / close issues --> E + H -- Manage org settings --> F + + style C fill:#d4edda + style E fill:#fff3cd + style F fill:#f8d7da +``` + +### Setting Up a New Project Team + +1. Create a contributors team: `your-project-team` with **Write** permission +2. If leads are identified, create: `your-project-leads` with **Maintain** permission +3. Add the team(s) to the repository — do not add individual users directly +4. Remove any existing direct user-to-repo assignments once team coverage is confirmed +5. Add a `CODEOWNERS` file pointing to the team (see below) + +### Removing Direct User Assignments + +To check whether a repo has any direct assignments: +```bash +gh api repos/CivicTechWR/REPO/collaborators?affiliation=direct --jq '.[].login' +``` +If the list is non-empty and those users are already covered by a team, remove the direct grants: +```bash +gh api repos/CivicTechWR/REPO/collaborators/USERNAME -X DELETE +``` +Do not remove a direct assignment until the user's team membership is confirmed — removing it without team coverage will cut off their access entirely. + +--- + ## How CODEOWNERS Works in This Organization **Important:** GitHub does not propagate a `CODEOWNERS` file from the org-level `.github` repository to other repositories in the organization. The `CODEOWNERS` file in this repository protects only *this* repository. @@ -82,11 +140,13 @@ Projects without a dedicated team fall back to `@CivicTechWR/organizers` as revi ## Implementation Checklist -1. **Confirm team access:** Ensure each team has triage or write permissions on its corresponding repositories. -2. **Add a CODEOWNERS file to each active project repo:** Use the templates above. Do not rely on the organizer-level `.github` file — it does not propagate. -3. **Update branch protection per repo:** Apply the recommended rule to each repository's default branch. -4. **Communicate the change:** Post in `#organizers`, update `CTWR-Organization-Documentation`, and mention during the weekly meetup. -5. **Track coverage:** Open an issue to track which repos still lack a CODEOWNERS file and invite project teams to add their own. +1. **Create teams before adding people.** Never add a user directly to a repo. Create (or identify) the appropriate team first. +2. **Apply the right permission level.** Contributors → Write. Leads → Maintain. Do not use Admin for project teams. +3. **No direct user assignments.** Verify the repo has no direct collaborators (`Settings → Collaborators → Direct access`). If it does, confirm team coverage first, then remove the direct grants. +4. **Add a CODEOWNERS file to each active project repo.** Use the templates above. Do not rely on the organizer-level `.github` file — it does not propagate. +5. **Update branch protection per repo.** Apply the recommended rule to each repository's default branch. +6. **Communicate the change.** Post in `#organizers`, update `CTWR-Organization-Documentation`, and mention during the weekly meetup. +7. **Track coverage.** Open an issue to track which repos still lack a CODEOWNERS file and invite project teams to add their own. ## Break-Glass Procedure @@ -123,6 +183,7 @@ Do not use this procedure to skip security or compliance reviews. ## Risks and Pitfalls +- **Direct user assignments bypass team governance:** Adding users directly to repos instead of via teams makes them invisible to team-level audits and creates stale access that persists after someone leaves a project. Always use teams. - **CODEOWNERS does not auto-propagate:** Repos without their own CODEOWNERS file have no code-owner enforcement, regardless of what this governance repo contains. - **Stale team membership:** CODEOWNERS blocks merges when listed reviewers are inactive. Reconfirm rosters every quarter and any time a maintainer steps back. - **Automation permissions:** Workflows that create teams or modify permissions require an organization-level Personal Access Token with `admin:org`; keep the token scoped and rotate it periodically.