-
Notifications
You must be signed in to change notification settings - Fork 21
chore: Strengthen commitlint validation rules #97
Description
Title
chore: Strengthen commitlint validation rules
Labels
chore, dx
Body
Problem
The current commitlint configuration (commitlint.config.js) only extends @commitlint/config-conventional without any project-specific overrides. This leaves several gaps where commits that violate project conventions (as documented in AGENTS.md and CONTRIBUTING.md) pass validation silently.
Gaps identified
1. Warnings treated as non-blocking for structural rules
body-leading-blank and footer-leading-blank are warnings (severity 1) in the default config. Commits that omit the required blank line after the subject still exit 0 and are accepted.
# This passes (exit 0) — blank line missing between subject and body
printf "feat: subject\nno blank line" | npx commitlint
# ⚠ body must have leading blank line [body-leading-blank]
# ⚠ found 0 problems, 1 warnings ← exit 0
Expected: These should be errors (severity 2) to enforce well-formed commit structure.
2. No scope-case enforcement
Scopes accept any casing. Uppercase, mixed-case, and otherwise inconsistent scopes all pass.
echo "feat(UPPER): test" | npx commitlint # ✅ passes
echo "feat(MixedCase): test" | npx commitlint # ✅ passes
Expected: scope-case should enforce lower-case for consistency.
3. No minimum subject length
Single-character subjects pass, allowing meaningless commit messages.
echo "feat: x" | npx commitlint # ✅ passes
Expected: subject-min-length should enforce a reasonable minimum (e.g. 10 characters) to require descriptive messages.
4. Type enum mismatch with CONTRIBUTING.md
The default config-conventional type enum includes 11 types:
build, chore, ci, docs, feat, fix, perf, refactor, revert, style, test
CONTRIBUTING.md documents only 9 types — missing ci and revert.
| Type | In commitlint | In CONTRIBUTING.md |
|---|---|---|
ci |
✅ | ❌ |
revert |
✅ | ❌ |
Both ci and revert are actively used in the git history (fix(ci):, Revert "...") so the fix here is likely updating CONTRIBUTING.md to document them rather than removing them from the enum — but the discrepancy should be resolved explicitly.
5. No CI-level commit message validation
Commitlint only runs as a local lefthook commit-msg hook. The CI workflow (.github/workflows/ci.yml) does not validate commit messages. This means:
- Contributors who skip or lack lefthook can push non-conforming commits
- GitHub merge commits bypass validation entirely
- Squash-merge subjects edited in the GitHub UI are never checked
Expected: Add a CI job that runs commitlint --from on PR commits (e.g. using @commitlint/config-conventional with the commitlint GitHub Action or a manual npx commitlint --from ${{ github.event.pull_request.base.sha }} --to ${{ github.event.pull_request.head.sha }}).
6. No enforcement of the "no AI co-author" rule
AGENTS.md states:
No AI co-author trailers. Do not add
Co-authored-byfor AI assistants.
This is currently a documentation-only rule with no automated enforcement.
printf "feat: add feature\n\nCo-authored-by: AI <ai@bot.com>" | npx commitlint # ✅ passes
This is harder to enforce reliably via commitlint alone (there's no built-in trailer-content rule). Options include a commitlint plugin, a custom lefthook script, or a CI check. This item is lower priority but worth tracking.
Proposed changes
| Change | File | Complexity |
|---|---|---|
Upgrade body-leading-blank to error |
commitlint.config.js |
trivial |
Upgrade footer-leading-blank to error |
commitlint.config.js |
trivial |
Add scope-case: [2, "always", "lower-case"] |
commitlint.config.js |
trivial |
Add subject-min-length: [2, "always", 10] |
commitlint.config.js |
trivial |
Align type enum with CONTRIBUTING.md (add ci + revert to docs) |
CONTRIBUTING.md |
trivial |
| Add commitlint CI job for PRs | .github/workflows/ci.yml |
small |
| Investigate AI co-author trailer enforcement | TBD | separate issue |
Acceptance criteria
-
commitlint.config.jsoverrides the five rules listed above - CONTRIBUTING.md type table matches the commitlint type enum
- CI runs commitlint on PR commit ranges
- All existing commits on
mainstill pass (no retroactive breakage) - Verification: the six test cases from "Gaps identified" behave as expected