Commit Messages

Conventional-commit format enforced by commitlint and a husky commit-msg hook.

Every commit in this monorepo is validated against the Conventional Commits specification. The check is wired into a husky commit-msg hook that runs commitlint against the message you just wrote — if the message does not match the rules, the commit is rejected and nothing is written to history.

Source of truth. The rules live in commitlint.config.js at the monorepo root, and the enforcement is wired in .husky/commit-msg. If this page disagrees with those files, the files win — open a PR to fix the docs.

The format

text
type(scope): short description
 
[optional body]
 
[optional footer]
5 lines of text code
  • type — one of the prefixes listed below. Required.
  • scope — optional free-form hint at what area of the code is affected (e.g. ui, gateway, docs). Wrapped in parentheses.
  • short description — imperative mood, lower-case, no trailing period. Think "add button variant", not "Added a Button variant."
  • body / footer — optional. Use the body to explain why the change was made. Use footers for BREAKING CHANGE: notices or issue references.

The repo extends @commitlint/config-conventional, so the default conventional-commit rules apply (subject length, non-empty subject, lower-case type, etc.) on top of the custom type-enum defined below.

Allowed types

TypeUse when...
featYou are adding a new user-facing feature or a new public API.
fixYou are fixing a bug — something that used to behave incorrectly now behaves correctly.
choreRoutine maintenance that does not touch source behaviour: bumping dependencies, tidying configs, housekeeping.
ciYou are changing CI configuration — GitHub Actions workflows, husky hooks, turbo pipelines, release tooling.
docsDocumentation-only changes — MDX in apps/docs, READMEs, inline JSDoc, concept notes, openspec specs.
refactorYou are restructuring code without changing its observable behaviour. No new features, no bug fixes.
testAdding or updating tests. No production code changes.
variantAdding or adjusting a CVA variant on a UI component in @itu/ui (monorepo-specific type — see note below).
styleFormatting, whitespace, missing semicolons, Prettier/ESLint auto-fixes. No code logic changes.
perfA code change that improves performance without changing behaviour.
buildChanges to the build system, bundlers, or external dependencies (tsup, turbo build config, package.json build scripts).
revertReverting a previous commit. The body should reference the reverted SHA.

variant is monorepo-specific. The standard conventional-commit spec does not include variant — this repo adds it because CVA variant tweaks are frequent enough in @itu/ui to deserve their own prefix. Anything you would normally tag feat(ui) or style(ui) for a pure variant change can use variant instead.

Passing examples

A well-formed conventional commit looks like this:

text
feat(ui): add compact density to DataTable
1 line of text code

Scope is optional — skip it when the change is cross-cutting:

text
docs: document the commit message convention
1 line of text code

Longer message with a body explaining why:

text
fix(gateway): strip HTML from search snippets
 
The WP REST API returns rendered HTML in `excerpt.rendered`.
Passing that straight to the search index broke highlight ranges
in the docs search modal.
5 lines of text code

A refactor with no behavioural change:

text
refactor(ui): extract Button variant config into cva factory
1 line of text code

A variant tweak using the monorepo-specific variant type:

text
variant(ui): tighten Hero spacing on the compact variant
1 line of text code

Failing examples

Each failing example below is followed by the exact error commitlint prints when the commit is rejected. The ⧗ input: line echoes your message, and the line explains what broke.

Missing type

text
added a new button variant
1 line of text code
text
⧗ input: added a new button variant
✖ subject may not be empty [subject-empty]
✖ type may not be empty [type-empty]
 
✖ found 2 problems, 0 warnings
ⓘ Get help: https://github.com/conventional-changelog/commitlint/#what-is-commitlint
 
husky - commit-msg script failed (code 1)
8 lines of text code

Commitlint cannot parse a subject at all because there is no type: prefix, so both type-empty and subject-empty fire.

Unknown type

text
feature(ui): add compact density to DataTable
1 line of text code
text
⧗ input: feature(ui): add compact density to DataTable
✖ type must be one of [feat, fix, chore, ci, docs, refactor, test, variant, style, perf, build, revert] [type-enum]
 
✖ found 1 problems, 0 warnings
ⓘ Get help: https://github.com/conventional-changelog/commitlint/#what-is-commitlint
 
husky - commit-msg script failed (code 1)
7 lines of text code

feature is not in the allowed list — use feat instead.

Upper-case type

text
Feat(ui): add compact density to DataTable
1 line of text code
text
⧗ input: Feat(ui): add compact density to DataTable
✖ type must be lower-case [type-case]
✖ type must be one of [feat, fix, chore, ci, docs, refactor, test, variant, style, perf, build, revert] [type-enum]
 
✖ found 2 problems, 0 warnings
ⓘ Get help: https://github.com/conventional-changelog/commitlint/#what-is-commitlint
 
husky - commit-msg script failed (code 1)
8 lines of text code

@commitlint/config-conventional enforces lower-case types, and type-enum matches case-sensitively — so Feat trips both rules.

Empty subject

text
chore:
1 line of text code
text
⧗ input: chore:
✖ subject may not be empty [subject-empty]
 
✖ found 1 problems, 0 warnings
ⓘ Get help: https://github.com/conventional-changelog/commitlint/#what-is-commitlint
 
husky - commit-msg script failed (code 1)
7 lines of text code

A type without a subject is not a message — describe what you actually did.

How the hook runs

The commit-msg hook is a one-liner:

bash
npx --no -- commitlint --edit $1
1 line of bash code

$1 is the path to the temporary file that git uses to hold your message. --edit tells commitlint to read from that file, and --no makes npx refuse to fetch commitlint from the network — it must already be installed locally (it is, via the root package.json devDependencies). If the hook exits non-zero, git aborts the commit before it is written to history.

Fixing a rejected commit

When the hook rejects your message, git has not created the commit yet — your staged changes are still staged. Just run git commit again with a corrected message:

bash
git commit -m "feat(ui): add compact density to DataTable"
1 line of bash code

If you had already opened the editor and written a long body, use:

bash
git commit --edit
1 line of bash code

to re-open the last attempted message in your editor and fix it in place.

What not to do

  • Do not bypass the hook with --no-verify. It exists for a reason — see the git hooks guide for why skipping hooks is actively discouraged in this repo.
  • Do not pad types onto a non-conforming message just to make the hook happy (chore: fix stuff). Pick the type that matches reality.
  • Do not use past tense (fix: fixed button). Conventional commits use imperative mood: fix: fix button hover state.

Related

  • Branch naming — the sibling convention for branch names.
  • Git hooks — the full tour of husky hooks, including why --no-verify is off-limits.