Skip to content

A hook whose import can't resolve fails silently on every event, so Safety.hook.ts can be off for months with no signal; add a post-install load check (and consider failing closed) #2306

Description

@aicatails26-cmd

What happened

After migrating a PAI 5.0.0 install to LifeOS 6.0.5 (July 2026), 8 of 41 registered hooks could not load for about three months:

Hook Error at load
Safety.hook.ts Cannot find module './lib/safety-classifier'
SystemFileGuard.hook.ts Cannot find module './lib/system-file-guard-core'
TheRouter.hook.ts Export named 'setModeToken' not found in './lib/tab-setter'
PromptProcessing.hook.ts Cannot find module './lib/notification-channel'
VoiceCompletion.hook.ts Cannot find module './lib/notification-channel'
ISASync.hook.ts Export named 'bumpLastToolActivityBySlug' not found in './lib/isa-utils'
DocIntegrity.hook.ts Cannot find module './handlers/MemoryDirIntegrity'
OutputFormatGate.hook.ts Cannot find module './lib/ai-speak-patterns'

Plus LoadContext, SessionCleanup and WorkCompletionLearning (missing getLifeosDir / findActiveSessionByUUID).

Claude Code treats a hook that exits non-zero as non-blocking, so every one of these failed on every event with nothing more than an occasional hook error line. Nothing in LifeOS surfaced it: HookHealer, ConfigAudit and the memory health grade all stayed quiet. The only reason it was found was a manual health check.

Root cause (on our side, to be clear)

The installer is not at fault for the file layout. InstallHooks.ts uses cpSync(..., { recursive: true }), which merges correctly. Our migration skipped it: because the Setup workflow has no PAI 5 to LifeOS content path, the migration was done by hand, and a one-off script ran cp -R install/hooks/lib ~/.claude/hooks/lib while hooks/lib already existed. That nested the new libraries at hooks/lib/lib/ and hooks/handlers/handlers/, leaving the stale PAI 5 copies in place.

So the trigger was user-side. The upstream issue is that any path that leaves a hook's imports unresolved (hand migrations, partial updates, a stray file deletion, a renamed export in a future release) silently disables that hook, including the security ones.

Suggestions

  1. Post-install / post-update load check. After InstallHooks.ts (and in Update), statically verify that every hook registered in settings.json resolves its relative imports and named exports, and print any failures loudly. A ~40-line check (parse each hook's import { a, b } from './...', compare to the target file's export names) found all 11 failures here in under 2 seconds.
  2. Run the same check from HookHealer at SessionStart, and surface a failure on the statusline or in Pulse. A one-line "3 hooks failing to load" would have caught this on day one.
  3. Consider failing closed for Safety. If Safety.hook.ts can't load its classifier, that's currently indistinguishable from "allowed everything to fall through to the default prompt." A tiny wrapper that catches the import failure and emits a visible warning (or denies until fixed) would make that state impossible to miss.
  4. (Optional) A documented PAI 5 to LifeOS migration path in the Setup workflow, so people upgrading don't hand-roll file copies.

Happy to share the check script if it's useful.

Environment: macOS 26 (arm64), Bun 1.3.11, Claude Code 2.1.234, LifeOS 6.0.5 (Algorithm v6.24.0).

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

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions