Driver
CLAUDE.md and .claude/rules/workflow-gates.md mandate one ticket per PR. The PR title validator already enforces a single ticket reference. The PR body is not checked — a title with one ticket can coexist with Closes #1 Closes #2 Closes #3 in the body, and the latter is what GitHub acts on at merge time (auto-close). This loophole defeats the rule's purpose.
Scope
Add a PR-body check to validate-pr-create.sh:
-
Scan the body for the GitHub closing-keyword patterns, case-insensitive, with #NNN: close #N, closes #N, closed #N, fix #N, fixes #N, fixed #N, resolve #N, resolves #N, resolved #N.
-
Count distinct issue numbers referenced.
-
If count > 1: exit 2 with a message listing the referenced issues and explaining the rule.
-
Configurable override for teams that explicitly want multi-ticket PRs:
# .claude/project-config.json
pr:
allow_multiple_closes: false # default
-
Skip marker: <!-- multi-close: approved --> for the rare legitimate umbrella PR (e.g. a rollback PR closing a set of tickets together).
Acceptance Criteria
Risks / Dependencies
- Some teams legitimately batch rollbacks or revert multiple tickets in one PR. Mitigation: opt-in config + skip marker.
- Keyword regex needs to be conservative — don't match
Closes #1-related code or fixes inside code blocks. Mitigation: strip code blocks before scanning; require whitespace or line-start before the keyword.
Glossary
| Term |
Definition |
| Closing keyword |
A GitHub-recognised phrase (Closes, Fixes, Resolves, tensed variants) that auto-closes the referenced issue when the PR merges. |
| Umbrella PR |
A PR that legitimately maps to multiple tickets — typically a rollback, a dependency bump touching several tracking tickets, or a cross-cutting rename. |
Driver
CLAUDE.mdand.claude/rules/workflow-gates.mdmandate one ticket per PR. The PR title validator already enforces a single ticket reference. The PR body is not checked — a title with one ticket can coexist withCloses #1 Closes #2 Closes #3in the body, and the latter is what GitHub acts on at merge time (auto-close). This loophole defeats the rule's purpose.Scope
Add a PR-body check to
validate-pr-create.sh:Scan the body for the GitHub closing-keyword patterns, case-insensitive, with
#NNN:close #N,closes #N,closed #N,fix #N,fixes #N,fixed #N,resolve #N,resolves #N,resolved #N.Count distinct issue numbers referenced.
If count > 1: exit 2 with a message listing the referenced issues and explaining the rule.
Configurable override for teams that explicitly want multi-ticket PRs:
Skip marker:
<!-- multi-close: approved -->for the rare legitimate umbrella PR (e.g. a rollback PR closing a set of tickets together).Acceptance Criteria
Closes #1 Closes #2is blocked.Closes #1(exactly one) passes.#2without a closing keyword does NOT count (it's a cross-reference, not an auto-close).allow_multiple_closes: truedisables the check for teams that opt in..claude/hooks/tests/cover: one close, two closes, cross-ref without keyword, skip marker, opt-in config.Risks / Dependencies
Closes #1-related codeorfixesinside code blocks. Mitigation: strip code blocks before scanning; require whitespace or line-start before the keyword.Glossary
Closes,Fixes,Resolves, tensed variants) that auto-closes the referenced issue when the PR merges.