VibeGuard hooks run in the critical path of Claude Code and Codex sessions. A correct hook is not acceptable if it turns every agent action into a slow or high-CPU operation.
The benchmark surface is hook_e2e_ms: end-to-end hook latency in milliseconds.
It includes shell wrapper/process startup, stdin/stdout handling, config lookup,
event-log access, status/logging work, runtime dispatch, and the hook logic
itself. These numbers are SLA budgets for the installed hook path, not claims
about pure core classifier speed.
Future pure Rust/core classifier microbenchmarks must use the separate
core_us surface. core_us is reserved for in-process core logic measured in
microseconds and must not include hook wrappers, shell process startup,
stdin/stdout adaptation, config discovery, or logging overhead.
The latency gate measures P50, P95, P99, and max for each benchmark fixture. CI fails when --fail-on-regression is enabled and any fixture exceeds its P95 budget.
These are cross-OS CI budgets, not ideal-machine optimization targets. Static performance gates still block dangerous patterns before they can hide behind a broad absolute budget.
| Fixture | P95 budget |
|---|---|
pre-edit-guard |
300ms |
pre-write-guard |
500ms |
pre-bash-guard |
300ms |
post-edit-guard (100) |
500ms |
post-write-guard (100) |
400ms |
post-edit-guard (5000) |
500ms |
post-write-guard (5000) |
500ms |
stop-guard (5000) |
400ms |
learn-evaluator (5000) |
400ms |
codex-wrapper pre-bash-guard |
900ms |
codex-wrapper post-edit-guard (100) |
900ms |
post-build-check (fake cargo) |
900ms |
Run the gate locally:
cargo build --manifest-path vibeguard-runtime/Cargo.toml --quiet
bash tests/bench_hook_latency.sh --runs=3 --fail-on-regressionUse --sla=<ms> only when deliberately testing a temporary global threshold. The default contract is per-hook.
Most fixtures invoke hook scripts directly so regressions in hook logic, JSON parsing, logging, and bounded event-log reads are isolated to the hook under test.
Codex wrapper hooks invoke a temporary installed-wrapper copy with the same helper files installed by scripts/setup/targets/codex-home.sh and a repo-path file pointing at the repository. These fixtures include Codex event parsing, installed wrapper/helper lookup, runtime policy lookup, status diagnostics, output adaptation, and wrapper finalization before the underlying hook returns. They are intentionally budgeted separately from direct hooks because they measure the installed Codex path, not just the hook body.
Post-build fixtures use fake build commands and disable the post-build cache so CI measures hook overhead, timeout wrapping, project detection, and command dispatch without running a real full build.
Benchmark output includes surface=hook_e2e_ms and a hotspot= field. JSON
output carries the same surface marker at the top level and per result.
bench-output.json uses compact e2e ... P95 names so GitHub benchmark
comments stay readable. When a regression fails, the console row must identify
both the surface and fixture size, such as
surface=hook_e2e_ms ... post-write-guard (5000).
Synthetic slow fixtures are part of the regression suite:
bash tests/test_hook_perf_contract.shThat test intentionally runs a slow hook and requires the latency gate to fail, proving the CI gate is not a report-only metric.
scripts/ci/validate-hook-perf.sh blocks dangerous shell patterns in hook scripts:
- Python reading full JSONL logs instead of bounded
tailinput. findwithout-maxdepthunless a nearbyPERF-OKcomment explains the bounded scope.- subprocess work inside
fororwhileloops unless a nearbyPERF-OKcomment explains the cap.
PERF-OK is not a blanket bypass. It must state the bound, such as a single file, one-per-session cleanup, an output cap, or VG_SCAN_MAX_DEFS.
Run the static gate locally:
bash scripts/ci/validate-hook-perf.shCI runs all three layers:
bash scripts/ci/validate-hook-perf.shbash tests/test_hook_perf_contract.shbash tests/bench_hook_latency.sh --runs=3 --fail-on-regression
If any layer fails, the PR must either make the hook faster or add a precise PERF-OK explanation for a bounded case.