From fb9001a1175b18ba2649742f150f056ae9340cdc Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 5 Aug 2026 12:01:08 +0000 Subject: [PATCH 1/6] fix: stop the landing page scrolling sideways on phones and tablets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The "trusted by" and community bands bleed past the content column with a -1.5rem margin. Material sets the root font to 125%, making that 30px, but below its 76.25em breakpoint the column only has a 16px gutter — so the bands hung 14px off the right edge and the whole page could be dragged sideways. Wide screens have a bigger gutter, which is why it only showed up below 1220px. Measured across 320-1440px: the overflow is gone at every width from 390px up, and the bands now stop exactly at the column edge instead of short of it. A 10px overflow remains at 320px, from Material's own off-canvas drawer rather than anything here. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01U9zFxq8V4qxG4aMzJhGBFn --- docs/stylesheets/extra.css | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/docs/stylesheets/extra.css b/docs/stylesheets/extra.css index fcabb68..bee1857 100644 --- a/docs/stylesheets/extra.css +++ b/docs/stylesheets/extra.css @@ -185,6 +185,22 @@ th { } } +/* Both tinted bands above bleed past the content column with a negative + margin. Material sets the root font to 125%, so -1.5rem is 30px — but below + its 76.25em breakpoint the column only has a 16px gutter to give back. The + bands hung 14px off the right edge, which made the whole page scroll + sideways on every phone and tablet. Match the gutter there instead. + + (76.25em is Material's own breakpoint. Media-query em units resolve against + the initial 16px font size rather than the 125% root, so this is 1220px.) */ +@media screen and (max-width: 76.25em) { + .trusted-by, + .community-section { + margin-left: -0.8rem; + margin-right: -0.8rem; + } +} + /* Mobile navigation drawer ------------------------------------------------- Material leaves this at its own default indigo, which clashes with the header right above it. */ From 8bb8e4624572b11fd87e311c6c99e25734d9c1b7 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 5 Aug 2026 12:01:08 +0000 Subject: [PATCH 2/6] perf: reserve space for the adopter logos and defer loading them MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The eighteen avatars on the landing page are hotlinked from github.com with no dimensions, so the browser could not reserve their boxes and the section reflowed as each one arrived. They also all loaded eagerly, below the fold, competing with the content above them. The grid already pins them to 60x60 in CSS; say so in the markup, and mark them lazy. Hotlinking itself is unchanged — the page still depends on github.com being reachable, which is worth revisiting separately. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01U9zFxq8V4qxG4aMzJhGBFn --- docs/index.md | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/docs/index.md b/docs/index.md index fa4d36e..60710d0 100644 --- a/docs/index.md +++ b/docs/index.md @@ -36,7 +36,7 @@ whatever your AI agent is committing on your behalf. ```yaml title=".pre-commit-config.yaml" repos: - repo: https://github.com/commit-check/commit-check - rev: v2.13.0 + rev: v2.13.1 hooks: - id: check-message - id: check-branch @@ -270,75 +270,75 @@ graph TB
- Apache + Apache Apache
- Discovery Unicamp + Discovery Unicamp Discovery Unicamp
- Texas Instruments + Texas Instruments Texas Instruments
- OpenCADC + OpenCADC OpenCADC
- Extrawest + Extrawest Extrawest
- Chainlift + Chainlift Chainlift
- Mila + Mila Mila
- RLinf + RLinf RLinf
- Istio Ecosystem + Istio Ecosystem Istio Ecosystem
- Juniper Networks + Juniper Networks Juniper Networks
- French National Parks + French National Parks French National Parks
- OpenDriveLab + OpenDriveLab OpenDriveLab
- UT Austin RobIn + UT Austin RobIn UT Austin RobIn
- WorldArena2 + WorldArena2 WorldArena2
- moniqo + moniqo moniqo
- elu mobility + elu mobility elu mobility
- Open Energy Platform + Open Energy Platform Open Energy Platform
- Collective + Collective Collective
From a5d00c8c7f2c2f6ff8cae33b12c5afb78b366b3b Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 5 Aug 2026 12:01:20 +0000 Subject: [PATCH 3/6] fix: pin the install snippets to the released version, and guard it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Five snippets across four pages still pinned rev: v2.13.0 after 2.13.1 shipped. A stale pin is invisible — the snippet keeps working, it just installs an older release than the page around it describes — and nothing here read those lines, so they only move when somebody remembers. Add a guard alongside the existing anti-drift tests: every rev: outside the blog must name the version pytest has installed, which CI installs unpinned from PyPI. Blog posts are exempt because they are dated records of what was current when they were written. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01U9zFxq8V4qxG4aMzJhGBFn --- docs/configuration.md | 2 +- docs/example.md | 2 +- docs/guides/integrations.md | 4 ++-- tests/docs_sync_test.py | 34 ++++++++++++++++++++++++++++++++++ 4 files changed, 38 insertions(+), 4 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index d67a25d..b0d221a 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -189,7 +189,7 @@ The primary use case for CLI arguments is configuring commit-check in `.pre-comm ```yaml repos: - repo: https://github.com/commit-check/commit-check - rev: v2.13.0 + rev: v2.13.1 hooks: - id: check-message args: diff --git a/docs/example.md b/docs/example.md index 1c38170..41a412b 100644 --- a/docs/example.md +++ b/docs/example.md @@ -104,7 +104,7 @@ pushed: ```yaml title=".pre-commit-config.yaml" repos: - repo: https://github.com/commit-check/commit-check - rev: v2.13.0 + rev: v2.13.1 hooks: - id: check-no-force-push stages: [pre-push] diff --git a/docs/guides/integrations.md b/docs/guides/integrations.md index e108a11..f15a237 100644 --- a/docs/guides/integrations.md +++ b/docs/guides/integrations.md @@ -23,7 +23,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.13.0 + rev: v2.13.1 hooks: - id: check-message - id: check-branch @@ -70,7 +70,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.13.0 + rev: v2.13.1 hooks: - id: check-message args: diff --git a/tests/docs_sync_test.py b/tests/docs_sync_test.py index 80d174d..4eac633 100644 --- a/tests/docs_sync_test.py +++ b/tests/docs_sync_test.py @@ -14,6 +14,7 @@ from __future__ import annotations import re +from importlib.metadata import version from pathlib import Path from typing import Any @@ -93,6 +94,39 @@ def test_every_rule_explains_itself(self): ) +#: A pre-commit revision pin, e.g. ``rev: v2.13.1``. +_REV_PIN = re.compile(r"^\s*rev:\s*v(\d+\.\d+\.\d+)\s*$", re.M) + + +class TestDocumentedRevisions: + """The revisions the install snippets pin are the released version. + + Copy-pasteable snippets are the most-used thing on the site, and a pin is + invisible once it goes stale: the snippet keeps working, it just installs + an older release than the page around it describes. Nothing else here + reads these — the other guards compare reference tables against the + package — so a release would leave five pages pinned to the version + before it. + """ + + def test_pinned_revisions_match_the_released_version(self): + """Every ``rev:`` outside the blog names the installed version.""" + installed = version("commit-check") + stale = [] + for page in DOCS.rglob("*.md"): + # Blog posts are dated: they record what was current when they + # were written, and moving their pins forward would falsify them. + if "blog" in page.relative_to(DOCS).parts: + continue + for pinned in _REV_PIN.findall(page.read_text("utf-8")): + if pinned != installed: + stale.append( + f"{page.relative_to(DOCS)}: pins v{pinned}, " + f"the released version is {installed}" + ) + assert not stale, "install snippets are out of date:\n " + "\n ".join(stale) + + _OPTIONS_ROW = re.compile( r"^\|\s*(commit|branch|push)\s*" # section r"\|\s*(\w+)\s*" # option name From a3d5579c88012d7ee1735f857b17c9bc2edfe526 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 5 Aug 2026 12:01:20 +0000 Subject: [PATCH 4/6] fix: keep rule names in the index tables on one line MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Eleven of them wrapped mid-identifier even at 1440px — `subject- capitalized` split across two lines is hard to scan and hard to copy. The stylesheet already carried a rule for this, written against a `table.rules-index` class no table ever had, so it never applied and was dropped as dead code. Restore the intent properly: attr_list cannot put a class on a table, so the four index tables get a wrapper div. Other tables hold long regexes and allow-lists that must stay wrappable, which is why this is scoped rather than applied to every table. On a phone the tables now scroll inside Material's own wrapper; the page itself still does not scroll horizontally. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01U9zFxq8V4qxG4aMzJhGBFn --- docs/assets/extra.css | 9 +++++++++ docs/rules.md | 16 ++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/docs/assets/extra.css b/docs/assets/extra.css index a5fdfc1..7b2b300 100644 --- a/docs/assets/extra.css +++ b/docs/assets/extra.css @@ -48,6 +48,15 @@ vertical-align: top; } +/* In the rule index tables only, keep rule names and CLI flags on one line so + the tables stay scannable — without this, names like `subject-capitalized` + break across two lines even on a wide screen. Other tables hold long regexes + and allow-lists that must stay wrappable, hence the wrapper class rather + than a rule for every table. */ +.md-typeset .rules-index td code { + white-space: nowrap; +} + /* Sidebar section headings ("Getting started", "Reference", ...) */ .md-nav__item--section > .md-nav__link { font-weight: 700; diff --git a/docs/rules.md b/docs/rules.md index cf19ec4..ddafe1e 100644 --- a/docs/rules.md +++ b/docs/rules.md @@ -62,6 +62,8 @@ actively harmful for one that does not. Run with `-m` / `--message`. +
+ | Code | Name | Message | Check | Default | |---|---|---|---|---| | [CC001](#cc001) | `message` | The commit message should follow Conventional Commits | `-m` | ✅ On | @@ -78,28 +80,42 @@ Run with `-m` / `--message`. | [CC012](#cc012) | `require-signed-off-by` | Signed-off-by not found in latest commit | `-m` | ⚪ Off | | [CC013](#cc013) | `ai-attribution` | AI attribution policy violation | `-m` | ⚪ Off | +
+ ### Author rules (`CC1xx`) { #author-rules } +
+ | Code | Name | Message | Check | Default | |---|---|---|---|---| | [CC101](#cc101) | `author-name` | The committer name seems invalid | `-n` | ✅ On | | [CC102](#cc102) | `author-email` | The committer's email seems invalid | `-e` | ✅ On | +
+ ### Branch rules (`CC2xx`) { #branch-rules } Run with `-b` / `--branch`. +
+ | Code | Name | Message | Check | Default | |---|---|---|---|---| | [CC201](#cc201) | `branch` | The branch should follow Conventional Branch | `-b` | ✅ On | | [CC202](#cc202) | `merge-base` | Current branch is not rebased onto target branch | `-b` | ⚪ Off | +
+ ### Push rules (`CC3xx`) { #push-rules } +
+ | Code | Name | Message | Check | Default | |---|---|---|---|---| | [CC301](#cc301) | `no-force-push` | Force push is not allowed | `--no-force-push` | ⚪ Off | +
+ ## Commit message rules ### message (CC001) { #cc001 } From 1921a8a41c472dc6c2b046eea44e8db128b858a2 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 5 Aug 2026 12:18:59 +0000 Subject: [PATCH 5/6] docs: rewrite the three pages carried over from the Sphinx site MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit configuration, migration and troubleshoot were the pages the earlier consolidation left untouched, and it showed: Title Case headings against sentence case everywhere else, and prose that never got the same pass. Headings are now sentence case throughout. Beyond that: configuration. "Configuration Files" and "Configuration File Locations" said where the file goes three times between them; they are one section. The worked example mixed defaults with tightened values and marked only two of them, so copy-pasting it silently narrowed allow_commit_types and allow_branch_types — every line that departs from a default now says so, and the claim is checked against get_default_config() rather than asserted. Adds a note that a losing value is not warned about, which is the usual reason a setting "does nothing". migration. Led with a YAML-to-TOML diff of two whole files, which is the least useful framing: v1 files were regex, v2 files are booleans, so nothing translates line by line. It now explains that, gives the check-to-option mapping as a table with rule IDs, and keeps the worked example short. Verified each mapping against rules_catalog. troubleshoot. One topic, on a failure that no longer happens out of the box, and the bypass was presented before the fix. It now starts from reading the diagnostic, distinguishes skipping one hook from skipping all, and says plainly that neither affects CI — the thing people discover by pushing. Adds the two failures that actually get reported: a rule firing that nobody enabled, and nothing running because the flag was missing. The examples no longer use a maintainer's real name and address as the value to copy. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01U9zFxq8V4qxG4aMzJhGBFn --- docs/configuration.md | 293 ++++++++++++++++++------------------------ docs/migration.md | 195 ++++++++++++---------------- docs/troubleshoot.md | 93 ++++++++++++-- 3 files changed, 285 insertions(+), 296 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index b0d221a..694f44a 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -1,192 +1,158 @@ # Configuration -`commit-check` reads its settings from four places, in this priority order -(highest to lowest): +Commit Check reads its settings from four places. When the same option is set +in more than one, the first one listed wins: -1. **Command-line arguments** (`--subject-imperative=true`) -2. **Environment variables** (`CCHK_SUBJECT_IMPERATIVE=true`) -3. **Configuration files** (`cchk.toml` or `commit-check.toml`) +1. **Command-line arguments** — `--subject-imperative=true` +2. **Environment variables** — `CCHK_SUBJECT_IMPERATIVE=true` +3. **A configuration file** — `cchk.toml` or `commit-check.toml` 4. **Built-in defaults** -This flexibility allows you to: +That ordering is what makes the layering useful: the file carries the policy +the project agreed on, the environment overrides it for a single CI job, and a +flag overrides both for a single run. -* Use configuration files for project-wide settings -* Override with environment variables in CI/CD pipelines -* Override specific settings via CLI for one-off checks -* Use without any configuration files (relies on defaults) +!!! tip "Defaults are not "nothing"" -## Configuration Files + With no configuration file at all, Commit Check still enforces Conventional + Commits ([CC001](rules.md#cc001)), Conventional Branch + ([CC201](rules.md#cc201)), subject lengths of 5–80 characters + ([CC004](rules.md#cc004), [CC005](rules.md#cc005)) and the author name and + email patterns ([CC101](rules.md#cc101), [CC102](rules.md#cc102)). -`commit-check` configuration files support the TOML format. See `cchk.toml` for an example configuration. + Off by default: subject capitalization, imperative mood, required body and + signoff, rebase requirements, and every `allow_*` restriction. The *Default* + column in the [rules reference](rules.md#rule-index) is the full picture. -!!! tip "Default Behavior" +## Where the config file lives - * When no configuration file exists, commit-check uses sensible defaults with minimal restrictions. - * Enforced by default: the Conventional Commits format ([CC001](rules.md#cc001)), the Conventional Branch format ([CC201](rules.md#cc201)), the subject length limits of 5–80 characters ([CC004](rules.md#cc004), [CC005](rules.md#cc005)), and the author name and email patterns ([CC101](rules.md#cc101), [CC102](rules.md#cc102)). - * **Off** by default: subject capitalization, imperative mood, body and signoff requirements, rebase requirements, and every `allow_*` restriction. +The file is TOML, and may be called `cchk.toml` or `commit-check.toml`. Commit +Check searches four locations and uses the first that exists: - See [rules](rules.md) for the default state of every rule. - -commit-check can be configured via a `cchk.toml` or `commit-check.toml` file. - -The file should be placed in the root of your repository or in the `.github` folder. - -## Configuration File Locations - -commit-check searches for configuration files in the following order (first found is used): - -1. `cchk.toml` (root directory) -2. `commit-check.toml` (root directory) +1. `cchk.toml` +2. `commit-check.toml` 3. `.github/cchk.toml` 4. `.github/commit-check.toml` -!!! tip "GitHub Best Practice" - - Placing configuration files in the `.github` folder helps keep your repository root clean and follows GitHub conventions used by tools like Dependabot and Renovate. +Pass `--config` to point at one directly and skip the search: -!!! tip "IDE Autocompletion" +```console +$ commit-check --config path/to/cchk.toml --message +``` - commit-check's TOML schema is published on [SchemaStore](https://www.schemastore.org/), - so editors like VS Code (via [Even Better TOML](https://marketplace.visualstudio.com/items?itemName=tamasfe.even-better-toml)), - PyCharm, and IntelliJ provide autocompletion, validation, and documentation - tooltips for `cchk.toml` out of the box — no manual schema path configuration needed. +!!! tip "Why `.github/`" -## Organization-Level Configuration (inherit_from) + Putting the file in `.github/` keeps the repository root uncluttered and + matches where Dependabot and Renovate already keep theirs. Commit Check + treats both locations identically. -For organizations that want to share a common base configuration across many repositories, commit-check supports an `inherit_from` directive at the top level of your TOML config file. +!!! tip "Editor autocompletion" -**How it works:** + The TOML schema is published on [SchemaStore](https://www.schemastore.org/), + so VS Code (via + [Even Better TOML](https://marketplace.visualstudio.com/items?itemName=tamasfe.even-better-toml)), + PyCharm and IntelliJ offer completion, validation and inline documentation + for `cchk.toml` with nothing to configure. -1. The `inherit_from` value can be a `github:` shorthand, a local file path, or an HTTPS URL. -2. The parent (inherited) configuration is loaded first. -3. Local settings in the current config file **override** the parent values. -4. The `inherit_from` key itself is not passed to the validation engine. +## Inheriting a shared config -**Example — inherit from a GitHub repository (recommended):** +An organization can keep one base policy and have every repository build on it. +Point `inherit_from` at the shared file; the parent loads first, and anything +set locally overrides it. The key itself is never passed to the validation +engine. -```toml -# .github/cchk.toml +```toml title=".github/cchk.toml" inherit_from = "github:my-org/.github:cchk.toml" [commit] -subject_max_length = 72 # Overrides parent value +subject_max_length = 72 # overrides whatever the parent set ``` -**GitHub shorthand format:** +The value can be a GitHub shorthand, a local path or an HTTPS URL: -* `github:owner/repo:path/to/cchk.toml` — uses `HEAD` (default branch) -* `github:owner/repo@main:path/to/cchk.toml` — pins to the `main` branch +| Form | Example | +|---|---| +| GitHub, default branch | `github:owner/repo:path/to/cchk.toml` | +| GitHub, pinned branch | `github:owner/repo@main:path/to/cchk.toml` | +| Local file | `../../shared/org-cchk.toml` | +| HTTPS URL | `https://example.com/shared/cchk.toml` | -**Example — inherit from a local file:** +!!! note "Inheritance fails quietly" -```toml -# repo/.github/cchk.toml -inherit_from = "../../shared/org-cchk.toml" + If the target is unreachable or the format is not recognized, Commit Check + ignores the inheritance and uses the local configuration alone. Plain HTTP + URLs are rejected outright. A repository that silently stops inheriting + still passes its own checks, so pin the branch when the policy matters. -[commit] -allow_wip_commits = true # Override for this project only -``` - -**Example — inherit from an HTTPS URL:** - -```toml -# .github/cchk.toml -inherit_from = "https://example.com/shared/cchk.toml" -``` +## A worked example -!!! note +Every line below that differs from the built-in default is marked, so it is +clear what this file is actually changing: - If the `inherit_from` target is unreachable or the format is unrecognized, commit-check silently ignores the inheritance and uses only the local configuration. HTTP (non-TLS) URLs are rejected for security. - -## Example Configuration - -```toml +```toml title="cchk.toml" [commit] # https://www.conventionalcommits.org conventional_commits = true -# message_pattern = "" # Optional - custom regex (overrides conventional_commits) +# message_pattern = "" # optional: a custom regex, replacing the above subject_capitalized = false -subject_imperative = false -subject_max_length = 80 # Default - set to your own limit -subject_min_length = 5 # Default - set to your own minimum +subject_imperative = true # changed: off by default +subject_max_length = 80 +subject_min_length = 5 +# changed: a subset of the default list, which also has perf, build and ci allow_commit_types = ["feat", "fix", "docs", "style", "refactor", "test", "chore"] allow_merge_commits = true allow_revert_commits = true -allow_empty_commits = false +allow_empty_commits = false # changed: allowed by default allow_fixup_commits = true -allow_wip_commits = false +allow_wip_commits = false # changed: allowed by default require_body = false -# ignore_authors = [] # Optional - bypass checks for these commit/co-authors require_signed_off_by = false -ai_attribution = "forbid" # "ignore" (default) or "forbid" — rejects AI tool trailers +ai_attribution = "forbid" # changed: "ignore" by default +# ignore_authors = [] # optional: bypass all commit checks for these authors [push] -# Block force pushes when used as a pre-push hook or with --no-force-push -allow_force_push = true # Set to false to block force pushes +allow_force_push = true # set false to block force pushes [branch] # https://conventionalbranch.org conventional_branch = true -# Optional: defaults are a superset of the Conventional Branch spec — the -# spec types plus Conventional Commit types, AI agent prefixes and bot -# prefixes (see the Options table below for the full list). Omit this -# option to use the defaults, or set your own list for a strict subset. -allow_branch_types = [ - "feature", - "bugfix", - "hotfix", - "release", - "chore", - "feat", - "fix", - "build", - "ci", - "docs", - "perf", - "refactor", - "style", - "test", -] -# allow_branch_names = [] # Optional - additional standalone branch names (e.g., ["develop", "staging"]) -# require_rebase_target = "main" # Optional - no rebase requirement by default -# ignore_authors = [] # Optional - no authors ignored by default +# 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. +allow_branch_types = ["feature", "bugfix", "hotfix", "release", "chore"] +# 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 ``` -## Command-Line Arguments - -All configuration options can be specified via command-line arguments, which take precedence over environment variables and configuration files. +!!! warning "`allow_*` options describe what is permitted" -**Syntax:** + They read backwards from most linters. `allow_wip_commits = false` is the + setting that *rejects* WIP commits; leaving it at its default of `true` + lets them through. -* Boolean options: `--option-name=true` or `--option-name=false` -* Integer options: `--option-name=80` -* List options: `--option-name=value1,value2,value3` (comma-separated) -* String options: `--option-name=value` +## Command-line arguments -**Examples:** +Every option can be set as a flag, which is what makes a TOML file optional +entirely — useful when the policy lives in `.pre-commit-config.yaml` instead. -```bash -# Disable imperative mood check -commit-check --message --subject-imperative=false +| Type | Form | +|---|---| +| Boolean | `--option-name=true` / `--option-name=false` | +| Integer | `--option-name=80` | +| List | `--option-name=value1,value2,value3` | +| String | `--option-name=value` | -# Set custom subject length limit -commit-check --message --subject-max-length=72 - -# Restrict allowed commit types -commit-check --message --allow-commit-types=feat,fix,docs - -# Combine multiple options -commit-check --message --subject-imperative=true --subject-max-length=50 --allow-commit-types=feat,fix - -# Branch configuration via CLI -commit-check --branch --allow-branch-types=feature,bugfix,hotfix +```console +$ commit-check --message --subject-imperative=false +$ commit-check --message --subject-max-length=72 +$ commit-check --message --allow-commit-types=feat,fix,docs +$ commit-check --branch --allow-branch-types=feature,bugfix,hotfix ``` -**Pre-commit Hook Usage:** - -The primary use case for CLI arguments is configuring commit-check in `.pre-commit-config.yaml` without requiring a TOML file: +Used from a hook definition, with no config file anywhere in the repository: -```yaml +```yaml title=".pre-commit-config.yaml" repos: - repo: https://github.com/commit-check/commit-check rev: v2.13.1 @@ -198,39 +164,19 @@ repos: - --allow-merge-commits=false ``` -## Environment Variables - -Configuration can also be set via environment variables with the `CCHK_` prefix. This is useful for CI/CD pipelines and temporary overrides. - -**Naming Convention:** - -* Convert option name to uppercase -* Replace hyphens with underscores -* Add `CCHK_` prefix +## Environment variables -**Examples:** +Any option can also be set through the environment, which is the practical way +to vary policy per CI job without editing the file. Uppercase the option name, +replace hyphens with underscores, and prefix `CCHK_`: -```bash -# Set boolean options -export CCHK_SUBJECT_IMPERATIVE=true -export CCHK_SUBJECT_CAPITALIZED=false - -# Set integer options -export CCHK_SUBJECT_MAX_LENGTH=72 -export CCHK_SUBJECT_MIN_LENGTH=10 - -# Set list options (comma-separated) -export CCHK_ALLOW_COMMIT_TYPES=feat,fix,docs,chore -export CCHK_ALLOW_BRANCH_TYPES=feature,bugfix,hotfix - -# Set string options -export CCHK_REQUIRE_REBASE_TARGET=main - -# Use in CI/CD -CCHK_SUBJECT_MAX_LENGTH=100 commit-check --message +```console +$ export CCHK_SUBJECT_MAX_LENGTH=72 +$ export CCHK_ALLOW_COMMIT_TYPES=feat,fix,docs,chore +$ CCHK_SUBJECT_MAX_LENGTH=100 commit-check --message ``` -**Complete Mapping:** +The full mapping between the three forms: | TOML Config | Environment Variable | CLI Argument | |---|---|---| @@ -259,24 +205,29 @@ CCHK_SUBJECT_MAX_LENGTH=100 commit-check --message | `ai_attribution = "forbid"` | `CCHK_AI_ATTRIBUTION=forbid` | `--ai-attribution=forbid` | | `ignore_authors = ["bot"]` (in branch section) | `CCHK_BRANCH_IGNORE_AUTHORS=bot,user` | `--branch-ignore-authors=bot,user` | -## Configuration Priority Example +## Which value wins -When the same option is specified in multiple places, the priority determines which value is used: +The four sources layer, so the same option can be set in several at once. Only +the highest-priority one takes effect: -```bash -# In cchk.toml: -# subject_max_length = 100 +```console +$ grep subject_max_length cchk.toml +subject_max_length = 100 -# Set via environment: -export CCHK_SUBJECT_MAX_LENGTH=80 +$ export CCHK_SUBJECT_MAX_LENGTH=80 -# Override via CLI: -commit-check --message --subject-max-length=50 - -# Result: subject_max_length = 50 (CLI wins) +$ commit-check --message --subject-max-length=50 ``` -## Options Table Description +The limit applied is 50 — the flag beats the environment, which beats the file. +Nothing warns about the values that lost, which is worth remembering when a +setting in the file appears to have no effect. + +## Every option + +Types are as TOML understands them. A default shown as `""` means the option is +empty, not that the check is off — read the description, because for the two +pattern options an empty value means "use the built-in pattern". | Section | Option | Type | Default | Description | |---|---|---|---|---| diff --git a/docs/migration.md b/docs/migration.md index 45d57ff..94f1696 100644 --- a/docs/migration.md +++ b/docs/migration.md @@ -1,53 +1,22 @@ -# Migration Guide +# Migrating from v1 -This guide helps you migrate from commit-check v1.x (YAML configuration) to v2.0+ (TOML configuration). +Version 2.0 replaced the YAML configuration with TOML. The change is mechanical +— nothing about what Commit Check validates went away — but the two formats +express policy very differently, and there is no automatic conversion. -## Overview +In v1 a config file was a list of checks, each carrying its own regex, error +message and suggestion. You wrote the pattern; Commit Check ran it. In v2 the +patterns are built in and the file selects and tunes them by name. A v1 file +was mostly regex; a v2 file is mostly booleans. -Version 2.0 introduces significant changes to commit-check: +The practical consequence: you do not translate a v1 file line by line. You +decide which rules you want and write those down, which is usually far shorter. -* **Configuration format**: `.commit-check.yml` → `cchk.toml` or `commit-check.toml` -* **Simplified architecture**: New validation engine with cleaner design -* **Enhanced functionality**: Better error messages and more flexible configuration options +## Converting the file -## Quick Migration Steps +Take a representative v1 config: -1. **Backup your existing configuration**: - -```bash -cp .commit-check.yml .commit-check.yml.backup -``` - -2. **Create new TOML configuration**: - -```bash -touch cchk.toml # or commit-check.toml -``` - -3. **Convert YAML to TOML format** (see examples below) - -4. **Test the new configuration**: - -```bash -commit-check --help -commit-check --message --branch --author-name --author-email --dry-run -``` - -5. **Remove old YAML file**: - -```bash -rm .commit-check.yml.backup # after confirming everything works -``` - -## Configuration Format Changes - -The configuration structure has changed from YAML to TOML format - -### YAML (v1.x) vs TOML (v2.0+) - -**Old YAML format** (`.commit-check.yml`): - -```yaml +```yaml title=".commit-check.yml (v1)" checks: - check: message regex: '^(build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test){1}(\([\w\-\.]+\))?(!)?: ([\w ])+([\s\S]*)|(Merge).*|(fixup!.*)' @@ -64,7 +33,7 @@ checks: suggest: run command `git checkout -b type/branch_name` - check: author_name - regex: ^[A-Za-zÀ-ÖØ-öø-ÿ\u0100-\u017F\u0180-\u024F ,.\'-]+$|.*(\[bot]) + regex: ^[A-Za-zÀ-ÖØ-öø-ÿĀ-ſƀ-ɏ ,.\'-]+$|.*(\[bot]) error: The committer name seems invalid suggest: run command `git config user.name "Your Name"` @@ -82,108 +51,110 @@ checks: regex: main # it can be master, develop, devel etc based on your project. error: Current branch is not rebased onto target branch suggest: Please ensure your branch is rebased with the target branch - -- check: imperative - regex: '' # Not used for imperative mood check - error: 'Commit message should use imperative mood (e.g., "Add feature" not "Added feature")' - suggest: 'Use imperative mood in commit message like "Add", "Fix", "Update", "Remove"' ``` -**New TOML format** (`cchk.toml` or `commit-check.toml`): +Every one of those checks still exists. Named rather than spelled out, the +whole file becomes: -```toml +```toml title="cchk.toml (v2)" [commit] -# https://www.conventionalcommits.org conventional_commits = true -subject_capitalized = false -subject_imperative = true -subject_max_length = 80 -subject_min_length = 5 -allow_commit_types = ["feat", "fix", "docs", "style", "refactor", "test", "chore", "ci"] -allow_merge_commits = true -allow_revert_commits = true -allow_empty_commits = false -allow_fixup_commits = true -allow_wip_commits = false -require_body = false -require_signed_off_by = false -ignore_authors = ["dependabot[bot]", "copilot[bot]"] +require_signed_off_by = true [branch] -# https://conventionalbranch.org conventional_branch = true -allow_branch_types = ["feature", "bugfix", "hotfix", "release", "chore", "feat", "fix"] require_rebase_target = "main" ``` -### CLI Changes +The regexes are gone because they were restating the built-in behaviour. So are +`error` and `suggest`: Commit Check now supplies both, along with a stable rule +ID and a link to the rule's documentation. -The command-line interface has been simplified: +### What each v1 check became -**Old CLI** (v1.x): +| v1 `check:` | v2 option | Rule | +|---|---|---| +| `message` | `[commit] conventional_commits` | [CC001](rules.md#cc001) | +| `branch` | `[branch] conventional_branch` | [CC201](rules.md#cc201) | +| `author_name` | `[commit] author_name_pattern` | [CC101](rules.md#cc101) | +| `author_email` | `[commit] author_email_pattern` | [CC102](rules.md#cc102) | +| `commit_signoff` | `[commit] require_signed_off_by` | [CC012](rules.md#cc012) | +| `merge_base` | `[branch] require_rebase_target` | [CC202](rules.md#cc202) | +| `imperative` | `[commit] subject_imperative` | [CC003](rules.md#cc003) | -```bash -commit-check --config .commit-check.yml -``` +The two `*_pattern` options are the exception to "no more regexes": they exist +so an author policy stricter than the default stays expressible. + +### Keeping a custom message format -**New CLI** (v2.0+): +If the v1 `regex` enforced something that is not Conventional Commits — JIRA +smart commits, say — that is what `message_pattern` is for. Set it and it +replaces the generated Conventional Commits pattern entirely: -```bash -commit-check --config cchk.toml # or commit-check.toml -# Or use defaults (no config file needed) -commit-check --message --branch +```toml title="cchk.toml" +[commit] +message_pattern = "^PROJ-\\d+: .+" ``` -### Custom Regex (`message_pattern`) +## Converting the command line -If you relied on the custom `regex` field in v1.x to enforce a non-Conventional-Commits -format (e.g. JIRA smart commits `PROJ-123: description`), use the `message_pattern` -option in the `[commit]` section: +Only the config flag changed, and only because the file did: -```toml -[commit] -message_pattern = "^PROJ-\\d+: .+" +```console +$ commit-check --config .commit-check.yml # v1 +$ commit-check --config cchk.toml # v2 ``` -When `message_pattern` is set (non-empty), it replaces the auto-generated Conventional -Commits regex entirely, giving you full control over the accepted message format. +A config file is now optional. With none, the defaults apply immediately: -## Troubleshooting +```console +$ commit-check --message --branch +``` -### Common Issues +## Doing the migration -**Issue**: "Configuration file not found" +1. Keep the old file until you are done: -**Solution**: Ensure your file is named `cchk.toml` or `commit-check.toml` and placed in the repository root or in the `.github` folder. + ```console + $ cp .commit-check.yml .commit-check.yml.backup + ``` -**Issue**: "Invalid TOML syntax" +2. Write `cchk.toml` — in the repository root or in `.github/`. Use the table + above rather than translating regexes. -**Solution**: Use a TOML validator or check the syntax. Common issues include: +3. Check it against real commits without failing anything, which is what + `--dry-run` is for: -* Missing quotes around strings -* Incorrect boolean values (use `true`/`false`, not `True`/`False`) -* Invalid array syntax + ```console + $ commit-check --message --branch --author-name --author-email --dry-run + ``` -**Issue**: "Validation rules not working as expected" +4. Try a message that should fail, so you know the policy is doing something: -**Solution**: Check the [Configuration Documentation](configuration.md) for the correct option names and formats. + ```console + $ echo "nonsense" | commit-check --message + ``` -### Validation and Testing + A passing run on a message that ought to fail usually means the config file + was not found — see + [where the config file lives](configuration.md#where-the-config-file-lives). -After migration, test your configuration: +5. Delete `.commit-check.yml` and the backup. -```bash -# Test commit message validation -echo "feat: test commit message" | commit-check --message +## If something does not work -# Test branch validation -commit-check --branch +**The config file is not found.** It has to be named `cchk.toml` or +`commit-check.toml`, in the repository root or `.github/`. Any other name needs +`--config`. -# Test with dry-run flag -commit-check --message --branch --author-name --author-email --dry-run -``` +**TOML fails to parse.** Usually unquoted strings, `True` instead of `true`, or +a trailing comma in an array. Editors validate the file against the published +schema — see +[where the config file lives](configuration.md#where-the-config-file-lives). -## Getting Help +**A rule does not behave as expected.** Check the option name and its default in +[every option](configuration.md#every-option); the `allow_*` options in +particular describe what is *permitted*, so `false` is the strict setting. -* **Documentation**: Check the [Configuration Guide](configuration.md) -* **Issues**: Report problems on [GitHub Issues](https://github.com/commit-check/commit-check/issues) +**Something else.** [Open an issue](https://github.com/commit-check/commit-check/issues) +— include the output of `commit-check --message --format json`. diff --git a/docs/troubleshoot.md b/docs/troubleshoot.md index 6eacc8d..c639185 100644 --- a/docs/troubleshoot.md +++ b/docs/troubleshoot.md @@ -1,8 +1,14 @@ # Troubleshooting -## How to Skip Author Name Check +## A check fails and you need the commit through anyway -In some cases, Commit Check may fail due to an invalid `author_name`, as shown below: +Every check can be bypassed. Doing so is sometimes the right call — you are +mid-rebase, or the rule is wrong and fixing it properly can wait — but the +bypass is the second thing to reach for. The first is reading what failed: +every diagnostic names the rule, quotes the value that failed it, and links to +the page explaining it. + +Take an author name check that rejects a one-character name: ```shell check committer name.....................................................Failed @@ -17,25 +23,86 @@ Suggest: git config user.name 'Your Name' Docs: https://commit-check.com/rules/#cc101 ``` -To fix it, you can either update your Git config or temporarily skip the check using one of the following methods. +`12` is the value Git actually recorded as the author — usually a sign that +`user.name` was never set on this machine, or was set by a script. The fix is +the one the `Suggest:` line gives: + +```shell +git config user.name "Your Name" +git commit --amend --reset-author --no-edit +``` + +### Skipping one hook + +When the check is genuinely wrong for a single commit, skip that hook by ID and +leave the rest running. `SKIP` is a +[pre-commit](https://pre-commit.com/#temporarily-disabling-hooks) feature, so it +takes the hook's `id`, not the rule ID: + +```shell +SKIP=check-author-name git commit --amend --no-edit +``` + +The IDs are `check-message`, `check-branch`, `check-author-name`, +`check-author-email` and `check-no-force-push`. + +### Skipping every hook + +`--no-verify` bypasses the whole pre-commit run — Commit Check and everything +else you have configured: + +```shell +git commit --amend --no-edit --no-verify +``` + +!!! warning "A local bypass is not a CI bypass" + + `SKIP` and `--no-verify` only affect the hooks on your machine. If the same + policy runs in CI — through the + [GitHub Action](guides/integrations.md#in-github-actions), say — it will + check the commit again when you push, and reject it there. To exempt a + commit everywhere, change the policy rather than the invocation: turn the + rule off in `cchk.toml`, or add the author to + [`ignore_authors`](configuration.md#every-option) if the exemption is + permanent. + +## A rule fires that you never turned on -### Bypass All Hooks +Commit Check is not silent by default. With no config file at all it still +enforces Conventional Commits, Conventional Branch, subject length limits and +the author name and email patterns. The *Default* column in the +[rules reference](rules.md#rule-index) shows which rules those are. -Use the `--no-verify` flag to skip the pre-commit hook: +To see what a given repository is actually enforcing, check which config file +it picked up — the search order is in +[where the config file lives](configuration.md#where-the-config-file-lives), and +`--config` overrides it: ```shell -# Amend the commit without running hooks -git commit --amend --author="Xianpeng Shen " --no-edit --no-verify +commit-check --config cchk.toml --message ``` -### Bypass A Specific Hook +## Nothing is checked at all -Alternatively, use the `SKIP=your-hook-name` environment variable, like below: +A check only runs when its own flag is passed. `commit-check --message` never +evaluates branch rules, and `commit-check --branch` never reads the commit +message — so a hook wired up with the wrong flag passes silently, forever. ```shell -# Set the correct Git author name -git config user.name "Xianpeng Shen" +commit-check --message --branch --author-name --author-email +``` + +Each rule in the [rules reference](rules.md) lists the flag that activates it. + +## Something else -# Force amend while skipping the specified hook -SKIP=check-author-name git commit --amend --author="Xianpeng Shen " --no-edit +If the failure does not match anything above, the JSON output shows exactly +what was evaluated and why, which is usually enough to see where the config +disagrees with the expectation: + +```shell +commit-check --message --format json ``` + +Failing that, [open an issue](https://github.com/commit-check/commit-check/issues) +with that output attached. From cc8f20a62d89ab370058f11fa4950954cb00e4ed Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 5 Aug 2026 12:57:28 +0000 Subject: [PATCH 6/6] fix: separate rule defaults from rule activation, and name the migration gaps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three corrections from review, all of them things the rewrite stated too loosely. The defaults note claimed a no-file run "still enforces" a list of rules without saying that a check only runs when its own flag is passed, while troubleshoot said the opposite in isolation. Both now say which flag brings which defaults. The same note claimed every allow_* restriction is off by default, which is wrong for the two that matter most: allow_commit_types and allow_branch_types are allow-lists enforcing from the start. The six that really do permit everything are named instead of generalised. An empty default was described as always meaning "use the built-in pattern". It means three different things — message_pattern leaves conventional_commits to generate the pattern, author_name_pattern falls back to the built-in one, and require_rebase_target genuinely disables its check — so each is spelled out, and the table cell that said "" (disabled) now says "" (no custom pattern). The migration example dropped two types silently: v1 accepted `revert` as a commit type and `task` as a branch type, and neither is in the v2 defaults. That is the same footgun this branch fixed on the configuration page, so it gets the same treatment — the gap is named, with the lists to restore it and a note that setting either option replaces the default rather than extending it. Verified against get_default_config(). Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01U9zFxq8V4qxG4aMzJhGBFn --- docs/configuration.md | 43 +++++++++++++++++++++++++++++++------------ docs/migration.md | 31 +++++++++++++++++++++++++++++++ docs/troubleshoot.md | 12 ++++++++---- 3 files changed, 70 insertions(+), 16 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index 694f44a..fe891bf 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -14,15 +14,27 @@ flag overrides both for a single run. !!! tip "Defaults are not "nothing"" - With no configuration file at all, Commit Check still enforces Conventional - Commits ([CC001](rules.md#cc001)), Conventional Branch - ([CC201](rules.md#cc201)), subject lengths of 5–80 characters - ([CC004](rules.md#cc004), [CC005](rules.md#cc005)) and the author name and - email patterns ([CC101](rules.md#cc101), [CC102](rules.md#cc102)). - - Off by default: subject capitalization, imperative mood, required body and - signoff, rebase requirements, and every `allow_*` restriction. The *Default* - column in the [rules reference](rules.md#rule-index) is the full picture. + Two different things decide whether a rule fires: whether you asked for + that check at all, and what the option defaults to. A check only runs when + its flag is passed — `--message` never evaluates branch rules — but once a + check is running, these apply with no configuration file present: + + | Check | Enforced by default | + |---|---| + | `--message` | Conventional Commits ([CC001](rules.md#cc001)), subject lengths of 5–80 characters ([CC004](rules.md#cc004), [CC005](rules.md#cc005)), and an allow-list of ten commit types | + | `--branch` | Conventional Branch ([CC201](rules.md#cc201)) and an allow-list of twenty-one branch types | + | `--author-name` / `--author-email` | The built-in name and email patterns ([CC101](rules.md#cc101), [CC102](rules.md#cc102)) | + + Off until you turn them on: subject capitalization, imperative mood, + required body and signoff, and rebase requirements. + + The `allow_*` options are a mix, so read them individually rather than + assuming: `allow_commit_types` and `allow_branch_types` are allow-lists + that restrict from the start, while `allow_merge_commits`, + `allow_revert_commits`, `allow_empty_commits`, `allow_fixup_commits`, + `allow_wip_commits` and `allow_force_push` all default to permitting + everything. The *Default* column in the + [rules reference](rules.md#rule-index) is the full picture. ## Where the config file lives @@ -226,13 +238,20 @@ setting in the file appears to have no effect. ## Every option Types are as TOML understands them. A default shown as `""` means the option is -empty, not that the check is off — read the description, because for the two -pattern options an empty value means "use the built-in pattern". +unset, which is never the same as the check being off — but it does not mean +the same thing twice, so read the description rather than the cell: + +- `message_pattern` unset leaves `conventional_commits` to generate the + pattern. [CC001](rules.md#cc001) still runs. +- `author_name_pattern` unset falls back to the built-in name pattern. + [CC101](rules.md#cc101) still runs. +- `require_rebase_target` unset is the one case where the check really does not + run — there is no branch to compare against. | Section | Option | Type | Default | Description | |---|---|---|---|---| | commit | conventional_commits | bool | true | Enforce Conventional Commits specification. | -| commit | message_pattern | str | "" (disabled) | Custom regex pattern for commit message validation. When set, this pattern replaces the auto-generated Conventional Commits regex entirely, making it possible to enforce custom formats such as JIRA smart commits (e.g., `"^PROJ-\\d+: .+"`). When `message_pattern` is set (non-empty) it takes precedence over `conventional_commits`. | +| commit | message_pattern | str | "" (no custom pattern) | Custom regex pattern for commit message validation. When set, this pattern replaces the auto-generated Conventional Commits regex entirely, making it possible to enforce custom formats such as JIRA smart commits (e.g., `"^PROJ-\\d+: .+"`). When `message_pattern` is set (non-empty) it takes precedence over `conventional_commits`. | | commit | subject_capitalized | bool | false | Subject must start with a capital letter. | | commit | subject_imperative | bool | false | Subject must be in imperative mood. Forms of verbs can be found at [imperatives.py](https://github.com/commit-check/commit-check/blob/main/commit_check/imperatives.py) | | commit | subject_max_length | int | 80 | Maximum length of the subject line. | diff --git a/docs/migration.md b/docs/migration.md index 94f1696..9d23ac3 100644 --- a/docs/migration.md +++ b/docs/migration.md @@ -70,6 +70,37 @@ The regexes are gone because they were restating the built-in behaviour. So are `error` and `suggest`: Commit Check now supplies both, along with a stable rule ID and a link to the rule's documentation. +!!! warning "Check your type lists before deleting the old file" + + "Restating the built-in behaviour" is nearly true, not exactly true, and + the gap is silent — the shorter file above accepts slightly less than the + v1 one it replaces. Two types in that v1 example are missing from the v2 + defaults: + + - `revert` as a commit type. `revert: drop the cache` passed under v1 and + is rejected by the default `allow_commit_types`. (Unrelated to + `allow_revert_commits`, which governs Git's own `Revert "..."` commits + and is on by default.) + - `task` as a branch type. `task/CC-42` passed under v1 and is rejected by + the default `allow_branch_types`. + + Keep them by naming the list you want, remembering that setting either + option replaces the default rather than adding to it: + + ```toml + [commit] + allow_commit_types = ["build", "chore", "ci", "docs", "feat", "fix", + "perf", "refactor", "revert", "style", "test"] + + [branch] + allow_branch_types = ["bugfix", "chore", "feature", "hotfix", "release", "task"] + ``` + + Compare your own v1 regex against + [every option](configuration.md#every-option) before deleting it — this is + the one part of the migration that fails quietly, months later, on a commit + that used to be fine. + ### What each v1 check became | v1 `check:` | v2 option | Rule | diff --git a/docs/troubleshoot.md b/docs/troubleshoot.md index c639185..5143083 100644 --- a/docs/troubleshoot.md +++ b/docs/troubleshoot.md @@ -68,10 +68,14 @@ git commit --amend --no-edit --no-verify ## A rule fires that you never turned on -Commit Check is not silent by default. With no config file at all it still -enforces Conventional Commits, Conventional Branch, subject length limits and -the author name and email patterns. The *Default* column in the -[rules reference](rules.md#rule-index) shows which rules those are. +Commit Check is not silent by default. Whichever checks you asked for run with +their defaults already applied, even with no config file present: `--message` +enforces Conventional Commits, the 5–80 character subject limits and an +allow-list of ten commit types; `--branch` enforces Conventional Branch and an +allow-list of twenty-one branch types; `--author-name` and `--author-email` +apply the built-in patterns. The *Default* column in the +[rules reference](rules.md#rule-index) shows every rule's starting state, and +the [configuration page](configuration.md) spells the split out. To see what a given repository is actually enforcing, check which config file it picked up — the search order is in