From 8aeee8e254b9ac15ce6a7b6375ca783729be1a6c Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 14 Aug 2026 19:58:51 +0000 Subject: [PATCH 1/2] AGENTS.md: adopt the commit-message standard MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `main` takes one squash commit per PR, titled ` (#NNN)` with the PR description as its body, so the title and description are the commit message. A changelog generated from Git history could read nothing else, and the option expires for every PR that merges before the format is fixed. New §8.5 documents it: the `: ` title within 72 characters including the ` (#NNN)` GitHub appends, the mandatory `Changelog:` section — the last section of the description before an optional trailer block, holding the same list items as `changelog/unreleased/.md`, or `Changelog: none` — and squash-merge-only landing. §8.4 gains the `vX.Y.Z` release tag, so "what shipped in this release" is a range between tags rather than a heuristic over version-bump titles. §2 step 6 and CONTRIBUTING.md point at the new section. `todo/commit-message-standard.md` stays: the repository settings it asks for (squash-only, default squash message, branch protection) need admin rights and cannot land in a PR, and until they are set §8.5 is a convention rather than a rule. Its format section is now reasoning behind AGENTS.md rather than the normative text. `todo/commit-message-enforcement.md` was waiting on this adoption and no longer is. Changelog: none Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01WQFBgRCxeiy5gw1is28t2H --- AGENTS.md | 55 ++++++++++++++++++++++++++++-- CONTRIBUTING.md | 9 +++++ todo/commit-message-enforcement.md | 18 +++++----- todo/commit-message-standard.md | 20 ++++++++--- 4 files changed, 85 insertions(+), 17 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 0f6a6d818..eabc39a3f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -130,9 +130,11 @@ cargo fmt -- --check # verify formatting cargo fmt -- --check ``` 5. Delete the `todo/` issue file in the same PR that fixes it. -6. Open the PR. If it changes code, add the CHANGELOG entry using the real PR - number ([§8.3](#83-changelog)) — PRs that only touch `todo/`, `AGENTS.md`, or - other documentation don't need one. +6. Open the PR. Its title and description become the commit message on `main`, + so write them as one ([§8.5](#85-commit-messages)). If it changes code, add + the CHANGELOG entry using the real PR number ([§8.3](#83-changelog)) — PRs + that only touch `todo/`, `AGENTS.md`, or other documentation don't need one, + and say `Changelog: none` in the description instead. --- @@ -1311,3 +1313,50 @@ Only add CHANGELOG entries for code changes — PRs that only touch `todo/`, carries it into the release branch — outside the renamed directory. Move any such file into `changelog/X.Y.Z/` before merging the release, or its change ships unrecorded in the changelog. Check again right before merging. +- **Tag the release commit `vX.Y.Z`** once it lands on `main`, and push the + tag. "Which entries shipped in this release" is then a range between two + tags — a fact — instead of a heuristic over version-bump commit titles. + Releases through `0.44.0` are untagged; leave them so. + +### 8.5 Commit messages + +`main` takes exactly one commit per PR: the squash merge, titled +` (#NNN)` with the PR description as its body. Both halves are +reviewed text that outlives the PR page, and a changelog generated from Git +history could read nothing else, so write the title and the description as the +commit message they become. Commits on the branch are discarded by the squash, +so their messages are working notes. + +- **Title.** `: ` — `` is the module path + (`types/bit_vec`, `djs/tokenizer`) or an area (`ci`, `docs`, `changelog`, + `AGENTS.md`), the same topic the CHANGELOG entry starts with; the + description is imperative, lower-case after the colon, and has no trailing + period. Keep it within 72 characters **including** the ` (#NNN)` GitHub + appends, and never write a `(#NNN)` of your own. A release PR's title is the + bare version: `0.45.0`. +- **Description.** Free prose — motivation, design, measurements, alternatives + considered — then a `Changelog:` section, the last section before an optional + trailer block (`Co-Authored-By:`, generated-with lines, session links): + + ``` + + + Changelog: + - `types/bit_vec`: `tryListToVec` reuses the shared balanced fold, at the + same cost as the accumulator it replaces + ``` + + The section holds exactly the list items of `changelog/unreleased/.md` — + same Markdown subset, same `**BREAKING CHANGES:**` prefix where it applies, + no PR link ([§8.3](#83-changelog)). A PR that needs no entry writes + `Changelog: none`. The section is **mandatory** either way, so a forgotten + entry is a visible omission rather than a silent one. + + It duplicates the entry file on purpose: the file is what today's release + process reads, the section is what a generator reading Git history would + read. Neither is derived from the other, so keep them identical. +- **How it lands.** Squash and merge, always. The merge box offers the reviewed + title and description as the default message — don't edit it there, where + nobody reviews the result. A rebase merge would replay the branch's commits + with their working-note messages and no `(#NNN)`; a merge commit would bury + the PR in a two-parent graph. Nothing lands on `main` outside a PR. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 61afdfa87..c4c9f05aa 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -124,6 +124,15 @@ same pull request, and — for code changes — a changelog entry added as `changelog/unreleased/.md`, named by the real pull request number once the pull request exists (see [changelog/README.md](./changelog/README.md)). +The pull request lands on `main` as a single squash commit titled +` (#NNN)` with the pull request description as its body, so +both are written as that commit message +([AGENTS.md §8.5](./AGENTS.md#85-commit-messages)): a +`: ` title within 72 characters including the +` (#NNN)` GitHub appends, and a description ending in a `Changelog:` section +that repeats the changelog entry — or `Changelog: none` when the change needs +no entry. + ## OpenAI Codex environment Set Node.js to 22. Both `npm test` and `npm run cov` work in this environment; diff --git a/todo/commit-message-enforcement.md b/todo/commit-message-enforcement.md index e730cad62..032f73e2e 100644 --- a/todo/commit-message-enforcement.md +++ b/todo/commit-message-enforcement.md @@ -1,13 +1,12 @@ # Enforce the commit-message standard before merge **Priority:** P3 -**Status:** open — do not start until the format from -[commit-message-standard.md](./commit-message-standard.md) is adopted into -AGENTS.md §8; the linter enforces the documented rule, not the proposal. -The gap between adoption and enforcement is deliberate trial time: the -format is used by hand on real PRs first, and whatever it gets wrong is -fixed while a fix is still a documentation edit rather than a linter -change plus a rule migration. +**Status:** open — the format is adopted, in +[AGENTS.md §8.5](../AGENTS.md#85-commit-messages), so this is no longer +waiting on it; the linter enforces that documented rule. The gap between +adoption and enforcement is deliberate trial time, so let the format run by +hand on real PRs first: whatever it gets wrong is fixed while a fix is still a +documentation edit rather than a linter change plus a rule migration. ## Problem @@ -50,8 +49,9 @@ would block it outright but require an Enterprise plan. ## Related -- [commit-message-standard.md](./commit-message-standard.md) — the format - this enforces; adopting it into AGENTS.md §8 unblocks this issue +- [AGENTS.md §8.5](../AGENTS.md#85-commit-messages) — the format this enforces +- [commit-message-standard.md](./commit-message-standard.md) — the reasoning + behind that format, and the repository settings it still waits on - [changelog-website.md](./changelog-website.md) — plans the changelog Markdown-subset parser the `Changelog:` section validator reuses - [changelog-from-git-history.md](./changelog-from-git-history.md) — the diff --git a/todo/commit-message-standard.md b/todo/commit-message-standard.md index 44e87db0f..fcd0d5fd6 100644 --- a/todo/commit-message-standard.md +++ b/todo/commit-message-standard.md @@ -1,7 +1,13 @@ # Standard for commit messages merged into `main` **Priority:** P2 -**Status:** proposed +**Status:** wip — the format is adopted, in +[AGENTS.md §8.5](../AGENTS.md#85-commit-messages) (title, `Changelog:` section, +squash-only) and [§8.4](../AGENTS.md#84-breaking-changes-and-versioning) +(release tags). AGENTS.md is the normative text from now on; the proposal +below is kept for the reasoning behind it, and only the repository settings +remain undone — they need a maintainer with admin rights and cannot land in a +PR. ## Problem @@ -104,10 +110,14 @@ version-bump titles works but is a heuristic where a tag is a fact. - [ ] Repository settings: squash-only, default squash message "Pull request title and description", branch protection (PRs required, linear - history) -- [ ] Document the title and `Changelog:` section format in AGENTS.md §8 once - adopted -- [ ] Tag `v0.45.0` at the next release and each release after + history). Until these are set, AGENTS.md §8.5 is a convention a + maintainer can defeat with one click in the merge dialog or one + `git push`. +- [x] Document the title and `Changelog:` section format in AGENTS.md §8 once + adopted — [§8.5](../AGENTS.md#85-commit-messages), with the release-tag + step in [§8.4](../AGENTS.md#84-breaking-changes-and-versioning) +- [ ] Tag `v0.45.0` at the next release and each release after (now part of + the release procedure in AGENTS.md §8.4) Machine-checking the format before merge is a separate, later step: [commit-message-enforcement.md](./commit-message-enforcement.md), unblocked From 90bebdc4ba5cae19812766213c959f40c0bd334c Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 14 Aug 2026 21:03:11 +0000 Subject: [PATCH 2/2] Drop the release-tag rule: no tags, now or later MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Review found the tag bullet's "releases through 0.44.0 are untagged" boundary off by one — 0.45.0 has shipped — and the answer is that the repository has no tags and does not plan to have any. AGENTS.md §8.4 now records that instead of prescribing tags: release membership is already in changelog/X.Y.Z/, one file per PR that shipped, so a tag would be a second hand-maintained copy of it. The todo's "Tag releases" section is kept as a rejected design with the reasoning, and its task is dropped. changelog-from-git-history.md's design 1 took "between release tags" as its range query; it now reads the release commit's bare-version title. --- AGENTS.md | 8 +++---- todo/changelog-from-git-history.md | 6 +++-- todo/commit-message-standard.md | 35 +++++++++++++++++------------- 3 files changed, 28 insertions(+), 21 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index eabc39a3f..eb69aa5aa 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1313,10 +1313,10 @@ Only add CHANGELOG entries for code changes — PRs that only touch `todo/`, carries it into the release branch — outside the renamed directory. Move any such file into `changelog/X.Y.Z/` before merging the release, or its change ships unrecorded in the changelog. Check again right before merging. -- **Tag the release commit `vX.Y.Z`** once it lands on `main`, and push the - tag. "Which entries shipped in this release" is then a range between two - tags — a fact — instead of a heuristic over version-bump commit titles. - Releases through `0.44.0` are untagged; leave them so. +- **The repository has no Git tags and is not going to get any.** "Which + entries shipped in this release" is answered by `changelog/X.Y.Z/`, which + holds one file per PR that shipped in it; a tag would be a second copy of + that fact, kept in step by hand. ### 8.5 Commit messages diff --git a/todo/changelog-from-git-history.md b/todo/changelog-from-git-history.md index a6f5223a0..eab1eb397 100644 --- a/todo/changelog-from-git-history.md +++ b/todo/changelog-from-git-history.md @@ -26,8 +26,10 @@ Evaluate at least these designs before removing `changelog/`: 1. **Commit-message extraction.** The entry lives in the squash/merge commit message (e.g. a `Changelog:` trailer, reviewed as part of the PR). The - generator deterministically extracts trailers between release tags — no - summarization, reviewed text, `changelog/` genuinely redundant. + generator deterministically extracts trailers between release commits (the + repository has no tags and is not getting any, so the bare-version commit + title is the boundary) — no summarization, reviewed text, `changelog/` + genuinely redundant. 2. **Authoring assistant.** A tool drafts the entry from the PR's diff at PR time; the reviewed result is still committed as a `changelog/` file. The generator stays out of the build; `changelog/` remains the source of truth. diff --git a/todo/commit-message-standard.md b/todo/commit-message-standard.md index fcd0d5fd6..0469a7281 100644 --- a/todo/commit-message-standard.md +++ b/todo/commit-message-standard.md @@ -2,12 +2,11 @@ **Priority:** P2 **Status:** wip — the format is adopted, in -[AGENTS.md §8.5](../AGENTS.md#85-commit-messages) (title, `Changelog:` section, -squash-only) and [§8.4](../AGENTS.md#84-breaking-changes-and-versioning) -(release tags). AGENTS.md is the normative text from now on; the proposal -below is kept for the reasoning behind it, and only the repository settings -remain undone — they need a maintainer with admin rights and cannot land in a -PR. +[AGENTS.md §8.5](../AGENTS.md#85-commit-messages): title, `Changelog:` section, +squash-only. Release tagging was rejected, see below. AGENTS.md is the +normative text from now on; the proposal below is kept for the reasoning +behind it, and only the repository settings remain undone — they need a +maintainer with admin rights and cannot land in a PR. ## Problem @@ -100,11 +99,20 @@ Changelog: section remains; if it loses, the section cost was a few reviewed lines per PR. -### Tag releases - -Tag each release commit `vX.Y.Z` when it lands on `main`. Between-tags is the -natural range query for "entries in this release"; falling back to parsing -version-bump titles works but is a heuristic where a tag is a fact. +### Tag releases — rejected + +This section proposed tagging each release commit `vX.Y.Z`, so that "entries +in this release" is a range between two tags rather than a parse of +version-bump titles. **Decided against**: the repository has no tags and is +not going to get any +([#1561](https://github.com/functionalscript/functionalscript/pull/1561)). +The changelog already records release membership per PR — `changelog/X.Y.Z/` +holds one file per PR that shipped in that release — so a tag would be a +second copy of a fact the tree already carries, and one a release could +forget. A generator takes the boundary from the release commit, whose title is +the bare version, or from the changelog directories themselves. Recorded in +[AGENTS.md §8.4](../AGENTS.md#84-breaking-changes-and-versioning) so the +question is not reopened by the next reader. ## Tasks @@ -114,10 +122,7 @@ version-bump titles works but is a heuristic where a tag is a fact. maintainer can defeat with one click in the merge dialog or one `git push`. - [x] Document the title and `Changelog:` section format in AGENTS.md §8 once - adopted — [§8.5](../AGENTS.md#85-commit-messages), with the release-tag - step in [§8.4](../AGENTS.md#84-breaking-changes-and-versioning) -- [ ] Tag `v0.45.0` at the next release and each release after (now part of - the release procedure in AGENTS.md §8.4) + adopted — [§8.5](../AGENTS.md#85-commit-messages) Machine-checking the format before merge is a separate, later step: [commit-message-enforcement.md](./commit-message-enforcement.md), unblocked