fix(#52): reject closed issue refs in PR and commit hooks#53
Merged
Conversation
- validate-pr-create.sh: also capture issue state. Block PR when the referenced issue is CLOSED (in addition to the existing "does not exist" block). Blocking message explains the three common causes (follow-up, auto-closed premature, typo) and what to do instead. - verify-commit-refs.sh: same fetch, but CLOSED is WARN (stderr, exit 0) rather than BLOCK. A commit may legitimately reference the closed issue it just finished — the PR-level hook is the right place to enforce "every PR needs its own OPEN ticket". Prevents incidents like the one described in the upstream issue: #52 Upstream issue: #52
me2resh
approved these changes
Apr 14, 2026
atlas-apex
commented
Apr 14, 2026
atlas-apex
left a comment
Collaborator
Author
There was a problem hiding this comment.
Code Review: PR #53
Commit: 2960b4dc9d803dbe62350170df2d627b240c04ec
Summary
Tightens the ticket-vocabulary backstop hooks so that PR titles referencing a CLOSED issue are blocked, while commit-level references to CLOSED issues are warned (not blocked). Closes the observability gap revealed by me2resh/curios-dog#218.
Checklist Results
- Architecture & Design: Pass
- Code Quality: Pass
- Testing: Pass (manual smoke tests documented in PR body; no harness exists for hooks)
- Security: Pass (no new shellouts beyond
gh/jq, both already used) - Performance: Pass (still one
gh issue viewper ref, same as before) - PR Description & Glossary: Pass — Glossary is thorough (tracker repo, cross-fork PR,
--json number,state, backstop hook, GitHub linking keywords, WARN vs BLOCK) - Technical Decisions (AgDR):N/A — incremental tightening of an existing rule, no new pattern/library/architecture choice
Verification
bash -nclean on both scripts- Live
gh issue view --json number,stateagainstme2resh/apexstack: missing issue → empty[](correctly triggers BLOCK as missing); CLOSED issue (#49) →{"number":49,"state":"CLOSED"}(correctly triggers CLOSED branch) - The OPEN path is unchanged — the new
ISSUE_STATEcheck only fires on= "CLOSED", so any other state (OPEN, or missing-state edge case) falls through unaffected - WARN-vs-BLOCK split between the two hooks matches the stated rationale and is well-commented inline
Findings
None blocking.
Suggestions (non-blocking, follow-ups)
- Cross-repo issue refs from forks — file as a separate ticket. The commit-level hook on a fork can't reference upstream issues by
#N. Workaround (full URL in body) is fine; longer-term, consider letting.claude/project-config.jsondeclare anupstream_tracker_repothat the hook also checks. - Hook test harness — file as a separate ticket. Two hooks now have non-trivial branching logic; a tiny
bats(or pure-bash) harness with mockedghwould let us regression-test the missing/CLOSED/OPEN matrix. - Note:
gh issue view <N>resolves PRs too (issues and PRs share the numbering namespace). Pre-existing behavior, not introduced by this PR — flagging only because it means a PR-number typo in a title can theoretically pass the existence check. Out of scope here.
Verdict
APPROVED (posted as comment — cannot self-approve as PR author)
Reviewed by Rex (Code Reviewer Agent)
Reviewed commit: 2960b4dc9d803dbe62350170df2d627b240c04ec
This was referenced Apr 14, 2026
osama-abu-baker
pushed a commit
to osama-abu-baker/apexyard
that referenced
this pull request
Jun 3, 2026
- validate-pr-create.sh: also capture issue state. Block PR when the referenced issue is CLOSED (in addition to the existing "does not exist" block). Blocking message explains the three common causes (follow-up, auto-closed premature, typo) and what to do instead. - verify-commit-refs.sh: same fetch, but CLOSED is WARN (stderr, exit 0) rather than BLOCK. A commit may legitimately reference the closed issue it just finished — the PR-level hook is the right place to enforce "every PR needs its own OPEN ticket". Prevents incidents like the one described in the upstream issue: me2resh#52 Upstream issue: me2resh#52 Co-authored-by: me2resh <ahmed.abdelaliem@gmail.com>
mosta7il
pushed a commit
to mosta7il/apexyard
that referenced
this pull request
Jun 8, 2026
- validate-pr-create.sh: also capture issue state. Block PR when the referenced issue is CLOSED (in addition to the existing "does not exist" block). Blocking message explains the three common causes (follow-up, auto-closed premature, typo) and what to do instead. - verify-commit-refs.sh: same fetch, but CLOSED is WARN (stderr, exit 0) rather than BLOCK. A commit may legitimately reference the closed issue it just finished — the PR-level hook is the right place to enforce "every PR needs its own OPEN ticket". Prevents incidents like the one described in the upstream issue: me2resh#52 Upstream issue: me2resh#52 Co-authored-by: me2resh <ahmed.abdelaliem@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
validate-pr-create.sh: in addition to rejecting PRs that reference a non-existent issue, now also reject PRs that reference a closed issue. Every PR needs its own OPEN ticket.verify-commit-refs.sh: same fetch on the commit-level hook, but CLOSED is warn (stderr, exit 0), not block. A commit may legitimately reference the issue it just closed (reverts, follow-ups); the PR-level check is the right place to enforce the "open ticket" rule.Why
PR
me2resh/curios-dog#218was created with titlefix(#210): ...but#210was already closed. The old hook passed becausegh issue view #210still resolves successfully for closed issues — it just didn't check state. The CEO caught it by eye, but the hook is supposed to be the backstop for exactly this failure mode, so it's closing an observability gap in the governance layer.Full incident + reproduction context lives on the upstream issue:
#52
Changes
.claude/hooks/validate-pr-create.sh— one call becomes one call that captures both number and state:The new block message lists the three common root causes (follow-up, premature auto-close, typo) and tells the author what to do — create a new open ticket, reopen, or fix the number.
.claude/hooks/verify-commit-refs.sh— tracks missing and closed separately. Missing still blocks, closed warns:Testing
Self-tested on the ops fork:
#99999→ blocks (existing behavior preserved)bash -nsyntax check passes on both scriptsNote on testing: the commit message of this very PR could not use
Closes #52because the commit-level hook (on this ops-repo fork) detectsme2resh/opsas the tracker viagit remote get-url origin, but issue#52lives onme2resh/apexstack(upstream). The hook treats that as "issue does not exist in tracker" and blocks. Workaround here: cite the upstream issue by URL in the commit body. A follow-up ticket is worth filing on upstream to let contributors from forks reference upstream issues via full URL syntax — but that's out of scope for this fix.Glossary
.claude/project-config.jsonor fromgit remote get-url origin.me2resh/ops(a fork of apexstack), but the PR targetsme2resh/apexstack:main. Standard OSS contribution pattern.--json number,stategh issue viewcall so the hook can distinguish "does not exist" (empty response) from "exists but CLOSED" (non-empty, state=CLOSED). Cheaper than two separate HTTP calls.Closes #N/Refs #N/Fixes #N/Resolves #NCloses/Fixes/Resolves.exit 2blocks the tool use (commit refused); anything written to stderr withexit 0is a warning that the user sees but the action proceeds. Chosen per-hook based on how often a false positive would be disruptive.