Context
!head -40 changelog.md 2>/dev/null || echo "No changelog.md found"
!git log --oneline -10 2>/dev/null
![ -f repomatic/__init__.py ] && echo "CANONICAL_REPO" || echo "DOWNSTREAM"
Instructions
You help users manage their changelog.md file. See § Style rules below for how an entry must read.
Mechanical layer
The changelog.yaml workflow's fix-changelog job runs lint-changelog --fix in CI, checking release dates against PyPI, orphaned versions, over-long unreleased bullets, and released sections holding no entry. The check and fix subcommands below invoke the same tool locally. The add subcommand is purely analytical — it reviews git history and drafts entries, which no CI job does.
Determine invocation method
- If the context above shows
CANONICAL_REPO, useuv run repomatic. - Otherwise, use
uvx -- repomatic. - Gate the
uvxform with the supply-chain cooldown:uvx --exclude-newer '1 week' --exclude-newer-package repomatic=P0D -- repomatic. The window matches[tool.repomatic] minimum-release-age; repomatic itself is exempt because a fresh release must stay installable, while its dependency tree stays gated.
Argument handling
- (default when
$ARGUMENTSis empty): Runaddthenconsolidateon the unreleased section, sequentially. add: Review recent git commits and draft changelog entries. Place entries under the current unreleased section. Describe what changed, not how or why: one sentence per user-facing change, ~10-25 words. Mechanism, internal names, and rationale go in the commit and PR, not the entry.check: Run<cmd> lint-changelogand report results. Explain each issue found.fix: Run<cmd> lint-changelog --fixand show what was changed.consolidate [VERSION]: Consolidate redundant entries in a changelog section. This is analytical work with no CLI equivalent — read the entries, compare againstgit logfor the relevant range, and rewrite. See § Consolidation rules below. IfVERSIONis omitted, target the unreleased section. IfVERSIONis given (e.g.,consolidate 6.8.0), target that released section instead — locate it inchangelog.mdby matching the heading, and use the git range between its tag and the previous tag (e.g.,v6.7.0..v6.8.0).- A bare version number (e.g.,
6.8.0orv6.8.0) is shorthand forconsolidate VERSION. Strip thevprefix if present.
Consolidation rules
Entries accumulate during development as features are built incrementally. Before release, they need consolidation. The goal is a changelog that reads as a release summary, not a development diary.
- Read the target section and
git logfor its range. For the unreleased section, usegit logsince the last release tag. For a released version like6.8.0, usegit log v6.7.0..v6.8.0(derive the previous tag from the next heading inchangelog.md). - Reconcile against the end state, not the commit trail. The changelog records the net diff from the last tag to
HEAD.git logincludes work that was later undone or superseded, so verify every entry against the current code and docs (openpyproject.toml, the source, the docs) instead of trusting commit messages. Collapse a value that a later commit corrected into its final form: an entry bumping a dependency floor to one version, when a subsequent commit moved it higher, should state the higher version. Drop any change introduced and then reverted within the same cycle: a dependency pinned to a branch until a fix ships and unpinned once it did, or a temporary workaround added then deleted. It never reached a release, so it is a no-op for users. - Merge entries that describe the same feature at different stages. Multiple bullets about adding tools to a registry, then migrating workflows for those tools, then wiring up their version pins — that is one feature ("add unified tool runner with 13 managed tools"), not twelve.
- Merge entries that describe infrastructure and its usage together. "Add binary download infrastructure" + "add 5 binary tools" + "migrate 5 workflow steps" = one bullet covering the feature end-to-end.
- Keep distinct user-facing changes as separate entries. A breaking config key change and a new CLI command are separate features even if they landed in the same development cycle.
- Keep the names users need, shed the rest. Tool names, config keys, CLI options, and breaking-change notes stay explicit. But consolidation cuts per-entry length, not just bullet count: a merged entry is one short sentence naming the feature, not a paragraph stacking every mechanism and rationale from the bullets it replaced. Target ~10-25 words; push implementation detail and "why" to the commit, PR, code comment, or
docs/. - Remove implementation details that don't affect users: internal refactors, helper functions, test additions.
Also strip upstream issue commentary: trailing prose that links to upstream tickets and narrates their status ("Click does not ship an equivalent: the upstream conversation is in
pallets/click#NNNN(open)…", "mirrors the upstream fix in PR…#NNNN"). The status rots within days and the prose duplicates what the linked thread already says. A bare upstream link is acceptable on a direct backport entry; longer rationale belongs in a code comment, docstring, or PR body. - Never leave the section empty. A heading with no bullets still gets tagged and published, and reads as broken to whoever scans the release notes. When rules 02-07 collapse the unreleased section to zero bullets and the net cycle is genuinely mechanical, backfill one generic bullet naming what moved — e.g. "Sync CI tooling, workflow pins and dependency floors with the latest repomatic release." This is a fallback for an empty net cycle, not a substitute for drafting: it fires only after
addhas had its own chance to draft real entries fromgit log. As the section's sole bullet it needs no category ordering. - Order entries by category, breaking changes first: lead with
**Breaking:**entries, then**Deprecated:**entries, then new features, then broad/global changes, then bug fixes, then documentation and testing. Breaking changes are what a reader scans for before upgrading, so they go at the top of the block.**Deprecated:**marks a surface that still resolves but warns and is slated for removal in a named future release. - Apply directly. Write the consolidated section to
changelog.mdwithout asking for approval. Summarize what was merged, dropped, or reordered after writing. - Validate after writing. A bulk rewrite can introduce malformed markup, silently drop structure, or leave entries over-long. Run
<cmd> lint-changelog, which measures the bullet lengths againstchangelog.bullet-word-threshold— gate on what it reports, not on a delegated agent's self-report of how much it compressed. Then run<cmd> run mdformat --verify -- <file>, which reports what the write path would change without touching the file; a baremdformat/mdformat --with mdformat-mystdiverges on MyST directive colon-options like a{list-table}'s:header-rows:, so gate on the pinned runner. Confirm by eye what neither tool measures: rule 08 (its empty-section check covers released sections only, and deliberately skips the unreleased one you just consolidated, which is legitimately empty for most of a cycle), no doubled list markers (a stray- -), and the## [...]heading count and availability-admonition count unchanged from before the edit. Breaking entries lead each section (rule 9).
Style rules
- Write bare versions in changelog headings, with no
vprefix. Thevprefix names a git tag, not a package version. - One bullet per user-facing change, one sentence of ~10-25 words, saying what changed rather than how it was built or why. A second sentence only flags a breaking change or a migration step.
- Cut what the user cannot act on. Mechanism goes to the commit or PR; rationale goes to a code comment or
docs/. - Mark a change breaking when a surface the reader uses is gone, so their code, invocation, config or workflow must change to keep working.
Next steps
Suggest the user run:
/repomatic-shipto reconcile the tree and drive the release to a ready-to-merge PR.