Skip to content

Commit cf16a87

Browse files
docs(contributing): add agent-facing playbooks and validation guide
Adds docs/contributing/agents/ with a router index, a validation guide (targeted-test map + CI failure recipes), and five task playbooks for recurring contributions: add a version provider, add a changelog format, add or modify a CLI flag, deprecate a public API, and update snapshots/screenshots. Aggressively deduplicates against existing human docs (contributing.md, contributing_tldr.md, pull_request.md, command pages) by linking rather than restating. Adds one-line `For AI agents'' callouts in contributing.md and pull_request.md so human contributors discover the parallel track. Hooks the new pages into mkdocs nav under a new `For AI Agents'' section and into the mkdocs-llmstxt plugin so they appear in llms-full.txt. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 197ca17 commit cf16a87

10 files changed

Lines changed: 835 additions & 0 deletions

File tree

docs/contributing/agents/index.md

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# For AI Agents
2+
3+
These pages are written for AI agents contributing to Commitizen. Human
4+
contributors may also find them useful as a quick reference. They
5+
**complement** the existing human-facing contributor docs rather than
6+
replace them — anything covered by the human docs is linked, not restated.
7+
8+
> If you are an AI agent looking to **use** Commitizen as a tool (validate
9+
> commit messages, bump versions in a downstream project), see the skill
10+
> definition at `.agents/skills/commitizen/SKILL.md` in the repo root.
11+
> The pages here are for working **on** Commitizen itself.
12+
13+
## Source-of-truth map
14+
15+
When two documents could host a piece of guidance, this table is the
16+
tie-breaker. Agent pages that drift from it should be fixed, not the
17+
human pages.
18+
19+
| Topic | Lives in | Why |
20+
|---|---|---|
21+
| Setup, dev workflow, PR lifecycle, labels | [Contributing](../contributing.md) | Same for humans and agents |
22+
| poe command reference | [Contributing TL;DR](../contributing_tldr.md) | One canonical cheat sheet |
23+
| PR etiquette, AI-assisted policy | [Pull Request Guidelines](../pull_request.md) and [PR template](https://github.com/commitizen-tools/commitizen/blob/master/.github/pull_request_template.md) | One canonical policy |
24+
| Codebase topology and extension points | [Architecture Overview](../architecture.md) | Useful to humans too |
25+
| Always-loaded agent rules | [`AGENTS.md`](https://github.com/commitizen-tools/commitizen/blob/master/AGENTS.md) | Auto-loaded as system context every session |
26+
| Targeted test selectors and CI failure recipes | [Validation Guide](validation.md) | Agent-specific |
27+
| Recipes for recurring task types | [Playbooks](#playbooks) | Agent-specific |
28+
29+
## When to read what
30+
31+
| You want to... | Read |
32+
|---|---|
33+
| Set up a local dev environment | [Contributing](../contributing.md#prerequisites-setup) |
34+
| Look up a poe command | [Contributing TL;DR](../contributing_tldr.md#command-cheat-sheet) |
35+
| Understand the codebase layout and extension points | [Architecture Overview](../architecture.md) |
36+
| Open a pull request | [Pull Request Guidelines](../pull_request.md) and the [PR template](https://github.com/commitizen-tools/commitizen/blob/master/.github/pull_request_template.md) |
37+
| Pick the right test selector for a change | [Validation Guide](validation.md#targeted-test-map) |
38+
| Recover from a CI failure | [Validation Guide](validation.md#ci-failure-recipes) |
39+
| Implement a recurring task type | [Playbooks](#playbooks) |
40+
41+
The repo-root [`AGENTS.md`](https://github.com/commitizen-tools/commitizen/blob/master/AGENTS.md)
42+
is the auto-loaded entry point for most agent tools. It holds the rules an
43+
agent needs in every session; this page is the deeper reference.
44+
45+
## Agent-specific deltas
46+
47+
Humans absorb these rules through review; agents need them stated:
48+
49+
1. **Complete the PR template fully**, including the AI-disclosure checkbox
50+
and the `Generated-by:` trailer. The maintainers re-run the commands you
51+
list under "Steps to Test This Pull Request" — make them exact.
52+
2. **`uv run poe all` is the pre-push verification command** named in the
53+
PR template. `poe ci` is the CI-equivalent runner (uses `prek` and does
54+
not auto-format); run it too if you want to mirror CI exactly. See the
55+
[Validation Guide](validation.md#choosing-a-final-check) for the
56+
distinction.
57+
3. **Do not touch generated artifacts.** See the do-not-touch list in
58+
[`AGENTS.md`](https://github.com/commitizen-tools/commitizen/blob/master/AGENTS.md).
59+
4. **Prefer targeted test selectors during iteration** — see the
60+
[targeted-test map](validation.md#targeted-test-map). The full suite
61+
is fine for a final pre-push run.
62+
63+
## Playbooks
64+
65+
Recipes for recurring task types. Each playbook is self-contained: trigger,
66+
files to read first, ordered steps, verification commands, and known
67+
pitfalls. They link out to the human-facing concept docs rather than
68+
restating concepts.
69+
70+
- [Add a version provider](playbooks/add-version-provider.md)
71+
- [Add a changelog format](playbooks/add-changelog-format.md)
72+
- [Add or modify a CLI flag](playbooks/add-cli-flag.md)
73+
- [Deprecate a public API](playbooks/deprecate-public-api.md)
74+
- [Update generated snapshots and screenshots](playbooks/update-snapshots.md)
75+
76+
If your task does not match a playbook, fall back to the general flow:
77+
78+
1. Read the [Architecture Overview](../architecture.md) for the relevant
79+
subsystem.
80+
2. Read 1–2 existing examples in the same directory to match local
81+
conventions.
82+
3. Make the change, plus tests, plus user-facing docs.
83+
4. Iterate with targeted tests; finish with `uv run poe all`.
84+
5. Open the PR using the template; check the AI-disclosure box.
85+
86+
## Updating these pages
87+
88+
Treat these pages like any other code change: open a PR, follow the
89+
template, run `uv run poe doc:build` to verify the mkdocs build, and
90+
check internal links for breakage. If you find yourself restating
91+
something that already lives in a human-facing doc, link to it instead
92+
and shorten the agent doc.
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
# Playbook: Add a Changelog Format
2+
3+
A changelog format handles parsing and rendering a `CHANGELOG.<ext>` file
4+
in a specific markup language. End-user documentation:
5+
[Changelog command](../../../commands/changelog.md). Built-ins are
6+
`markdown` (default), `asciidoc`, `textile`, `restructuredtext`.
7+
8+
Architectural context:
9+
[Architecture § Extension points](../../architecture.md#extension-points).
10+
11+
## Trigger
12+
13+
- "Support `<markup>` changelogs."
14+
- A user wants `cz changelog` to emit something other than Markdown.
15+
- An incremental-changelog use case fails because the user's existing
16+
`CHANGELOG` file is not Markdown.
17+
18+
## Read first
19+
20+
- `commitizen/changelog_formats/__init__.py``ChangelogFormat` Protocol,
21+
entry-point group `commitizen.changelog_format`, `KNOWN_CHANGELOG_FORMATS`
22+
registry, `_guess_changelog_format` extension-based fallback.
23+
- `commitizen/changelog_formats/base.py:BaseFormat` — abstract
24+
implementation; you only need to override `parse_version_from_title` and
25+
`parse_title_level`.
26+
- A close-match existing format:
27+
- Heading-prefix-based: `commitizen/changelog_formats/markdown.py`
28+
(uses `#`, `##` prefixes).
29+
- Underline-based: `commitizen/changelog_formats/restructuredtext.py`
30+
(uses `===`, `---` lines).
31+
- `commitizen/templates/` — Jinja2 templates named `CHANGELOG.<ext>.j2`
32+
control rendering.
33+
- `tests/test_changelog_format_<name>.py` — every format has parity tests;
34+
copy the closest one.
35+
36+
## Steps
37+
38+
1. **Create the format module** at
39+
`commitizen/changelog_formats/<name>.py`. Subclass `BaseFormat`. Set
40+
the class attributes:
41+
- `extension: ClassVar[str]` — primary file extension (no dot).
42+
- `alternative_extensions: ClassVar[set[str]]` — other accepted
43+
extensions for the same format.
44+
2. **Implement two methods**:
45+
- `parse_version_from_title(line: str) -> VersionTag | None` — given
46+
one line, return a `VersionTag` if the line is a release heading.
47+
- `parse_title_level(line: str) -> int | None` — return the heading
48+
level (1, 2, 3, ...) if the line is a heading.
49+
The base class `BaseFormat.get_metadata_from_file` walks the file once
50+
and calls both methods per line.
51+
3. **Add the Jinja2 template** at
52+
`commitizen/templates/CHANGELOG.<ext>.j2`. Mirror the structure of
53+
`CHANGELOG.md.j2` — same blocks, different markup. Make sure the
54+
loops over `tree`, `entries`, and `change_type` match.
55+
4. **Register the built-in** in `pyproject.toml` under
56+
`[project.entry-points."commitizen.changelog_format"]`:
57+
58+
```toml
59+
<name> = "commitizen.changelog_formats.<name>:<Name>"
60+
```
61+
62+
5. **Add tests** at `tests/test_changelog_format_<name>.py`. Copy the
63+
closest existing test file and adapt the fixtures.
64+
6. **Update the cross-format suite** `tests/test_changelog_formats.py`
65+
if it parametrizes over all formats — add the new one to its lists.
66+
7. **Update user docs** at `docs/commands/changelog.md` and
67+
`docs/customization/changelog_template.md` — list the new format and
68+
show how to opt in via `changelog_format`.
69+
8. **Re-run the install** so the entry point registers:
70+
71+
```bash
72+
uv sync --frozen --group base --group test --group linters
73+
```
74+
75+
## Validate
76+
77+
```bash
78+
uv run pytest tests/test_changelog_format_<name>.py tests/test_changelog_formats.py tests/test_changelog.py tests/test_incremental_build.py -n auto
79+
uv run poe lint
80+
uv run poe doc:build # if docs changed
81+
uv run poe all # final pre-push
82+
```
83+
84+
## Pitfalls
85+
86+
- **`KNOWN_CHANGELOG_FORMATS` is populated at import time** from entry
87+
points, so you must re-run `uv sync` after editing `pyproject.toml`
88+
before tests can see your new format.
89+
- **Forgetting `alternative_extensions`**`_guess_changelog_format`
90+
uses both `extension` and `alternative_extensions` when the user does
91+
not set `changelog_format` explicitly. If a user has
92+
`CHANGELOG.<alt-ext>`, your format will not auto-detect without it.
93+
- **Template encoding** — Jinja2 reads templates with the active encoding;
94+
keep them ASCII-safe or test with non-UTF-8 `encoding` settings.
95+
- **Heading regex anchoring** — match the whole line (`^...$`) when the
96+
markup is line-anchored (Markdown headings); a substring match will
97+
pick up non-heading lines that mention `unreleased`.
98+
- **Snapshot updates** — many changelog tests use `pytest-regressions`.
99+
See the [update-snapshots playbook](update-snapshots.md) when output
100+
intentionally changes.
101+
102+
## Stop and ask if
103+
104+
- The target format requires structured metadata that does not fit the
105+
`parse_title_*` Protocol (e.g., front-matter in YAML).
106+
- The format implies a fundamentally different rendering tree (e.g., one
107+
file per release) — that is a bigger change than a format addition.
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
# Playbook: Add or Modify a CLI Flag
2+
3+
Commitizen's CLI is built declaratively with [decli](https://github.com/woile/decli)
4+
and `argparse` in `commitizen/cli.py`. Flags are dicts inside a `data["subcommands"]`
5+
list. End-user documentation: [Commands](../../../commands/init.md).
6+
7+
## Trigger
8+
9+
- "Add a `--<name>` flag to `cz <subcommand>`."
10+
- "Make `--<name>` configurable via the config file."
11+
- "Change the default of `--<flag>`."
12+
13+
## Read first
14+
15+
- `commitizen/cli.py` — the entire CLI schema. Search for the subcommand
16+
name in the `subcommands` block to find where its `arguments` list
17+
lives.
18+
- `commitizen/commands/<subcommand>.py` — the command class that receives
19+
the parsed arguments via `self.arguments`.
20+
- `commitizen/defaults.py:Settings` — TypedDict of all settings; required
21+
if your flag should also be config-file-readable.
22+
- `tests/test_cli.py` and `tests/test_cli/` — flag-parsing tests.
23+
- `tests/commands/test_<subcommand>_command.py` — behavior tests.
24+
- `docs/commands/<subcommand>.md` — user-facing reference for the
25+
subcommand.
26+
- `scripts/gen_cli_help_screenshots.py` — regenerates `--help` SVGs.
27+
28+
## Steps
29+
30+
1. **Add the flag** in `commitizen/cli.py` inside the relevant subcommand's
31+
`arguments` list. Follow the existing dict shape:
32+
33+
```python
34+
{
35+
"name": ["--my-flag", "-m"], # or just "--my-flag" if no short
36+
"action": "store_true", # or "store", "store_false", ParseKwargs, ...
37+
"default": False, # only when not store_true
38+
"help": "<one-line description, period at end>",
39+
}
40+
```
41+
42+
Look at neighboring flags in the same subcommand to match style
43+
(option grouping, help-text tone).
44+
2. **Consume the flag** in `commitizen/commands/<subcommand>.py`. It will
45+
arrive as `self.arguments["my_flag"]` (dashes become underscores).
46+
3. **Config-file support (optional)**. If the flag should also be settable
47+
in the user's config file:
48+
- Add the key to `commitizen/defaults.py:Settings` (and to
49+
`DEFAULT_SETTINGS` if there is a non-`None` default).
50+
- In the command, fall back to `self.config.settings["my_flag"]` when
51+
the CLI value is `None`.
52+
- Document the setting in the relevant `docs/config/<area>.md` page.
53+
4. **Add tests**:
54+
- CLI parsing: extend `tests/test_cli/` or `tests/test_cli.py` with a
55+
case that invokes `cz <subcommand> --my-flag` and asserts the
56+
parsed namespace.
57+
- Behavior: extend `tests/commands/test_<subcommand>_command.py`.
58+
5. **Update user docs** at `docs/commands/<subcommand>.md`. If the flag
59+
has a corresponding config setting, also update
60+
`docs/config/<area>.md`.
61+
6. **Regenerate the help SVGs** so the new flag appears in the rendered
62+
docs. See the [update-snapshots playbook](update-snapshots.md) for the
63+
`poe doc:screenshots` workflow.
64+
65+
## Validate
66+
67+
```bash
68+
uv run pytest tests/test_cli/ tests/test_cli.py tests/commands/test_<subcommand>_command.py -n auto
69+
uv run poe lint
70+
uv run poe doc:build
71+
uv run poe all # final pre-push
72+
```
73+
74+
## Pitfalls
75+
76+
- **Underscores vs dashes** — argparse converts `--my-flag` to
77+
`my_flag` in the namespace, but `decli` accepts both. Be consistent
78+
with neighboring flags.
79+
- **`store_true` with explicit `default`** — argparse uses `False` as the
80+
implicit default for `store_true`; do not set `default` unless you
81+
need `None` to detect "user did not pass the flag" (which matters for
82+
config-file fallback).
83+
- **Mutually exclusive flags** — argparse does not enforce mutual
84+
exclusion through the `decli` dict schema; validate in the command
85+
class and raise `commitizen.exceptions.InvalidCommandArgumentError`
86+
with a clear message.
87+
- **Forgetting the `Settings` TypedDict** when adding a config-file key
88+
`read_cfg` will accept the value but `mypy` will flag every read of
89+
`self.config.settings["my_flag"]`.
90+
- **Breaking flag removals** — see the
91+
[deprecate-public-api playbook](deprecate-public-api.md). A flag is
92+
user-facing surface; do not remove it without a deprecation window.
93+
- **Stale `--help` screenshots** — CI does not regenerate them. Run
94+
`uv run poe doc:screenshots` after any flag change and commit the
95+
result.
96+
97+
## Stop and ask if
98+
99+
- The flag would change the **exit code** of an existing success path —
100+
that breaks scripts that depend on exit codes. See
101+
[Exit Codes](../../../exit_codes.md).
102+
- The flag's behavior overlaps with an existing flag with subtly
103+
different semantics — propose a deprecation plan first.
104+
- The flag controls something that is currently determined by config
105+
precedence rules (CLI > env > config); make the precedence explicit
106+
in the issue.

0 commit comments

Comments
 (0)