|
| 1 | +# 0040 — Publish the repository settings contract |
| 2 | + |
| 3 | +- **Status:** Accepted |
| 4 | +- **Date:** 2026-08-18 |
| 5 | + |
| 6 | +## Context |
| 7 | + |
| 8 | +The organization tree ([ADR-0035](0035-author-organization-authority-as-strict-json.md)) |
| 9 | +says which repositories are in the fleet, what programs they run, and which |
| 10 | +files they must carry (the surfaces contract). It says nothing about the |
| 11 | +GitHub *repository settings* that decide whether that maintenance is safe: |
| 12 | +whether merged branches are deleted, whether the default branch can be pushed |
| 13 | +to directly, which checks are required and from which producer, what |
| 14 | +permissions a workflow token gets, whether secret scanning is on. |
| 15 | + |
| 16 | +A survey of the five fleet repositories on 2026-08-18 found no default-branch |
| 17 | +ruleset or protection anywhere; workflow tokens with `write` and the right to |
| 18 | +approve pull requests on four of five; secret scanning off everywhere; |
| 19 | +Dependabot alerts off on one; branch deletion on merge on one; two without a |
| 20 | +license. Fluent's own required-checks evaluator |
| 21 | +([Fluent ADR-0056](https://github.com/frostyard/fluent/blob/main/docs/adr/0056-derive-required-checks-from-enforced-github-rules.md)) |
| 22 | +had already specified the v1 default-branch ruleset that nothing had enabled. |
| 23 | +Each of these is an admin action a person clicks or forgets; none is reviewed, |
| 24 | +none is recorded, and drift is invisible. |
| 25 | + |
| 26 | +## Decision |
| 27 | + |
| 28 | +- Core publishes **one repository settings contract**, |
| 29 | + `organization/contracts/repository-settings/v1.json`, validated by |
| 30 | + `organization/schemas/v1/repository-settings.schema.json` and conformance |
| 31 | + fixtures, alongside the surfaces contract and under the same rules: strict |
| 32 | + JSON, closed vocabularies, immutable schema version once a consumer supports |
| 33 | + it (compatible enum widening per [ADR-0039](0039-widen-maintenance-programs-within-schema-v1.md)). |
| 34 | +- Version one is **organization-wide**: every enrolled repository must match |
| 35 | + every value. It covers merge hygiene (delete branch on merge, suggest |
| 36 | + updating branches, no auto-merge, merge methods and commit-message |
| 37 | + defaults so the conventional pull-request title becomes the commit subject, |
| 38 | + wiki and projects off), the default-branch ruleset (active, no bypass, pull |
| 39 | + request required with zero approvals, conversation resolution required, |
| 40 | + strict required status checks, no deletion or force push, no merge queue, |
| 41 | + no classic protection — the Fluent ADR-0056 shape), a tag ruleset on `v*` |
| 42 | + (no deletion or force update, creation restricted), Actions token |
| 43 | + permissions (read, cannot approve pull requests), security features |
| 44 | + (Dependabot alerts and security updates, secret scanning with push |
| 45 | + protection, private vulnerability reporting), metadata (license, |
| 46 | + description, the `frostyard` topic), and the labels the fleet depends on |
| 47 | + (`fluent`). Values a repository may legitimately differ on — visibility, |
| 48 | + discussions, code scanning, the exact required-check names — are |
| 49 | + deliberately not in version one; they are observed, not required, until a |
| 50 | + later version adds per-repository declarations. |
| 51 | +- **The contract is read, not applied, by Fluent.** Fluent's conformance |
| 52 | + sweep compares each enrolled repository's live settings with the contract |
| 53 | + through read-only GitHub calls and proposes drift for the operator; Fluent |
| 54 | + holds no admin credential and changes no setting. |
| 55 | +- **Applying is a human act, scripted here**: `scripts/apply-repo-settings.sh |
| 56 | + <owner/repo>` issues the exact idempotent GitHub API calls the contract |
| 57 | + implies (dry-run by default), and takes the repository's required-check |
| 58 | + names as arguments because the contract does not carry them. An operator |
| 59 | + runs it once per repository and again whenever Fluent reports drift. |
| 60 | +- The contract may only tighten in place: a change that would relax a value |
| 61 | + is a new ADR, not an edit. |
| 62 | + |
| 63 | +## Consequences |
| 64 | + |
| 65 | +- Repository safety settings become reviewed organization decisions with a |
| 66 | + digest, like declarations and surfaces; drift becomes a proposal in |
| 67 | + Fluent's inbox rather than a surprise. |
| 68 | +- Enabling the default-branch ruleset changes how every fleet repository is |
| 69 | + updated: no direct pushes to `main`, every change through a pull request |
| 70 | + whose named checks pass. Fluent's required-checks evaluator finally has |
| 71 | + enforced rules to observe. |
| 72 | +- Version one cannot express per-repository variance; a repository that |
| 73 | + needs it (a private repository, discussions on) is a finding until a later |
| 74 | + version — acceptable for a five-repository fleet, revisited with the first |
| 75 | + real exception. |
| 76 | +- Consumers that pin schema digests (Fluent) bundle the new schema before |
| 77 | + this merges; older Fluent revisions accept a core tree without the contract |
| 78 | + (it is an addition, like verification profiles were). |
| 79 | + |
| 80 | +## Alternatives considered |
| 81 | + |
| 82 | +- **Per-repository settings inside each declaration:** rejected for version |
| 83 | + one; adds a field to the declaration schema (a compatibility question under |
| 84 | + ADR-0039) for variance the fleet does not yet have. |
| 85 | +- **Enforce from Fluent with an admin token:** rejected; Fluent's boundary is |
| 86 | + read and propose, and an admin credential in the coordinator is a wider |
| 87 | + blast radius than any setting it would fix. |
| 88 | +- **Organization-level rulesets and GitHub's own policy features only:** |
| 89 | + useful later, but they are not reviewable JSON in core and do not cover |
| 90 | + merge hygiene, Actions permissions, or labels; the contract can point at |
| 91 | + them once they exist. |
| 92 | + |
| 93 | +## References |
| 94 | + |
| 95 | +- Shapes: [organization repository enrollment](../specs/organization-repository-enrollment.md), |
| 96 | + [organization authority](../design/organization-authority.md), |
| 97 | + Fluent's [maintenance programs plan](https://github.com/frostyard/fluent/blob/main/docs/plans/maintenance-programs.md) |
| 98 | + and [required-check ruleset operations](https://github.com/frostyard/fluent/blob/main/docs/design/required-check-ruleset-operations.md) |
| 99 | +- Builds on: [ADR-0035](0035-author-organization-authority-as-strict-json.md), |
| 100 | + [ADR-0019](0019-governance-as-code-and-risk-tiers.md), |
| 101 | + [ADR-0039](0039-widen-maintenance-programs-within-schema-v1.md) |
0 commit comments