---
description: >-
  How Watcher reviews a live session: blocking review gates tool calls before
  they run, trailing review grades the trajectory as the session progresses.
---

> Part of the Watcher docs. Index of all pages, with summaries: https://watcher-docs.apolloresearch.ai/llms.txt
> Before upgrading Watcher, read the release pages and full changelogs for every version crossed: https://watcher-docs.apolloresearch.ai/releases/index.md
> Found an issue with this page? Report it: https://github.com/ApolloResearch/watcher-bin/issues/new?title=Docs%20feedback%3A%20Blocking%20and%20trailing%20review&body=Page%3A%20Blocking%20and%20trailing%20review%0AURL%3A%20https%3A%2F%2Fwatcher-docs.apolloresearch.ai%2Fconcepts%2Fblocking-and-trailing-review%2F%0A%0A

# Blocking and trailing review

Watcher reviews a running [session](sessions-and-trajectories.md) in two
lanes. **Blocking review** evaluates tool calls before they run and can stop
them. **Trailing review** grades the session's
[trajectory](sessions-and-trajectories.md) as it grows and surfaces findings
after the fact. The dividing line: if a single action could cause
irreversible harm, it belongs to blocking review; patterns that emerge across
turns belong to trailing review.

Blocking review runs from the [Watcher client](../client/index.md) on the
developer's machine and is configured through the client's settings: which
[graders](graders.md) run, the thresholds, and the enforcement mode. These
resolve on each machine, but they do not have to be set machine by machine:
admins can distribute and lock them across the organization with
[managed settings](organization-settings.md#managed-client-settings).
Trailing review runs on the Watcher server, over the sessions the client has
ingested, and needs no client configuration.

## Blocking review

Blocking review evaluates tool calls the coding agent proposes before they
run. Coverage depends on the [agent](../supported-agents.md): with Claude Code, every tool call is
evaluated; with Codex, review runs through the agent's approval flow, so a call
Codex does not ask about executes without a pre-execution decision (it is still
recorded, and graded after the fact). Codex asks about the commands it considers
dangerous, and about anything that needs to reach outside its sandbox — which is
why Watcher keeps that sandbox on when it is enforcing. Because the
agent is paused while a call is evaluated, the pipeline is staged from
fastest to slowest, and most calls never reach the slow stages:

1. **Terminal thresholds.** A tool whose thresholds are set to a terminal
   value is decided instantly: always-allow (both thresholds disabled),
   always-deny, or always-flag. These outrank everything that follows,
   including command rules: setting a tool to always-allow also switches off
   that tool's command rules, deny rules included.
2. **Command rules.** Instant allow/deny list matching.
3. **Triage.** A fast, cheap grader scores the call. Triage can only
   auto-approve: a low-scoring call proceeds immediately, and anything else
   falls through to deep review. It never denies or escalates.
4. **Deep review.** The gateway grader scores the call from 1 to 10. The
   score is compared against the tool's configured thresholds: at or above
   the deny threshold the call is denied, at or above the flag threshold it
   is escalated, otherwise it is allowed.
5. **Human escalation.** An escalated call waits for a person: the developer
   approves or denies it in the Watcher client's UI, or answers the coding
   agent's own permission prompt, whichever comes first.

Every outcome is recorded as a decision on the session, so escalations and
denials are visible in the [Analyzer](analyzer.md) transcript afterwards. If
grading fails or exceeds its time budget, Watcher applies the configured
failure decision: escalate (the default: a human decides, via the Watcher
client's UI or the coding agent's own permission prompt), deny, or allow.
Transient failures (the grading service being unavailable, an empty grading
result) are retried first, once by default; other failures, such as
authentication errors, a grader model your deployment does not serve, or a
transcript too long for the grading model, apply the decision immediately. A grade that runs out of time is not retried: one
attempt may use the whole time budget, so there is nothing left to retry
with, and a grader slow enough to exhaust it would only be slow again. Neither
is a client that cannot reach its Watcher backend at all: the connection is
checked before grading starts, and a refused or unanswered one applies the
failure decision within a couple of seconds rather than holding the call for
the full time budget. The time budget is for a backend that answers slowly,
where a deep review may legitimately need most of it. When triage fails,
Watcher checks whether the deployment is answering before spending what is
left of the budget on deep review: if it is, deep review still runs (the two
stages use different models, so one failing says nothing about the other), and
if it is not, the failure decision applies straight away.

Repeated failures pause a stage rather than the client. Triage and deep review
are counted separately, so an outage affecting one leaves the other working: if
deep review is failing, triage keeps auto-approving the calls it can, and only
the rest take the failure decision. The exception is a failure decision of
`allow`: with deep review paused, every call would be allowed whichever way
triage scored it, so calls are allowed immediately and not graded at all until
deep review resumes. Failures that show the deployment itself is
unreachable are counted against it instead, and enough of those pause grading on
the machine for a minute, with each gated call going to the coding agent's own
permission prompt. Every pause lifts by itself. The one exception is when an SSO-mode
machine with no stored sign-in credentials gets a 401 from grading: that
call is blocked with a sign-in prompt instead. Any other failure kind on
such a machine still follows the configured decision above.

An agent that keeps being denied is stuck in a loop that more denials will
not resolve, so two limits convert a would-be deep-review denial into a
human escalation instead. After two consecutive deep-review denials, the
next one escalates; and after ten deep-review denials since the developer
last interacted (a new prompt, or answering an escalation), the next one
escalates even when allows were interspersed — only a human interaction
resets that count. Denials from terminal thresholds and command rules never
count toward either limit and always stand. Both limits apply in enforce
mode only and are tunable in the client settings' admin-only `enforcement`
block: `escalate_after_consecutive_denies` (default 2) and
`escalate_after_accumulated_denies` (default 10).

### Enforcement modes

The client's enforcement mode controls what blocking review does with its
judgments:

- **Enforce.** Decisions take effect: denials block the call, escalations
  wait for a human.
- **Observe.** The pipeline still grades calls and records what it would
  have decided, but never intervenes (with Codex, observe-mode grading
  happens after each call has run). Useful for tuning thresholds before
  turning enforcement on.
- **Paused.** LLM grading stops, but deterministic checks still apply: a
  command rule or an always-deny tool threshold still denies. Calls that
  would have needed grading or escalation fall through to the coding
  agent's own permission flow.

A [`.watcher-ignore`](../client/watcher-ignore.md) file skips LLM grading the
same way for the sessions it covers, and keeps them from being uploaded, but it
never turns off these deterministic checks.

## Trailing review

Trailing review runs asynchronously on the Watcher server, on a periodic
schedule over recently active sessions — nothing on the developer's machine
triggers or configures it. Two built-in jobs run today:

- **Summaries.** The built-in Summarize grader produces a title and short
  summary for each session: an early first summary while the session is
  still active, an updated one once it goes quiet, and periodic refreshes
  for sessions that never go quiet.
- **Deep review.** Sessions the blocking pipeline scored highly get a
  second, more thorough security review once they settle.

Trailing review never blocks the agent and never feeds anything back into
the agent's context. Its grades appear in the Analyzer, alongside grades
from any other grader. Sessions whose scores reach your organization's flag
threshold are flagged for review and can trigger a Slack notification (see
[organization settings](organization-settings.md)).

### What drives the schedule when you self-host

A `xylon-grading-scheduler` container triggers trailing review every five
minutes by default. It asks the API to grade each organization's recent
sessions. The API performs the grading; the scheduler reads no session
content, holds no provider keys, and contacts no provider. It runs the
`xylon-api` image with a different entrypoint, so it is not a separate
image to deploy or review.

The scheduler is the only component that triggers trailing review. The
`async-grading` Compose profile starts it, and `.env.example` enables the
profile for new installs. Without the scheduler, a deployment records
sessions but does not run trailing review. See
[Update Watcher](../self-hosting/updates.md) for update instructions.

## Choosing what goes where

| | Blocking review | Trailing review |
| --- | --- | --- |
| Evaluates | one tool call, before it runs | the trajectory so far |
| Can interrupt the agent | yes | no |
| Latency budget | seconds; the agent is paused | generous; runs behind the session |
| Suited to | irreversible single actions: destructive commands, secret exposure, dangerous infrastructure changes | cross-turn patterns: wrong diagnoses, premature completion claims, ignored instructions, scope creep |
| Results appear in | the Watcher client (live) and the Analyzer (as decisions) | the Analyzer (as grades) |
