Skip to content

[Chore] Enforce single Closes-keyword per PR body (one ticket per PR) #114

@atlas-apex

Description

@atlas-apex

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:

  1. 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.

  2. Count distinct issue numbers referenced.

  3. If count > 1: exit 2 with a message listing the referenced issues and explaining the rule.

  4. Configurable override for teams that explicitly want multi-ticket PRs:

    # .claude/project-config.json
    pr:
      allow_multiple_closes: false   # default
  5. Skip marker: <!-- multi-close: approved --> for the rare legitimate umbrella PR (e.g. a rollback PR closing a set of tickets together).

Acceptance Criteria

  • A PR body with Closes #1 Closes #2 is blocked.
  • A PR body with Closes #1 (exactly one) passes.
  • A PR body referencing #2 without a closing keyword does NOT count (it's a cross-reference, not an auto-close).
  • allow_multiple_closes: true disables the check for teams that opt in.
  • Skip marker passes with a warning.
  • Fixtures in .claude/hooks/tests/ cover: one close, two closes, cross-ref without keyword, skip marker, opt-in config.

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Medium — plan-worthy, not urgentenhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions