Writes human-friendly changelog entries and release notes. Use when populating changelog, preparing releases, or reviewing release notes quality.
Scanned 9/1/2026
Install to Claude Code
npx -y skills add dork-labs/dorkos --skill writing-changelogs --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Writing Changelogs?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/dork-labs-writing-changelogs)More formats (shields.io, HTML) on the badges page.
---
name: writing-changelogs
description: Writes human-friendly changelog entries and release notes. Use when populating changelog, preparing releases, or reviewing release notes quality.
---
# Writing Changelogs
Write changelog entries and release notes that humans actually want to read. This skill activates when writing changelog entries, preparing GitHub releases, or reviewing release notes quality.
**Sources**: [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), [Common Changelog](https://github.com/vweevers/common-changelog)
## When to Use
- Writing a changelog fragment for a change
- Preparing GitHub release notes via `/system:release`
- Reviewing changelog entries before release
- Transforming commit messages into user-friendly descriptions
## Where entries live: fragments
Unreleased entries do **not** go in `CHANGELOG.md`. Each change adds one **fragment** file
under `changelog/unreleased/` — a coordination-free scheme that keeps parallel worktrees from
colliding on a shared `[Unreleased]` block (ADR `260707-231641`; full guide in
`changelog/README.md`). The workflow:
- **Filename** — `<YYMMDD-HHMMSS>-<kebab-slug>.md`: a timestamp id from `.claude/scripts/id.ts`
followed by a short slug. The post-commit hook names it from your commit subject.
- **Body** — optional `covers:` frontmatter (which commits this fragment covers; the
post-commit hook fills it in, and the PR check reads it so your prose stays free to be
rewritten for a human), then one or more `### Category` headings (Added, Changed, Deprecated,
Removed, Fixed, Security) with bullets written per the principles below. One fragment may
carry multiple categories.
- **Creation** — the `post-commit` hook writes a fragment from each conventional commit; curate
it (or hand-author one) before opening a PR. **Never edit `CHANGELOG.md`'s `[Unreleased]`
section** — it no longer holds entries.
- **Release** — `/system:release` compiles all fragments into the new `## [X.Y.Z]` section and
deletes them. Only the release process writes `CHANGELOG.md`.
The entry-quality guidance below applies identically to fragment bullets.
## Core Principles
Changelog entries are user-facing prose, so the **`writing-for-humans`** skill sets the readability bar: 9th-grade level, one idea per sentence, benefit before mechanism, every acronym glossed. Read it first. The rules specific to changelogs:
1. **Communicate impact, not implementation** - Focus on what users can DO, not what files changed
2. **Use imperative verbs** - "Add", "Fix", "Remove" not "Added", "Fixed", "Removed"
3. **Include references, but never let them carry the meaning** - Link to commits, PRs, or issues where they exist; the sentence must stand alone without the `(DOR-123)` or `(#42)`
4. **Internal notes never ship** - batch, task, and tracking entries ("Batch 9 — acceptance PASS") get cut at curation, not published
## Entry Format
### Basic Template
```markdown
- [Imperative verb] [user benefit/what changed] ([reference])
```
### With Sub-details (for significant changes)
```markdown
- **[Feature Name]** - [User benefit explanation]
- [Technical detail 1]
- [Technical detail 2]
```
## Good vs Bad Examples
| Bad (Developer-focused) | Good (User-focused) |
| --------------------------------------------------- | -------------------------------------------------------------- |
| Add obsidian_manager.py for auto vault registration | Open files in Obsidian without manual vault setup |
| fix: Use relative paths in theme commands | Fix theme commands failing when run from different directories |
| Accept 'default' as theme alias | Use 'default' to quickly apply the standard theme |
| Add changelog-populator.py hook | Changelog entries are now auto-generated from commits |
| Update AGENTS.md with new patterns | (Skip - internal documentation, not user-facing) |
| refactor: Extract helper function | (Skip - internal refactoring, no user impact) |
## Imperative Verbs Reference
| Verb | Use For | Example |
| ------------- | ---------------------------------- | --------------------------------------- |
| **Add** | New features, capabilities | Add dark mode support |
| **Fix** | Bug corrections | Fix login failing on Safari |
| **Change** | Modifications to existing behavior | Change default timeout to 30 seconds |
| **Remove** | Deleted features | Remove deprecated v1 API |
| **Improve** | Performance, UX enhancements | Improve search speed by 50% |
| **Update** | Dependencies, configurations | Update to React 18 |
| **Deprecate** | Scheduled for removal | Deprecate XML export (use JSON instead) |
## What to Skip
Not everything belongs in the changelog, and the test is **audience, not commit type**: the changelog's reader _operates_ DorkOS. A change only someone _building_ DorkOS notices ships no fragment, even when it lands as `feat:` or `fix:`. Skip:
- Internal refactoring with no user impact
- Documentation typo fixes
- Development-only changes (CI, tests, linting)
- Harness, ADR, and contributor-documentation changes — the builders' record lives in git history, `decisions/`, and `contributing/`, not here
- Dependency updates (unless security-related)
- Code style changes
- Fixes to changes that haven't been released yet (see below)
**Exception**: Include if it affects how users interact with the system.
### The audience test
The "You can now" test has a companion: **if the "you" is a DorkOS contributor rather than someone running DorkOS, the entry doesn't ship.** Watch for builder-facing work dressed in user language — test coverage, build tooling, code moves between packages — rewritten so smoothly it reads like a feature. The tell: nothing an operator sees or does changed.
How to satisfy the PR gate for builder-only work: the `fragment-present` check requires coverage for every `feat:`/`fix:`/`refactor:`/`perf:` commit, and `no-fragment-under-skip-label` makes fragment and label mutually exclusive — so a builder-only PR takes the **`skip-changelog` label**, never a fragment. In a **mixed PR** (user-facing and builder-only commits together), write the fragment for the user-facing part and add the builder-only commit subjects to its `covers:` list: coverage is independent of prose, so the gate stays green without shipping builder bullets.
### Fixes to unreleased changes
If a bug was introduced after the last release, no user ever saw it — from the reader's side the feature simply ships working. A separate "Fix …" bullet only tells them about a bug they could never have hit ([Common Changelog](https://github.com/vweevers/common-changelog) makes this an explicit rule).
- **Writing the fragment**: write it as usual. At commit time you rarely know whether the feature will release before the fix, and the PR check wants the commit covered. If the fix lands on the same branch as the feature it fixes, fold it into the feature's fragment instead (move the `covers:` items across).
- **At release**: `/system:release` classifies every Fixed bullet (Phase 4 curation) and drops the ones that fix behavior introduced since the last tag. If the fix changed what a user should _know_ about the feature — a limitation lifted, behavior that settled differently from the feature's entry — that substance folds into the feature's Added/Changed entry rather than surviving as a Fixed line.
- **When unsure** whether the buggy behavior existed at the last tag: keep the fix. Over-including is harmless; wrongly dropping a real fix is not.
## Theme Blockquote (Optional)
Add a single-line blockquote below version headings to provide a theme/summary for the release:
```markdown
## [0.3.0] - 2026-02-20
> DorkOS 0.3.0 adds a scheduler and dynamic MCP tools.
### Added
- ...
```
This theme line feeds:
- Blog post descriptions
- GitHub Release "What's New" opening paragraph
- Quick reference for users scanning the changelog
The blockquote is optional and backward-compatible. Older versions without it work fine.
## Changelog Categories
Use these standard categories in order:
1. **Added** - New features
2. **Changed** - Modifications to existing features
3. **Deprecated** - Soon-to-be removed features
4. **Removed** - Removed features
5. **Fixed** - Bug fixes
6. **Security** - Vulnerability fixes
## GitHub Release Notes Template
When creating GitHub releases, use this narrative format (different from the changelog):
```markdown
## What's New in vX.Y.Z
[1-2 sentence theme describing the focus of this release]

_[One line caption. See it move → [docs page or /features section]]_
### Highlights
🎨 **[Feature Name]** - [One sentence explaining the benefit and how to use it]
📂 **[Feature Name]** - [One sentence explaining the benefit and how to use it]
🔧 **[Fix/Improvement]** - [One sentence explaining what's better now]
### All Changes
- [Bullet list of all changes - can be slightly more technical]
- [Include references: (#123) or (abc1234)]
**Full Changelog**: https://github.com/[owner]/[repo]/compare/v[prev]...v[new]
```
**Rule: every release with user-visible UI changes includes at least one visual.** Embed the shot's poster PNG from the version's frozen archive (`archive/vX.Y.Z/<shot-id>-<theme>.png` — never the live `/product/<file>` path, which repoints on the next capture and would change what an old release shows). GitHub renders PNG/GIF inline but does not play `.webm`, so link the caption to a docs page or `/features` section for the motion version instead of linking the webm directly. The `/system:release` media phase (Phase 6.6) selects and archives these shots before this step runs.
### Emoji Reference for Highlights
| Emoji | Use For |
| ----- | --------------------------- |
| ✨ | Major new feature |
| 🎨 | UI/UX improvements, themes |
| 📂 | File handling, organization |
| 🔧 | Fixes, improvements |
| ⚡ | Performance |
| 🔒 | Security |
| 📝 | Documentation |
| 🗑️ | Removals, deprecations |
## Pre-Release Checklist
Before publishing release notes, verify each entry:
- [ ] Starts with imperative verb (Add, Fix, Change, Remove, Improve)
- [ ] Describes user benefit, not just implementation detail
- [ ] Reads at a ~9th-grade level (no unexplained jargon; every acronym glossed or cut)
- [ ] No ticket ID carries the meaning: the sentence stands alone without `(DOR-123)` or `(#42)`
- [ ] No internal batch/task/tracking entries: those are cut at curation, never shipped
- [ ] The reader is someone operating DorkOS, not someone building it (see "The audience test")
- [ ] Appropriate for someone who doesn't know the codebase
For the overall release:
- [ ] Has a theme sentence summarizing the release focus
- [ ] 2-3 highlights with context for significant changes
- [ ] Link to full changelog for details
## Transforming Commit Messages
When converting conventional commits to changelog entries:
| Commit Message | Changelog Entry |
| -------------------------------- | --------------------------------------------------------------- |
| `feat: Add obsidian_manager.py` | Add automatic Obsidian vault registration |
| `fix(theme): Use relative paths` | Fix theme commands failing outside project root |
| `feat!: Change config format` | **BREAKING**: Change configuration format (see migration guide) |
| `chore: Update deps` | (skip) |
| `docs: Fix typo` | (skip) |
### Breaking Changes
Always make breaking changes prominent:
```markdown
### Changed
- **BREAKING**: [Description of breaking change]
- Migration: [How to update]
```
## Integration with /system:release
During release preparation:
1. **Analysis phase**: Transform raw changelog entries to user-friendly language
2. **Preview phase**: Show transformed entries for approval
3. **GitHub release**: Generate narrative release notes using the template
4. **Quality check**: Apply the pre-release checklist
## Writing Tips
### The "You Can Now" Test
For each entry, mentally prepend "You can now..." - if it doesn't make sense, rewrite it.
- ❌ "Add obsidian_manager.py" → "You can now add obsidian_manager.py" (nonsense)
- ✅ "Open files in Obsidian automatically" → "You can now open files in Obsidian automatically" (makes sense)
### The "So What?" Test
If someone asks "so what?" after reading your entry, you need more context.
- ❌ "Fix path handling" → So what?
- ✅ "Fix theme commands failing when run from different directories" → Clear impact
### Avoid These Patterns
- Starting with "This release..." (the heading already says the version)
- Using passive voice ("was added" vs "Add")
- Listing file names without explaining benefit
- Technical jargon without explanation
- Commit hash references without descriptions
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!