Skip to content

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.