feat(hooks): 4 new hooks for Opus 4.7 friction mitigation (v4.4.0)#2
Conversation
…itigation
H1 of 4 new hooks addressing 4.7 friction patterns (META-ANALYSIS audit-2).
Two-event hook:
- UserPromptSubmit: scan prompt for bug-evidence keywords (DE+EN), update
state with timestamp + source. No output.
- PreToolUse (Edit): if no recent bug-evidence in state (default: <10 min),
emit advisory; else silent pass. Never blocks.
Mitigates "false-positive bug invention" pattern observed in 4.7 audit
(+21% Wrong-Approach trend post-swap). Hook is structural — Code-enforced
verification gate before edits, not prompt-level guidance which 4.7's
higher confidence overrides.
Implementation:
- BUG_PATTERNS: 20 regex (DE+EN bug/error keywords, word-bounded)
- FAILURE_PATTERNS: 6 regex (test-failure markers in tool output)
- DoS-guard: truncate prompt to 100KB before regex (Judge B finding)
- Clock-skew defense: future timestamps not counted as recent (Judge A)
- State namespace: false_positive_guard.{last_evidence_seen_at,
last_evidence_source, evidence_total, advisories_emitted, last_advisory_at}
Tests: 47 passed (parametrized DE/EN keywords, time-window logic,
DoS-guard, subprocess integration, state persistence, edge cases).
Coverage: hook fully exercised; lib/state.py shared paths unchanged.
Code-Reviewers (judgment-day pattern): 2 blind judges
- Judge A (Pattern-Compliance): 7/9 → CONDITIONAL YES → fixed Issue 1
- Judge B (Security/Performance): NO → 3 critical issues identified
(path-traversal, race-condition, DoS). DoS fixed in-scope (input-cap).
Path-traversal + race-condition are lib/state.py-wide concerns;
filed as separate ERPNext follow-up tasks (out-of-scope for H1).
hooks.json: registered for UserPromptSubmit (matcher "") and
PreToolUse (matcher "Edit"). Total hook-commands: 13 → 15.
Refs: audit-2026-05-01/META-ANALYSIS.md (4.7-friction analysis)
Plan: ~/.claude/plans/jaundder-plan-hashed-tiger.md
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
H2 of 4 new hooks. PreToolUse Bash matcher; only fires on `git push` commands.
Reads cwd's .git/config, parses origin URL, classifies org:
- allow: AI-Engineering-at, LEEI1337, FoxLabs-ai → silent pass
- typo: AI-Engineerings-at → advisory with set-url remediation
- unknown: any other org → advisory (intentional fork? re-check)
- none: no origin / non-GitHub → silent pass
Default mode: advisory only (never blocks). Push works via redirect
for typo-org. Future config flag could enable strict block-mode after
a soak window.
Implementation:
- is_git_push_command: regex word-boundary, allows `git -C path push`
- parse_org_from_url: handles https:// and git@ URLs
- _read_origin_url: pure file-read (no subprocess), tolerates missing config
- State namespace: org_naming_pre_push.{push_count, violations_warned,
last_org, last_classification}
Tests: 40 passed (parametrized URL/org/command parsing, fake-repo
fixtures, edge cases).
hooks.json: registered alongside approach-guard in PreToolUse Bash matcher.
Total hook-commands: 15 → 16.
Refs: audit-2026-05-01/MASTER-FIX-EXECUTION.md (#7 Org-Naming-Drift)
Plan: ~/.claude/plans/jaundder-plan-hashed-tiger.md
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
H3 of 4 new hooks. SessionStart event. Iterates a configurable watch-list of repos and runs `git rev-list --count origin/<branch>..HEAD` (NO fetch — pure local) for each. Emits an advisory on Session-Start if any repo is ≥ warn threshold (5), critical (20). Watch-list resolution: 1. Env AHEAD_WARN_WATCH (comma-separated paths) — for testing/override 2. Default: phantom-ai, nomos, zeroth, Playbook01, wiki under ~/Documents Pure-local: no `git fetch`, no network. count_ahead() returns None when repo is invalid, has no origin, or git times out (5s per call). Multiple repo checks are sequential; total bound by 30s hook timeout in hooks.json. Implementation: - classify_severity: count → ok|warn|critical|unknown - count_ahead: subprocess git rev-list, defensive - _current_branch: subprocess git symbolic-ref - _resolve_watch_list: env-or-defaults - _build_advisory: formatted multi-repo output with severity icons Tests: 13 passed (severity classification, real-git fixture with clone+commits, env-override watch-list, edge cases). hooks.json: registered alongside session-start in SessionStart. Total hook-commands: 16 → 17. Refs: audit-2026-05-01/COMPREHENSIVE-AUDIT (nomos 97 unpushed pattern) Plan: ~/.claude/plans/jaundder-plan-hashed-tiger.md Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…igation H4 of 4 new hooks. SessionStart event. Scans configurable inboxes (default: ~/Downloads, ~/Documents/Downloads) for strategy/concept/decision files older than threshold. Emits advisory listing stale files with migration suggestion. Threshold: warn ≥7 days, critical ≥30 days. Capped at 20 files reported. Strategy-file patterns (filename, .md/.py/.yaml/.yml/.json only): Action_Plan* Compliance_* Lineage_* DEC-* M0* *_Concept_* Implementation: - is_strategy_file: pattern + extension whitelist - classify_age_days: ok/warn/critical - scan_inbox: per-inbox file walk + filter, returns list of findings - _resolve_inboxes: env WORKING_SET_INBOXES or defaults - _build_advisory: formatted output with severity icons + migration CTA Tests: 20 passed (filename patterns, age classification, real-fs fixtures with os.utime mtime control, edge cases). hooks.json: registered alongside ahead-of-remote-warning in SessionStart. Total hook-commands: 17 → 18. Refs: audit-2026-05-01/MASTER-FIX-EXECUTION (Action Plan v1.0 lived unversioned in Downloads/, migrated in Session A2) Plan: ~/.claude/plans/jaundder-plan-hashed-tiger.md Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ation - plugin.json: 4.3.0 → 4.4.0, description updated to reflect 16 hooks - CHANGELOG.md: full v4.4.0 entry with hook details + judge findings + lib/state.py follow-ups (path-traversal, race-condition) - CLAUDE.md: hook-count 12 → 16, list of 4 new hooks - README.md: hook-count 12 → 16 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 09f428ab8a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| for line in content.splitlines(): | ||
| stripped = line.strip() | ||
| if stripped.startswith("[") and stripped.endswith("]"): | ||
| in_origin = stripped == '[remote "origin"]' |
There was a problem hiding this comment.
Resolve the pushed remote instead of hardcoding origin
The hook always parses [remote "origin"] from .git/config, so git push upstream main (or any push to a non-origin remote) is classified against the wrong target. This can suppress warnings when the actual destination org is typo/unknown, or emit misleading warnings when origin differs from the pushed remote. Since git push accepts an explicit <repository> argument, the hook should derive and validate that destination instead of assuming origin.
Useful? React with 👍 / 👎.
| cwd = data.get("cwd", "") | ||
| origin_url = _read_origin_url(cwd) |
There was a problem hiding this comment.
Use
git -C target repo when reading remote config
is_git_push_command explicitly matches commands like git -C /some/path push, but main() always reads .git/config from data["cwd"]. In that case the hook inspects the wrong repository (or none), so org checks can be skipped or reported for the wrong repo. Parse and prefer the -C path from the command before falling back to cwd.
Useful? React with 👍 / 👎.
…eview #2) Codex re-review on PR #3 (commit eafebc1) APPROVED the file-lock + RMW fix but flagged one minor blind spot: get() returns the live reference, not a copy. Callers who mutate via reference (e.g. `qg = s.get("ns"); qg["k"] = v`) without a follow-up s.set(ns, qg) won't trigger dirty- namespace tracking, so the mutation is silently lost on save(). In practice this is mitigated because all existing hooks follow the established `set()-after-mutation` pattern (visible throughout test fixtures and hook code). But the contract was implicit. This commit makes it explicit in the docstring so future hook authors won't be surprised. No code change. Lint clean. 49 state tests still green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
Hooks Added
false-positive-guard.pyorg-naming-pre-push.pyahead-of-remote-warning.pyworking-set-watch.pyTest Plan
pytest tests/test_<hook>.py→ 120/120 passruff format --checkcleanruff checkclean (own code; pre-existing RUF100 in session-start.py and statusline_lib.py untouched)Code Reviewers (Pre-Commit Pattern)
2 blind judges via Agent-Team for H1:
lib/state.py(session_id not validated) — lib-wide, separately filedstate.save()— lib-wide, separately filedOut-of-Scope (Filed for Follow-up)
lib/state.py: session_id validation (path-traversal mitigation) — affects all 16 hooks, not introduced by this PRlib/state.py: file-locking or atomic-write for concurrentsave()— affects all 16 hooksReferences
Documents/audit-2026-05-01/(CODEBASE-ANALYSIS, ZUGANGSWEG-DEEP-DIVE, META-ANALYSIS, MASTER-FIX-EXECUTION)Documents/COMPREHENSIVE-AUDIT-2026-05-01.md~/.claude/plans/jaundder-plan-hashed-tiger.md🤖 Generated with Claude Code