ADR 0013: Changelog and Release Notes
Ratification
Adopted before ADR 0018. There was no separate ratification process. Git history for this file on main is the record.
- Discussion Issue: not recorded (before ADR 0018)
- Merge PR: see git history for this file
- Accepted: as merged to
main
Context
Why this matters: When something breaks in production, the first question is “what changed?” A changelog in the repo answers that without digging through dozens of commits. It is also the polite surface for integrators: they should not have to read your internal tickets to learn about API or security changes.
Ad hoc notes in chat do not scale. We keep Keep a Changelog at the repository root, aligned with Docs as Code, and enforce updates when user-facing paths change.
Decision
Where and how
-
The repository maintains a root
CHANGELOG.mdfollowing Keep a Changelog, with an[Unreleased]section for ongoing work and dated sections when versions are released. -
User-facing changes (behavior, API contract, security, notable fixes) belong under
### Added,### Changed,### Fixed,### Deprecated,### Removed,### Securityas appropriate. Purely internal refactors, test-only edits, or mechanical CI updates that do not affect consumers may be omitted when they do not match the trigger paths in Validation (below).
Merge and PR conventions
-
Prefer squash merge into
main(ormaster) so one commit per change carries a clear summary; that message is a good place for a changelog-related pointer when useful. -
When a pull request touches user-facing areas (see Validation), it must also update
[Unreleased]inCHANGELOG.mdin the same PR, unless an escape hatch applies. -
Escape hatches (documented for automation and humans):
-
Include
[skip changelog]orskip-changelogin the PR title (for pull-request checks) or in commit messages in the pushed range (for post-merge push checks) when the change is mechanical and needs no release note. -
Optional GitHub label
skip changelogmay be used where enabled; teams should mirror the same intent as the text tokens above.
-
Include
Automation (LLM)
An optional script may call an HTTP API (OpenAI-compatible or similar) using keys from the developer or CI
environment to draft bullet points from git log. The output is assistive only:
humans review and edit before merge. Nothing automatically overwrites CHANGELOG.md on
main without a normal PR.
Local workflow: scripts/changelog_draft.py
The script does not write CHANGELOG.md. You copy or adapt its stdout into
[Unreleased] in a normal commit/PR.
-
What
--sinceand--headmean. It uses the same range as Git:since..headmeans commits reachable fromheadthat are not insince(for example new work on your branch that is not yet inmain). Defaults:--since main --head HEAD. -
Do you need a new branch? Not for the script itself. For collaboration, use a feature
branch and open a PR; point
--headat that branch name or stay checked out on it and use--head HEAD. -
git addis not enough. Until yougit commit, your changes are not in the history, somain..your-branchcan show no commits even if files are staged. The script then prints_No changes in this range._(or hints on stderr). -
Draft before a commit. Run
python scripts/changelog_draft.py --since main --head HEAD --include-working-tree. That addsgit diff --cached --stat(staged) andgit diff --stat(unstaged) to the prompt so the model can summarize work that is not committed yet. -
After one or more commits on the branch. Omit
--include-working-tree(or keep it if you still have local edits). Example:python scripts/changelog_draft.py --since main --head feature/my-feature. -
Inspect the prompt without calling the API:
python scripts/changelog_draft.py --print-log(add the same flags you would use for a real run). -
Save the draft to a file:
python scripts/changelog_draft.py ... -o /tmp/changelog-llm-draft.mdwrites UTF-8 in addition to stdout. Still merge intoCHANGELOG.mdby hand under the right###headings — do not raw-append stdout to the changelog file. -
Secrets (local only). Set
OPENROUTER_API_KEYorOPENAI_API_KEYin the repository root.envfile (seeenv/example). Optional:OPENAI_BASE_URL,OPENAI_MODEL, OpenRouter attribution headers described there. -
Smoke-test the API key:
make llm-pingorpython scripts/llm_ping.py. -
One-step draft (Makefile):
make changelog-draftrunsscripts/changelog_draft.pywithmain..HEAD, includes working-tree stats by default, and writeschangelog-llm-draft.md. Override withCHANGELOG_HEAD=feature/x CHANGELOG_SINCE=mainorCHANGELOG_DRAFT_FLAGS=for commits-only.
Validation
-
Pull requests targeting
mainormaster: if the diff changes any path underapp/,docs/openapi/, or the rootREADME.md, thenCHANGELOG.mdmust appear in the same diff unless a skip hatch applies. -
Pushes to
mainormaster: the same path rule applies to the rangebefore..afterfrom GitHub Actions. Commit messages in that range may satisfy the skip hatch. Ifbeforeis all zeros (e.g. new branch pointer), the gate does not apply to that push.
Consequences
Positive
- Consumers and contributors share one obvious place for “what changed.”
- CI enforces the habit without parsing every intermediate commit in a multi-commit branch.
Trade-offs
- Path filters may need tuning; false positives are mitigated by skip tokens and documented exemptions.
- Optional LLM usage requires secrets hygiene and is never required for the core app runtime.
Related
- CHANGELOG.md (repository root)
- ADR 0001 — Docs as Code
- ADR 0007 — OpenAPI governance
Page history
| Date | Change | Author |
|---|---|---|
| Added Page history section (repository baseline). | Ivan Boyarkin |