#16960: perf: skip cache-busting for bundled hooks, use mtime for workspace hooks
size: S
Cluster:
Hooks Enhancements and Fixes
## Summary
Bundled hooks in `dist/` are immutable between `npm install` runs — there is no reason to append `?t=Date.now()` to their import URLs on every gateway restart. This forced V8 to re-parse and re-compile ~30 transitive module chunks per hook (×4 hooks = ~120 redundant compilations per restart).
## Changes
- **New:** `src/hooks/import-url.ts` — `buildImportUrl(path, source)` helper that decides cache-busting strategy based on hook source:
- `openclaw-bundled`: bare URL (no query string) → V8 module cache effective across restarts
- `openclaw-workspace` / `openclaw-managed` / `openclaw-plugin`: `?t=<mtime>` → only re-parsed when file actually changes
- Falls back to `?t=Date.now()` if `stat()` fails (safety net)
- **Updated:** `src/hooks/loader.ts` — use `buildImportUrl()` for both directory-based and legacy handler imports
- **Updated:** `src/hooks/plugin-hooks.ts` — use `buildImportUrl()` for plugin hook imports
- **New:** `src/hooks/import-url.test.ts` — 7 unit tests covering all source types, cacheability, and fallback behavior
## Observed impact
Cold restart (first of day) — hooks take ~1.3s to load as V8 compiles all modules fresh:
```
07:49:39 → boot-md registered
07:49:40 → bootstrap-extra-files (1.3s gap)
```
With this fix, bundled hook imports will hit V8 module cache on subsequent restarts, eliminating redundant parse+compile cycles for ~30 immutable dist chunks.
## Testing
- `pnpm build` ✅
- `pnpm lint` ✅ (0 warnings, 0 errors)
- `pnpm test -- src/hooks/` ✅ (90 tests, 11 files, all passing)
Fixes #16953
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
Eliminates redundant V8 module compilation on gateway restarts by using smart cache-busting strategies: bundled hooks skip cache-busting entirely (immutable between installs), while workspace/managed/plugin hooks use mtime-based invalidation (only re-parsed when files actually change).
- Adds `buildImportUrl()` helper that selects cache strategy based on hook source type
- Updates `loader.ts` and `plugin-hooks.ts` to use the new helper for all hook imports
- Includes 7 unit tests covering all source types, cacheability guarantees, and fallback behavior
- Addresses performance issue where ~120 module chunks were redundantly re-compiled on every restart
<h3>Confidence Score: 5/5</h3>
- Safe to merge - focused performance optimization with proper fallbacks
- Clean implementation with comprehensive test coverage (7 tests covering all source types and edge cases), proper error handling (falls back to Date.now() if stat fails), and minimal surface area for issues. The logic correctly distinguishes between immutable bundled hooks and mutable workspace/plugin hooks.
- No files require special attention
<sub>Last reviewed commit: 10805c4</sub>
<!-- greptile_other_comments_section -->
<!-- /greptile_comment -->
Most Similar PRs
#11817: fix(build): compile bundled hook handlers into dist
by AnonO6 · 2026-02-08
81.5%
#11339: fix: resolve bundled hooks path on npm global install
by matthewpoe · 2026-02-07
80.9%
#9914: fix(hooks): resolve bundled hook dist paths and packaging checks
by zimmra · 2026-02-05
80.0%
#15611: fix(gateway): invalidate hook transform cache on config reload
by AI-Reviewer-QS · 2026-02-13
78.7%
#23019: fix(hooks): use globalThis singleton for internal hooks handlers Map
by karmafeast · 2026-02-21
77.6%
#14746: fix(hooks): use globalThis for handler registry to survive bundler ...
by openperf · 2026-02-12
77.3%
#16915: fix: await compaction hooks with timeout to prevent cross-session d...
by maximalmargin · 2026-02-15
76.6%
#9603: fix: initialize global hook runner on plugin registry cache hit
by kevins88288 · 2026-02-05
76.2%
#6405: feat(security): Add HTTP API security hooks for plugin scanning
by masterfung · 2026-02-01
76.0%
#10679: fix(hooks): invoke gateway_start and gateway_stop in lifecycle
by yassinebkr · 2026-02-06
74.5%