What happens
scripts/check_pr_issue_batching.py exists because the batching rule "was documented in CLAUDE.md and checked by nothing" (.github/workflows/pr-issue-validation.yml). Its stated intent: "This is not a ban on single-issue PRs; it requires the author to say why one stands alone."
It does not require that in practice. check() returns early:
issues = referenced_issues(body)
if len(issues) >= 2:
return True, f"Batched: references {len(issues)} issues (...)."
and _REFERENCE counts refs and part of alongside the closing keywords:
r"(?:resolves|closes|fixes|refs|references|part of)\s+..."
So a PR with Closes #A plus Refs #B scores as two issues and is reported as "Batched", with no rationale ever required. That is not an edge case: linking the umbrella is exactly what ~/.claude/docs/issue-decomposition.md tells every child PR to do, so the common, correct shape silently exempts itself from the gate.
Evidence
Why it matters
The gate reads as enforcing something it does not, which is worse than an absent check: a reviewer seeing "Check same-scope batching ✓" reasonably concludes the author justified standing alone. Guards must distinguish satisfied from not examined.
Options
- Count only the closing keywords (
resolves|closes|fixes) toward "batched" — a Refs/part of link is context, not a second delivered issue. Most precise, and matches what "batched" means everywhere else in the process (Closes #A, #B).
- Keep counting them, but require the rationale whenever a PR closes exactly one issue, regardless of how many it references.
Both fixes are in one function. (1) also makes the check's own message truthful: #16788 is not "batched", and saying so is the defect.
Acceptance criteria
Found during
Cross-checking my own PRs after autobot-ai-01 mentioned adding a rationale line that mine had passed without.
What happens
scripts/check_pr_issue_batching.pyexists because the batching rule "was documented in CLAUDE.md and checked by nothing" (.github/workflows/pr-issue-validation.yml). Its stated intent: "This is not a ban on single-issue PRs; it requires the author to say why one stands alone."It does not require that in practice.
check()returns early:and
_REFERENCEcountsrefsandpart ofalongside the closing keywords:So a PR with
Closes #AplusRefs #Bscores as two issues and is reported as "Batched", with no rationale ever required. That is not an edge case: linking the umbrella is exactly what~/.claude/docs/issue-decomposition.mdtells every child PR to do, so the common, correct shape silently exempts itself from the gate.Evidence
Closes #16775,Refs #16772— the umbrella).Check same-scope batchingreports SUCCESS.Closes #16770,Refs #16777) and feat(documents): verify ZIP-based office and ODF formats by content (#16773) #16783 (Closes #16773,Refs #16772), have the same shape.Single-issue rationale:line when they passed. I have since added one to each by hand — the gate did not ask.Why it matters
The gate reads as enforcing something it does not, which is worse than an absent check: a reviewer seeing "Check same-scope batching ✓" reasonably concludes the author justified standing alone. Guards must distinguish satisfied from not examined.
Options
resolves|closes|fixes) toward "batched" — aRefs/part oflink is context, not a second delivered issue. Most precise, and matches what "batched" means everywhere else in the process (Closes #A, #B).Both fixes are in one function. (1) also makes the check's own message truthful: #16788 is not "batched", and saying so is the defect.
Acceptance criteria
Single-issue rationale:line.Closes #A, #B) still passes with no rationale.Found during
Cross-checking my own PRs after autobot-ai-01 mentioned adding a rationale line that mine had passed without.