Skip to content

The per-edit hook chain costs about 1.9 s per Markdown edit, and its skip notice re-fires per subagent at 2,098 bytes a time #4260

Description

@kyle-sexton

Packets: markdown-format-hook/.../audit-notes.md F5 and F2 (medium), typos-format-hook/.../audit-notes.md F1 (medium), perf/target-ranking.md rank 1. Supersedes perf/issues/06-write-edit-formatter-chain.md; corrections from the packets' evidence-2.md.

Cost, tier E1 from hook_success.durationMs across all 35 transcripts (perf/target-ranking.md): markdown-format median 1,875 ms, n=15, range 387 to 2,229; typos-format median 484 ms, n=16, range 428 to 547. Both are PostToolUse; https://code.claude.com/docs/en/hooks.md (fetched 2026-09-19, line 416) states "All matching hooks run in parallel", so a qualifying edit waits on the slower, roughly 1.9 s, not the sum. hook_success is emitted only for hooks that surface a message: a sample, not a census.

Fan-out census, with scope. Installed PostToolUse: 29 Write|Edit rows from 10 plugins, 28 if-gated, plus 1 Write|Edit|NotebookEdit (perf/audit-performance.md by_matcher). Repo-wide across all events: 34 entries across 14 plugins, 6 ungated, adding guardrails PreToolUse run-guards.sh, context-guard zone-gate.sh, context-budget node and claude-ops PostToolUseFailure (the verified correction in .work/orchestration-efficiency/EXPLORE.md). The PostToolUse figure alone understates the per-edit fan-out by three processes.

Corrections. The 14 PostToolUse:Edit rows in perf/session-latency.md section 5 are not failures; markdown-format.sh exits 0 or via hook::finish, but that bucket counts hook_system_message attachments, so exit-0 advisory output reads as failure. Missing-binary skips measure 425 to 546 ms, near the 387 ms corpus minimum, so 14 skipped edits cost roughly 6 to 8 s, not 28 s; the 1,875 ms median is the markdownlint-cli2 arm. And typos-format is report-only at its installed default (typos_format_write_changes false, verified by direct invocation and in-harness), so batch formatting is meaningless for it.

Context, not time: hook::notice_once keys on session and agent (hook-utils.sh), so every subagent gets its own first full notice, re-noticed every 8th skip, 2,098 bytes each. The CHANGELOG (#3128) records per-agent keying as intended; README L93-96 and L124 still say "once-per-session".

Cheapest fixes: correct the README, or drop the agent component from the marker key. Then skip the lint when the content hash is unchanged, reusing the existing digest store. Fuller fix (EXPLORE's recommendation): one dispatcher for the chain. Rank async: true last: --fix mutates the file, so a background rewrite can land after the next Edit computed its old_string (hooks.md 3766, 3767, 3694).

Verification: re-measure hook_success.durationMs per hook where markdownlint-cli2 is present; count full notices across a multi-subagent run. Baseline 14 notice rows, 2,098 bytes each.

🤖 Generated with Claude Code

https://claude.ai/code/session_01F7GFS5autRMSaea6kSoXzx

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    needs-humanHuman-in-the-loop required; autonomous sessions must not resolve items carrying this.priority: mediumReal value, no hard deadline; normal backlog flow.work-class: structuralRefactors, migrations, contract changes; cross-cutting and hard to reverse.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions