From 581391d7c3acdbff03a2d2efc68c75a60c57c905 Mon Sep 17 00:00:00 2001 From: Xianpeng Shen <3353385+shenxianpeng@users.noreply.github.com> Date: Thu, 15 Oct 2026 06:47:58 +0000 Subject: [PATCH] docs: bring the site in step with v2.19.0 The pre-commit pins move from v2.18.3 to v2.19.0, and the changelog gains the v2.19.0 entry (2026-10-14) with a Highlights row. - CC201 and the configuration reference document require_description_grammar, and CC201 gives the config that agrees with every Conventional Branch conformance case git can create. The default type list is described against spec 1.1.0, which includes feat, fix and the AI agent prefixes. - CC013, the AI attribution guide and the blog post name Amp, OpenHands, Factory Droid and Jules. The guide says that a bot-authored Jules or OpenHands commit with no trailer passes, since only the message is read. - The JSON samples and the Python API schema carry the new reason field, with a transcript of a skip caused by ignore_authors. - The pre-commit guide says which tags check-tag checks on a push. --- .../posts/ai-commit-attribution-policy.md | 4 +- docs/changelog.md | 48 +++++++++++++++++++ docs/configuration.md | 15 ++++-- docs/example.md | 17 ++++++- docs/guides/policies.md | 11 +++-- docs/guides/pre-commit.md | 10 ++-- docs/guides/python-api.md | 4 +- docs/rules.md | 41 +++++++++++++--- 8 files changed, 128 insertions(+), 22 deletions(-) diff --git a/docs/blog/posts/ai-commit-attribution-policy.md b/docs/blog/posts/ai-commit-attribution-policy.md index 25ba1d5..d90a86c 100644 --- a/docs/blog/posts/ai-commit-attribution-policy.md +++ b/docs/blog/posts/ai-commit-attribution-policy.md @@ -97,7 +97,9 @@ sign-off. The detection covers co-author and sign-off lines that name Claude Code, GitHub Copilot, Codex, Gemini, Cursor, Devin, Aider, Windsurf, Tabby or a model, and -vendor marks such as "Generated with Claude Code". It matches the tools' +vendor marks such as "Generated with Claude Code"; +[v2.19.0](../../changelog.md#v2190) added Amp, OpenHands, Factory Droid and +Jules. It matches the tools' noreply addresses, so a human co-author who happens to be called Claude is left alone. [CC013](../../rules.md#cc013) keeps the current list. diff --git a/docs/changelog.md b/docs/changelog.md index 7facb1c..564ab60 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -11,6 +11,7 @@ below and to the page that documents the feature properly. | Version | What changed | Documented in | |---|---|---| +| [2.19.0](#v2190) | Branch descriptions held to the Conventional Branch grammar; Amp, OpenHands, Factory Droid and Jules recognised | [CC201](rules.md#cc201) · [CC013](rules.md#cc013) | | [2.18.3](#v2183) | `--fix` corrects a message in place; `--branch` reads GitLab, Bitbucket, Azure and Jenkins variables | [Fixing a message in place](example.md#fixing-a-message-in-place) · [Other CI](guides/other-ci.md) | | [2.18.0](#v2180) | AI disclosure policy: three rules, and the correction written for you | [Policy guides](guides/policies.md#asking-for-disclosure-instead) · [CC014](rules.md#cc014) | | [2.17.0](#v2170) | Concrete corrections for mechanical slips; a `warn` level per rule | [Reading the JSON](example.md#reading-the-json) · [Report without enforcing](configuration.md#report-a-rule-without-enforcing-it) | @@ -30,6 +31,53 @@ below and to the page that documents the feature properly. | [2.5.0](#v250) | Organization-wide config with `inherit_from` | [Across an organization](guides/organization.md) | | [2.0.0](#v200) | Configuration moved from YAML to TOML — breaking | [Migrating from v1](migration.md) | +## v2.19.0 (2026-10-14) { #v2190 } + +### Added + +* **`require_description_grammar` checks what follows the branch type** — + `conventional_branch` checks the `/` prefix and accepted anything + after it, so `feature/Add_Login`, `fix/header_bug` and `release/v1.-2.0` + all passed. With `[branch] require_description_grammar = true` the + description must follow the specification's grammar: lowercase letters and + digits, words joined by single hyphens, dots only inside a word. It is off + by default, because bot branches such as + `dependabot/npm_and_yarn/lodash-4.17.21` do not follow that grammar. + See PR [#581](https://github.com/commit-check/commit-check/pull/581) and + [CC201](rules.md#cc201). + +* **Amp, OpenHands, Factory Droid and Jules are recognised** — co-author and + sign-off lines naming them (`Amp `, `openhands`, + `factory-droid[bot]`, `google-labs-jules[bot]`) and Amp's + `Amp-Thread-ID:` trailer now count as AI attribution, for + [CC013](rules.md#cc013) and for the `disclose` rules alike. People named + Jules or Amp are not flagged. + See PR [#606](https://github.com/commit-check/commit-check/pull/606) and + [Forbidding AI-attributed commits](guides/policies.md#forbidding-ai-attributed-commits). + +* **`--format json` says why a check was skipped** — a check skipped because + the author or a co-author is on an `ignore_authors` list carries a `reason`, + such as `author dependabot[bot] is in [commit].ignore_authors`. Every check + has the key; it is empty when there is no reason to give. + See PR [#605](https://github.com/commit-check/commit-check/pull/605) and + [When a check is skipped](example.md#when-a-check-is-skipped). + +### Fixed + +* **`check-tag` checks the tags a push sends** — under pre-commit the hook + judged the tags on `HEAD`, so a bad tag sent by `git push --follow-tags` + from another branch got through, and a tag push could be rejected over a + local tag on `HEAD` that it did not carry. It now checks the tag a tag push + names, and on a branch push the tags on every commit the push carries. + See PR [#603](https://github.com/commit-check/commit-check/pull/603). + +* **A GitHub Actions tag build has no branch to judge** — on a tag push, + `GITHUB_REF_NAME` holds the tag, so `--branch` failed + [CC201](rules.md#cc201) on `v1.2.0`. A tag build now falls back to `HEAD`, + which is always accepted, as tag builds on GitLab, Bitbucket, Azure and + Jenkins already did. + See PR [#603](https://github.com/commit-check/commit-check/pull/603). + ## v2.18.3 (2026-10-07) { #v2183 } ### Added diff --git a/docs/configuration.md b/docs/configuration.md index 8c10a40..c2cd45e 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -222,10 +222,13 @@ allow_force_push = true # set false to block force pushes [branch] # https://conventionalbranch.org conventional_branch = true -# changed: spec types only. The default is a superset — these plus the -# Conventional Commit types, AI agent prefixes and bot prefixes — so setting -# this at all narrows it. Omit the line to accept all of them. +# changed: the spec's five purpose types only. The default is a superset — +# these plus their aliases feat and fix, AI agent prefixes, the other +# Conventional Commit types and bot prefixes — so setting this at all narrows +# it. Omit the line to accept all of them. allow_branch_types = ["feature", "bugfix", "hotfix", "release", "chore"] +# true also holds the part after "/" to the spec's grammar +require_description_grammar = false # allow_branch_names = [] # optional: extra standalone names, e.g. ["develop"] # require_rebase_target = "main" # optional: no rebase requirement by default # ignore_authors = [] # optional: as above, for branch checks @@ -261,7 +264,7 @@ Used from a hook definition, with no config file anywhere in the repository: ```yaml title=".pre-commit-config.yaml" repos: - repo: https://github.com/commit-check/commit-check - rev: v2.18.3 + rev: v2.19.0 hooks: - id: check-message args: @@ -306,6 +309,7 @@ The full mapping between the three forms: | `conventional_branch = true` | `CCHK_CONVENTIONAL_BRANCH=true` | `--conventional-branch=true` | | `allow_branch_types = ["feature"]` | `CCHK_ALLOW_BRANCH_TYPES=feature,bugfix` | `--allow-branch-types=feature,bugfix` | | `allow_branch_names = ["develop"]` | `CCHK_ALLOW_BRANCH_NAMES=develop,staging` | `--allow-branch-names=develop,staging` | +| `require_description_grammar = true` | `CCHK_REQUIRE_DESCRIPTION_GRAMMAR=true` | `--require-description-grammar=true` | | `require_rebase_target = "main"` | `CCHK_REQUIRE_REBASE_TARGET=main` | `--require-rebase-target=main` | | `allow_force_push = true` | `CCHK_ALLOW_FORCE_PUSH=true` | `--no-force-push` (sets `allow_force_push` to `false`) | | `ai_attribution = "forbid"` | `CCHK_AI_ATTRIBUTION=forbid` | `--ai-attribution=forbid` | @@ -371,7 +375,8 @@ the same thing twice, so read the description rather than the cell: | commit | ai_disclosure_trailers | list[str] | ["Assisted-by", "Generated-by"] | The trailers that disclose AI assistance under `"disclose"`. The first one is what a correction is written with. Listing `Co-authored-by` says the project accepts the tool as a co-author, and CC015 then passes. `Signed-off-by` is refused: only a person can certify the DCO. | | commit | ai_disclosure_pattern | str | "" (any value) | A regex the disclosure trailer's value must match under `"disclose"`, e.g. `'^\S+/\S+$'` for an `agent/model` format. Empty accepts any value, but a trailer with no value at all is reported whatever the pattern is. | | branch | conventional_branch | bool | true | Enforce Conventional Branch specification. | -| branch | allow_branch_types | list[str] | ["feature", "bugfix", "hotfix", "release", "chore", "feat", "fix", "build", "ci", "docs", "perf", "refactor", "style", "test", "ai", "claude", "codex", "copilot", "cursor", "dependabot", "renovate"] | Allowed branch types when `conventional_branch` is true. The default is a superset of the [Conventional Branch spec](https://conventionalbranch.org/): the spec types (`feature`, `bugfix`, `hotfix`, `release`, `chore`) plus the Conventional Commit types (`build`, `ci`, `docs`, `perf`, `refactor`, `style`, `test`), AI agent prefixes (`ai`, `claude`, `codex`, `copilot`, `cursor`) and bot prefixes (`dependabot`, `renovate`). For strict spec-only validation, set this option explicitly (e.g. `["feature", "bugfix", "hotfix", "release", "chore"]`). | +| branch | allow_branch_types | list[str] | ["feature", "bugfix", "hotfix", "release", "chore", "feat", "fix", "build", "ci", "docs", "perf", "refactor", "style", "test", "ai", "claude", "codex", "copilot", "cursor", "dependabot", "renovate"] | Allowed branch types when `conventional_branch` is true. The default is a superset of the [Conventional Branch spec](https://conventionalbranch.org/): the spec's types (`feature`, `bugfix`, `hotfix`, `release`, `chore`, their aliases `feat` and `fix`, and the AI agent prefixes `ai`, `claude`, `codex`, `copilot`, `cursor`) plus the other Conventional Commit types (`build`, `ci`, `docs`, `perf`, `refactor`, `style`, `test`) and bot prefixes (`dependabot`, `renovate`). For strict spec-only validation, set this option explicitly to the spec's types; [CC201](rules.md#cc201) has the full config. | +| branch | require_description_grammar | bool | false | Also hold the description after `/` to the Conventional Branch grammar: lowercase letters and digits, words joined by single hyphens, dots only inside a word. Off by default, because bot branches such as `dependabot/npm_and_yarn/lodash-4.17.21` do not follow it. See [CC201](rules.md#cc201). | | branch | allow_branch_names | list[str] | [] (empty list) | Additional standalone branch names allowed when conventional_branch is true (e.g., ["develop", "staging"]). By default, master, main, HEAD, and PR-* are always allowed. | | branch | require_rebase_target | str | "" (no requirement) | Target branch for rebase requirement. If not set, no rebase validation is performed. | | push | allow_force_push | bool | true | Allow force pushes. Set to `false` to block force pushes when used as a pre-push hook or with `--no-force-push`. | diff --git a/docs/example.md b/docs/example.md index dd582f0..f2be3a2 100644 --- a/docs/example.md +++ b/docs/example.md @@ -159,7 +159,7 @@ pushed: ```yaml title=".pre-commit-config.yaml" repos: - repo: https://github.com/commit-check/commit-check - rev: v2.18.3 + rev: v2.19.0 hooks: - id: check-no-force-push stages: [pre-push] @@ -286,6 +286,18 @@ $ echo "Merge branch 'main' into topic" | commit-check -m --no-banner ``` In JSON each skipped check carries `"status": "skip"`, distinct from `"pass"`. +When a check is skipped because the author or a co-author is on an +`ignore_authors` list, `reason` says so. Here `HEAD` is a Dependabot commit +and `cchk.toml` lists `dependabot[bot]` under `[commit] ignore_authors`: + +```console +$ commit-check -m --rev HEAD --format json | jq -c '.checks[] | {rule_id, status, reason}' +{"rule_id":"CC001","status":"skip","reason":"author dependabot[bot] is in [commit].ignore_authors"} +{"rule_id":"CC004","status":"skip","reason":"author dependabot[bot] is in [commit].ignore_authors"} +{"rule_id":"CC005","status":"skip","reason":"author dependabot[bot] is in [commit].ignore_authors"} +``` + +For any other skip, such as a merge subject, `reason` is empty. !!! warning "A green run can still have validated nothing" @@ -323,7 +335,8 @@ $ echo "Fix: add streaming support" | commit-check -m --format json | jq '.check "error": "The commit message should follow Conventional Commits. See https://www.conventionalcommits.org", "suggest": "Use \"fix: add streaming support\"", "fix": "fix: add streaming support", - "docs_url": "https://commit-check.com/rules/#cc001" + "docs_url": "https://commit-check.com/rules/#cc001", + "reason": "" } ``` diff --git a/docs/guides/policies.md b/docs/guides/policies.md index ec18ef5..0aade37 100644 --- a/docs/guides/policies.md +++ b/docs/guides/policies.md @@ -132,13 +132,18 @@ Docs: https://commit-check.com/rules/#cc013 ``` Recognised signatures are co-author and sign-off lines naming Claude Code, -GitHub Copilot, Codex, Gemini, Cursor, Devin, Aider, Windsurf and Tabby, the -disclosure trailers `Assisted-by:` and `Generated-by:`, vendor marks such as -`🤖 Generated with Claude Code`, and generic AI model names. This is the +GitHub Copilot, Codex, Gemini, Cursor, Devin, Aider, Windsurf, Tabby, Amp, +OpenHands, Factory Droid and Jules, the disclosure trailers `Assisted-by:` and +`Generated-by:`, vendor marks such as `🤖 Generated with Claude Code` and +Amp's `Amp-Thread-ID:`, and generic AI model names. This is the position [Kubernetes](https://www.kubernetes.dev/docs/guide/pull-requests/) takes — no AI trailers in the history, disclosure in the pull request instead. +These rules read only the message. Jules and OpenHands can commit under their +own bot identity instead of adding a co-author line, and such a commit passes +when its message carries no trailer. + !!! warning "This checks disclosure, not authorship" These rules read commit metadata. They detect a commit that *says* it diff --git a/docs/guides/pre-commit.md b/docs/guides/pre-commit.md index 98aca63..4e95f24 100644 --- a/docs/guides/pre-commit.md +++ b/docs/guides/pre-commit.md @@ -8,7 +8,7 @@ Add Commit Check to `.pre-commit-config.yaml`: ```yaml title=".pre-commit-config.yaml" repos: - repo: https://github.com/commit-check/commit-check - rev: v2.18.3 + rev: v2.19.0 hooks: - id: check-message - id: check-branch @@ -50,6 +50,10 @@ The three `pre-push` hooks need their own install too: $ pre-commit install --hook-type pre-push ``` +`check-tag` checks the tags the push sends: the tag a tag push names, and on +a branch push, such as `git push --follow-tags`, the tags on every commit the +push carries. + pre-commit runs no hook for a push that creates a ref without carrying new commits. So a new tag pushed on its own, when the commit it points at is already on the remote, is never seen by `check-tag`. @@ -61,7 +65,7 @@ Options can be passed as hook arguments, which keeps everything in one file: ```yaml title=".pre-commit-config.yaml" repos: - repo: https://github.com/commit-check/commit-check - rev: v2.18.3 + rev: v2.19.0 hooks: - id: check-message args: @@ -83,7 +87,7 @@ colon, and lets the commit through: ```yaml title=".pre-commit-config.yaml" repos: - repo: https://github.com/commit-check/commit-check - rev: v2.18.3 + rev: v2.19.0 hooks: - id: check-message args: [--fix] diff --git a/docs/guides/python-api.md b/docs/guides/python-api.md index 6d1d662..43191cd 100644 --- a/docs/guides/python-api.md +++ b/docs/guides/python-api.md @@ -70,6 +70,7 @@ Every function returns the same shape: "suggest": "...", # advice for a person "fix": "...", # the corrected value, or "" when it takes judgment "docs_url": "https://commit-check.com/rules/#cc001", + "reason": "...", # why a skipped check did not run, when known; else "" }, ], } @@ -80,7 +81,8 @@ Every function returns the same shape: - **`skip` is not `pass`.** A check skips when it never ran — the author is in `ignore_authors`, or there was nothing to check — and the top-level `status` is `skip` only when every check skipped. A skipped run validated nothing, so - do not read it as approval. + do not read it as approval. When the author or a co-author is in + `ignore_authors`, the check's `reason` names them. - **`warn`** is a rule listed under the config's `warn`: reported in full, never a failure. The top-level `status` stays `pass`, and `warnings` counts them. diff --git a/docs/rules.md b/docs/rules.md index 8ed465d..a16acac 100644 --- a/docs/rules.md +++ b/docs/rules.md @@ -537,9 +537,10 @@ git commit --amend --signoff Rejects a commit whose message carries any AI attribution: a co-author or sign-off line naming Claude Code, Copilot, Codex, Gemini, Cursor, Devin, -Aider, Windsurf or Tabby, a disclosure trailer such as `Assisted-by:` or -`Generated-by:`, a vendor's own mark such as `🤖 Generated with Claude Code`, -and generic AI model names. +Aider, Windsurf, Tabby, Amp, OpenHands, Factory Droid or Jules, a disclosure +trailer such as `Assisted-by:` or `Generated-by:`, a vendor's own mark such +as `🤖 Generated with Claude Code` or Amp's `Amp-Thread-ID:` trailer, and +generic AI model names. **Why is this bad?** @@ -762,6 +763,11 @@ Checks that the current branch name follows the `master`, `main`, `HEAD`, and `PR-*` are always accepted. +By default only the type is checked, and any description after the `/` is +accepted. With `require_description_grammar` the description has to follow +the specification's grammar too: lowercase letters and digits, words joined +by single hyphens, dots only inside a word, as in `release/v1.2.0`. + **Why is this bad?** A predictable prefix is something automation can act on: CI can skip expensive @@ -787,14 +793,35 @@ feature/role-caching * `branch.conventional_branch` — set to `false` to disable this rule. * `branch.allow_branch_types` — the accepted `` values. The default is - a superset of the specification: the spec types plus the Conventional Commit - types, AI agent prefixes (`ai`, `claude`, `codex`, `copilot`, - `cursor`), and bot prefixes (`dependabot`, `renovate`). Set it - explicitly for strict spec-only validation. + a superset of the specification: the spec's types (`feature`, `bugfix`, + `hotfix`, `release`, `chore`, their aliases `feat` and `fix`, and the AI + agent prefixes `ai`, `claude`, `codex`, `copilot` and `cursor`), plus the + other Conventional Commit types and the bot prefixes `dependabot` and + `renovate`. Set it explicitly for strict spec-only validation. +* `branch.require_description_grammar` — set to `true` to hold the + description to the specification's grammar, so `feature/Add_Login`, + `feature/new--login` and `feature/a/b` are rejected. Off by default, + because bot branches such as `dependabot/npm_and_yarn/lodash-4.17.21` do + not follow it. * `branch.allow_branch_names` — additional standalone names to accept, such as `["develop", "staging"]`. * `branch.ignore_authors` — bypass the check for specific authors. +To check branches exactly as the specification does, set the spec's types, +the description grammar, and its third trunk branch, `develop`: + +```toml title="cchk.toml" +[branch] +conventional_branch = true +allow_branch_types = ["feature", "feat", "bugfix", "fix", "hotfix", "release", "chore", "ai", "claude", "codex", "copilot", "cursor"] +require_description_grammar = true +allow_branch_names = ["develop"] +``` + +With that, `commit-check --branch` agrees with every case of the +specification's [conformance suite](https://github.com/conventional-branch/conventional-branch/blob/main/tests/fixtures.json) +that git can create as a branch. + ### merge-base (CC202) { #cc202 } **What it does**