# Severity and CI gating

> The five-level severity scale, --fail-on semantics, exit codes, and recipes for wiring CommitBrief into pre-commit hooks and CI pipelines.

CommitBrief docs · v1.x · Reviewing

Canonical URL: https://commitbrief.com/docs/1.x/severity

---

Findings are tagged with a severity. CommitBrief uses a five-level
scale and exposes the highest matched level via `--fail-on` for CI
gating.

## The five levels

Highest impact → lowest:

```
critical > high > medium > low > info
```

| Level | What it flags |
|-------|---------------|
| `critical` | Exploitable defects, data-loss bugs, regulatory violations, hard security holes (auth bypass, injection, key leak). Treat as release-blocking. |
| `high` | Likely defects with significant impact: race conditions, missing authorization checks, broken business invariants, severe performance regressions. Should be fixed before merge. |
| `medium` | Real bugs with moderate impact or significant maintainability issues: incorrect-but-recoverable error handling, missing input validation that downstream catches, expensive but non-critical inefficiencies. |
| `low` | Minor defects or notable improvements that do not block the PR: inconsistent style breaking project conventions, small efficiency wins, missed cleanup. |
| `info` | Pure informational notes: educational pointers, alternative approaches, "consider this for v2". Not a defect. |

The level *names* are part of the public JSON schema and cannot
be renamed. The level *definitions* come from the embedded
[`COMMITBRIEF.md`](/docs/1.x/review-rules#commitbriefmd--the-system-prompt) default —
your project-local `COMMITBRIEF.md` can tighten or relax them for
your codebase.

## Severity drives four things

- **`--fail-on=<severity>`** — "fail at this level or worse" (exit code).
- **`--min-severity=<severity>`** — hide findings below a level in
  the rendered output (display only).
- **Cards panel color** — severity-coded left border (red →
  orange → yellow → blue → grey).
- **`--compact` ordering** — sorted critical-first.

## The `--fail-on` flag

Maps the highest finding severity to an exit code. Case-insensitive.

```
--fail-on=<critical|high|medium|low|info|any|none>
```

| Value | Meaning |
|-------|---------|
| `""` (default) | Off. Pipeline failures still exit 1; findings do not. |
| `none` | Explicit off. |
| `any` | Any finding at any severity fails. |
| `critical` | Fail when one or more findings have severity `critical`. |
| `high` | Fail at `high` OR `critical`. |
| `medium` | Fail at `medium`, `high`, OR `critical`. |
| `low` | Fail at `low`, `medium`, `high`, OR `critical`. |
| `info` | Fail on any finding (since `info` is the lowest level). Equivalent to `any`. |

Anything else is a parse error before the review fires:

```
invalid --fail-on value "X" (expected: critical, high, medium, low, info, any, none)
```

## Exit codes

| Code | Meaning |
|------|---------|
| 0 | Review completed; no `--fail-on` threshold reached. |
| 1 | An error occurred (git failure, provider error, parse failure, guard abort, etc.) OR `--fail-on` threshold was reached. |

There is no distinction between "pipeline failed" and "`--fail-on`
matched" at the exit-code level — both exit 1. Stderr disambiguates:
pipeline failures print the underlying error; `--fail-on` matches
print `<N> finding(s) at or above '<severity>' severity`.

## Graceful degrade behavior

When the LLM produces unparseable JSON (and the one-shot retry
also fails), CommitBrief degrades to markdown rendering and the
findings list is nil. In that case `--fail-on` is **intentionally
skipped** and stderr prints:

```
ℹ --fail-on skipped: LLM produced unparseable output, no findings to evaluate.
```

Failing CI on a flaky model invocation is worse than letting the
run succeed and surfacing the markdown text — you still see the
review content.

## Display filter — `--min-severity`

`--min-severity=<level>` hides findings below the given severity in
the **rendered** output — cards, markdown, and `--copy`. It is a
display convenience for noisy reviews, not a gate.

```sh
commitbrief --staged --min-severity=high
```

Crucially, it never weakens CI:

- **`--json` stays complete.** The machine contract always carries
  every finding, regardless of `--min-severity`.
- **`--fail-on` evaluates the full, unfiltered set.** A `low`
  finding hidden from the display still trips `--fail-on=low`.

Accepts `critical | high | medium | low | info | none`; an invalid
value errors before the provider call. Use `--fail-on` to govern the
exit code and `--min-severity` to govern what you *see*.

## CI recipes

### GitHub Actions — fail on critical

```yaml
- name: CommitBrief review
  run: commitbrief --staged --fail-on=critical --no-cost-check
  env:
    ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
```

### The official GitHub Action

[`CommitBrief/commitbrief-action`](https://github.com/CommitBrief/commitbrief-action)
is a composite action that runs CommitBrief on pull requests in two
modes:

- **`comment`** (default) — posts each finding as an inline review
  comment and submits a verdict via
  [`commitbrief remote pr`](/docs/1.x/remote-pr). Needs
  `pull-requests: write`.
- **`gate`** — runs `commitbrief diff <base>...<head>
  --fail-on=<sev>` and fails the job on a finding at or above the
  threshold. No comments; only needs `contents: read`.

```yaml
name: CommitBrief
on: pull_request
permissions:
  contents: read
  pull-requests: write          # comment mode posts the review
jobs:
  review:
    runs-on: ubuntu-latest
    steps:
      - uses: CommitBrief/commitbrief-action@v1
        with:
          provider: anthropic
          api-key: ${{ secrets.ANTHROPIC_API_KEY }}
          # mode: comment             # default
          # request-changes-on: high  # default: critical
```

Gate mode (pass/fail only, no comments):

```yaml
      - uses: CommitBrief/commitbrief-action@v1
        with:
          provider: openai
          api-key: ${{ secrets.OPENAI_API_KEY }}
          mode: gate
          fail-on: high
```

Pin `version:` to a released tag (e.g. `v1.4.0`) for reproducible
CI. CLI-tool providers (`claude-cli` / `gemini-cli` / `codex-cli`) are
not usable in CI — they need a locally-authenticated host CLI.

### GitLab CI — save review JSON

```yaml
review:
  script:
    - commitbrief --staged --fail-on=high --quiet --json --output review.json
  artifacts:
    paths: [review.json]
```

### Pre-commit hook

The generated pre-commit hook embeds
`--fail-on=critical --quiet --no-cost-check` already — install it
with one command:

```sh
commitbrief install-hook
```

See [Git hooks](/docs/1.x/git-hooks).

## Interaction with other guards

Each guard exits 1 independently of `--fail-on` — guards run
before the review completes; `--fail-on` only evaluates after a
successful provider call.

| Guard | Exit on abort |
|-------|---------------|
| `.commitbrief/` pre-send guard | 1 with `aborted by pre-send guard`. |
| Secret scanner | 1 with `aborted: pre-send secret scanner`. |
| Cost preflight | 1 with `aborted: cost preflight`. |
| `--fail-on` | 1 with the count + severity label. |

See [Safety and cost](/docs/1.x/safety-and-cost) for what each
guard does and how to opt out.