Browse docs
Docs /signals/overview
The four signals

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 Does the diff match its stated intent?
izri/tests Are the tests for this change any good — do they pass, run the changed lines, and check anything?
izri/hallucination Is what the change says about itself true?
izri/visual Did the rendered UI change?

All four report through one GitHub check run, Izri, whose conclusion is the izri/quality umbrella verdict and whose body lists each signal.

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 tests Executed coverage shows no test ran the changed lines of a file. The silent-regression case.
test_failure tests The test run failed. One red test fails the verdict, however high the composite.
runner_error tests The run broke before producing results. No evidence never resolves to "safe".
evidence_missing tests The project uploads from CI and nothing arrived for this commit within the grace window.
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."

A project can add opt-in hard rules through its merge policy — a minimum diff coverage (block.diff_coverage_below), zero-test runs, sensitive paths, a visual diff limit. They are off by default.

2. Soft signals — weighted composite

Everything else feeds a weighted average that must reach 70.

Signal Weight
Hallucination score 40
Tests score — the lower of pass rate and diff coverage 36
Scope alignment 24
Visual diff 15

Weights are relative, not fractions: with all four signals present, hallucination is 40 of 115. These are the defaults; a project can change them in its merge policy.

Izri judges the delta a PR introduces, so every number here is about the changed lines. The rest of the project is context, never what's graded — coverage means diff coverage.

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 Lowers its signal's score, except advisory test-quality findings, which are shown but not scored. Does not block on its own.
info A "look here" pointer. Never blocks; costs 2 points where findings are scored.

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.

An LLM can't block. A finding backed only by a model's judgment is capped at warn, whatever severity the model assigned. Only findings with tool evidence — coverage, an import resolver, test results — can reach error.

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.

Tests

Category Severity Meaning
test_failure error The test run failed. Hard rule.
runner_error error The run broke before producing results. Hard rule.
evidence_missing error No upload arrived from CI within the grace window. Hard rule.
test_evidence_untrusted warn Green evidence from a PR that edited what produced it. Hard, opt-in via block.untrusted_evidence.
change_uncovered error No test exercised the changed lines. Hard rule.
change_partially_covered warn Some changed lines were exercised, some weren't.
low_diff_coverage warn Under 60% of the added lines were executed. Named low_overall_coverage before September 2026.
file_not_instrumented info The coverage tool never saw the file. Could be a filter, could be a gap.
diff_coverage_below error Diff coverage is under the project's configured minimum. Hard rule, opt-in via block.diff_coverage_below. Silent when change_uncovered already fired.
zero_tests error A passing run executed no tests. Hard rule, opt-in via block.zero_tests.
change_lacks_test warn Advisory, not scored. A changed source file has no changed test importing it.
weak_assertion warn Advisory, not scored. An LLM judged the assertions too weak to catch a regression.
assertion_free_test warn Advisory, not scored. A test block this PR touched contains no assertion.
tautological_assertion warn Advisory, not scored. An assertion that cannot fail.
test_mocks_subject warn Advisory, not scored. A test mocks a module this PR changed that no test in the PR runs unmocked.
test_passes_without_change warn Advisory, not scored. A test this PR wrote passes with the change reverted.

Hallucination

Category Severity Meaning
import_unresolved warn An import on an added line matches no file in the repository.
export_missing warn A named import a first-party module does not export.
dependency_undeclared warn A bare import of a package no package.json declares.
package_not_found warn A newly added dependency the npm registry has never seen.
package_suspicious warn A newly added dependency under 30 days old with under 1,000 weekly downloads.
env_undeclared info An env var read that no template, env config or workflow declares.
reference_unparsed_import info An import whose specifier is computed (not a string literal) was not checked.
reference_file_unparseable info The file has syntax errors; its import list may be incomplete.
change_unverified warn Every selected test still passed with the change reverted: nothing depends on this change.
verification_evidence_untrusted warn The PR could have shaped its own test evidence; the revert check is inconclusive.
verification_skipped_budget info Too many tests selected for the revert check; nothing ran.
analyzer_stage_error info One analysis stage crashed; the others still reported. Appears on the signal that owns the stage.

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".

Reading this with an agent? /docs/signals/overview.md serves the raw markdown.

Edit this page on GitHub →All docs