Writing the Changelog
CHANGELOG.md is the record of what shipped. The page at
docs.proteanhq.com/changelog is a second rendering of that
file, generated by scripts/build_changelog_page.py and committed alongside it.
Two jobs are described here. Every pull request writes a fragment. Each release
assembles the fragments into CHANGELOG.md and regenerates the page.
Every pull request writes a fragment
Drop one file in changes/, named <issue-number>.<category>.md, where the
category is added, changed, deprecated, removed, fixed or security.
An issue that spans categories gets one fragment per category. The full naming
and content rules are in changes/README.md.
What one entry looks like
One sentence. 75 to 300 characters. A scope tag in square brackets at the front, the issue number in parentheses at the end.
- [CLI] Added `protean verify`, which runs init, `protean check` and the project's pytest suite in order and returns one verdict, with `--json` and a stable exit-code contract. (#1323)
Keep exact command, flag and symbol names: a reader searches for them. Cut the mechanism. Detail worth keeping goes to the documentation page a user would look at for that subject, and the entry can link it.
An entry that announces a break carries the **Breaking.** marker right after
the scope tag, or a narrowed form such as **Breaking (memory cache only).**
when only some users are affected.
Where one change produced several distinct behaviours, write one entry each and repeat the issue number. An entry that needs a second paragraph is two entries.
Scope tags
Use a tag already in CHANGELOG.md. The set in use:
[Broker], [Cache], [Check], [CI], [CLI], [Database], [Docs],
[DX], [Event store], [Events], [Examples], [Fields], [Handlers],
[Internals], [IR], [MCP], [Observability], [Observatory],
[Packaging], [Projections], [Scaffold], [Server], [Testing],
[Types].
A new tag is a judgment call: it should name a part of the framework a reader already knows by that name.
Each release assembles the fragments
Upgrade Notes come first
A release with breaking changes opens with an ### Upgrade Notes group, before
every other group. It reads:
### Upgrade Notes
Read the [0.18 migration guide](https://docs.proteanhq.com/reference/migration/v0-18/) before you upgrade. These changes can need action:
- [`protean ir` rejects an unknown format value](https://docs.proteanhq.com/reference/migration/v0-18/#protean-ir-rejects-an-unknown-format-value) (#1435)
Each line links one section of that release's migration guide, in the order the
guide covers them, with the issue number at the end. Put the migration link
here only: the entry itself keeps its **Breaking.** marker and carries no
**Migration:** line. A fragment does carry that line, because
tests/test_changelog_fragments.py uses it to check that a declared break names
a real migration section. Assembly turns it into an Upgrade Notes link and drops
it from the entry.
Past about a dozen links, wrap the list in a <details markdown="1"> block whose
summary counts them, as 0.17.0 and 0.18.0 do.
Check every anchor
mkdocs build --strict validates links to local files. It does not validate an
anchor on an absolute URL, and every Upgrade Notes link is one. Build the site
and check the anchors against the rendered guide:
uv run mkdocs build
grep -o 'id="[a-z0-9_-]*"' site/reference/migration/v0-18/index.html
An anchor strips backticks and punctuation and keeps underscores, so
`get_ttl` returns None becomes #get_ttl-returns-none.
Regenerate the page
Any edit to CHANGELOG.md needs the page and the feed rebuilt:
uv run python scripts/build_changelog_page.py
tests/test_changelog_page_is_current.py fails when the committed page is out
of step, and a CI step runs the same check. Commit CHANGELOG.md,
docs/changelog.md and docs/changelog.xml together.
Two settings in scripts/build_changelog_page.py govern what the page shows.
OLDEST_SHOWN is the oldest release on it, currently 0.15.0; anything older
stays in CHANGELOG.md on GitHub, which the page links. COLLAPSE_OVER_CHARS
is the point, in characters of entry text, past which a group collapses behind a
summary. Upgrade Notes never collapses.