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

Guardrails / Collectors

Code owners

The CODEOWNERS file the repository carries, read as rules rather than as text: every pattern in file order with the owners it names, how many rules each owner is named by, and the owners that apply to the paths a guardrail asks about, resolved the way GitHub resolves them, where the last rule to match a path is the one that owns it.

Facts keycodeowners
Versionv1
Scriptcodeowners.py
Timeout60 seconds

Inputs

InputDescriptionDefaultEnvironment
pathsComma separated paths to resolve owners for, relative to the repository root. A guardrail that cares about the files a pull request changed names them here.README.md,LICENSEGUARDRAIL_INPUT_PATHS

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": ["codeowners"]
}
python
codeowners = guardrail.facts("codeowners")

Facts

The document codeowners 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
pathPath of the CODEOWNERS file that was read: the first of CODEOWNERS, .github/CODEOWNERS and docs/CODEOWNERS that exists, the three locations GitHub reads a CODEOWNERS file from.
rulesEvery rule the file declares, in file order, which is the order they are resolved in.
rules[].patternPattern exactly as it is written, so an anchored /docs/ reads differently from a docs/ matched at any depth.
rules[].ownersOwners the rule names, as users (@dana), teams (@acme/platform) or email addresses. Empty when the rule names none, which removes ownership from everything it matches.
rules[].lineLine the rule is declared on, so a verdict points at the file.
ownersEvery owner the file names, by owner, with how many rules name them.
matchedOwners resolved for each path the paths input named, by the path exactly as it was given. A path nobody asked about is absent, so read the ones the guardrail configured rather than iterating.
matched[path].ownersOwners the last matching rule names. Empty when no rule matched the path, and empty when the last one to match names no owners.
matched[path].rulePattern of the last rule that matched, or null when none did.
matched[path].lineLine that rule is declared on, or null when none matched.
unparsedEvery line that is not a rule, in file order. Comments and blank lines are not among them, because they are ignored rather than misread.
unparsed[].lineLine that could not be read as a rule.
unparsed[].reasonWhy it could not be read as one.
sourcereport when a CODEOWNERS file was read, none when the repository carries none.
reasonWhy nothing was read, present only when source is none.
reportsThe CODEOWNERS file that was read, as the one report this collector reads.
reports[].pathPath of the file, relative to the directory the CLI runs in.
reports[].modifiedWhen the file was last written, ISO 8601.
reports[].formatFormat it was read as, always codeowners.

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 codeowners collector:

json
{
  "path": "CODEOWNERS",
  "rules": [
    {
      "pattern": "*",
      "owners": [
        "@acme/platform"
      ],
      "line": 2
    },
    {
      "pattern": "*.md",
      "owners": [
        "@acme/docs",
        "docs@acme.example"
      ],
      "line": 5
    },
    {
      "pattern": "/service/",
      "owners": [
        "@acme/service-team"
      ],
      "line": 8
    },
    {
      "pattern": "/service/build/",
      "owners": [],
      "line": 11
    }
  ],
  "owners": {
    "@acme/platform": 1,
    "@acme/docs": 1,
    "docs@acme.example": 1,
    "@acme/service-team": 1
  },
  "matched": {
    "README.md": {
      "owners": [
        "@acme/docs",
        "docs@acme.example"
      ],
      "rule": "*.md",
      "line": 5
    },
    "LICENSE": {
      "owners": [
        "@acme/platform"
      ],
      "rule": "*",
      "line": 2
    }
  },
  "unparsed": [],
  "source": "report",
  "reports": [
    {
      "path": "CODEOWNERS",
      "modified": "2026-03-04T10:15:00Z",
      "format": "codeowners"
    }
  ]
}

Collected for

GuardrailCategoryInputs
ownership/catch-all-ruleownershipnone
ownership/codeowners-parsesownershipnone
ownership/codeowners-presentownershipnone
ownership/no-unowned-rulesownershipnone
ownership/owners-per-ruleownershipnone
ownership/team-ownedownershipnone

All collectors

Buildnote Limited
Registered in England and Wales, Reg: 16140412