---
title: Signals overview
description: How four independent signals aggregate into one izri/quality verdict — every weight, every hard rule, and what each severity does.
order: 10
---

# Signals overview

Izri computes four independent signals and aggregates them into a single `izri/quality` verdict. This page is the aggregation model in full: if you want to predict whether a given finding will block your merge, everything you need is here.

## The four children

| Check | Question |
| --- | --- |
| [`izri/scope`](/docs/signals/scope) | Does the diff match its stated intent? |
| [`izri/tests`](/docs/signals/tests) | Did the relevant tests run and pass? |
| [`izri/hallucination`](/docs/signals/hallucination) | Did those tests exercise the changed lines? |
| [`izri/visual`](/docs/signals/visual) | Did the rendered UI change? |

Each publishes as its own GitHub check alongside the `izri/quality` umbrella.

## Two ways to fail

The umbrella aggregates the children through two independent mechanisms. Understanding which one fired is the whole diagnostic.

### 1. Hard rules — worst-of

One hard finding fails the umbrella regardless of every other signal. There is no averaging, no override, no threshold to clear. The list is deliberately short:

| Hard rule | Source | Why it's absolute |
| --- | --- | --- |
| `pr_type_forbidden_category` | scope | The diff changed a category the PR's declared type forbids. |
| `change_uncovered` | hallucination | No test exercised the diff at all. The silent-regression case. |
| *analyzer error* | any | An analyzer crashed. Signal-agnostic — triggered by the row's `error` status, not a finding category. |

An analyzer crash being a hard rule is deliberate. A crashed analyzer produces no findings, and "no findings" must never be mistaken for "nothing wrong."

### 2. Soft signals — weighted composite

Everything else feeds a weighted average that must reach **70**.

| Signal | Weight |
| --- | --- |
| Hallucination coverage | 0.40 |
| Test pass rate | 0.36 |
| Scope alignment | 0.24 |
| Visual diff | 0.15 |

Hallucination carries the most weight on purpose: a partially covered diff should drag the composite down even when scope and tests look clean.

**Absent signals redistribute, they don't penalize.** Weights are proportionally renormalized across the signals that actually ran. A backend PR that skips `izri/visual` scores exactly as it would have if the visual signal didn't exist. Skipped is not zero.

## Severities

Every finding carries `info`, `warn`, or `error`. Only `error` blocks by default.

| Severity | Effect |
| --- | --- |
| `error` | Blocks. Hard rules are emitted at this level. |
| `warn` | Contributes to the composite. Does not block on its own. |
| `info` | Advisory. A "look here" pointer with no gating effect. |

This is why `sensitive_path` doesn't fail your build — it is emitted at `info`. It flags that a diff touched something worth a human glance, which is not the same as saying the diff is wrong.

## Full findings reference

Every category the analyzers emit, and what each one does:

### Scope

| Category | Severity | Meaning |
| --- | --- | --- |
| `pr_type_forbidden_category` | `error` | Change in a category the declared PR type forbids. **Hard rule.** |
| `pr_type_mismatch` | `warn` | The diff doesn't look like what the PR type claims. |
| `test_removal` | `warn` | Tests were deleted. |
| `scale_anomaly` | `warn` / `info` | The diff is much larger or smaller than the stated intent implies. |
| `structural_drift` | `info` | Module boundaries shifted. |
| `sensitive_path` | `info` | A path your config marks sensitive was touched. |

### Hallucination

| Category | Severity | Meaning |
| --- | --- | --- |
| `change_uncovered` | `error` | No test exercised the changed lines. **Hard rule.** |
| `change_partially_covered` | `warn` | Some changed lines were exercised, some weren't. |
| `low_overall_coverage` | `warn` | Project coverage is low enough to weaken the signal. |
| `file_not_instrumented` | `info` | The coverage tool never saw the file. Could be a filter, could be a gap. |

## The three umbrella states

| State | Meaning |
| --- | --- |
| `passing` | No hard rule fired, composite ≥ 70. |
| `failing` | A hard rule fired, or composite < 70. |
| `unknown` | Not enough signal — delta in flight, or every child skipped. |

Treat `unknown` as "wait", never as "go".

## Related

- [Your first verdict](/docs/start/your-first-verdict) — reading this on a real PR.
- [Verdicts and exit codes](/docs/reference/verdicts-and-exit-codes) — the CI mapping.
- [`.izri/scope.yml`](/docs/configure/scope-yml) — tuning the scope signal.
