# Verdicts

> The three verdicts agentjail can return, what triggers each one, and what the agent receives for each outcome.

Every tool call evaluated by agentjail produces exactly one of three verdicts:
**allow**, **ask**, or **deny**.

## Allow

A call is allowed when no `deny` or `ask` rule in the active policy fires.
Allowed calls pass through to the shell, filesystem, or network exactly as the
agent intended. agentjail exits with status code 0 and does nothing further.

## Ask

A call produces an **ask** verdict when an `ask` rule fires and no `deny` rule
fires. The agent is prompted to confirm before the call proceeds. agentjail
exits with status code 0 for ask verdicts (the agent handles the confirmation
UI).

## Deny

A call is denied when at least one `deny` rule fires and produces a message.
It only takes one rule to block a call: if multiple rules match, the call is
still denied (with the message from whichever rule fired, or all of them,
depending on how the policy is structured).

When a call is denied:

- agentjail exits with **status code 2** (the Claude fast-block convention).
- A **structured block message** is returned to the agent describing why the
  call was blocked.
- The command **never reaches the shell**. No side effects occur.

### What the agent sees

The hook returns a Claude-format JSON verdict to the agent:

```json
{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "deny",
    "permissionDecisionReason": "Blocked: command targets sensitive path ~/.ssh/"
  }
}
```

The `permissionDecision` field is `"allow"`, `"ask"`, or `"deny"`. The
`permissionDecisionReason` comes from the `msg` binding in the matching Rego
rule. For example, if this rule fires:

```rego
deny[msg] {
  input.tool_name == "Bash"
  contains(input.tool_input.command, "/.ssh/")
  msg := "Blocked: command targets sensitive path ~/.ssh/"
}
```

The agent receives the JSON above with `permissionDecision: "deny"` and stops
rather than proceeding. In practice, most agents will report the denial to the
user and wait for further instruction.

### Testing a verdict

You can evaluate any tool call against your active policy directly from the
command line while the daemon is running:

```sh
echo '{"hook_event_name":"PreToolUse","tool_name":"Bash","tool_input":{"command":"rm -rf ~/.ssh/"}}' \
  | agentjail-hook
```

A denied call exits with code 2 and prints the denial reason. An allowed call
exits with code 0.

You can also unit-test your Rego policy files directly with `opa test` without
needing the daemon running.

### Fail-open on internal error

If the hook encounters an internal error (for example, cannot reach the daemon),
it fails open: it allows the call and exits with code 0. This ensures agent
work is not silently interrupted by infrastructure issues, but you should
monitor `agentjail status` and `agentjail logs` to catch daemon problems early.

## The deny-by-rule model

The semantics are worth stating plainly: **allow is the default**. If no rule
matches, the call goes through. You write rules to deny (or ask about) specific
things rather than rules to allow a specific set of things. This keeps policies
focused and easy to read: each rule expresses exactly one thing you want to
block or confirm.

See [The policy model](/docs/concepts/policy-model) for how `deny` and `ask`
rules are written and what fields are available in `input`.
