feat(hooks): one-shot PR-merge warning per session+PR#19
Conversation
Adopt the pattern from anthropics/claude-code's security-guidance plugin so
the merge reminder fires once per logical unit of work (session + repo + PR)
instead of every single time. Before: the reminder fired on every merge
attempt, even immediately after acknowledging it — noisy and easy to ignore.
After: first attempt prints the reminder and blocks (exit 2); the user/Claude
sees it, confirms readiness, re-issues the command, and it proceeds silently.
Mechanism (matches the security-guidance reference implementation):
- Per-session state file at ~/.claude/git_guard_warnings_{session_id}.json
- session_id pulled from the hook's stdin JSON input
- Warning key: merge:<owner>/<repo>/<pr_num> — extracted from the command
(--repo flag or current repo via `gh repo view`; PR number from arg, or
"current" if invoked without one)
- First detection of a key: print reminder to stderr + sys.exit(2) (BLOCK).
Subsequent detections of the same key in the same session: silent allow.
- Periodic cleanup: 10% chance per invocation removes state files older than
30 days (matches the security plugin's housekeeping)
Behavior change for the reminder output: switched from `additionalContext`
(informational, never blocks) to stderr+exit-2 (BLOCK on first attempt).
This matches the reference and makes "confirm and retry" the explicit gate.
The on-main commit-block and direct-push-to-main blocks are unchanged —
those are absolute and shouldn't be dedupable.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Claude finished @tombakerjr's task in 1m 19s —— View job Reviewed commit: 4967c2e FIX: FIX: |
Summary
Adopt the dedup pattern from
anthropics/claude-code'ssecurity-guidanceplugin so the merge-readiness reminder fires once per logical unit of work (session + repo + PR) instead of every single time. Before: noisy and easy to start ignoring. After: see it once, confirm, retry — quiet on subsequent attempts.Mechanism
Mirrors
plugins/security-guidance/hooks/security_reminder_hook.py:~/.claude/git_guard_warnings_{session_id}.json, holding a set of warning keys already shown this session.session_idpulled from the hook's stdin JSON input.merge:<owner>/<repo>/<pr_num>— extracted from the command (--repoflag if present, else current repo viagh repo view; PR number from arg, or"current"if invoked without one).sys.exit(2)(BLOCK). Subsequent detections of the same key in the same session: silent allow.Behavior change
The reminder output switched from
additionalContext(informational, never blocks) to stderr + exit 2 (BLOCK on first attempt). This matches the reference implementation and makes "confirm and retry" the explicit gate.What's not changed
The on-main commit-block and direct-push-to-main blocks are unchanged — those are absolute, not dedupable.
Why
additionalContextreminders fire every single invocation. When Claude (or me) makes multiple PR-merge attempts in a session — across different PRs, or retrying after fixes — every single attempt printed the same long reminder, which trains the eye to ignore it. The block-then-allow pattern forces one explicit acknowledgment per real unit of work, then gets out of the way.Test plan
gh pr merge <num>invocation prints reminder and is blocked. Re-issue → succeeds silently.pr_num→ different key). Re-issue → succeeds.~/.claude/git_guard_warnings_<session>.jsonexists after first warning and contains the key.🤖 Generated with Claude Code