The Notification and Hooks Pipeline
How events propagate through Claude Code — from lifecycle hooks that intercept tool calls to system reminders that steer model behavior.
Two event systems
Claude Code has two distinct event propagation mechanisms that serve different purposes:
- Hooks — user/extension-configurable interceptors that can block, modify, or log operations
- System reminders — model-facing behavioral instructions injected into conversation messages
Hooks: intercepting operations
The hooks system (src/utils/hooks.ts, 3789 lines) exposes 27 lifecycle events across the agent lifecycle: tool boundaries, session events, permissions, compaction, subagents, file changes, and MCP elicitation.
Event flow
Operation about to happen (e.g., BashTool execution)
→ Find matching hooks (settings.json priority: user > project > local > policy > plugin)
→ Run all matching hooks in parallel
→ Each hook receives JSON on stdin: session_id, cwd, tool_name, tool_input, etc.
→ Each hook responds via exit code + optional JSON on stdout
→ Merge responses: any block wins, modifications applied, context injected
→ Operation proceeds (or is blocked)
Four hook types
| Type | Execution | Use case |
|---|---|---|
| Command | Shell child process (bash/powershell) | Linting, audit logging, CI integration |
| HTTP | POST to URL with JSON payload | External webhook systems |
| Prompt | Single-turn LLM verification (Haiku) | AI-based policy enforcement |
| Agent | Multi-turn agent with tool access | Complex verification workflows |
The PreToolUse power
PreToolUse is the most powerful hook — it can:
- Block the tool call (exit code 2, stderr sent to model)
- Modify input (return updatedInput in JSON response)
- Override permissions (return permissionDecision: allow/deny/ask)
This enables patterns like: "block all Bash commands matching rm -rf unless in /tmp" or "rewrite git push to always include --no-force."
Async hooks
Hooks can declare themselves async by emitting {"async": true} as their first stdout line. The operation proceeds immediately; the hook continues in the background. On completion, results are delivered via the async hook registry. A special asyncRewake mode lets hooks wake the model after long-running background checks.
Fail-open design
Non-exit-code-2 errors don't block operations. Timeouts kill the hook process and proceed. This ensures a broken hook never permanently blocks Claude Code.
System reminders: steering the model
System reminders are 40+ short behavioral instructions injected as <system-reminder> tags into user messages (not the system prompt). This design preserves the prompt cache — adding a reminder to a user message doesn't invalidate the static system prompt.
When reminders fire
- Tool boundaries (before/after specific tools)
- Pre-compaction
- Pre-commit
- Context changes (date, model, permissions)
- Task notifications (from background agents)
The injection mechanism
prependUserContext() in src/utils/api.ts creates a user message with <system-reminder> content blocks containing CLAUDE.md, git status, current date, and other session context. This message is prepended to the conversation on every API call.
Additional reminders are injected by the message normalization pipeline — smooshSystemReminderSiblings folds reminder text blocks into adjacent tool results.
How hooks and reminders interact
Hooks are operational (they intercept and control what happens). Reminders are informational (they tell the model about context changes). They can trigger each other:
- A
PostToolUsehook might inject additional context → which becomes a system reminder in the next turn - A
SessionStarthook can returnadditionalContext→ injected as a reminder - A
PreCompacthook can block compaction → model receives a reminder explaining why
Environment variable injection
Special case: hooks for SessionStart, Setup, CwdChanged, and FileChanged receive CLAUDE_ENV_FILE. Bash hooks can write shell exports (export VAR=value), which accumulate into a session environment script injected into subsequent Bash tool commands. This enables dynamic environment configuration based on project context.
Cross-references
- hooks-system — 27 lifecycle events, execution model, JSON protocol
- system-prompt-assembly — system reminder injection, cache boundary
- message-normalization — smooshing system reminders into tool results
- permission-pipeline — hooks can override permission decisions