diff --git a/AGENTS.md b/AGENTS.md index 0f6a6d818..eb69aa5aa 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. +- **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 + +`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/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-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..0469a7281 100644 --- a/todo/commit-message-standard.md +++ b/todo/commit-message-standard.md @@ -1,7 +1,12 @@ # 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. 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 @@ -94,20 +99,30 @@ 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 - [ ] 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) Machine-checking the format before merge is a separate, later step: [commit-message-enforcement.md](./commit-message-enforcement.md), unblocked