Summary
Claude Code runs every matching PostToolUse hook in parallel for one tool call. Multiple hooks in this fleet write to the same edited file with no locking or ordering primitive between them:
Any file matched by both an unscoped writer and a scoped formatter (e.g. any .py file, with eol-normalizer and ruff-format both applicable — or, once #872 merges, typos-format and ruff-format on any .py file governed by both a typos config and a Ruff config) has two hooks independently reading-then-writing the same file with no coordination. A nondeterministic clobber is possible: whichever write lands last silently discards the other's fix.
Surfaced explicitly during #872's stress-test and independent review; documented (not fixed) in typos-format's hook header comment and README "Known limitation" section as a pre-existing, fleet-wide gap this single plugin cannot address.
Impact
Low-frequency (requires two applicable hooks + concurrent scheduling), but silent when it hits — no error, no diagnostic, just a dropped fix. Grows with every new unscoped or overlapping-scope hook plugin added to the fleet.
Constraints
No native Claude Code feature exists today for sequential/ordered hook execution within one event (verified during #872's research). Any fix is either:
- A convention/pattern this repo's hooks adopt (e.g. a shared file lock via
flock, already used elsewhere in hook::append_jsonl).
- An upstream feature request to Claude Code for ordered or serialized
PostToolUse hook execution.
Related
Summary
Claude Code runs every matching
PostToolUsehook in parallel for one tool call. Multiple hooks in this fleet write to the same edited file with no locking or ordering primitive between them:eol-normalizer, and nowtypos-format(feat(typos-format): add per-file typos autofix hook plugin #872) — both fire on everyWrite/Edit.ruff-format(.py/.pyi),markdown-format(.md/.mdc),biome-format,bash-format,powershell-format.Any file matched by both an unscoped writer and a scoped formatter (e.g. any
.pyfile, witheol-normalizerandruff-formatboth applicable — or, once #872 merges,typos-formatandruff-formaton any.pyfile governed by both a typos config and a Ruff config) has two hooks independently reading-then-writing the same file with no coordination. A nondeterministic clobber is possible: whichever write lands last silently discards the other's fix.Surfaced explicitly during #872's stress-test and independent review; documented (not fixed) in
typos-format's hook header comment and README "Known limitation" section as a pre-existing, fleet-wide gap this single plugin cannot address.Impact
Low-frequency (requires two applicable hooks + concurrent scheduling), but silent when it hits — no error, no diagnostic, just a dropped fix. Grows with every new unscoped or overlapping-scope hook plugin added to the fleet.
Constraints
No native Claude Code feature exists today for sequential/ordered hook execution within one event (verified during #872's research). Any fix is either:
flock, already used elsewhere inhook::append_jsonl).PostToolUsehook execution.Related
docs/topics/lint-static-analysis-gaps/PLAN.md). Sibling epic item 6 (hook-observability convention) and item 7 (bespoke CI guards) may be a natural home for a locking convention if one is adopted; alternatively a standalone convention doc.