Summary
hooks/hook_runner.py _check_and_set_dedup() writes one marker file hook-dedup-{event}-{payload_hash} per invocation to suppress double-fired hooks within a 500 ms window. The payload hash is a per-call MD5, so every unique tool call creates a new file that is never deleted.
Impact (Facts)
- Observed
~/.copilot/markers/ containing 134,426 hook-dedup-* files (ls | grep -c hook-dedup).
- Every hook invocation does
mkdir + write_text into that directory; a directory with 130k+ entries slows all hook events (sessionStart/preToolUse/postToolUse/...).
Root cause
The 500 ms dedup window only needs a marker for a fraction of a second, but nothing ever removes stale markers.
Fix
Add a time-gated _prune_stale_dedup_markers() that removes hook-dedup-* files older than a 5 s TTL, at most once per 60 s (stamp file dedup-sweep.stamp), fully fail-open.
Acceptance criteria
Summary
hooks/hook_runner.py_check_and_set_dedup()writes one marker filehook-dedup-{event}-{payload_hash}per invocation to suppress double-fired hooks within a 500 ms window. The payload hash is a per-call MD5, so every unique tool call creates a new file that is never deleted.Impact (Facts)
~/.copilot/markers/containing 134,426hook-dedup-*files (ls | grep -c hook-dedup).mkdir+write_textinto that directory; a directory with 130k+ entries slows all hook events (sessionStart/preToolUse/postToolUse/...).Root cause
The 500 ms dedup window only needs a marker for a fraction of a second, but nothing ever removes stale markers.
Fix
Add a time-gated
_prune_stale_dedup_markers()that removeshook-dedup-*files older than a 5 s TTL, at most once per 60 s (stamp filededup-sweep.stamp), fully fail-open.Acceptance criteria
hook-dedup-*markers are pruned automatically.tests/test_hook_runner_entrypoints.pypasses.