Skip to content

commit-msg hook should reject a subject with no issue reference #17029

Description

@mrveiss

The installed `commit-msg` hook (`tools/git-hooks/commit-msg`, installed via `scripts/install-git-hooks.sh` into every worktree's shared hooks dir) currently only strips co-author trailers. Nothing enforces `CLAUDE.md`'s "commit subject includes the issue reference" rule at commit time — it is unenforced anywhere right now (not CI, not a hook).

Verified by reading the installed hook content directly (not inferred from `.pre-commit-config.yaml`, which is not what's wired to this hook slot — see #16923/#16938 for the same class of gap on the pre-commit side).

Scope

  • Add a subject-format check (`grep -qE '#[0-9]{3,}'`, matching `scripts/lint-conventions.sh --commit-msg`'s existing logic used elsewhere) to `tools/git-hooks/commit-msg`
  • Keep the existing trailer-stripping behavior unchanged
  • Update `scripts/install-git-hooks.sh` doc-comment noting the new behavior (no logic change needed there, per fix(ci): chain formatter dispatch after the branch guard in pre-commit (#16923) #16938's precedent)

Discovered while correcting an incorrect claim in PR #17026.

Activity

  1. added this to the v0.9.0 milestone on Sep 18, 2026
  2. github-actions commented on Sep 18, 2026

    @github-actions
    Contributor

    👋 Auto-triage could not confidently classify this issue (no confident signal). Could a maintainer add the appropriate labels (frontend, backend, infrastructure, docs, testing and good-first-issue, intermediate, advanced)?

  3. mrveiss commented on Sep 19, 2026

    @mrveiss
    OwnerAuthor

    Post-merge AC verification (vehicle #17047 → f921ee335, PR #17032)

    Verified directly against git show f921ee335:<path> — not inferred from the PR body.

    • Add a subject-format check to tools/git-hooks/commit-msg — implemented not as a duplicated grep but by calling the one existing implementation of the rule: tools/git-hooks/commit-msg (lines ~40-50) runs bash "$lint" --commit-msg "$msg_abs" where lint="$PWD/scripts/lint-conventions.sh", so it cannot drift from lint-commit-subject's own logic. File is tracked and executable (100755). Covered by repo_tests/commit_msg_hook_17029_test.py:57 test_a_conventional_subject_with_an_issue_reference_passes and :66 test_a_subject_off_the_convention_is_rejected.
    • Keep the existing trailer-stripping behaviour unchanged — trailer stripping (Co-authored-by, Claude Code promo line, Claude-Session:) present at the top of the hook, byte-for-byte the same three grep -viE pipeline. repo_tests/commit_msg_hook_17029_test.py:77 test_trailers_are_still_stripped.
    • Update scripts/install-git-hooks.sh doc-comment noting the new behaviour — scripts/install-git-hooks.sh:56: "commit-msg (commit-msg hook should reject a subject with no issue reference #17029) strips co-author trailers and [enforces the subject convention]"; MANAGED_HOOKS="pre-commit pre-push commit-msg" at line 59 (the one logic change needed to actually install it, beyond the doc comment). repo_tests/git_hooks_installer_test.py:38: _MANAGED = ("pre-commit", "pre-push", "commit-msg") — the installer's existing test suite now exercises commit-msg too.

    Additional coverage beyond the stated scope, found during review: the lint script's own exemptions (Merge, Revert, fixup!, squash!, worktree claims) apply through the hook (repo_tests/commit_msg_hook_17029_test.py:73 test_the_lint_scripts_own_exemptions_apply); a checkout without the lint script (an old branch) warns but is not blocked (:88 test_a_checkout_without_the_lint_script_is_not_blocked); and a real git commit with the hook installed is refused then accepted (:95 test_a_real_commit_is_refused_then_accepted).

    All ACs met. Verified against the actual merged head f921ee335 (PR #17047, a multi-PR vehicle), post the ledger's recorded approval — confirming the vehicle's update-branch churn did not drop or corrupt this fix. Leaving closed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions