<!-- Generated from release sources; edit site/content/docs, not this file. -->

# Hook Evaluation

The sequence from Event to Hook Result Event.

To reason about HooKit, follow what happens to one **Native Event**. HooKit
derives an immutable **Enabled Hook Set** for the Event from the Catalog entry
order and Preset reference order, snapshots bounded context once, then runs
the matching native Hooks first, then processes reactions:

```text
Native Event + one Enabled Hook Set
  ├─ each native Hook: Filter → when → shell → immutable Hook Result
  ├─ aggregate native Hook Outcomes → freeze Native Event Outcome
  └─ each originating result, in order:
       ├─ select its owned Action
       └─ evaluate Hook Result Event → reactive Hooks and Actions (bounded)
Ordered Effects → best-effort Pi delivery
```

1. **Event.** The Native Event arrives (`tool_call`, `tool_result`, `turn_end`,
   `agent_settled`, and similar session changes).
2. **Filter.** The candidate is projected from bounded Event data. A miss runs
   nothing.
3. **Precondition (`when`).** Optional shell command checked inside the Hook
   Invocation. An ordinary false Precondition means "not applicable": no Hook
   Result, no Action, and no report row. Inability to complete it (a timeout,
   cancellation, or spawn failure) **fails closed** with code `null`.
4. **Shell.** The effective decision command runs and produces one immutable
   **Hook Result** carrying the Hook Reference, Invocation ID, **Hook
   Outcome**, and code.
5. **Aggregation.** After all matching native shells finish, HooKit freezes
   the aggregate Native Event Outcome.
6. **Reactions, result-major.** For each originating result, the owned Action
   is selected against that owner's outcome and code first. Then its
   `hook_result` Event is evaluated so configured reactive Hooks can observe
   it and select their own Actions. Reactive results are never redispatched.
7. **Delivery.** Pi receives ordered Effects best-effort. Delivery cannot
   change the frozen Event Outcome.

## Hook outcome vs event outcome

The **Hook Outcome** is one Hook's individual decision — `pass`, or the
Event's failure kind (`block`, `patch`, `cancel`, `report`). The **Event
Outcome** is the complete, event-specific decision aggregated from every Hook
Outcome for that Event, carrying only the needed response data (a combined
reason or replacement patch). Every Event has exactly one Event Outcome,
including a minimal `pass`.

These are deliberately distinct:

- Hooks run **sequentially**; tool and lifecycle Events run every matching Hook
  and **aggregate** all failures into one Event Outcome.
- The Native Event Outcome is **frozen** before any owned Action or reactive
  Hook runs, so reactions can observe but never alter it.
- An Action selects only its owner's Hook Outcome and code—a passing Hook may
  request a pass Action even when a sibling blocks the Event, and delivery of
  an Effect can never weaken a block, patch, cancellation, or report.

## Outcomes as effects

The Hook Evaluation Outcome is deeply immutable: a non-empty sequence of Event
Outcomes (the Native Event first, then one Hook Result Event Outcome per
originating result), ordered delivery-neutral **Effects**, and an optional
Evaluation Report. The thin Pi adapter delivers Effects best-effort; Hook
Evaluation and every delivery attempt complete before the observation closes,
so Execution Duration covers the full callback-owned work. See
[Execution Reports](../reference/execution-report.md) for how this becomes a row in
the transcript, and [Events](../reference/events.md) for the event-by-event outcome
table.
