v1.0.0 to v1.0.1

13 added, 11 removed. Audit A to A.

---
name: conventional-commits
description: "The Conventional Commits format — also called \"semantic commits\" / semantic commit messages — a `type(scope): description` header (`feat`, `fix`, …) plus an optional `BREAKING CHANGE` footer, and how it makes history machine-readable to drive automated version bumps and changelogs. Use when writing a commit message or PR title, deciding the type/scope/bump for a change, setting up or fixing a repo's commit convention, making commits parseable by semantic-release / changelog tooling, validating PR titles, or when a release didn't bump or the changelog came out blank because a commit wasn't conventional. Covers the type→semver mapping, breaking changes, scopes, monorepo scopes, and squash-merge PR titles."
metadata:
author: stealth-engine
- version: "1.0.0"
+ version: "1.0.1"
---
# Conventional Commits (a.k.a. semantic commit messages)
A commit convention that makes history **machine-readable**: a parser reads each
message, decides the next semver version, and builds the changelog from it. "Semantic
commits" is the common nickname for the same thing. It's the foundation the release
- automation stands on — the `semantic-release-automation` skill (next in this set)
+ automation stands on — [`semantic-release-automation`](../semantic-release-automation/SKILL.md)
covers the tooling that consumes these messages.
## The format
```text
<type>(<optional scope>)<optional !>: <description>
<optional body — the "why", wrapped>
<optional footer(s) — BREAKING CHANGE:, Closes #123, Refs: ABC-12>
```
- `type` / `scope`: lowercase **by convention** — the spec is case-*insensitive*,
except `BREAKING CHANGE`, which MUST be uppercase. `scope` is an optional noun in
parens for the area touched.
- `!` before the colon **or** a `BREAKING CHANGE:` footer marks a breaking change
(`BREAKING-CHANGE:` with a hyphen is an accepted synonym).
- **description**: imperative mood, concise, lowercase, **no trailing period**.
- Blank line before any body/footer.
```text
feat(auth): add passkey sign-in
fix(parser): handle empty input without throwing
feat(api)!: drop the v1 token endpoint
docs: fix typo in install steps
```
## Types → meaning → version bump
These are the **semantic-release defaults** (the `angular` preset plus
commit-analyzer's built-in release rules). They're configurable via `releaseRules`,
but treat them as the contract.
| Type | Use for | Default release |
| --- | --- | --- |
| `feat` | a new feature | **minor** (`x.Y.0`) |
| `fix` | a bug fix | **patch** (`x.y.Z`) |
| `perf` | performance improvement | patch |
| `docs`, `style`, `refactor`, `test`, `build`, `ci`, `chore` | maintenance, no user-facing behaviour change | **no release** on their own |
| `revert` | revert a previous commit | **patch** (the change is being undone) |
| any type with `!` / `BREAKING CHANGE:` | incompatible change | **major** (`X.0.0`) |
The "no release" types still belong in history — the tooling just won't cut a
version for them alone. A release containing only `chore`/`docs` commits produces
**no new version**, which is usually correct.
## Breaking changes
Either form triggers a **major** bump and a highlighted changelog section:
```text
feat(api)!: require auth on all routes
```
…or via a footer (works on any type, and lets you explain the migration):
```text
refactor(db): rename users.email column
BREAKING CHANGE: `users.email` is now `users.email_address`; update queries.
```
## Scopes
Optional, but meaningful: a noun naming the area (`fix(toggle): …`). **In a
monorepo the scope is how releases/changelogs are routed per package** — repos
often *require* it (e.g. `feat(piaf-web): …`, `feat(react-typed-form-kit): …`).
Keep a scope vocabulary documented so it stays consistent.
## Squash merges: the PR title *is* the commit
With squash merging (the common modern setup), **the PR title becomes the single
commit message on `main`** — so the **PR title must be a valid Conventional
Commit**, or the release/changelog step sees a non-conventional message and skips
- it. Put the individual semantic commits in the squash **body** for detail. The
- `git-trunk-branch-and-pr-automation` skill (next in this set) covers the CI that
- enforces this.
+ it. Put the individual semantic commits in the squash **body** for detail.
+ [`git-trunk-branch-and-pr-automation`](../git-trunk-branch-and-pr-automation/SKILL.md)
+ covers the CI that enforces this.
- **Single-package repo:** the title's scope is optional.
- **Monorepo:** scope the title to the package, and keep a PR to **one package**
where practical — a squash collapses everything to one type+scope, so a
multi-package PR applies the same bump to all and muddies per-package changelogs.
## Writing good ones
- **Imperative mood:** "add", not "added"/"adds" (it completes "this commit will…").
- Keep the subject short: the spec sets no limit; commitlint defaults to 100 chars
and Git display favours ~72 — match the project's commitlint config. The body
explains **why**, not what.
- **One logical change per commit.** If a change is genuinely both a feature and a
fix, split it; if you can't, the type reflects the **highest-impact** change
(a `feat` that also fixes something is `feat`).
- Reference issues in the **footer**: `Closes #123`, `Refs: ABC-12`.
## Gotchas
- **Non-conventional → silently no release.** A typo'd type or a prose subject is
*ignored* by the analyzer: no bump, no changelog entry. This is the #1 "why
didn't it release / why is the changelog blank?" cause — check the merge commit /
PR title first.
- **Don't hand-write `chore(release): …` commits.** Those are produced *by* the
release bot; writing one yourself can confuse tooling (and such commits are often
- CI-skipped on purpose).
+ CI-skipped deliberately).
- **Issue IDs in the subject can misbehave.** A Linear/Jira key like `ABC-123` in
the subject may auto-transition the ticket or render oddly in changelog links;
some setups keep IDs in the footer or de-link them (`ABC-123` → `ABC - 123`) in
the generated changelog.
- **Revert format:** a header `revert: <subject of the reverted commit>` plus a
**body line** `This reverts commit <sha>.` — that body line is what the parser
keys on. `git revert` generates `Revert "<subject>"` + that line; lowercase it to
`revert:` so it's recognised. Reverts default to a **patch** release.
## Verify
- Lint locally or in CI with **commitlint** (`@commitlint/config-conventional`), or
- validate PR titles with a PR-title action (see `git-trunk-branch-and-pr-automation`).
+ validate PR titles with a PR-title action (see
+ [`git-trunk-branch-and-pr-automation`](../git-trunk-branch-and-pr-automation/SKILL.md)).
- Confirm the intended bump with a semantic-release **dry run**
(`npx semantic-release --dry-run`) — it prints the next version and release notes
without publishing.
## See also
- Companion skills in this set (built alongside this one):
+ Companion skills in this set:
- - `semantic-release-automation` — turns these commits into versions, a changelog,
- and GitHub releases.
- - `git-trunk-branch-and-pr-automation` — branch naming + squash + the PR-title
+ - [`semantic-release-automation`](../semantic-release-automation/SKILL.md) — turns
+ these commits into versions, a changelog, and GitHub releases.
+ - [`git-trunk-branch-and-pr-automation`](../git-trunk-branch-and-pr-automation/SKILL.md)
+ — branch naming + squash + the PR-title
validation that enforces this format.
## Sources
- **Conventional Commits 1.0.0** — grammar, the `!` / `BREAKING CHANGE` rules, the
`BREAKING-CHANGE` hyphen synonym, and case-insensitivity (except `BREAKING CHANGE`,
which must be uppercase): <https://www.conventionalcommits.org/en/v1.0.0/>
- **semantic-release `commit-analyzer` default release rules** (`angular` preset) —
`feat`→minor, `fix`/`perf`→patch, breaking→major, **`revert`→patch**, every other
type → no release:
<https://github.com/semantic-release/commit-analyzer/blob/master/lib/default-release-rules.js>
- The type list and per-package scope rules also match conventions enforced in
production repos (`cphk`'s PR-title validator allows
`feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert`; `piaf-monorepo`
requires per-package scopes).