security_reminder_hook: per-tool-call git diff receives ~115 KB pathspecs, runs 6-deep for 10-25 s, and leaves orphaned index.lock files
Filed by an automated Claude Code session on behalf of the repository owner (no human review of the wording).
Symptom
Twice on 2026-09-17 (19:09:43 and 20:29:38) a 0-byte .git/index.lock in a large Rust/Node monorepo blocked every commit for about 30 minutes under the owner's own lock-age-gate protocol. Process-table snapshots taken during both incidents showed 3-6 concurrent children of security_reminder_hook.py:
git -c core.fsmonitor=false -c core.hooksPath=/dev/null -c core.quotePath=false diff --no-color --no-ext-diff --no-textconv HEAD -- <thousands of paths>each 5-30 seconds old.
Measurement
On 2026-09-17 at 21:10, after the owner git-ignored a large untracked directory in the repo (.cargo-home/, cargo's registry cache, ~88 MB untracked), the same pattern was still observed: 6 concurrent security_reminder_hook.py git diff children, elapsed 9-23 seconds each, with argv lengths of 115,708-115,799 characters per invocation. The pathspecs being diffed were files under .cargo-home/registry/src/index.crates.io-*/... touched by cargo builds — so the cost is the pathspec-collection step itself, not the size of the untracked tree (gitignoring it did not reduce argv size or child count, because the hook appears to enumerate paths independent of .gitignore, or before it takes effect for this purpose).
Impact
- Per-tool-call latency of 10-25+ seconds while the hook's
git diffruns. - A child process killed at a hook timeout can leave
.git/index.lockbehind. This is not proven to be caused bysecurity_reminder_hook.pyspecifically, but it is the leading candidate given the repeated 3-6 concurrent long-runninggit diffchildren observed at both lock incidents.
Suggested fix
- Skip paths that are untracked or gitignored before building the diff pathspec list.
- Cap the number of pathspec arguments passed to
git diff, or batch them. - Do not spawn a new
git diffinvocation while a previous one for the same tool call is still running. - Never
SIGKILLa git process mid-index-write; instead run git with--no-optional-locksorGIT_OPTIONAL_LOCKS=0so an interrupted hook cannot leave a staleindex.lock.
Environment
- Plugin:
security-guidance2.0.8 - Claude Code on macOS
- Repository: a large Rust/Node monorepo with an ~88 MB untracked
.cargo-home/directory (cargo registry cache)
Source: anthropics/claude-plugins-official