From a117779a647e70fdc61657118af0bd9a18fda351 Mon Sep 17 00:00:00 2001 From: Justin Merrell Date: Tue, 25 Aug 2026 17:06:29 +0000 Subject: [PATCH] docs(repo): record that the merged subject must be the validated one MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `squash_merge_commit_title` was GitHub's default, `COMMIT_OR_PR_TITLE`, under which a pull request with exactly one commit lands that commit's subject rather than the title `Conventional PR title` validated. Normalising the title did nothing about Dependabot's commit message, so #67 landed on main as `build(deps-dev): Bump the tooling group across 1 directory with 5 updates` after being validated as `bump` — ADR 0016 decision 3 was true of every pull request and false of every merge. It was never only the bot's problem. Any single-commit pull request landed a subject that only the local commit-msg hook had seen, and that hook checks the type, not the case. The setting is now PR_TITLE. Being a repository setting rather than a ruleset field, nothing in .github/rulesets/ can carry it, so it is recorded as invariant 7 in RULESETS.md — with the command to verify it, alongside the two other invariants that are only visible against live GitHub state. Signed-off-by: Justin Merrell --- .github/rulesets/RULESETS.md | 13 ++++++++++-- docs/adr/0016-dependency-update-policy.md | 25 ++++++++++++++++++++--- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/.github/rulesets/RULESETS.md b/.github/rulesets/RULESETS.md index c0cc6b6..a0aea86 100644 --- a/.github/rulesets/RULESETS.md +++ b/.github/rulesets/RULESETS.md @@ -60,8 +60,8 @@ thread resolved. `0` is GitHub's sanctioned "no blanket reviewers" value, and the code-owner requirement is evaluated per changed file. [ADR 0015](../../docs/adr/0015-selective-code-owner-review.md) says why. -Six invariants keep the mechanism working. Each one breaks it *silently* — the -first four are checked by `task check:rulesets` (RUL-05..RUL-09); the last two +Seven invariants keep the mechanism working. Each one breaks it *silently* — the +first four are checked by `task check:rulesets` (RUL-05..RUL-09); the last three are only visible against live GitHub state: 1. **No `*` catch-all in `.github/CODEOWNERS`.** It makes every pull request @@ -83,6 +83,15 @@ are only visible against live GitHub state: 6. **No org-level ruleset may impose an approval count on this repository.** Same aggregation. `spec` is deliberately absent from the org `pr-workflow` ruleset's include list; do not add it. +7. **`squash_merge_commit_title` stays `PR_TITLE`.** It is a repository setting, + not a ruleset field, so nothing in this directory can carry it. Under the + default `COMMIT_OR_PR_TITLE`, a pull request with exactly one commit lands + *that commit's* subject rather than the title `Conventional PR title` + validated — so a subject no CI check ever read reaches `main`. It already + has: #67 was validated as `build(deps-dev): bump the tooling group …` and + landed as `Bump …`. Verify with `gh api repos/musher-dev/spec --jq + .squash_merge_commit_title`. See + [ADR 0016](../../docs/adr/0016-dependency-update-policy.md). **An owner's own pull requests are exempt.** GitHub cannot request a review from the author, so authorship waives the requirement for the patterns that author diff --git a/docs/adr/0016-dependency-update-policy.md b/docs/adr/0016-dependency-update-policy.md index e30c779..76eb919 100644 --- a/docs/adr/0016-dependency-update-policy.md +++ b/docs/adr/0016-dependency-update-policy.md @@ -112,7 +112,26 @@ conventional-*shaped* prefix. Whether the type and scope in that prefix are real remains the check's business; correcting them here would paper over exactly the `dependabot.yml` bug described above. -### 4. Every copy of the vocabulary is checked, including the bot's +### 4. The merged subject is the one that was validated + +`squash_merge_commit_title` moves from GitHub's default `COMMIT_OR_PR_TITLE` to +`PR_TITLE`. Under the default, a pull request with exactly one commit lands +*that commit's* subject rather than its title — and normalising the title does +nothing about Dependabot's commit message, which stays capitalised. #67 landed +on `main` as `build(deps-dev): Bump the tooling group across 1 directory with 5 +updates` after being validated as `bump`. Decision 3 would have been true of +every pull request and false of every merge. + +It is not only the bot's problem. Any single-commit pull request landed a +subject that only the local commit-msg hook had seen, and that hook checks the +type, not the case. `PR_TITLE` makes the string CI validates and the string that +reaches `main` the same string, for everyone. + +Being a repository setting rather than a ruleset field, nothing in +`.github/rulesets/` can carry it; it is recorded as invariant 7 in +`.github/rulesets/RULESETS.md` with the command to verify it. + +### 5. Every copy of the vocabulary is checked, including the bot's `tools/src/commits.ts` now reads `.github/dependabot.yml` and `.github/CONTRIBUTING.md` alongside the three files it already reconciled. A @@ -125,7 +144,7 @@ A `commits` job in `.config/lefthook.yml` runs the check before the push as well as in CI. The prefixes in `dependabot.yml` are exercised once a week by a bot nobody watches, which is the longest possible feedback loop for a typo. -### 5. `wrangler` is excluded from the grouped update +### 6. `wrangler` is excluded from the grouped update GOVERNANCE.md → Tooling dependencies names `wrangler` as the only dependency here handed a credential, and rests its guarantee on that exact pin moving "only @@ -169,7 +188,7 @@ revisiting the first time a major lands broken, not before. Dependency updates merge on green CI without a bypass. They are correctly typed and scoped, so they read as maintenance in `git log` and cannot cut a release. The vocabulary now has five copies and a check that holds all five, rather than -three copies and two that drifted unobserved. `wrangler` moves only in a pull +three copies and two that drifted unobserved. What CI validates is what lands. `wrangler` moves only in a pull request opened for it. The normalisation step is not a gate; the action in the same job is. Because