| name | branch-and-commit |
|---|---|
| description | Use when starting work — branch naming, Conventional Commits, issue linkage. |
| category | delivery |
Branch names and commit messages are the mechanical link between an issue and the
code that closes it. Getting the format right keeps git log, the changelog, and
gh queries all machine-readable — no archaeology required later.
-
Never commit directly to
main. Branch first, always:<type>/<issue#>-<slug>(e.g.feat/42-label-sync). The type mirrors the Conventional Commit type. -
Commit messages follow Conventional Commits:
<type>: <description>, English, imperative mood, subject line ≤72 chars. -
Pick the type deliberately — release-please reads it directly into the changelog:
Type Meaning Changelog / version impact featNew capability Minor bump, "Features" section fixBug fix Patch bump, "Bug Fixes" section perfPerformance improvement Patch bump refactorRestructure, no behavior change Patch bump docsDocumentation only No version bump choreMaintenance, tooling No version bump ciCI/CD workflow changes No version bump testTest-only changes No version bump -
Breaking changes append
!after the type (feat!:) or add aBREAKING CHANGE:footer — either triggers a major bump. Use whichever is more visible for the change; don't rely on prose in the body alone. -
One logical change per commit. A commit that mixes a
fixand an unrelatedrefactorforces release-please to miscategorize part of the diff no matter which type you pick — split it instead. -
Squash merge is the merge strategy for this repo: the PR title, not any individual commit message, becomes the commit on
main. Individual commits on the branch can be loose checkpoints; the PR title is what must be a clean Conventional Commit (seepr-authoring).
# Branch from an issue
git checkout -b feat/42-label-sync
# Conventional commit, imperative, scoped
git commit -m "feat: add label sync phase to bootstrap"
# Breaking change, explicit footer
git commit -m "feat!: drop support for legacy label schema" \
-m "BREAKING CHANGE: type:* labels renamed; run scripts/bootstrap.sh to resync"
# Verify branch naming before pushing
git branch --show-current # expect <type>/<issue#>-<slug>- Typing
fix:for what is actually afeat:(or vice versa) — this silently miscategorizes the changelog entry; release-please trusts the type literally. - Committing straight to
main"because it's a one-liner" — there is no exception; every change goes through a branch and a PR. - Cramming unrelated changes into one commit because splitting feels slower — it costs more later when the changelog entry doesn't match what actually shipped.
- Using past tense or a period at the end of the subject ("Added label sync.") instead of imperative, no trailing period ("add label sync").
- Forgetting the issue number in the branch slug — it's the cheapest possible link between branch and issue and costs nothing to include.
CONTRIBUTING.md— branch and commit conventions`docs/adr/ADR-0002-release-flow.md`— why commit type discipline matters for release-please`skills/issue-writing/SKILL.md`— the issue a branch should trace back to`skills/pr-authoring/SKILL.md`— turning a branch into a mergeable PR