---
name: "changelog_discipline"
description: "Keep a real CHANGELOG.md in Keep-a-Changelog format: what earns an entry, Unreleased discipline, release hygiene, and the changelog as a contract for downstream consumers."
metadata: { "includeInPrompt": true }
---

## Purpose
Maintain the project's release-facing record of what changed, when, and whether it breaks consumers. This covers one artifact, the CHANGELOG. General prose craft and doc-set structure belong elsewhere. Trigger this when starting a changelog, reviewing one before a release, or deciding whether a change deserves an entry.

## Workflow
1. Start from this canonical skeleton (matches the Keep-a-Changelog 1.1.0 template, verified 2026-09-26). Replace `org/repo` with the real project path and delete sections you don't need for a release.

```markdown
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Added

### Changed

### Deprecated

### Removed

### Fixed

### Security

## [1.0.0] - 2026-09-26

### Added
- ...

[Unreleased]: https://github.com/org/repo/compare/v1.0.0...HEAD
[1.0.0]: https://github.com/org/repo/releases/tag/v1.0.0
```

2. Write the entry when the change merges, not at release time. The `Unreleased` section is a staging area that fills up continuously. On release day you rename it, add the version and date, and start a fresh empty one.
3. Decide what earns an entry with the observable-behavior test: if a consumer could notice the change in behavior, API surface, configuration, or performance, it gets an entry. Internal refactors, comment cleanups, and test-only changes stay in git history. Breaking changes go under `Changed` or `Removed` and are flagged in plain language, e.g. `**BREAKING:** config key \`timeout_ms\` renamed to \`timeout\``.
4. On release day: set the version, stamp the date in ISO 8601 (`YYYY-MM-DD`), link the version heading to its tag, and refresh the `[Unreleased]` compare link to point at the new tag. Then run the pre-release diff check in Operating Rules before announcing.
5. Treat the changelog as a contract with downstream consumers, including agents that pin releases. Run the reader-scan test: a consumer scanning only the changelog should catch every breaking change and find every new capability. If an entry would fail that test, rewrite it before release.
6. Never ship a changelog that is only an auto-generated commit dump. Generated lists record work; a changelog reports consequences. If you use a generator to gather candidates, curate every line before it goes under a version heading.

## Output Contract
- A CHANGELOG.md that follows the skeleton above: `Unreleased` section always present, dated and tag-linked version sections, entries grouped by type.
- Entries that are short, user-facing, and specific, each ideally pointing at a PR or issue: `- Added --json flag to export (#42)`.
- A release process that produces the entry before the tag, the date on release day, and a diff check confirming nothing merged since the last tag went unrecorded.

## Operating Rules
1. One entry per user-visible change. Write in the consumer's language, not the diff's. Say "checkout now rejects expired cards with a 402" rather than "refactored PaymentProcessor#authorize".
2. Breaking changes get the bold `**BREAKING:**` prefix and a one-line migration note. A breaking change buried in a long `Changed` list is a broken contract.
3. Write entries when merging the PR. Backfilling a whole release from memory produces vague entries and missed ones. Flag PRs that change behavior without a changelog entry during review.
4. Do not rewrite entries under a released version except to mark a release `[YANKED]` or fix an outright error. Corrections to the story of a release go in a new entry under `Unreleased`.
5. Security fixes go under `Security`, kept factual and minimal until the issue is patched everywhere, then expanded.
6. Before cutting a release, compare the git history since the last tag against the changelog. This snippet is verified to run as-is:

```bash
# Everything merged since the last release tag: check each commit
# against the changelog before you cut the release. If the repo has no
# tags yet, replace "${last_tag}..HEAD" with HEAD and check every commit.
last_tag=$(git describe --tags --abbrev=0)
git log "${last_tag}..HEAD" --oneline --no-merges
```

7. When in doubt about an entry, include it. A changelog that occasionally mentions a minor fix is fine; a changelog that silently ships a breaking change is not.
