Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 83 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,89 @@ All **notable changes** to this project will be documented in this file.

Full changelog available at [GitHub releases](https://github.com/commit-check/commit-check/releases).

## v2.13.0 (2026-08-04)

### New Features

* **Stable rule IDs** — every diagnostic now carries an ID such as `CC003` that
never changes once released. The ID appears in terminal output and in
`--format json` as `rule_id`, alongside a `docs_url` pointing at the rule's
section of the [rules reference](rules.md), so a failure can be cited in
review, looked up, or matched by tooling.
See PR [#512](https://github.com/commit-check/commit-check/pull/512).
* **More branch types accepted by default** — `docs/`, `ci/`, `test/`,
`refactor/`, `build/`, `perf/` and `style/` join the default
`allow_branch_types`, which stays a superset of the Conventional Branch
specification.
See PR [#505](https://github.com/commit-check/commit-check/pull/505).

### Bug Fixes

* **Rule IDs link to their documentation** — where the terminal renders OSC 8
hyperlinks, the ID itself is the link and the separate `Docs:` line is
dropped. Piped output and CI logs keep the printed URL. Failure output also
names rules the way the reference titles them (`subject-min-length` rather
than `subject_min_length`), and the advice for the length rules now names the
configured limit instead of referring to it.
See PR [#520](https://github.com/commit-check/commit-check/pull/520).

### Documentation

* The documentation, the landing page and the blog moved to
[commit-check.com](https://commit-check.com) and are published from one
domain. `docs.commit-check.com` URLs redirect.
See PRs [#515](https://github.com/commit-check/commit-check/pull/515),
[#518](https://github.com/commit-check/commit-check/pull/518) and
[#519](https://github.com/commit-check/commit-check/pull/519).

## v2.12.2 (2026-08-01)

### Bug Fixes

* **Space-separated AI model names** — Co-author trailers naming a model with
spaces rather than hyphens are now recognised by the AI attribution check.
See PR [#506](https://github.com/commit-check/commit-check/pull/506).

### Chores

* Recognised more common imperative verbs, so ordinary subjects stop being
rejected by `subject_imperative`.
See PR [#496](https://github.com/commit-check/commit-check/pull/496).

## v2.12.1 (2026-07-31)

### Bug Fixes

* **Running without the package installed** — `PackageNotFoundError` is handled
instead of crashing when the version cannot be resolved.
See PR [#483](https://github.com/commit-check/commit-check/pull/483).
* Hardened the `pip install` step flagged by SonarCloud code scanning.
See PR [#479](https://github.com/commit-check/commit-check/pull/479).

## v2.12.0 (2026-07-24)

### New Features

* **Configurable author patterns** — `author_name` and `author_email` accept a
custom regex, so organisations can require their own naming or email domain
instead of the built-in patterns.
See PR [#459](https://github.com/commit-check/commit-check/pull/459).

### Bug Fixes

* **Crash on Chinese Windows** — commit messages are no longer decoded with the
system GBK codec, which raised `UnicodeDecodeError`.
See PR [#475](https://github.com/commit-check/commit-check/pull/475).
* **Author validation reads git config first** — `git config user.name` is the
identity the *next* commit will carry, so it is checked before falling back
to the last commit's author. A misconfigured identity used to pass whenever
the previous commit happened to be valid.
See PR [#461](https://github.com/commit-check/commit-check/pull/461).
* `require_signed_off_by` accepts any name, and is skipped for authors listed
in `ignore_authors`.
See PRs [#462](https://github.com/commit-check/commit-check/pull/462) and
[#464](https://github.com/commit-check/commit-check/pull/464).

## v2.11.0 (2026-07-06)

### New Features
Expand Down
2 changes: 1 addition & 1 deletion docs/guides/ai-attribution.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ ai_attribution = "forbid"
Commits carrying a recognised AI signature now fail:

```text
CC013 ai_attribution check failed ==> feat: add caching layer
CC013 ai-attribution check failed ==> feat: add caching layer
AI attribution policy violation
Suggest: This project forbids AI-assisted commits. Remove AI trailers and re-commit.
Docs: https://commit-check.com/rules/#cc013
Expand Down
2 changes: 1 addition & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ whatever your AI agent is committing on your behalf.

```console
$ commit-check --message --branch
CC003 subject_imperative check failed ==> docs: revamped the profile
CC003 subject-imperative check failed ==> docs: revamped the profile
Commit message should use imperative mood (e.g., 'fix bug' not 'fixed bug')
Suggest: Change the first verb to imperative form
Docs: https://commit-check.com/rules/#cc003
Expand Down
2 changes: 1 addition & 1 deletion docs/rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ review comments, issue templates, and tooling.
Rule IDs appear in commit-check's output and in `--format json` results:

```text
CC003 subject_imperative check failed ==> docs: revamped the profile
CC003 subject-imperative check failed ==> docs: revamped the profile
Commit message should use imperative mood (e.g., 'fix bug' not 'fixed bug')
Suggest: Change the first verb to imperative form, e.g., 'fix' instead of 'fixed'
Docs: https://commit-check.com/rules/#cc003
Expand Down
36 changes: 36 additions & 0 deletions docs/what-is-new.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,42 @@ The changes worth knowing about, newest first, each pointing at the page that
documents it properly. For the full record of every change, see the
[changelog](changelog.md).

## 2.13.0 — Stable rule IDs

Every diagnostic now carries an ID that never changes once released, so a
failure can be cited in review, looked up, or matched by tooling.

```console
$ commit-check --message
CC003 subject-imperative check failed ==> docs: revamped the profile
Commit message should use imperative mood (e.g., 'fix bug' not 'fixed bug')
Suggest: Change the first verb to imperative form
Docs: https://commit-check.com/rules/#cc003
```

The ID also appears in `--format json` as `rule_id`, next to a `docs_url`. In a
terminal that renders hyperlinks the ID *is* the link, and the `Docs:` line is
dropped — piped output and CI logs keep it.

`docs/`, `ci/`, `test/`, `refactor/`, `build/`, `perf/` and `style/` also join
the default branch types.

[:octicons-arrow-right-24: Rules reference](rules.md)

## 2.12.0 — Custom author patterns

`author_name` and `author_email` accept a regex of your own, so a team can
require its own naming convention or email domain rather than the built-in
patterns.

```toml title="cchk.toml"
[commit]
author_email = "^.+@example\\.com$"
```

[:octicons-arrow-right-24: CC101](rules.md#cc101) ·
[CC102](rules.md#cc102)

## 2.11.0 — AI attribution policy

Commits carrying the trailers AI coding tools add — Claude Code, Copilot,
Expand Down
25 changes: 25 additions & 0 deletions tests/docs_sync_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ def _read_doc(name: str) -> str:
return (DOCS / name).read_text(encoding="utf-8")


#: A pasted failure line, e.g. ``CC003 subject-imperative check failed ==> ...``
_SAMPLE_FAILURE = re.compile(r"(CC\d{3}) (\S+) check failed ==>")


def _rule_section(content: str, rule_id: str) -> str:
"""Return just the part of the rules page belonging to one rule."""
_, _, after = content.partition(f"{{ #{rule_id.lower()} }}")
Expand Down Expand Up @@ -57,6 +61,27 @@ def test_every_rule_has_a_section_heading(self):
f"docs/rules.md has no section titled '{heading}'"
)

def test_sample_output_matches_what_the_tool_prints(self):
"""Pasted terminal output has to name rules the way the tool does.

These blocks are transcripts, so nothing regenerates them and nothing
else here reads them: the heading and options-table guards both look
at reference tables. When the printed name moved from the config key
to its kebab-case form, six samples across four pages kept showing the
old one and every test still passed.
"""
by_id = {entry.rule_id: entry for entry in ALL_RULES}
stale = []
for page in DOCS.rglob("*.md"):
for rule_id, printed in _SAMPLE_FAILURE.findall(page.read_text("utf-8")):
entry = by_id.get(rule_id)
if entry and printed != entry.name:
stale.append(
f"{page.relative_to(DOCS)}: {rule_id} shown as "
f"'{printed}', the tool prints '{entry.name}'"
)
assert not stale, "sample output is out of date:\n " + "\n ".join(stale)

def test_every_rule_explains_itself(self):
"""Each rule section must answer what it does and why it matters."""
content = _read_doc("rules.md")
Expand Down