v1.108.294 — The hook layer, and the three seconds behind it
Two halves, and the larger one is a contributor's.
Ten confirmed hook defects — @marcelruhf, #535
The "Claude forgets to call jCodeMunch" symptom turned out to be the enforcement hooks failing from several directions at once. Every fix was reproduced against the unfixed tree first.
Several were features that ran, returned nothing, and reported success — the failure mode with no symptom. Three loops read owner/name keys off list_repos() entries that only ever carry repo, so the SubagentStart briefing, the PreCompact/SessionStart landmarks and the TaskCompleted diagnostics skipped every repo, always. The only thing producing the wrong shape was a test mock, which is why a green suite never noticed.
Also in that set: the PreToolUse matcher was Read|Grep, so Bash(grep/rg/find …) and Glob — the dominant native search routes — never fired it, and strict mode funnelled a denied Grep into unmonitored Bash. A symlink anywhere in the checkout path silenced steering for that repo entirely. PreCompact was emitting its snapshot into systemMessage, a field Claude Code discards, so it reached nobody and paid a full snapshot build every compaction; SessionStart(compact) has delivered it since v1.108.255.
cli/hooks.py is now the cli/hooks/ package, one module per hook family. The facade re-exports the run_* entry points, so dispatch is unchanged, and the split costs nothing measurable.
The three seconds behind it
Reviewing that PR meant measuring the hook cost, and the cost was not where anyone thought.
verify_package_integrity() runs on every CLI invocation to answer one question: which distribution owns the running jcodemunch_mcp. It answered with packages_distributions(), which builds a top-level-name to distribution map for every distribution on sys.path — 3.35 s uncached on a box with 894 top-level names, and on that box it then returned nothing, because the code was running from source.
packages_distributions() 3.350s
distribution("jcodemunch-mcp") 0.005s
main() argparse tree 0.007s
Cheapest-first ordering now settles a source tree instantly and an ordinary install in 5 ms. A hook-pretooluse spawn goes 4.0 s → 0.94 s. The full map stays reachable, because it is the only thing that can name an offending distribution, and the fast path proves the official distribution actually owns the running module — installed-and-correctly-named is the exact arrangement the warning exists to catch.
The first attribution was wrong, and that is the part worth keeping. The suspect was the 45-subparser argparse tree, which is 7 ms.
Verification
Local 8234 passed / 17 skipped / 0 failed; the 3.13 reproduce is identical, same 8251 total and the same skip split. Totals reconcile against 1.108.293's 8180: +5 from test_integrity_check_cost.py, +66 from #535 — and #535's own CI reported 8246 collected, which is 8251 minus those five exactly. ruff check src/ clean. All 9 CI jobs green on the released SHA before anything shipped.