-
Notifications
You must be signed in to change notification settings - Fork 5
docs(adr): propose a repository settings baseline by class #474
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
ss-o
wants to merge
6
commits into
main
Choose a base branch
from
docs/adr-repository-settings-baseline
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
6b9486f
docs(adr): propose a repository settings baseline by class
ss-o 9865442
docs(adr): correct the classic-versus-ruleset precedence claim
ss-o 8947e5a
docs(adr): use US spelling to match the other ADRs
ss-o 24ef1d5
docs(adr): name the classes exactly as ADR-0007 does
ss-o 4300684
docs(adr): clarify the no-CI exemption sentence
ss-o 13c7a47
docs(adr): correct the rollback claim for the default-branch row
ss-o File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,211 @@ | ||
| # 13. Repository Settings Baseline by Class | ||
|
|
||
| - **Status:** PROPOSED | ||
| - **Date:** 2026-07-22 | ||
| - **Deciders:** ss-o | ||
| - **Supersedes:** None | ||
| - **Superseded by:** None | ||
|
|
||
| ## Context | ||
|
|
||
| The organization declares requirements that nothing verifies. | ||
|
|
||
| ADR-0009 states that class-2 repositories have a test suite that is | ||
| **required** and that **gates release tags**. `z-shell/zsh-lint` is class 2. Its | ||
| default branch reports: | ||
|
|
||
| ```json | ||
| { "required_status_checks": null, "required_approving_review_count": 0, "enforce_admins": false } | ||
| ``` | ||
|
|
||
| The requirement is real policy and is enforced by nothing. This is the same | ||
| failure recorded in #464, where two documents described a commit-trailer check | ||
| that no workflow ran, generalized from CI to repository configuration. | ||
|
|
||
| A survey of all 86 active, public, non-fork repositories found: | ||
|
|
||
| | Measure | Count | | ||
| | --- | --- | | ||
| | No rules at all on the default branch | **75 (87%)** | | ||
| | No classic branch protection | 60 (70%) | | ||
| | No rulesets | 73 (85%) | | ||
| | Any rule present | 11 — six of which have only `copilot_code_review` | | ||
| | Repositories with more than one kind of rule | **2** (`zi`, `wiki`) | | ||
| | Required status checks configured | `zi` 5, `wiki` 1, all others 0 | | ||
| | Default branch still `master` | `z-shell/web` | | ||
|
|
||
| The two configured repositories do not agree with each other, and neither was | ||
| derived from a written standard. Nothing in `decisions/`, `runbooks/`, or | ||
| `AGENTS.md` mentions branch protection or rulesets, and | ||
| `runbooks/new-repository.md` bootstraps labels, CI templates, and dependency | ||
| automation without ever configuring protection. Settings have therefore been set | ||
| per repository, by hand, at different times, against no shared reference. | ||
|
|
||
| Two mechanisms are in play simultaneously. Classic branch protection and | ||
| repository rulesets are **independent** systems whose effective behavior is | ||
| their union. `z-shell/wiki` reports `required_linear_history: false` under | ||
| classic protection while a ruleset enforces it on the same branch. Reading | ||
| either source alone gives a wrong answer, and 19 repositories still carry | ||
| classic protection with no ruleset at all. | ||
|
|
||
| **Organization-level rulesets are not available on the current plan** | ||
| (`GET /orgs/z-shell/rulesets` returns `403 Upgrade to GitHub Team`), and no | ||
| repository custom properties are defined. Central enforcement is therefore not | ||
| an option today: per-repository configuration is the only mechanism, so drift is | ||
| structural rather than accidental. | ||
|
|
||
| ## Decision | ||
|
|
||
| Adopt a required settings baseline, expressed per ADR-0007 repository class, as | ||
| the reference that repositories are configured against and audited for. | ||
|
|
||
| ### Baseline by class | ||
|
|
||
| Classes are exactly those defined in | ||
| `decisions/0007-release-publication-flow.md`, referred to here by number so the | ||
| table stays readable: | ||
|
|
||
| 1. **Continuously deployed artifacts** — `wiki`, `src`, `zd` images | ||
| 2. **Versioned tools and packages** — `zunit`, `zsh-lint`, packaged `zsh` | ||
| 3. **Git-consumed source** — `zi`, most plugins and annexes | ||
| 4. **Meta/infrastructure** — `.github` | ||
|
|
||
| `R` = required. `S` = recommended, not required. | ||
|
|
||
| | Setting | Class 1 | Class 2 | Class 3 | Class 4 | | ||
| | --- | --- | --- | --- | --- | | ||
| | Default branch named `main` (audit only, never auto-applied) | R | R | R | R | | ||
| | Pull request required to the default branch | R | R | R | R | | ||
| | Deletion of the default branch blocked | R | R | R | R | | ||
| | Force push to the default branch blocked | R | R | R | R | | ||
| | Required status checks | R | R | S | S | | ||
| | Linear history | R | S | S | S | | ||
| | Signed commits | S | S | S | S | | ||
| | Copilot code review | R | R | S | R | | ||
|
|
||
| Rationale for the differences: | ||
|
|
||
| - **Required status checks** are mandatory only where a failing artifact reaches | ||
| users automatically (class 1) or is published under a version tag (class 2). | ||
| Class 3 is consumed from source at a ref the consumer chooses. | ||
| - **Linear history** is required only for class 1, where the deployed branch must | ||
| be trivially bisectable against what is live. Elsewhere it constrains merge | ||
| strategy for little benefit — and see the cost recorded below. | ||
| - **Copilot code review** is required wherever a change reaches users or other | ||
| repositories without a second human necessarily reading it. | ||
|
|
||
| ### Expressed as rulesets, not classic protection | ||
|
|
||
| New configuration uses repository rulesets. Classic branch protection is treated | ||
| as legacy and is not extended. | ||
|
|
||
| Where both exist, **neither one is authoritative**: the two systems apply | ||
| simultaneously and the effective rule is their union. A ruleset does not | ||
| override, disable, or supersede classic protection — the classic settings keep | ||
| applying until they are explicitly removed. Any audit must therefore read both | ||
| sources, and a repository is only fully migrated once its classic protection is | ||
| deleted, not merely once a ruleset exists alongside it. | ||
|
|
||
| Migration of the 19 classic-only repositories is not required by this ADR and | ||
| should not be bundled with adopting the baseline. Until a repository is | ||
| migrated, expect its behavior to reflect both systems at once. | ||
|
|
||
| ### Repositories with no CI | ||
|
|
||
| 23 repositories have no workflow files, so the **Required status checks** row is | ||
| unsatisfiable there — there is no check to require. Those repositories are | ||
| conformant on every other row and are reported as `n/a` for that row rather than | ||
| as failures. | ||
|
|
||
| ## Rollout and rollback | ||
|
|
||
| 1. Accept this ADR. It changes no repository by itself. | ||
| 2. Add a settings step to `runbooks/new-repository.md` so new repositories start | ||
| conformant. | ||
| 3. Build a read-only audit that reports drift per repository against this table, | ||
| following the `scripts/labels-sync.rb` pattern: read-only by default, an apply | ||
| mode behind both `--apply` and `--confirm-apply`, and a pilot allowlist. | ||
| 4. Apply per repository, deliberately, starting with class 1 and class 2. | ||
|
|
||
| Rollback differs by setting, and the distinction matters. | ||
|
|
||
| Every row except the first is expressed as a ruleset. Those are reversible per | ||
| repository and immediately: deleting the ruleset restores the prior state without | ||
| touching repository contents. | ||
|
|
||
| **Renaming a default branch is not in that category** and must not be treated as | ||
| one. It is not a ruleset, so deleting rulesets does not undo it. GitHub retargets | ||
| open pull requests and installs a redirect, but existing clones keep tracking the | ||
| old name, and anything referencing the branch by name — workflow triggers in | ||
| other repositories, documentation links, external CI, `uses:` references pinned | ||
| to a branch — is not updated. Reverting means renaming back and repairing the | ||
| same references twice. | ||
|
|
||
| The default-branch row is therefore **excluded from any automated apply**. Only | ||
| `z-shell/web` is currently non-conformant, and it should be migrated as a | ||
| deliberate one-off with its references audited first, not swept up in a | ||
| conformance run. | ||
|
|
||
| With that row excluded, nothing the audit applies is irreversible. | ||
|
|
||
| ## Consequences | ||
|
|
||
| ### Positive | ||
|
|
||
| - A declared standard exists, so "is this repository configured correctly?" has | ||
| an answer that is not a matter of opinion. | ||
| - Drift becomes measurable. Given per-repository configuration is the only | ||
| mechanism available on this plan, measurement is the only alternative to | ||
| discovering gaps by accident. | ||
| - The declared-versus-enforced gap in ADR-0009 becomes visible rather than | ||
| latent. | ||
|
|
||
| ### Negative / costs | ||
|
|
||
| - Requiring pull requests on repositories that currently allow direct pushes will | ||
| change day-to-day habits for single-maintainer repositories. | ||
| - Required status checks can deadlock. `z-shell/wiki` required a check whose | ||
| failure could only be cleared by the merge that the check was blocking; the fix | ||
| needed a direct-to-`main` commit. Any class-1 repository can reach this state, | ||
| and the escape route must stay available. | ||
| - Rules do not stop administrators. An admin push reports | ||
| `Bypassed rule violations` and succeeds. The baseline raises the floor; it does | ||
| not make configuration self-enforcing. | ||
| - Configuring 86 repositories is real work even when automated, and every applied | ||
| rule is a way to lock a maintainer out of their own repository. | ||
|
|
||
| ### Neutral | ||
|
|
||
| - `enforce_admins` is deliberately left out of the baseline. Enabling it would | ||
| have made the `wiki` deadlock unrecoverable without changing settings under | ||
| pressure. | ||
| - The baseline says nothing about branch names other than the default, so | ||
| ADR-0008's `next` branch model is unaffected. | ||
|
|
||
| ## Alternatives considered | ||
|
|
||
| - **Organization-level rulesets.** The correct mechanism, and what this ADR would | ||
| otherwise recommend instead of a per-repository baseline. Unavailable on the | ||
| current plan; revisit if the organization moves to GitHub Team. | ||
| - **Do nothing and fix repositories as problems surface.** This is the status | ||
| quo, and it is what produced a class-2 repository with an unenforced "required" | ||
| test suite. The cost is paid in incidents rather than in setup. | ||
| - **One uniform policy for all repositories.** Simpler to state and to audit, but | ||
| it would require status checks on 23 repositories that have no CI, and would | ||
| impose class-1 constraints on plugins consumed from source. | ||
| - **Automated remediation across all repositories.** Rejected: applying | ||
| protection unattended to 86 repositories risks locking maintainers out, and the | ||
| labels rollout showed that a per-repository confirm step is affordable. | ||
|
|
||
| ## References | ||
|
|
||
| - `decisions/0007-release-publication-flow.md` — the repository classes this | ||
| baseline is keyed to. | ||
| - `decisions/0009-testing-ci-strategy.md` — declares the class-2 test requirement | ||
| that is currently unenforced. | ||
| - `runbooks/new-repository.md` — bootstrap procedure that should gain a settings | ||
| step. | ||
| - `runbooks/release.md` — records that classic protection and rulesets are | ||
| independent systems whose effective rule is their union. | ||
| - `scripts/labels-sync.rb` — the audit-then-gated-apply pattern the eventual | ||
| settings audit should follow. | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.