Companion to the ### Commit cadence & message format rule in template/CLAUDE.md. The inline section gives the headline. This file holds the spec, the cadence rationale, and the bypass surface.
Commit early, commit often. Don't sit on 20+ minutes of edits in a dirty worktree. Split the work into the smallest logical chunks and commit each as soon as it's a coherent unit:
- Passing tests
- No half-finished functions
- A working state for the next collaborator to pick up
Past incident: a 90-minute session ended with 11 uncommitted file changes spanning three unrelated refactors. Restoring intent took an hour of git diff reading. Two small commits would have kept the story legible.
Pairs with Don't leave the worktree dirty and Smallest chunks, land ASAP. Cadence is the input; dirty worktree is what happens when cadence slips; small-chunks is the post-commit shape.
Every commit message follows the spec at https://www.conventionalcommits.org/en/v1.0.0/. The headline form is:
<type>[optional scope][!]: <description>
[optional body]
[optional footer(s)]
Where:
type(required, lowercase), one of:feat: new featurefix: bug fixchore: maintenance, deps, toolingdocs: documentation onlystyle: formatting, whitespace, no semantic changerefactor: internal restructure, no behavior changeperf: performance improvementtest: test-only changebuild: build system / packagingci: CI configurationrevert: undoes a prior commit
[scope](optional): a parenthesized noun describing the affected area (e.g.(parser),(extension),(lib),(hooks))[!](optional): flags a breaking change. Eitherfeat!: ...orfeat(api)!: .... AddingBREAKING CHANGE:in the footer is also acceptable but!is preferred.:(required): colon + space, separates the header from the description<description>(required): non-empty, lowercase-leading, short imperative summary
feat(parser): add ability to parse arraysfix: array parsing issue when multiple spaceschore!: drop support for Node 14refactor(api)!: drop legacy /v1 routesdocs(claude.md): document commit cadenceci: bump actions/checkout pin
update stuff: no typefeat:: empty descriptionFEAT: parser: uppercase typefeature(parser): X:featurenot in the allowed type listfeat parser: X: missing colonWIP/fix typo/more changes: no type, vague description
The fleet forbids AI-attribution markers in commit messages, PR
descriptions, and inline review replies. The patterns blocked by
commit-message-format-guard and reminded by commit-pr-reminder:
Generated with Claude/Generated with Anthropic(any case)Co-Authored-By: Claude/Co-Authored-By:Claude- 🤖 robot-emoji tag lines
<noreply@anthropic.com>footer references
The rule applies at draft time too. Rewrite the message to omit the strings before you run git commit.
Per the fleet's Hook bypasses require the canonical phrase rule
(Allow <X> bypass verbatim in a recent user turn):
Allow commit-format bypass: for format/type issues. Use when the commit message diverges from the spec on purpose (rare; usually the user is bringing in a fixup or an external patch with a pre-existing message).Allow ai-attribution bypass: for the AI-attribution check specifically. Use when a commit legitimately documents the forbidden strings (e.g. a CLAUDE.md edit that quotes them as examples, a test fixture, or a release note explaining why they're forbidden).- Env var
SOCKET_COMMIT_MESSAGE_FORMAT_GUARD_DISABLED=1: full disable for testing.
- When adding commits to an OPEN PR, update the PR title + description to match the new scope:
gh pr edit <num> --title … --body …. The reviewer should know what's in the PR without scrolling commits. - Replying to Cursor Bugbot: reply on the inline review-comment thread, not as a detached PR comment:
gh api repos/{owner}/{repo}/pulls/{pr}/comments/{comment_id}/replies -X POST -f body=…. - Backing out an unpushed commit: prefer
git reset --soft HEAD~1(orgit rebase -i HEAD~N) overgit revert. Revert commits are for changes already on origin; for local-only commits they just pollute history (enforced by.claude/hooks/fleet/prefer-rebase-over-revert-guard/). - No empty commits. Never use
git commit --allow-empty,git cherry-pick --allow-empty, or--keep-redundant-commits. Anchor releases on the actual version-bump commit + move the tag forward withgit tag -f vX.Y.Zinstead. Empty commits pollutegit logand break CHANGELOG generators /git log -p/ blame. Bypass:Allow empty-commit bypass(enforced by.claude/hooks/fleet/no-empty-commit-guard/). - Commit author: every commit must use the user's canonical GitHub identity, not a work email or substituted name. Canonical lives in
~/.claude/git-authors.json(or global git config);aliases[]are also accepted (enforced by.claude/hooks/fleet/commit-author-guard/). - Scan-internal labels stay out of commits:
B1/M9/H3/L4codes from/fleet:scanning-quality//fleet:scanning-securityreports are scaffolding. Inline the finding text in the commit body instead. Bypass:Allow scan-label-in-commit bypass(enforced by.claude/hooks/fleet/scan-label-in-commit-guard/). - Push policy: push, fall back to PR. Default to
git push origin <branch>(typicallymain). On rejection: open a PR viagh pr createagainst the default base. Don't pre-open PRs "to be safe"; don't force-push to recover. Reminder fires whengh pr createis invoked without an explicit user directive (enforced by.claude/hooks/fleet/pr-vs-push-default-reminder/). Enterprise-ruleset push rejections are unblocked via the repo'stemporarily-doesnt-touch-customerscustom property (canSkipReviewGate()inscripts/_shared/repo-properties.mts); Stop-time reminder surfaces this when the error pattern fires (enforced by.claude/hooks/fleet/enterprise-push-property-reminder/). Full rationale:docs/claude.md/fleet/push-policy.md.
Defense in depth:
- Edit-time draft:
commit-pr-reminderStop hook flags AI attribution in assistant prose. Catches the issue before the command is run. - Commit-time gate:
commit-message-format-guardPreToolUse hook parsesgit commit -m/--messageand blocks on type, format, or AI-attribution failure. The last line of defense before history carries the bad message.
Two surfaces by design. A draft can sneak past the Stop hook because it only sees the most recent assistant turn. The PreToolUse gate sees every command at commit time.