---
name: changelog-on-tag
description: Turns every new git tag into a CHANGELOG.md section written from the commits in that tag's range, and installs the hook and CI workflow that fire it automatically. Use when asked to set up automatic changelogs, to write or backfill the changelog for a tag or release, or when a tag was just created and CHANGELOG.md is out of date.
license: MIT
compatibility: git 2.28+, bash, python3 (stdlib only). Optional - GitHub Actions for the CI trigger. No network access required.
metadata:
  category: devops
  version: "1.0.0"
---

# Changelog on Tag

A tag is created; `CHANGELOG.md` gains a section for it, built from the commits
between the previous tag and this one. Nothing else changes. The commits are
raw material, not the output — an entry names what changed for the person
using the software.

## What this skill does and does not do

Does: resolve a tag's commit range, group the commits, write or replace that
version's section in `CHANGELOG.md`, keep the compare links correct, and
install the automation that runs it on every future tag.

Does not: decide version numbers, bump manifests, create tags, publish GitHub
Releases, or push anything. Those belong to a release process.

## Set up the automation (one time per repository)

```bash
scripts/install-tag-hook.sh --hook --ci
```

`--hook` writes `.git/hooks/reference-transaction`, which fires the moment a
tag ref is created locally. `--ci` copies a workflow to
`.github/workflows/changelog-on-tag.yml`, which runs on `push: tags` so a tag
pushed from any machine is still covered.

Install both. The hook gives the person tagging an immediate, reviewable diff;
CI is the backstop for tags created elsewhere or through the GitHub UI. Running
both on the same tag is safe — the writer replaces a version's section rather
than appending a second one.

Before running the installer, confirm with the user:

- The tag pattern that counts as a release (default `v*`, semver only).
- Whether the CI job may commit to the default branch. If that branch is
  protected, use `--ci-mode pr` so the workflow opens a pull request instead.
- The changelog path, if it is not `CHANGELOG.md`.

Check what already exists first. If the repository uses `git-cliff`,
`changesets`, `release-please` or `semantic-release`, say so and stop: two
tools writing the same file will fight. This skill is for repositories with no
changelog automation.

## Write a tag's section

### 1. Read the range

```bash
scripts/tag-range.sh v2.4.0
```

Prints the previous tag, the commit range, the tag date, and one record per
commit with subject, body and author. With no argument it uses the most recent
tag reachable from `HEAD`.

### 2. Draft the entries

Read [references/entry-rules.md](references/entry-rules.md) before writing a
line. It carries the grouping, the phrasing rules and the exclusion list.

The short version: one entry per user-visible change, in the present tense,
naming the effect rather than the commit. Merge commits, formatting, CI config,
dependency bumps with no behaviour change and internal refactors do not get
entries. If a tag range contains nothing user-visible, say so and write a
one-line section rather than padding it.

### 3. Write the section

Pipe the drafted markdown into the writer:

```bash
scripts/changelog-write.py --tag v2.4.0 --stdin <<'EOF'
### Breaking
- The `/v1/users` endpoint is removed. Use `/v2/users`; the response shape is unchanged.

### Added
- Refunds can be issued for part of a payment.

### Fixed
- Sessions no longer end early when the access token rotates.
EOF
```

The writer inserts the section in version order, creates `CHANGELOG.md` with a
Keep a Changelog header if it is missing, replaces an existing section for the
same tag instead of duplicating it, and maintains the `[2.4.0]: …/compare/…`
link definitions at the foot of the file.

`--from-commits` skips the drafting step and groups commit subjects
mechanically by Conventional Commit type. That is what the hook and CI use
unattended. It produces a correct file, not a well-written one — when a person
is in the loop, draft the entries yourself.

### 4. Verify

```bash
scripts/changelog-write.py --tag v2.4.0 --check
git diff CHANGELOG.md
```

`--check` exits non-zero if the section is missing or the compare links are
broken. Read the diff before committing: confirm exactly one section changed
and that no earlier release was rewritten.

## Backfilling

For a repository with tags but no changelog:

```bash
for tag in $(git tag --sort=version:refname); do
  scripts/changelog-write.py --tag "$tag" --from-commits
done
```

Then rewrite the entries for the last two or three releases by hand. Older
sections can stay mechanical; nobody reads them, and a wrong summary is worse
than a commit subject.

## Rules

- Never rewrite a section for an already-released version except to correct a
  factual error, and never in the same commit as a new release's section.
- The changelog commit lands after the tag, so its content is not inside the
  tag. This is the accepted trade-off for tag-triggered generation. If a
  release artifact must contain its own changelog, write the section against
  `HEAD` before tagging: `scripts/changelog-write.py --tag v2.4.0 --range
  v2.3.1..HEAD`.
- Do not commit or push from the hook. It edits the file and prints one line.
  The person tagging decides what to commit.
- Do not invent entries for commits you cannot explain. Ask, or quote the
  commit subject unchanged.
- A tag that only moves (`git tag -f`) is not a release. The hook ignores it.

## Output

Report the tag, the previous tag, the commit count in the range, how many
became entries, and the path written. Name anything you dropped and why, and
anything you could not classify. Do not claim the changelog is updated without
having seen `git diff` output in this run.


<!-- theskillz:store
Skill: Changelog on Tag (changelog-on-tag) — https://theskillz.dev/skills/changelog-on-tag
Author: @theskillz — https://theskillz.dev/u/theskillz
License: MIT · Version: 1.0.0 · Free
More agent skills: https://theskillz.dev/llms.txt
Buy paid skills without an account: https://theskillz.dev/agent-checkout.md
-->
