Skip to content

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 ## Why paragraph. git log becomes 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

  1. Staged / proposed diffgit diff --staged, or the contents of the impending change if not yet staged.
  2. 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.
  3. Repo commit-style detection — does the repo enforce Conventional Commits?
  4. pyproject.toml has [tool.commitizen] → Conventional Commits expected.
  5. .cz.toml / .cz.json / cz.yaml → Conventional Commits expected.
  6. commitlint.config.js / .commitlintrc* → usually Conventional Commits.
  7. 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 log and 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 add strategy, 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.