Commit conventions
rolter uses Conventional Commits for commit messages and PR titles. CI checks PR titles; the conventional-pre-commit hook managed by prek checks local messages.
Format
<type>(<scope>): <subject>
<body>
<footer>
- type (required):
feat,fix,perf,refactor,docs,test,build,ci,chore,revert - scope (recommended):
gateway,balancer,proxy,core,store,auth,control,ui,docs,infra,ci,deps,release - subject: imperative, lowercase, ≤ 72 chars, no trailing period
- breaking change: add
!after the scope and aBREAKING CHANGE:footer
Examples
feat(balancer): add precise kv-event cache-aware scorer
fix(gateway): stream anthropic sse without buffering
perf(proxy): reuse pooled client per egress proxy
docs(architecture): document reload-free config propagation
refactor(core)!: rename ModelRoute.targets to upstreams
BREAKING CHANGE: config field `targets` is now `upstreams`.
What BREAKING CHANGE: means after 1.0
Before 1.0 the trailer meant “something moved”, a Rust API included. From 1.0 it means one thing, decided in ADR-0032: a change to a promised surface that ships without a completed deprecation window. All crates share one version, so a single trailer takes the whole product — binaries, wheel and Docker tag — to the next major. It is expensive on purpose, and it needs a written migration note in the release.
It does not apply to:
- a removal that completed its deprecation window (two minor releases and 90
days for
/api/v1/*and for config keys). The window was the notice; the removal ships as an ordinaryfeat/refactorwhose release notes name it; - a Rust API change in any crate — they are internal and carry no promise;
- rolter tracking a change OpenAI or Anthropic made to their own dialect;
- a subsystem carrying the
experimentalstability marker, which is the documented exemption.
The rule behind it: if the change could have been deprecated first, it must be, and then it is not breaking.
Issues & PRs
- Link issues from the body/footer:
Closes #123,Refs #123. - PR title must be a single valid Conventional Commit line (enforced by CI via
amannn/action-semantic-pull-request). - Squash-merge so the PR title becomes the commit on
master; keeps history releasable and changelog-friendly. - Never put a coding agent’s session or remote-connection URL in a commit
message, a PR body, or anywhere else — once merged, a commit message can
only be corrected with a history rewrite.
scripts/check-agent-session-urls.shenforces this as theno-agent-session-urlsprek hook (commit-msgstage) and, in CI, in two places: theno agent session urls (commits)step ofquality.yml’sstatic checksjob re-checks every commit the PR introduces, so a--no-verifypush or an amend is still caught, and a step of theci-okjob inci.ymlchecks the PR body as the API returns it, not as the event payload froze it (plus the commit range on a dispatched or merge-queue run). The body half has to live inci.ymlbecause a body edit takes theeditedfast path, which skipsquality.ymlentirely — a body check inside that workflow could never see the edit that introduced a URL. See #1406, #2035 andci-gating.md.
Tooling
.config/commitlint.config.mjs— rules (types, scopes, lowercase subject, 72-char header).prek.toml— fast commit-time hygiene, secret scanning, formatting/linting, commit-message validation, and pre-push test/security gates.- Install all configured hook stages with
prek install --prepare-hooks. The configuration installspre-commit,commit-msg, andpre-pushshims. - Run commit-time checks manually with
prek run --all-files. - Run the push gate manually with
prek run --all-files --hook-stage pre-push. - The Rust fmt, clippy, test and
cargo-denyhooks skip a commit or push that touches no Rust input; see When the hooks skip Rust.
The commit stage uses prek’s built-in checks plus pinned Gitleaks and
Conventional Commit hooks. Project-specific checks require actionlint,
taplo, and typos on PATH. The push stage also requires cargo-deny and
Bun when UI files are part of the push. Install cargo-nextest for CI-equivalent
test execution; the hook falls back to cargo test when it is unavailable.