# Policy gate — guard

> A declarative merge gate that caps how many findings of each severity a change may carry, via an opt-in .commitbrief/policy.yml. Richer than a single --fail-on threshold; aimed at gating high-volume, often AI-authored, pull requests.

CommitBrief docs · v1.x · Operations

Canonical URL: https://commitbrief.com/docs/1.x/policy-gate

---

`commitbrief guard` is a **declarative merge gate**. Instead of one
severity cutoff, it evaluates a review's actionable findings against a
`.commitbrief/policy.yml` and exits non-zero when the policy is
breached. Think of it as a per-severity *budget* rather than the single
`--fail-on=<severity>` threshold — useful when you're gating
high-volume, often AI-authored, pull requests. New in v1.10.0
(ADR-0029).

## The policy file

The gate is **opt-in**: with no `.commitbrief/policy.yml` there is no
gate. Create one to declare the budget:

```yaml
# .commitbrief/policy.yml
version: 1
thresholds:        # max findings allowed per severity (omit or ~ = unlimited)
  critical: 0
  high: 0
  medium: 5
  low: ~
total: 20          # optional overall cap across all severities
```

A severity you omit (or set to `~`) is unlimited. `total` is an
optional cap on the combined count.

## Two ways to run it

### Run-mode — review, then evaluate

Run-mode reviews the diff (reusing the standard pipeline) and judges
the result against the policy:

```sh
commitbrief guard                     # staged diff
commitbrief guard --unstaged
commitbrief guard --diff main...HEAD
```

### Consume-mode — evaluate an existing review

`--from-json` evaluates a review you already produced, with **no
provider call** — so an agent's [MCP self-review](/docs/1.x/mcp-server)
or a prior `commitbrief --json` run can be gated cheaply:

```sh
commitbrief --json --staged > review.json
commitbrief guard --from-json review.json
```

`--from-json -` reads the review from stdin — which is also how a
[credential audit](/docs/1.x/leaks) gets gated, since `leaks --json`
emits the same schema-v1 document:

```sh
commitbrief leaks --json --fail-on none | commitbrief guard --from-json -
```

There was no model call in that pipeline (`meta.provider` is
`"builtin"`, `cost_usd` is `0`), and guard neither knows nor cares.

## What it evaluates

`guard` judges the findings that survive
[**baseline + suppression**](/docs/1.x/signal-control) (signal control)
— exactly the set `--json` shows. Baselined and inline-suppressed
findings are already gone before the policy sees them.

## Exit codes

The exit code is **0 (pass)** or **non-zero (blocked)**. Critically, a
**load or parse failure also blocks**: a missing or malformed policy, or
an unparseable review, fails the gate rather than passing it — a merge
gate must not pass when it cannot prove the change is within policy.

`--json` emits a machine-readable verdict:

```json
{
  "passed": false,
  "counts": { "critical": 0, "high": 2, "medium": 1 },
  "total": 3,
  "violations": ["high: 2 > 0"]
}
```

## guard vs. `--fail-on`

[`--fail-on=<severity>`](/docs/1.x/severity) is the simple
one-threshold gate — "fail if any finding meets or exceeds this
severity." `guard` is the per-severity budget — "allow up to N of each."
They complement each other; use either or both. A repo might run
`--fail-on=critical` on a developer's pre-commit hook and the richer
`guard` policy on the CI merge gate.

> Rule-id-scoped allow/deny lists are not yet supported — findings carry
> no stable rule id. The policy is severity- and total-count-based for
> now.

## Flags

| Flag | Notes |
|------|-------|
| `--policy <path>` | Policy file location. Default `.commitbrief/policy.yml`, resolved relative to the repo root. |
| `--from-json <file\|->` | Consume-mode: evaluate a prior schema-v1 review (no provider call). `-` reads stdin. |
| `--unstaged` | Run-mode: review the working tree instead of the staged diff. |
| `--diff <range>` | Run-mode: review an arbitrary `git diff` range. |
| `--json` | Emit the machine-readable verdict. |

`--provider` / `--model` / `--no-flaky` carry over from the persistent
flags in run-mode, as do the path filters (`--file`, `--dir`,
`--exclude-file`, `--exclude-dir`) and the
[commit filters](/docs/1.x/review-scopes#commit-filters-v1150)
(`--author`, `--committer`, `--start-date`, `--end-date`, `--text`,
`--max-commits`, `--merges`) from v1.15.0.

> **Sandbox-rerun never runs under `guard`.** Even with
> `review.sandbox_command` configured, the gate will not execute your
> repository's tests — unconditionally, with no toggle. Its flaky
> findings stay at the static-only confidence level. See
> [Flaky-test detector](/docs/1.x/flaky-tests#never-in-an-agent-context).

## See also

- [Severity and CI gating](/docs/1.x/severity) — `--fail-on`, the
  simpler one-threshold gate guard complements.
- [Signal control](/docs/1.x/signal-control) — baseline + suppression,
  the filters applied before guard evaluates.
- [MCP server](/docs/1.x/mcp-server) — produce a review an agent can
  feed to `guard --from-json`.
- [Output formats](/docs/1.x/output-formats) — the JSON schema v1 that
  consume-mode reads.