The agent loop of D1.1 runs the model’s tool calls automatically. Hooks are how the architect gets between the model and those calls — to block a dangerous one, rewrite its input, or clean up its output — without editing the model’s prompt. The exam tests three things: which events exist, the two modes of intervention, and who wins when hooks disagree.

Hooks intercept the loop at named events

A hook is a callback that runs your code in response to an agent event: “Hooks are callback functions that run your code in response to agent events, like a tool being called, a session starting, or execution stopping.” [Official] Intercept and control agent behavior with hooks · AnthropicT1-official original They arrive through two channels that share one lifecycle — programmatic hooks (callbacks in your query() options) and filesystem hooks (shell commands in settings.json, loaded via settingSources). [Official] Intercept and control agent behavior with hooks · AnthropicT1-official original

The events you must recognize

Hooks fire at named lifecycle points. The Python SDK exposes ten; the TypeScript SDK extends the same set to twenty. [Official] Intercept and control agent behavior with hooks · AnthropicT1-official original The ones an architect must recognize:

EventFires whenTypical use
PreToolUsea tool call is requestedblock or rewrite the call before it runs
PostToolUsea tool returns a resultnormalize or replace the result before the model sees it
PostToolUseFailurea tool execution failslog or handle the error
UserPromptSubmita prompt is submittedinject extra context
Stopthe agent stopspersist state before exit
SubagentStart / SubagentStopa subagent begins / endstrack spawned parallel work
PreCompactcompaction is about to runarchive the full transcript first
PermissionRequesta permission dialog would appearcustom permission handling
Notificationan agent status messageforward to Slack / PagerDuty

The TypeScript-only additions (PostToolBatch, SessionStart, SessionEnd, Setup, and others) are why SessionStart / SessionEnd are not available as Python SDK callbacks — Python apps needing them load filesystem hooks from settings instead. [Official] Intercept and control agent behavior with hooks · AnthropicT1-official original

PreToolUse gates; PostToolUse normalizes

The two most-tested events define the two interception modes, and they sit on opposite sides of tool execution. [Official] Intercept and control agent behavior with hooks · AnthropicT1-official original

Matchers select which calls a hook sees: they are regex strings tested against the tool name — "Write|Edit", "^mcp__" for all MCP tools, or omitted to match everything. [Official] Intercept and control agent behavior with hooks · AnthropicT1-official original Matchers do not filter by argument; filter on the file path or command inside the callback.

Precedence: deny beats defer beats ask beats allow

When several hooks (or permission rules) act on one event, the outcome is decided by a fixed precedence, not by who ran first: “When multiple hooks or permission rules apply, deny takes priority over defer, which takes priority over ask, which takes priority over allow. If any hook returns deny, the operation is blocked regardless of other hooks.” [Official] Intercept and control agent behavior with hooks · AnthropicT1-official original The four decisions are not symmetric, and defer is the one candidates miss:

Hooks and subagents

Two subagent-aware events — SubagentStart and SubagentStop — let you track spawned work, but the operational gotcha is about permissions. As D1.3 established, subagents do not inherit the parent’s permissions; each runs its own evaluation chain. [Official] Configure permissions · AnthropicT1-official original The clean way to pre-approve a subagent’s tools is a PreToolUse hook rather than re-prompting inside every child. [Official] Intercept and control agent behavior with hooks · AnthropicT1-official original

Practice

Exercise solutions

Solution ↑ Exercise

(a) is a gate, (b) is a normalization — opposite sides of tool execution, so they need different events. (a) Use a PreToolUse hook with a matcher of "Write|Edit"; in the callback, inspect the target path and return hookSpecificOutput.permissionDecision: "deny" when it ends in .env. The decision must come before the write runs. (b) Use a PostToolUse hook with a matcher of "Bash"; return hookSpecificOutput.updatedToolOutput containing the result with ANSI codes stripped — updatedToolOutput replaces the output before Claude sees it. (b) can’t reuse PreToolUse because the output does not exist yet when the call is requested; you can only normalize a result after the tool has produced it.

Solution ↑ Exercise

The call is blocked. All matching hooks run in parallel and the most restrictive result wins, so the deny overrides the allow and the ask — the precedence is deny > defer > ask > allow. The one-word rule is restrictive (equivalently, “deny wins”): one hook saying deny is enough to block; permitting requires every hook to agree.

Solution ↑ Exercise

Both expectations are wrong. defer does not run the call — it ends the query so the host can resume it later from the persisted session (a pause-and-hand-back, not an allow). And updatedInput is ignored with defer — that field applies only to allow (or ask). To run a rewritten command, the hook must return allow with updatedInput, not defer.

Exam essentials