---
title: "Review approval statistics and test cards"
description: "Review approval statistics and test cards for people running and reviewing agents."
canonical_url: "https://docs.oz0.ai/agents/approvals/statistics"
---
# Review approval statistics and test cards

> Review approval statistics and test cards for people running and reviewing agents.

For: people running and reviewing agents.

## Approver statistics

Approvals only protect anyone when people read them. **Govern › Policy › Statistics** in the console and `oz0 approval stats` show how the tenant's approvals were decided over a period (30 days by default, at most 90), for the tenant, for each team and for your own decisions:

- how many decisions, the share approved and the share approved with edits;
- the median decision time, and the share decided within a few seconds of the card being shown (5 by default; `--quick` sets 1 to 120).

The console measures how long a card was on screen; a decision made elsewhere counts its whole wait, which is never shorter;

- the longest run of approvals in a row without a denial or an edit;
- decisions per day, and per day and person of the team;
- how many [test approvals](https://docs.oz0.ai/agents/approvals/statistics#test-approvals) the group caught. Test cards are rare, so with works-council mode on this figure needs its own five people (the mode's threshold): those who decided the group's test cards, not only those who decided anything. Until then it reads *hidden*, while the rest of the row shows.

```bash [Terminal]
oz0 approval stats                     # the last 30 days, quick within 5 seconds
oz0 approval stats --days 7 --quick 3 --json
```

The figures go through [works-council mode](https://docs.oz0.ai/govern/works-council-mode): a team's figures show only for a team of at least its threshold of people (five by default) of whom at least that many decided, the tenant's only once that many people decided, and your own row only to you.

Only accounts (`user:`) count as people toward that threshold: an API key or an operator's certificate is never one of the people a figure needs (their decisions are in the figures, and a key's or a certificate's own row shows to it). Nobody's figures are ever listed to anyone else.

A team's figures are its current members' decisions; a person in two teams counts in both.

Decisions are read from the audit trail's `run.approval.decide` entries on each request (at most 200,000; past that the figures say they are short), so nothing is stored per person.

When half or more of a team's (or the tenant's) decisions in the last seven days, at least 20 of them, came within 5 seconds of the card being shown, the edge opens an **approval pattern** finding (`approval_pattern`) about that queue, never about a person: it says what share came that fast and suggests what to look at (the queue's load, the cards' size, a rule that asks about calls nobody needs to see).

The tenant's admins and the `approval.pattern` [notification rules](https://docs.oz0.ai/admin/notifications) are told once; the finding closes by itself when the share falls, and `oz0 doctor` lists open ones.

When the seven days hold more decisions than one check reads (200,000), the check reads the newest, says so in a finding it opens (`window_cut`), and closes none.

## Load and floods

The edge hears of every approval when its ladder starts. It keeps what it needs for two checks, per job tree and agent, never per person, for a day:

- A flood. One job tree that asks more than 20 approvals within 10 minutes, or one agent more than 50 across the tenant, opens an **approval flood** finding (`approval_flood`) and tells the tenant's admins and the `approval.flood` rules once. A flood of cards is how rubber-stamping starts; look at what the agent asks and whether a rule should decide those calls.
- A loaded pool. When the people who may decide an approval have 10 or more open approvals each, its ladder steps sooner: each step not reached yet comes at half its time, so the owner and the admins hear earlier. The deadline never moves, still denies, and nothing ever approves on its own. The deciders' step then goes to a share of the pool chosen from the approval (at twice the load, half the pool; at least two people), so notices go round the pool instead of every person getting every notice.

## Test approvals

Test approvals measure how often reviewers catch a bad call. They are **off by default**, and a tenant's admins turn them on with a reason (`oz0 tenant test-approvals`, or **Govern › Policy › Statistics**). Turning them on, raising the rate or adding queues is a loosening: with [four eyes](https://docs.oz0.ai/govern/reviewed-changes#four-eyes-for-governance-changes) on, a second admin approves it.

Tell your people that test approvals exist before you turn them on. Where a works council or other employee representatives exist, their agreement may be needed first; record it with `--decision-ref`. This page does not say whether it is.

```bash [Terminal]
oz0 tenant test-approvals default --on --team payments --reason "agreed with the works council" --decision-ref WC-2026-14
oz0 tenant test-approvals default                       # the setting, and how many cards wait and were decided
oz0 tenant test-approvals default --off --reason "pilot over"
```

While they are on, a share of the tool approvals a queue is asked (2% by default, `--rate` 0.001 to 0.1, at most one card an hour per queue) draws a **test card** for the same agent and function, in the same team's queue (`--team` limits it to named teams; without it every queue may get one).

It shows five to sixty minutes later, so it never sits beside the call it was drawn from.

Its arguments come from a catalogue of values plainly wrong to an attentive reader, shaped by the function's input schema: an outside drop address as the recipient, an absurd or negative amount, an outside host, the system's password file as a path, a command that pipes a download into a shell, a message that forwards invoices elsewhere.

Every address and host is under the reserved `.example` domain. Nothing of a real call is copied.

Its card flags those arguments as out of scope, as a real card would.

Only the roles the catalogue holds are seeded, whatever role a tool's schema names.

A card goes only to a queue whose people would decide the real approval: a team that every rule naming its deciders names, or the tenant's queue when no rule names anyone; an approval only named people, or only admins, may decide draws none.

In dev mode (`server start --dev`), with test approvals on, the first approval of each hour in a queue draws a test card whatever the rate, and it shows at once, so that you can see one while you try the platform.

A test card is never run. It is a row the platform keeps apart from jobs: deciding it records the decision and reaches no workflow, starts nothing and calls nothing.

It appears only in the console's and the CLI's queue, to people signed in themselves who may decide it (never API keys, apps acting for a person, or people whose access reaches only their own jobs or a key's scopes), and never in notifications, webhooks or chat channels, where an automated receiver or a shared room would decide it. Its job link leads to no job page.

After deciding, the person is told it was a test and what was wrong: **a yes counts as a miss, a no as a catch**, and approving with edits that change every wrong value counts as a catch too.

A card nobody decides within 24 hours expires and counts as neither.

Catch rates show per team in the approver statistics and per agent in the inventory's **deployment profile**, both through works-council mode. Each decision is in the audit trail as `approval.test.decide`.

## Agreement per agent and action class

An agent's deployment profile in the inventory, and its scorecard, show **agreement** for each action class: how often people approved the agent's proposed calls of that class as proposed (without an edit), over the last 90 days or since the agent's latest drift event (an eval regression, a model change, or a drift finding), whichever is later. Beside it are the shadow rules' sums for the same agent and class: calls a shadow rule would have allowed that a person approved, and denied.

The **promotion threshold** holds when agreement is at least 95% over at least 50 decisions in that window. It is data, shown as "meets it" or "not yet": nothing suggests, raises or lowers a level from it. A class decided by fewer people than works-council mode's threshold is hidden.


## Sitemap

See the full [sitemap](https://docs.oz0.ai/sitemap.md) for all pages.
