Commit Message
Consumers: any task that drafts a commit message — manual git commit, git commit --amend on an unreleased local-branch commit, squash messages, branch-rewrite scripts, agent-drafted commits.
Why this skill exists
Commit messages are the durable log a future bisecting engineer reads when something breaks years later. The reviewer reads the PR description; the future maintainer reads the commit log. They serve different audiences, and the failure mode of drafting agents is to conflate them.
Common failures:
- Body explains "why the problem existed" or "what alternatives we considered" — that is PR-description content (motivation + discussion), not durable log.
- Subject is internal-perspective (
"Initialize Dag bundles in CLI get_dag function") instead of user-impact ("Fix airflow dags test command failure without serialized Dags"). - Body forgets to leave forward pointers (newsfragment filename, related PR #, tracking issue URL) — the next reader has to do a network round-trip to recover them.
- Body bloats to 5+ lines that duplicate the PR
## Whyparagraph.git logbecomes harder to scan, not easier.
This skill encodes the short, durable style. It pairs with github-title-description: the PR body carries motivation and test plan; the commit body carries durable facts + pointers.
Inputs
- Staged / proposed diff —
git diff --staged, or the contents of the impending change if not yet staged. - Existing commit context (when amending) —
git log <base>..HEAD --pretty=format:'%h %s%n%b' --no-merges. Match the existing subject style on amend; do not change the framing just because a polish round happened. - Repo commit-style detection — does the repo enforce Conventional Commits?
pyproject.tomlhas[tool.commitizen]→ Conventional Commits expected..cz.toml/.cz.json/cz.yaml→ Conventional Commits expected.commitlint.config.js/.commitlintrc*→ usually Conventional Commits.- None of the above → freeform.
Subject rules
- One line, ≤70 chars, user-impact framing — what changes for the user / operator / Dag author, not which files were touched.
- Match the branch's existing subject style on amend. If HEAD already uses
feat(scope):form, the amend keeps it; if HEAD is freeform, stay freeform. Do not introduce Conventional Commits prefixes where they were not already in use, and do not strip them from a repo that uses them. - Verb-first ("Add", "Fix", "Allow", "Reject", "Block") is more direct than noun-first.
- No file names in the subject —
"Refactor auth.py"is bad;"Reject empty emails at signup"is good.
Bad → Good
| Bad | Good |
|---|---|
update temporal.py and tests |
Support timezone in SDK temporal partition mappers |
Initialize Dag bundles in CLI get_dag function |
Fix airflow dags test failure without serialized Dags |
WIP fix |
Stop retry storm on 5xx from upstream |
address review comments |
Reject empty emails before hashing |
Body rules
The body is for the future bisecting engineer, not the current reviewer:
- ≤3 sentences total, or equivalent in compact bullets.
- Cover only:
- What changed at the user-impact level (not the file list — the diff is right there in
git show). - Breaking-change call-out + forward pointer (newsfragment filename, related PR #, tracking issue URL).
- Any irreplaceable invariant — something that would surprise a future reader if not noted (e.g., "depends on alembic head being at
", "callers must set X before calling Y"). - Cut: "the problem existed because…" narrative, motivation paragraphs, "why we picked approach X over Y", review-history context — all of that belongs in the PR description (covered by
github-title-description). - Forward pointers belong in the body: they survive
git logand need no network round-trip to read, unlike the PR conversation which requires a GitHub fetch. - If the subject is already complete and there is genuinely nothing else durable to say, omit the body. An empty body is better than a body of restated subject.
Body anti-patterns
| Anti-pattern | Why it's wrong |
|---|---|
Repeating the PR ## Why paragraph |
Duplicates content one click away in the PR; bloats git log; ages poorly |
| File list — "Refactors X, renames Y, extracts helper Z" | Internal-perspective; the diff already lists files |
| Decision narrative — "After discussion we chose A over B" | Belongs in PR review thread, not commit log |
| No body at all on a breaking change | The break + newsfragment pointer must be on the commit |
| Restating the subject as the body | Adds bytes without adding signal |
Amend vs new commit
When polishing review-driven changes on an unreleased local-branch commit, default to git commit --amend rather than stacking a "address review" follow-up commit. After amending, rewrite the subject + body in line with this skill — do not let the original "draft" message survive into the squash-merge just because nobody looked at it again.
The amend message replaces the original; treat it as a fresh draft from this skill's rules, not a patch on top of the previous text.
Output format
<subject — one line, ≤70 chars, user-impact>
<body — ≤3 sentences or compact bullets; forward pointers explicit; omit entirely if nothing durable to add>
Provide the raw text to the caller. Do not wrap in a code fence by default (the caller usually pipes it into git commit -F - or git commit --amend -F - and an outer fence breaks that). If the caller explicitly asks for a quoted form, fence it then.
What this skill does NOT cover
- The PR description / motivation / test plan — that is
github-title-description. - Choosing what to commit (
git addstrategy, hunk staging) — caller's job. - Running
git commit— this skill only drafts text. - Conventional Commits scope taxonomy — if the repo uses CC, the caller decides the scope from the diff; this skill only mirrors the detected style.
- Co-author attribution rules — those are policy decisions of the host repo / agent platform, not commit-style.
- Multi-commit history shaping (squash decisions, rebase plans) — separate concern.