Commit conventions
OpenCodeHub follows Conventional Commits.
The commit log on main is the input to release-please — malformed
messages break version bumps, changelog generation, and release notes. So
we enforce the grammar three times: lefthook at commit-msg, the
commitlint GitHub Action on every PR, and release-please itself.
Grammar
Section titled “Grammar”<type>(<scope>): <subject>
[optional body]
[optional footer(s)]- Lowercase type.
- Scope is a single workspace package name or a meta-scope.
- Subject is imperative, ≤ 72 chars, no trailing period.
- Body wraps at 100 cols. Explain why, not what — the diff tells you what.
- Footers are standard (
BREAKING CHANGE:,Refs: #123,Signed-off-by: ...).
If you are unsure of the type or scope:
pnpm run commitThat wraps Commitizen and walks you through type, scope, subject, body, and breaking-change flags.
| Type | Use for | In changelog? |
|---|---|---|
feat | New user-facing capability (CLI flag, MCP tool, indexer, etc.) | Yes — “Features” |
fix | Bug fix | Yes — “Bug Fixes” |
perf | Performance improvement with no behaviour change | Yes — “Performance” |
revert | Revert an earlier commit | Yes — “Reverts” |
docs | Documentation-only change (this site, READMEs, ADRs, comments) | Yes — “Documentation” |
refactor | Internal reshuffle, no behaviour change | Yes — “Refactoring” |
test | Adding or fixing tests | Hidden |
build | Build system, dependency bumps, package metadata | Hidden |
ci | CI workflow change | Hidden |
chore | Housekeeping that fits nowhere else | Hidden |
style | Formatting only — Biome runs on pre-commit, so this is rare | Hidden |
release | Release-please-authored commits only (do not use by hand) | — |
“Hidden” means the commit is still enforced and still shows up in the
git log — it just does not appear in CHANGELOG.md. See
.release-please-config.json for the source of truth on which sections
are visible.
Scopes
Section titled “Scopes”Workspace-package scopes map 1:1 to packages/<scope>/:
| Scope | Package |
|---|---|
analysis | @opencodehub/analysis |
cli | @opencodehub/cli (bin: codehub) |
cobol-proleap | @opencodehub/cobol-proleap |
core-types | @opencodehub/core-types |
embedder | @opencodehub/embedder |
frameworks | @opencodehub/frameworks |
ingestion | @opencodehub/ingestion |
mcp | @opencodehub/mcp |
pack | @opencodehub/pack |
policy | @opencodehub/policy |
sarif | @opencodehub/sarif |
scanners | @opencodehub/scanners |
scip-ingest | @opencodehub/scip-ingest |
search | @opencodehub/search |
storage | @opencodehub/storage |
summarizer | @opencodehub/summarizer |
wiki | @opencodehub/wiki |
Meta-scopes cover cross-cutting changes:
| Meta-scope | Use for |
|---|---|
deps | Dependency bumps not tied to one package |
ci | .github/workflows/*.yml changes |
docs | packages/docs/** or top-level Markdown |
repo | Root-level repo files (.gitignore, mise.toml, etc.) |
release | Release-please-authored PRs only |
Breaking changes
Section titled “Breaking changes”A feat! and a BREAKING CHANGE: footer both bump the major
version.
The ! form is the short one:
feat(mcp)!: drop the `cypher` tool; use `sql` insteadThe footer form is equivalent and plays nicer with long explanations:
feat(mcp): switch to SCIP-backed references
BREAKING CHANGE: the `lsp-unconfirmed` reason suffix is now`scip-unconfirmed`. Consumers that pattern-match on the old suffixmust update.Use either form, not both.
Enforcement
Section titled “Enforcement”| Layer | Tool | Trigger |
|---|---|---|
| Local, pre-commit | lefthook + commitlint --edit | commit-msg hook |
| PR | .github/workflows/commitlint.yml | Every PR commit |
| Release | release-please action on push-to-main | New commit on main |
If commitlint rejects your message locally, re-run git commit with a
fixed message — do not --no-verify. The tenet applies: every failure
is a blocker.