--- name: maintain-changelog description: Use when the user says "update the CHANGELOG" / "changelog from PRs since {version}" / "draft the changelog entries" — fetches merged PRs + linked issues since the given version tag via the connected code host, filters for user-facing changes, groups them Keep-A-Changelog style (Added / Changed / Deprecated / Removed / Fixed / Security), and writes a DRAFT snippet to `changelog/{version}.md`. Hard no: never auto-commits the canonical CHANGELOG.md — user copies the snippet in themselves. --- # Maintain Changelog ## When to use - User: "update the CHANGELOG from merged PRs since {version}" / "draft changelog entries for {version}" / "what's new since {version-tag}". - Implicit trigger: the user mentions a version bump and references the last tagged release. ## Steps 1. **Read engineering context.** Open `../head-of-engineering/engineering-context.md`. If missing or empty, stop and tell the user: > "I need the engineering context doc to judge what's user-facing. > Run the Head of Engineering's `define-engineering-context` > first." 2. **Read config.** `config/changelog-format.md` (style reference — if it's an existing CHANGELOG.md, mirror its headings and tone). If missing or "none", default to Keep-A-Changelog (keepachangelog.com) with these headings: `Added`, `Changed`, `Deprecated`, `Removed`, `Fixed`, `Security`. 3. **Resolve inputs.** - Version source: the git tag the user named (e.g. `v1.4.0`). - Version target: what the user is about to ship (e.g. `v1.5.0` or `Unreleased`). If unstated, ask ONE question: > "What's the next version? (e.g. `v1.5.0`, or just > `Unreleased` for now.) I'll put the draft under that heading." - Repo: resolve via `config/repo.json`; if missing, ask OR run `composio search code-hosting` for a connected host and list accessible repos. 4. **Fetch merged PRs since the tag.** Run `composio search code-hosting` to find the GitHub (or GitLab / Bitbucket / Gitea) tool. Fetch merged PRs whose merge commit is after the tag, with: - Title - Body (first 500 chars — scan for "Closes #N" linked issues) - Author - Number - Labels - Merge date - Linked issue titles (fetch the issues that are referenced in-body with `Closes #` / `Fixes #` / `Resolves #`). If no connected code host, tell the user which category to link and stop. 5. **Filter for user-facing changes.** - **Include:** new features, behavior changes, bug fixes that affect user-visible output, security fixes, breaking changes, deprecations, new configuration options. - **Skip:** internal refactors with no user effect, CI-only changes, dependency bumps (unless they change behavior), tests only, docs-only (unless major), formatting / lint only. - Heuristic: if the PR title starts with `refactor:`, `chore:`, `test:`, `ci:`, skip by default — but **scan the body** for "user-facing" mentions and include if in doubt. Err toward including; the user will cut. 6. **Classify each kept PR** under Keep-A-Changelog headings: - `Added` — new features, new endpoints, new config options. - `Changed` — behavior changes to existing features (non- breaking). - `Deprecated` — features scheduled for removal. - `Removed` — features now gone (breaking). - `Fixed` — bug fixes. - `Security` — vulnerability fixes / auth changes. 7. **Rewrite each PR title** into user-facing language: - **From the user's perspective.** "Fixed race condition in widget saves" > "Refactor widget save mutex". - **Active voice, one line.** Lead with the verb (Add / Change / Fix / Remove). - **Link back** with `(#{PR-number})` at the end — links go in a footer section the user can strip if they don't want PR references. - **Breaking changes** prefixed with `**BREAKING:**` — this is critical, never omit. 8. **Compose the draft snippet.** Exact structure (the user will paste this under the target version in their canonical CHANGELOG.md): ```markdown ## [{target-version}] — {YYYY-MM-DD} ### Added - Add `/v1/widgets/batch` endpoint for bulk creation. (#142) - Add `WIDGET_RATE_LIMIT` env var to cap API writes. (#148) ### Changed - **BREAKING:** Rename `widget.price` to `widget.price_cents`. Clients on < v1.4 must migrate. (#151) - Log format is now JSON by default (was plaintext). Set `LOG_FORMAT=text` to revert. (#155) ### Fixed - Fix race condition when two clients save the same widget concurrently. (#144) - Fix silent failure when `price_cents` was negative (now returns 400). (#147) ### Security - Rotate JWT signing key on startup if `JWT_SECRET` is the default dev value. (#160) --- ### PR references - (#142) [Title] — @author, YYYY-MM-DD - (#144) [Title] — @author, YYYY-MM-DD - ... ``` 9. **Write** to `changelog/{version}.md` atomically (`*.tmp` → rename). Where `{version}` is the target version slug (e.g. `v1.5.0.md` or `unreleased.md`). 10. **Append to `outputs.json`** (`type: "changelog"`, `title: "Changelog draft — {target-version}"`, `summary: "{N} entries: {added} added, {changed} changed, {fixed} fixed, {security} security. {M} PRs considered, {K} skipped as non-user-facing."`, `path: "changelog/{version}.md"`, `status: "draft"`, timestamps). 11. **Summarize to user** — one paragraph: counts per Keep-A- Changelog heading, the top 1-2 breaking changes to call out, any PRs where you were unsure (ask the user to confirm), and the path. End with: **"This is a draft snippet. Copy the block above into your canonical CHANGELOG.md at the repo root, review, and commit. I never auto-commit the CHANGELOG."** ## Hard nos - **Never auto-commit the canonical CHANGELOG.md** — this is the load-bearing hard no for this skill. Output is a draft snippet at `changelog/{version}.md` only; the user pastes into `CHANGELOG.md` at their repo root and commits. - Never invent PRs / issues — every entry cites a real PR number. - Never omit `**BREAKING:**` prefix on breaking changes. - Never write without reading `engineering-context.md` first. ## Outputs - `changelog/{version}.md` — draft snippet for ONE release. - Appends to `outputs.json` with `{ id, type: "changelog", title, summary, path, status: "draft", createdAt, updatedAt }`.