2. Run the catalog

The built-in rules, how to read a finding, and how to fail a build on one.

agni check runs the shipped rule catalog over one design. These are the general rules, the ones that are true of most boards regardless of whose board it is. Your team’s own rules come later, in rungs 4 through 8.

Run it

$ agni check designs/gateway/gateway.edn
findings by rule:
  copper-clearance       12
  decoupling-present     1
  esd-protection         2
  gateway-profiles/can-esd-missing 2
  gateway-profiles/can-host-incomplete 1
  gateway/signal-net-naming 2
  i2c-pull-up            2
  intent/voltage-domain-mismatch 1
  reverse-blocking-absent 1
  supply-exceeds-abs-max 1
  test-point-coverage    2
  track-width            1

first 28:
  [error] copper-clearance: CAN1_CANH (copper of "CAN1_CANH" and "I2C_SCL" closer than 0.127mm at 1 place(s); worst gap -0.175mm near (5.00, -84.00)mm)
  [error] copper-clearance: CAN1_CANL (copper of "CAN1_CANL" and "I2C_SDA" closer than 0.127mm at 1 place(s); worst gap -0.250mm near (5.00, -85.50)mm)
  [error] copper-clearance: CAN1_RXD (copper of "CAN1_RXD" and "PMIC_PG" closer than 0.127mm at 1 place(s); worst gap -0.250mm near (5.00, -79.50)mm)
  [error] copper-clearance: CAN1_RXD (copper of "CAN1_RXD" and "PMIC_CORE_3V3" closer than 0.127mm at 1 place(s); worst gap -0.250mm near (5.00, -79.50)mm)
  [error] copper-clearance: CAN1_TXD (copper of "CAN1_TXD" and "PMIC_MAIN_12V0" closer than 0.127mm at 1 place(s); worst gap -0.250mm near (5.00, -78.00)mm)
  [error] copper-clearance: CAN1_TXD (copper of "CAN1_TXD" and "MCU_NRST" closer than 0.127mm at 1 place(s); worst gap -0.250mm near (5.00, -78.00)mm)
  [error] copper-clearance: GND (copper of "GND" and "XTAL_OUT" closer than 0.127mm at 1 place(s); worst gap -0.250mm near (5.00, -82.50)mm)
  [error] copper-clearance: MCU_NRST (copper of "MCU_NRST" and "PMIC_MAIN_12V0" closer than 0.127mm at 1 place(s); worst gap -0.250mm near (5.00, -78.00)mm)
  [error] copper-clearance: PMIC_CORE_3V3 (copper of "PMIC_CORE_3V3" and "PMIC_PG" closer than 0.127mm at 1 place(s); worst gap -0.250mm near (5.00, -79.50)mm)
  [error] copper-clearance: PMIC_EN (copper of "PMIC_EN" and "XTAL_IN" closer than 0.127mm at 1 place(s); worst gap -0.250mm near (5.00, -81.00)mm)
  [error] copper-clearance: PMIC_EN (copper of "PMIC_EN" and "PMIC_IO_1V8" closer than 0.127mm at 1 place(s); worst gap -0.250mm near (5.00, -81.00)mm)
  [error] copper-clearance: PMIC_IO_1V8 (copper of "PMIC_IO_1V8" and "XTAL_IN" closer than 0.127mm at 1 place(s); worst gap -0.250mm near (5.00, -81.00)mm)
  [warning] decoupling-present: PMIC_MAIN_12V0 (power rail has no decoupling capacitor)
  [info] esd-protection: CAN1_CANH (externally-exposed signal net has no ESD protection)
  [info] esd-protection: CAN1_CANL (externally-exposed signal net has no ESD protection)
  [warning] gateway-profiles/can-esd-missing: CAN1_CANH (CAN signal net CAN1_CANH is exposed on a connector with no ESD protection in reach)
  [warning] gateway-profiles/can-esd-missing: CAN1_CANL (CAN signal net CAN1_CANL is exposed on a connector with no ESD protection in reach)
  [error] gateway-profiles/can-host-incomplete: U4 (CAN host U4 declares the interface but is missing required signal STB)
  [warning] gateway/signal-net-naming: XTAL_IN (net name matches no allowed naming pattern)
  [warning] gateway/signal-net-naming: XTAL_OUT (net name matches no allowed naming pattern)
  [error] i2c-pull-up: I2C_SCL (I2C net has no pull-up resistor to a rail)
  [error] i2c-pull-up: I2C_SDA (I2C net has no pull-up resistor to a rail)
  [warning] intent/voltage-domain-mismatch: PMIC_IO_1V8 (rail "PMIC_IO_1V8" is declared in voltage domain "core" (3.3V) but its name declares 1.8V)
  [warning] reverse-blocking-absent: PMIC_MAIN_12V0 (connector feeds a power input with no reverse-blocking element in the path)
  [error] supply-exceeds-abs-max: U2 (power-input pin 1 on rail "PMIC_CORE_3V3": nominal 3.3V exceeds absolute-maximum VIN 3V — datasheet "ACME-LDO-1V8 (placeholder, not transcribed)" page 0, "" (mock, confidence 0.3))
  [info] test-point-coverage: GND (rail carries no test point; bring-up and factory test cannot probe it)
  [info] test-point-coverage: PMIC_MAIN_12V0 (rail carries no test point; bring-up and factory test cannot probe it)
  [error] track-width: CAN1_CANH (net has 1 track segment(s) narrower than the 0.127mm fabrication floor)

28 finding(s) total
258 subject(s) considered by 36 rule(s), 7 not considered (--verdicts for the detail)

Reading one finding

Take [error] i2c-pull-up: I2C_SCL (I2C net has no pull-up resistor to a rail). It has four parts.

The severity is error. The rule is i2c-pull-up. The subject is the net I2C_SCL, naming the specific thing on your board the rule is talking about. The reason in parentheses says what is wrong in plain language.

Severity is a policy signal, not a confidence signal. An error is something that will almost certainly not work: an I2C bus with no pull-up cannot signal at all, because the parts on it can only pull the line down and nothing pulls it back up. A warning is something that usually indicates a defect. An info is worth a look. None of them is a statement about how sure the tool is.

profile/can-esd-missing fires alongside esd-protection on the same two nets. That is not a duplicate. The general rule notices any externally exposed signal with no protection. The CAN profile knows those two nets are a CAN bus and applies what CAN specifically requires. Rung 5 is about that second kind.

Where a finding came from

The text form is a summary. --format json gives you the whole thing:

agni check designs/gateway/gateway.edn --format json
{
  "rule": "i2c-pull-up",
  "severity": "error",
  "subject": {
    "kind": "net",
    "ref": "I2C_SCL",
    "netId": "601209543ef5"
  },
  "message": "I2C net has no pull-up resistor to a rail",
  "inconclusive": false,
  "provenance": {
    "sourceFile": "designs/gateway/gateway.edn",
    "nativeId": "I2C_SCL",
    "nativeIdKind": "edif-rename-id"
  },
  "sheets": ["graph"],
  "datasheets": []
}

provenance points back at the file and at the identifier the source file itself used, which is how you get from a finding to the thing in your CAD tool. datasheets is empty here and carries the page and table a datasheet-backed finding rests on, which rung 6 covers. inconclusive marks a check that ran but could not decide, which matters enough to get its own rung later.

Two other formats are useful early. --format markdown organizes by severity, worst first, for pasting into a review. --format report is the long form with each rule’s full explanation.

Narrowing

Run one rule while you work on it:

agni check designs/gateway/gateway.edn --rule i2c-pull-up

Or a whole category:

agni check designs/gateway/gateway.edn --tag category=power

Failing a build

--fail-on makes check exit non-zero when anything at or above a severity is present, which is all you need to put it in CI:

$ agni check designs/gateway/gateway.edn --fail-on error
exit 2

The board has two error findings, so the command fails. With those gone it passes:

$ agni check designs/gateway/gateway.edn --fail-on error --rule test-point-coverage
exit 0

Starting at --fail-on error is the practical choice. It gates on the things that will not work at all, which almost nobody argues with, and it lets you tighten to warning later once the backlog is clear.

What the run says it looked at

Look at the last line of that first run again. Every run ends with it, whether or not it found anything:

258 subject(s) considered by 36 rule(s), 7 not considered (--verdicts for the detail)

This is the half a findings list cannot give you. A run that finds nothing and a run whose rules all examined the wrong thing produce an identical list of findings, namely none, so the findings alone can never tell you which one you are holding.

Read the three numbers separately. 258 considered is how many subjects were actually judged. 36 rules is how many were willing to say what they looked at, which is not the same as how many ran: most of the catalog has no subject in scope on any given board, and a rule with nothing to say is not a gap. 7 not considered is the one worth reading closely, and it gets its own look below.

A second line used to sit under that one, and its absence is the thing to notice:

2 rule(s) reported violations without stating what they examined, so silence from those is not evidence of anything

It counts rules that found something and never said what they looked at, so silence from them means nothing and the coverage number does not cover them. It is printed whenever the count is above zero, and on this board it now reads zero and is omitted entirely. It was 3 before the design-intent rules gained a considered set, then 2, now none. The rules you write for your own board were the last ones whose silence meant nothing, and they are the ones you most want a considered set from.

You will still see the line on a board of your own, and on any run whose read went wrong: the broken schematic from rung 1 reports 30 such rules. Treat it as the honest edge of the coverage claim above it.

That line is the claim. --verdicts is the evidence, one row per subject with passes included:

$ agni check --verdicts --rule decoupling-present designs/gateway/gateway.edn
decoupling-present  1 fail, 2 pass
    fail  PMIC_MAIN_12V0  no capacitor sits on the rail, which feeds at least one power-input
                          pin
    pass  PMIC_CORE_3V3   capacitor C1 sits on the rail
    pass  PMIC_IO_1V8     capacitor C3 sits on the rail

3 verdicts across 1 rule(s), 2 pass, 1 fail

Now the pass is checkable. It names C1 and C3, so you can open the schematic and confirm that those capacitors really are on those rails. Delete C1 next revision and this output changes, where the findings-only view would print the same nothing before and after.

Across the whole catalog that is a much larger table than the findings list, which is why the summary is the default and the rows are a flag:

$ agni check --verdicts designs/gateway/gateway.edn
265 verdicts across 36 rule(s), 230 pass, 28 fail, 7 not-considered

not-considered is the third outcome and the one with no counterpart in a findings list: the rule was willing to judge that subject and something stopped it, so it says what stopped it rather than passing on incomplete evidence.

On this board only one of them wants a datasheet value of the kind you seed in rung 6. The rest are the more interesting sort. Four are floating-input declining a net that carries a passive part, because a resistor on a net might be the pull that fixes it, might be a series element with the driver on the far side, or might be a footprint nobody stuffed, and a netlist cannot tell those apart. Two are esd-clamp-not-tvs handing a bare net to esd-protection, which is the rule that reports it. Neither is a gap you fill by seeding anything. They are the check telling you where its reach ends.

That distinction between “checked and fine”, “never checked” and “could not tell” runs through the whole tool, and rung 9 is entirely about reading it.

Next

See it, because a list of net names is not how anyone thinks about a board.