Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 52 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

---

Expand Down Expand Up @@ -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
`<PR title> (#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.** `<topic>: <short description>` — `<topic>` 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):

```
<free prose>

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/<PR>.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.
9 changes: 9 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,15 @@ same pull request, and — for code changes — a changelog entry added as
`changelog/unreleased/<PR>.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
`<pull request title> (#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
`<topic>: <short description>` 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;
Expand Down
6 changes: 4 additions & 2 deletions todo/changelog-from-git-history.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
18 changes: 9 additions & 9 deletions todo/commit-message-enforcement.md
Original file line number Diff line number Diff line change
@@ -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

Expand Down Expand Up @@ -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
Expand Down
35 changes: 25 additions & 10 deletions todo/commit-message-standard.md
Original file line number Diff line number Diff line change
@@ -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

Expand Down Expand Up @@ -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
Expand Down
Loading