AV
HomeAboutProjectBlog

© 2026 Ave syah Shina. All rights reserved.

  1. Home
  2. Blog
  3. 08 — Hooks — Deterministic Triggers at the Loop's Lifecycle Points

08 — Hooks — Deterministic Triggers at the Loop's Lifecycle Points

August 13, 20267 min read
Download as Markdown

"Git hooks but for AI" was the nearest hook to hand, and it captured the deterministic part but not the integration. The cleaner frame: a hook is a deterministic script or check fired at a specific lifecycle event of the agent, communicating via JSON to approve, block, or enrich the next step. [1] Where CLAUDE.md gives soft instructions the model might ignore, hooks give hard guarantees — something that runs _every single time_ a certain event occurs, regardless of what the model "decides."

The framing that clicked is the contrast with skills and subagents. Skills add knowledge the model loads on demand; subagents isolate work into bubbles. Hooks are different: they sit _outside_ the model entirely, on the lifecycle events of the loop itself. The model doesn't choose to call a hook — the harness fires it when the event happens, and the hook's output feeds back into what the model does next [1]. That determinism is the whole point, and it's why hooks are the right tool for enforcement (lint after every edit, block risky commands) rather than suggestion.

The lifecycle events

The roadmap names the lifecycle moments where a hook can fire, and reading them as points on the loop made them click [1][2]:

  • SessionStart — fires at the very beginning, before the first prompt. Bootstraps the environment and injects high-priority context.
  • UserPromptSubmit — fires the moment I press Enter, before the text reaches the model. Lets me programmatically rewrite, validate, or enhance the prompt on the fly.
  • PreToolUse — fires after the model decides to use a tool but before it runs. The validation gate: security checks, policy enforcement, input sanitization.
  • PostToolUse — fires immediately after a tool completes. For cleanup and quality control — format the file after an edit, run a check after a command.
  • Stop — fires once at the very end of a turn, when the model is about to return control to me. A final check or notification.
  • SessionEnd — fires on exit. Cleanup, archiving, final reporting.

Two ideas make this powerful. First, events have matchers — regex filters that decide which specific actions within an event fire the hook (e.g. a PreToolUse hook that only triggers on Bash, not on Read) [3]. Second, the placement lets me intercept at exactly the right granularity: prompt-level (UserPromptSubmit), tool-level (Pre/PostToolUse), or turn-level (Stop).

Loop perceive · decide · act SessionStart UserPromptSubmit PreToolUse validation gate · can block PostToolUse Stop SessionEnd stdin → {tool:"Bash", cmd:"rm -rf"} stdout ← {decision:"block"} exit code 2 · action denied

The three hook types

For each event I can configure one of three handler types, and the roadmap is explicit about the distinction [4]:

  • Command hooks (type: "command") — deterministic shell scripts. They run a standard command (like npm run lint) and use exit codes to approve or block. Exit 0 approves; a non-zero code blocks with the error message shown to the model. This is the "I want a hard guarantee" option.
  • Prompt hooks (type: "prompt") — a lightweight Claude model makes a single-turn judgement call ("is this commit message descriptive?") and returns a JSON yes/no. Use when the check itself needs interpretation, not just a fixed command.
  • Agent hooks (type: "agent") — the most sophisticated: spawns a multi-turn subagent with tool access (Read, Grep) to conduct deep, autonomous verification before deciding whether the main agent should proceed.

The rule of thumb I use: start with a command hook (deterministic, cheap, no model involved). Reach for a prompt hook when the check is a judgement ("is this good?"). Reach for an agent hook only when verification needs real investigation across files.

Inputs, outputs, and the JSON contract

Hooks communicate through a standardized JSON interface, and getting this straight was what made them usable [5]:

  • Inputs arrive via stdin — a context object with session metadata and event-specific data, such as the tool name and its arguments (the exact code being written or command being run).
  • Outputs return via stdout — a JSON object that influences the agent's next move. The shape of the output decides whether the action proceeds, gets blocked with a message, or gets enriched with extra context.

For command hooks specifically, the exit code is the blunt instrument: 0 to allow, non-zero (commonly 2) to block. This is what makes a PreToolUse command hook a real security gate — it can refuse a dangerous command before it ever runs, deterministically, every time.

The slash commands that manage them

  • /hooks — opens the interactive management menu for configuring automated workflows at lifecycle points [6].
  • /config — the central customization menu (global and project settings, defaults, permissions) [7].
  • /permissions — the security interface for granting permanent trust to specific tools or commands, reducing interruptions [8].
  • /model — switches the model mid-session (relevant because prompt/agent hooks use a model) [9].
  • /agents — manages subagents (relevant because agent hooks spawn one) [10].
  • /mcp — the MCP integration hub (covered in its own post) [11].

How I use this

The habit these notes left me with is a simple rule: _if a standard must hold every single time, it's a hook; if it's a preference, it's CLAUDE.md._ So PostToolUse command hook to run the formatter after every edit (the codebase is always formatted), PreToolUse command hook to block commands matching destructive patterns (the dangerous action never runs), Stop hook to notify me when a long turn ends. Soft preferences ("prefer named exports") stay in the memory file where the model can weigh them. Hard guarantees become hooks because the model cannot choose to skip them.

References

[1] Anthropic, "Automate workflows with hooks," Claude Code Docs, 2025. [Online]. Available: https://code.claude.com/docs/en/hooks-guide

[2] Anthropic, "Hooks reference," Claude Code Docs, 2025. [Online]. Available: https://code.claude.com/docs/en/hooks

[3] Anthropic, "Event Matchers," Claude Code Docs, 2025. [Online]. Available: https://code.claude.com/docs/en/hooks#matcher-patterns

[4] Anthropic, "Hook Types," Claude Code Docs, 2025. [Online]. Available: https://code.claude.com/docs/en/hooks

[5] Anthropic, "Hook Inputs & Outputs," Claude Code Docs, 2025. [Online]. Available: https://code.claude.com/docs/en/hooks#hook-input-and-output

[6] Anthropic, "The /hooks menu," Claude Code Docs, 2025. [Online]. Available: https://code.claude.com/docs/en/hooks#the-hooks-menu

[7] Anthropic, "Claude Code settings," Claude Code Docs, 2025. [Online]. Available: https://code.claude.com/docs/en/settings

[8] Anthropic, "Manage permissions," Claude Code Docs, 2025. [Online]. Available: https://code.claude.com/docs/en/permissions

[9] Anthropic, "Model configuration — /model," Claude Code Docs, 2025. [Online]. Available: https://code.claude.com/docs/en/model-config#setting-your-model

[10] Anthropic, "Create custom subagents — /agents," Claude Code Docs, 2025. [Online]. Available: https://code.claude.com/docs/en/sub-agents#use-the-agents-command

[11] Anthropic, "Connect Claude Code to tools via MCP — /mcp," Claude Code Docs, 2025. [Online]. Available: https://code.claude.com/docs/en/mcp

Knowledge check · Question 1 of 5

What is the core difference between a CLAUDE.md rule and a hook?

Comments

Leave a Comment

You must be signed in to comment

0 Comments

No comments yet. Be the first to comment!