Add "RFC-Hook-Efficiency-Improvements"
371
RFC-Hook-Efficiency-Improvements.-.md
Normal file
371
RFC-Hook-Efficiency-Improvements.-.md
Normal file
@@ -0,0 +1,371 @@
|
|||||||
|
# RFC: Hook Efficiency Improvements
|
||||||
|
|
||||||
|
**Status:** Proposal
|
||||||
|
**Created:** 2026-01-28
|
||||||
|
**Author:** Claude Code Analysis
|
||||||
|
**Priority:** Medium
|
||||||
|
**Estimated Effort:** 2-3 sprints
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Executive Summary
|
||||||
|
|
||||||
|
Analysis of the 16 hooks across 12 plugins revealed significant inefficiencies in trigger timing, matcher precision, and redundant operations. This RFC proposes changes to reduce session startup latency, minimize per-edit overhead, and eliminate duplicate logic.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Current State
|
||||||
|
|
||||||
|
### Hook Inventory
|
||||||
|
|
||||||
|
| Plugin | Event | Matcher | Script | Lines |
|
||||||
|
|--------|-------|---------|--------|-------|
|
||||||
|
| projman | SessionStart | - | startup-check.sh | 119 |
|
||||||
|
| pr-review | SessionStart | - | startup-check.sh | 31 |
|
||||||
|
| claude-config-maintainer | SessionStart | - | enforce-rules.sh | 69 |
|
||||||
|
| data-platform | SessionStart | - | startup-check.sh | 55 |
|
||||||
|
| viz-platform | SessionStart | - | inline echo | 1 |
|
||||||
|
| cmdb-assistant | SessionStart | - | startup-check.sh | 67 |
|
||||||
|
| contract-validator | SessionStart | - | auto-validate.sh | 196 |
|
||||||
|
| doc-guardian | PostToolUse | `Write\|Edit\|MultiEdit` | notify.sh | 75 |
|
||||||
|
| code-sentinel | PreToolUse | `Write\|Edit\|MultiEdit` | security-check.sh | 63 |
|
||||||
|
| data-platform | PostToolUse | `Edit\|Write` | schema-diff-check.sh | 139 |
|
||||||
|
| contract-validator | PostToolUse | `Edit\|Write` | breaking-change-check.sh | 175 |
|
||||||
|
| project-hygiene | PostToolUse | `Write\|Edit` | cleanup.sh | 29 |
|
||||||
|
| cmdb-assistant | PreToolUse | NetBox MCP tools | validate-input.sh | 80 |
|
||||||
|
| git-flow | PreToolUse | `Bash` | branch-check.sh | 103 |
|
||||||
|
| git-flow | PreToolUse | `Bash` | commit-msg-check.sh | 75 |
|
||||||
|
| clarity-assist | UserPromptSubmit | - | vagueness-check.sh | 217 |
|
||||||
|
|
||||||
|
**Total:** 16 hooks, ~1,493 lines of shell code
|
||||||
|
|
||||||
|
### Performance Impact
|
||||||
|
|
||||||
|
**Per session startup:**
|
||||||
|
- 7 bash processes spawned
|
||||||
|
- 3 Python invocations
|
||||||
|
- 4 HTTP calls (Gitea API, NetBox API)
|
||||||
|
- Estimated 200-500ms added latency
|
||||||
|
|
||||||
|
**Per file edit:**
|
||||||
|
- 4 bash processes spawned (PostToolUse hooks)
|
||||||
|
- 1 bash process spawned (PreToolUse - code-sentinel)
|
||||||
|
- Multiple `find` and `git` commands
|
||||||
|
- Each hook independently parses JSON stdin
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Issues Identified
|
||||||
|
|
||||||
|
### Issue 1: SessionStart Hook Overload
|
||||||
|
|
||||||
|
**Problem:** 7 hooks run on every session, regardless of whether the user will use that plugin.
|
||||||
|
|
||||||
|
**Affected plugins:**
|
||||||
|
- projman
|
||||||
|
- pr-review
|
||||||
|
- claude-config-maintainer
|
||||||
|
- data-platform
|
||||||
|
- viz-platform
|
||||||
|
- cmdb-assistant
|
||||||
|
- contract-validator
|
||||||
|
|
||||||
|
**Specific concerns:**
|
||||||
|
- `projman` makes HTTP calls to Gitea API to check open issues
|
||||||
|
- `cmdb-assistant` makes HTTP calls to NetBox API
|
||||||
|
- `data-platform` spawns Python and attempts PostgreSQL connection
|
||||||
|
- `contract-validator` computes MD5 hashes of all plugin files
|
||||||
|
- `viz-platform` just echoes "loaded" - zero value
|
||||||
|
|
||||||
|
### Issue 2: Duplicate Logic Across Hooks
|
||||||
|
|
||||||
|
**Problem:** Multiple hooks implement the same checks.
|
||||||
|
|
||||||
|
| Check | Implemented by |
|
||||||
|
|-------|----------------|
|
||||||
|
| MCP venv exists | projman, pr-review, data-platform |
|
||||||
|
| Git remote vs .env mismatch | projman, pr-review |
|
||||||
|
| JSON stdin parsing | 8 different hooks |
|
||||||
|
| File path extraction from JSON | 8 different hooks |
|
||||||
|
|
||||||
|
### Issue 3: Overly Broad Matchers
|
||||||
|
|
||||||
|
**Problem:** Some hooks trigger far more often than needed.
|
||||||
|
|
||||||
|
| Hook | Matcher | Problem |
|
||||||
|
|------|---------|---------|
|
||||||
|
| git-flow/branch-check.sh | `Bash` | Triggers on every bash command |
|
||||||
|
| git-flow/commit-msg-check.sh | `Bash` | Triggers on every bash command |
|
||||||
|
| project-hygiene/cleanup.sh | `Write\|Edit` | Runs `find` on entire project every edit |
|
||||||
|
| data-platform/schema-diff-check.sh | `Edit\|Write` | Checks every file, then pattern-matches |
|
||||||
|
|
||||||
|
### Issue 4: clarity-assist Always-On
|
||||||
|
|
||||||
|
**Problem:** `vagueness-check.sh` (217 lines) runs on every user prompt.
|
||||||
|
|
||||||
|
- Parses prompt
|
||||||
|
- Runs multiple regex matches
|
||||||
|
- Uses `bc` for floating-point calculations
|
||||||
|
- Adds latency to every message
|
||||||
|
|
||||||
|
**Current behavior:** Opt-out (runs unless `CLARITY_ASSIST_AUTO_SUGGEST=false`)
|
||||||
|
**Recommended:** Opt-in (disabled unless explicitly enabled)
|
||||||
|
|
||||||
|
### Issue 5: No Shared Utilities
|
||||||
|
|
||||||
|
**Problem:** Each hook implements its own:
|
||||||
|
- JSON parsing
|
||||||
|
- File path extraction
|
||||||
|
- Prefix output formatting
|
||||||
|
- Early exit conditions
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Proposed Changes
|
||||||
|
|
||||||
|
### Change 1: Consolidate SessionStart into Single Dispatcher
|
||||||
|
|
||||||
|
**Create:** `scripts/hooks/session-dispatcher.sh`
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
# Single entry point for all SessionStart checks
|
||||||
|
# Runs common checks once, dispatches to plugins only when relevant
|
||||||
|
|
||||||
|
# Common checks (run once)
|
||||||
|
check_marketplace_health() { ... }
|
||||||
|
check_mcp_venvs() { ... }
|
||||||
|
check_git_remote() { ... }
|
||||||
|
|
||||||
|
# Plugin-specific checks (conditional)
|
||||||
|
if is_projman_project; then
|
||||||
|
source_plugin_check "projman"
|
||||||
|
fi
|
||||||
|
```
|
||||||
|
|
||||||
|
**Benefits:**
|
||||||
|
- Single process instead of 7
|
||||||
|
- Common checks run once
|
||||||
|
- Plugin checks only run if relevant
|
||||||
|
|
||||||
|
**Files to modify:**
|
||||||
|
- Create `scripts/hooks/session-dispatcher.sh`
|
||||||
|
- Create `scripts/hooks/lib/common.sh` (shared functions)
|
||||||
|
- Update all plugin `hooks.json` to remove SessionStart
|
||||||
|
- Add single SessionStart hook at marketplace level
|
||||||
|
|
||||||
|
### Change 2: Add Path-Based Filtering to PostToolUse Hooks
|
||||||
|
|
||||||
|
**Current:** Hooks spawn, parse stdin, check path, usually exit.
|
||||||
|
|
||||||
|
**Proposed:** Check path relevance before detailed processing.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# At top of each PostToolUse hook
|
||||||
|
FILE_PATH=$(echo "$INPUT" | grep -o '"file_path"[[:space:]]*:[[:space:]]*"[^"]*"' | ...)
|
||||||
|
|
||||||
|
# Immediate exit for irrelevant paths
|
||||||
|
case "$FILE_PATH" in
|
||||||
|
*/node_modules/*|*/.git/*|*/venv/*|*/__pycache__/*)
|
||||||
|
exit 0 ;;
|
||||||
|
esac
|
||||||
|
```
|
||||||
|
|
||||||
|
**Affected hooks:**
|
||||||
|
- doc-guardian/notify.sh
|
||||||
|
- data-platform/schema-diff-check.sh
|
||||||
|
- contract-validator/breaking-change-check.sh
|
||||||
|
- project-hygiene/cleanup.sh
|
||||||
|
|
||||||
|
### Change 3: Smarter git-flow Bash Hooks
|
||||||
|
|
||||||
|
**Current:** Both hooks trigger on every Bash command.
|
||||||
|
|
||||||
|
**Option A:** Early command check before full parsing
|
||||||
|
```bash
|
||||||
|
# Quick check - if not git command, exit immediately
|
||||||
|
if ! echo "$INPUT" | grep -q '"command".*git'; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
```
|
||||||
|
|
||||||
|
**Option B:** Combine into single hook
|
||||||
|
```bash
|
||||||
|
# Single git-flow hook that handles both branch and commit validation
|
||||||
|
```
|
||||||
|
|
||||||
|
**Files to modify:**
|
||||||
|
- `plugins/git-flow/hooks/branch-check.sh`
|
||||||
|
- `plugins/git-flow/hooks/commit-msg-check.sh`
|
||||||
|
- Optionally merge into `plugins/git-flow/hooks/git-validator.sh`
|
||||||
|
|
||||||
|
### Change 4: Remove viz-platform SessionStart Hook
|
||||||
|
|
||||||
|
**Current:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"event": "SessionStart",
|
||||||
|
"type": "command",
|
||||||
|
"command": "echo 'viz-platform plugin loaded'"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Proposed:** Delete entirely. Provides zero value.
|
||||||
|
|
||||||
|
**Files to modify:**
|
||||||
|
- `plugins/viz-platform/hooks/hooks.json` - remove the hook
|
||||||
|
|
||||||
|
### Change 5: Make clarity-assist Opt-In
|
||||||
|
|
||||||
|
**Current:** Enabled by default, check `CLARITY_ASSIST_AUTO_SUGGEST=false` to disable.
|
||||||
|
|
||||||
|
**Proposed:** Disabled by default, check `CLARITY_ASSIST_AUTO_SUGGEST=true` to enable.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Change from:
|
||||||
|
AUTO_SUGGEST="${CLARITY_ASSIST_AUTO_SUGGEST:-true}"
|
||||||
|
|
||||||
|
# To:
|
||||||
|
AUTO_SUGGEST="${CLARITY_ASSIST_AUTO_SUGGEST:-false}"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Files to modify:**
|
||||||
|
- `plugins/clarity-assist/hooks/vagueness-check.sh` (line 11)
|
||||||
|
- `plugins/clarity-assist/README.md` (document opt-in)
|
||||||
|
|
||||||
|
### Change 6: Create Shared Hook Library
|
||||||
|
|
||||||
|
**Create:** `scripts/hooks/lib/common.sh`
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
# Shared utilities for all hooks
|
||||||
|
|
||||||
|
# Parse file_path from JSON stdin
|
||||||
|
extract_file_path() {
|
||||||
|
echo "$1" | grep -o '"file_path"[[:space:]]*:[[:space:]]*"[^"]*"' | \
|
||||||
|
head -1 | sed 's/.*"file_path"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/'
|
||||||
|
}
|
||||||
|
|
||||||
|
# Parse command from JSON stdin
|
||||||
|
extract_command() {
|
||||||
|
echo "$1" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('command',''))" 2>/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
# Standard prefix output
|
||||||
|
hook_output() {
|
||||||
|
local prefix="$1"
|
||||||
|
shift
|
||||||
|
echo "[$prefix] $*"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if path is in ignorable location
|
||||||
|
is_ignored_path() {
|
||||||
|
case "$1" in
|
||||||
|
*/node_modules/*|*/.git/*|*/venv/*|*/__pycache__/*|*/.venv/*)
|
||||||
|
return 0 ;;
|
||||||
|
esac
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Benefits:**
|
||||||
|
- Consistent behavior across hooks
|
||||||
|
- Single place to fix bugs
|
||||||
|
- Reduced code duplication
|
||||||
|
|
||||||
|
### Change 7: Optimize project-hygiene Cleanup
|
||||||
|
|
||||||
|
**Current:** Runs `find` across entire project on every edit.
|
||||||
|
|
||||||
|
**Proposed:**
|
||||||
|
1. Only run cleanup after significant operations (not every edit)
|
||||||
|
2. Use more targeted paths
|
||||||
|
3. Add cooldown period (don't run if ran in last 5 minutes)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Add cooldown
|
||||||
|
LAST_RUN_FILE="/tmp/project-hygiene-last-run"
|
||||||
|
if [[ -f "$LAST_RUN_FILE" ]]; then
|
||||||
|
LAST_RUN=$(cat "$LAST_RUN_FILE")
|
||||||
|
NOW=$(date +%s)
|
||||||
|
if (( NOW - LAST_RUN < 300 )); then
|
||||||
|
exit 0 # Skip if ran in last 5 minutes
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
```
|
||||||
|
|
||||||
|
**Files to modify:**
|
||||||
|
- `plugins/project-hygiene/hooks/cleanup.sh`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Implementation Order
|
||||||
|
|
||||||
|
### Phase 1: Quick Wins (Low effort, high impact)
|
||||||
|
1. Remove viz-platform SessionStart hook
|
||||||
|
2. Make clarity-assist opt-in
|
||||||
|
3. Add early exit to git-flow hooks
|
||||||
|
|
||||||
|
### Phase 2: Optimizations (Medium effort)
|
||||||
|
4. Add path filtering to PostToolUse hooks
|
||||||
|
5. Add cooldown to project-hygiene
|
||||||
|
6. Create shared hook library
|
||||||
|
|
||||||
|
### Phase 3: Architecture (Higher effort)
|
||||||
|
7. Consolidate SessionStart into dispatcher
|
||||||
|
8. Refactor all hooks to use shared library
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Testing Plan
|
||||||
|
|
||||||
|
### Before changes
|
||||||
|
- Measure session startup time (10 runs, average)
|
||||||
|
- Measure time to complete 10 file edits
|
||||||
|
- Count hook invocations via logging
|
||||||
|
|
||||||
|
### After each phase
|
||||||
|
- Re-measure all metrics
|
||||||
|
- Verify no functionality lost
|
||||||
|
- Test on both dev machine and Raspberry Pi (hotport)
|
||||||
|
|
||||||
|
### Expected improvements
|
||||||
|
- SessionStart: 200-500ms → 50-100ms (4-5x faster)
|
||||||
|
- Per-edit overhead: 100-200ms → 20-50ms (4x faster)
|
||||||
|
- Process spawns: 7 SessionStart + 5 per-edit → 1 SessionStart + 2-3 per-edit
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Risks and Mitigations
|
||||||
|
|
||||||
|
| Risk | Mitigation |
|
||||||
|
|------|------------|
|
||||||
|
| Breaking existing functionality | Comprehensive test plan, phase rollout |
|
||||||
|
| Shared library versioning | Keep library in marketplace root, plugins reference via relative path |
|
||||||
|
| User config migration (clarity-assist) | Document in CHANGELOG, mention in release notes |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Open Questions
|
||||||
|
|
||||||
|
1. Should we support a `hooks.json` path matcher natively? (Would require Claude Code changes)
|
||||||
|
2. Should SessionStart hooks be lazy-loaded on first plugin command instead?
|
||||||
|
3. Is the shared library approach compatible with plugins being installed independently?
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- Analysis performed: 2026-01-28
|
||||||
|
- Total hooks analyzed: 16
|
||||||
|
- Total shell code: ~1,493 lines
|
||||||
|
- Plugins affected: 12
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Changelog
|
||||||
|
|
||||||
|
| Date | Change |
|
||||||
|
|------|--------|
|
||||||
|
| 2026-01-28 | Initial RFC created |
|
||||||
Reference in New Issue
Block a user