Skip to content
BETAGuardrails are in beta. The library, the configuration format and the CLI command can still change.

Guardrails / Collectors

Code coverage

The coverage the build already measured, read from whichever report format it left behind and normalized into one shape: the line and branch totals across every report, and a summary per file. The covered and missed line numbers themselves are left out unless lineDetail asks for them, because a large repository's line map is bigger than the facts document is allowed to be. Reports that name the same file are merged line by line, so a per module report and a merged one agree, but a report carrying only totals adds to them.

Facts keycoverage
Versionv1
Scriptcoverage.py
Timeout60 seconds

Inputs

InputDescriptionDefaultEnvironment
reportsComma separated globs matched against every file in the working directory and against bare file names, naming the coverage reports to read.jacoco*.xml,jacocoTestReport.xml,cobertura*.xml,coverage*.xml,lcov.info,coverage-final.jsonGUARDRAIL_INPUT_REPORTS
maxFilesMost files to carry in byFile, the ones with the most uncovered lines first. The totals stay whole when files are dropped.500GUARDRAIL_INPUT_MAXFILES
lineDetailtrue to carry the covered and missed line numbers of every file in byFile, which a guardrail over the lines a change touched needs and everything else pays for in size.falseGUARDRAIL_INPUT_LINEDETAIL

A guardrail that declares an input of the same name passes it through, so the guardrail's configuration in buildnote.json is what decides these values.

A guardrail asks for these facts by name, and reads them back the same way:

json
{
  "collect": ["coverage"]
}
python
coverage = guardrail.facts("coverage")

Facts

The document coverage collects. A path carrying [] is an entry of the list before it, and one carrying [path] is a key of the object before it.

FactMeaning
sourcereport when a coverage report was read, none when there was none.
reasonWhy nothing was read, present only when source is none.
reportsEvery report that was read, in path order.
reports[].pathPath of the report, relative to the directory the CLI runs in.
reports[].modifiedWhen the report was last written, ISO 8601, so a stale report is recognisable.
reports[].formatFormat it was read as: jacoco, cobertura, istanbul or lcov.
lines.coveredLines at least one report saw executed, counted once across every report that names the file.
lines.missedLines every report saw unexecuted.
lines.percentCovered lines as a percentage of the lines the reports measured, to two decimal places, 0.0 when they measured none.
branchesThe branch totals, or null when no report that was read carries branch coverage.
branches.coveredBranches taken.
branches.missedBranches never taken.
branches.percentTaken branches as a percentage of the branches the reports measured, to two decimal places.
filesHow many files the reports measured, before maxFiles was applied.
byFileA summary per file, the ones with the most uncovered lines first, up to maxFiles.
byFile[path].coveredLines of that file at least one report saw executed.
byFile[path].missedLines of that file every report saw unexecuted.
byFile[path].percentCovered lines of that file as a percentage, to two decimal places.
byFile[path].coveredLinesThe covered line numbers, ascending, present only when lineDetail is true.
byFile[path].missedLinesThe missed line numbers, ascending, present only when lineDetail is true.
droppedFiles left out of byFile because maxFiles was reached.

A collector that cannot finish prints what it had along with an incomplete key saying why, so the facts below the point it stopped are absent. A check that cares reads incomplete before the rest.

Example facts

The example facts collected by coverage collector:

json
{
  "source": "report",
  "reports": [
    {
      "path": "jacocoTestReport.xml",
      "modified": "2026-03-04T10:15:00Z",
      "format": "jacoco"
    }
  ],
  "lines": {
    "covered": 4,
    "missed": 3,
    "percent": 57.14
  },
  "branches": {
    "covered": 1,
    "missed": 3,
    "percent": 25.0
  },
  "files": 2,
  "byFile": {
    "com/acme/widget/Broker.kt": {
      "covered": 1,
      "missed": 2,
      "percent": 33.33
    },
    "com/acme/widget/Queue.kt": {
      "covered": 3,
      "missed": 1,
      "percent": 75.0
    }
  },
  "dropped": 0
}

Collected for

GuardrailCategoryInputs
tests/coverage-floortestsnone

All collectors

Buildnote Limited
Registered in England and Wales, Reg: 16140412