Summary
Child of #3508. The Stop-event hook that audits for unsurfaced hook failures times out repeatedly, at roughly 44x the marketplace per-turn hook budget.
This issue was originally filed against guardrails. That was wrong — guardrails registers no Stop hooks at all. Corrected below.
The hook
|
|
| Plugin |
claude-ops |
| Hook |
plugins/claude-ops/hooks/hook-failure-audit.sh |
| Registration |
plugins/claude-ops/hooks/hooks.json, Stop event |
| Configured timeout |
10 s |
| Timeouts in window |
113 |
| Average recorded duration |
21.9 s |
| Worst observed |
74.1 s |
The bar
docs/conventions/hook-budget/README.md (adopted #1809, surfaced by .claude/rules/hook-budget.md) sets the per-turn ceiling for the whole always-on Stop-shaped set at <= 500 ms, measured as parallel wall time.
At 21.9 s average this hook alone is ~44x the entire per-turn budget, and it shares that budget with two siblings:
| Issue |
Plugin |
Stop timeout |
Timeouts |
Avg |
| #3512 (this) |
claude-ops |
10 s |
113 |
21.9 s |
| #3515 |
autonomy |
15 s |
73 |
27.6 s |
| #3516 |
disk-hygiene |
20 s |
52 |
31.8 s |
Three plugins, three separate processes, one 500 ms budget between them. Consolidation across the three is likely worth more than optimizing any one, and none of the three can meet the ceiling alone at current spawn cost.
Why it is slow
Per #3508, process creation on the affected host is bimodal at 180-2,841 ms (median 1,108 ms at 501 concurrent processes). Lead any fix with a spawn count, not a duration — durations on that host are not comparable across time.
This hook is also structurally last in line: it inspects other hooks' results, so it runs after the rest of the turn's fan-out and compounds whatever contention they created.
Proposed work
- Count spawns first, via a PATH shim of wrapper scripts that log their own name then exec the real tool. Deterministic, drift-immune, cheap for a reviewer to verify.
- Early-exit before any spawn when no hook failure was recorded this turn — the common case.
- Consider sampling or async rather than running on every
Stop.
- Evaluate consolidating the three Stop hooks into one process, given the shared 500 ms ceiling.
Acceptance criteria
Notes
Prior art in #1403 / #1385 (hook::jq_fields, strip_quoted_spans, deferred git rev-parse; a 65-spawn-per-Bash-call census and a 437 ms unit price). hook-utils.sh exists in 17 synced copies — changes go through scripts/sync-hook-utils.sh, never one plugin's copy.
Summary
Child of #3508. The Stop-event hook that audits for unsurfaced hook failures times out repeatedly, at roughly 44x the marketplace per-turn hook budget.
This issue was originally filed against
guardrails. That was wrong —guardrailsregisters noStophooks at all. Corrected below.The hook
claude-opsplugins/claude-ops/hooks/hook-failure-audit.shplugins/claude-ops/hooks/hooks.json,StopeventThe bar
docs/conventions/hook-budget/README.md(adopted #1809, surfaced by.claude/rules/hook-budget.md) sets the per-turn ceiling for the whole always-on Stop-shaped set at <= 500 ms, measured as parallel wall time.At 21.9 s average this hook alone is ~44x the entire per-turn budget, and it shares that budget with two siblings:
Three plugins, three separate processes, one 500 ms budget between them. Consolidation across the three is likely worth more than optimizing any one, and none of the three can meet the ceiling alone at current spawn cost.
Why it is slow
Per #3508, process creation on the affected host is bimodal at 180-2,841 ms (median 1,108 ms at 501 concurrent processes). Lead any fix with a spawn count, not a duration — durations on that host are not comparable across time.
This hook is also structurally last in line: it inspects other hooks' results, so it runs after the rest of the turn's fan-out and compounds whatever contention they created.
Proposed work
Stop.Acceptance criteria
EPOCHREALTIMEaround direct invocation, >= 10 runs, concurrent launch), is stated in theclaude-opsREADME per hook-budget Rule 1.scripts/affected-tests.sh --run.Notes
Prior art in #1403 / #1385 (
hook::jq_fields,strip_quoted_spans, deferredgit rev-parse; a 65-spawn-per-Bash-call census and a 437 ms unit price).hook-utils.shexists in 17 synced copies — changes go throughscripts/sync-hook-utils.sh, never one plugin's copy.