Skip to content

validate check the corpus against its schema

kac validate [--json] [--no-color]
Option What it does
--json Emit the summary and findings as JSON.
--no-color Turn colour off. NO_COLOR in the environment does the same.

What it does

validate checks every record in your corpus against the schema its type declares. CI runs it on every pull request, and you run it before you push.

It decides which files count as records, applies the checks the schema declares, and reports each fault against the file that caused it. Discovery is the pass that decides what it reads. Checks says where each check comes from.

Run it from inside your corpus. kac finds the corpus by walking up for a .corpus.yaml.

A corpus that consumes another

A corpus declaring consumes: cites records another corpus published. validate resolves those citations against the exports restore unpacked under .imports/. eng:pol-VURM.TIMEBOX reaches a clause of an imported policy, in prose and in a field declaring a ref: alike. A clause that corpus does not have fails here exactly as a local one would.

Run restore first. A declared import that has not arrived is an error, and the message names the command to run.

Each side keeps its own spelling. Cite a record your corpus owns bare, and cite an imported one with its producer's shortcode. validate refuses either written the other way, and says which spelling to use. The producer is whoever wrote the record, so a corpus reached through another is cited by its own shortcode and not by the one in your consumes: block. Imports says why resolution works this way, and what a check may ask of an imported record.

An import that has fallen behind

validate asks each source your consumes: block names what it publishes now. A newer version inside your range is a warning, and kac restore takes it. A newer version your range holds back is an info, and so is a source this run could not ask. That last one usually wants the token restore describes.

None of the three changes the exit code, and a corpus with no consumes: block reads no source at all. Imports says why being behind is never an error here.

Examples

A clean run

kac validate

validate prints the counts and exits 0:

validated 13 document(s) and 8 template(s), skipped 0 without frontmatter. 0 error(s), 0 warning(s)

A run that finds faults

validate groups the faults under the file that caused them, names the check that fired and the line, and exits 1:

adrs/0001-knowledge-as-code.md
  error  [required-field]  missing required field 'owner'.  (adrs/0001-knowledge-as-code.md:1)
  error  [id-format]       id 'adr-1' must be 'adr-' followed by 4 digits.  (adrs/0001-knowledge-as-code.md:1)
  error  [link-resolves]   link target '0099-nothing.md' does not resolve.  (adrs/0001-knowledge-as-code.md:8)
  error  [identity-id]     identity line id 'adr-0001' does not match the document's id 'adr-1'.  (adrs/0001-knowledge-as-code.md:12)

validated 13 document(s) and 8 template(s), skipped 0 without frontmatter. 4 error(s), 0 warning(s)

The name in brackets is a check id, and checks prints what every one of them proves. Troubleshooting covers the findings you meet first.

The findings as JSON

kac validate --json

Use this to feed a script or a reviewer bot. The summary comes first, then one object per finding:

{
  "summary": {
    "validated": 13,
    "templates": 8,
    "skipped": 0,
    "errors": 4,
    "warnings": 0,
    "infos": 0
  },
  "findings": [
    {
      "file": "adrs/0001-knowledge-as-code.md",
      "line": 1,
      "severity": "error",
      "check": "required-field",
      "message": "missing required field 'owner'."
    }
  ]
}

A pipeline step

dotnet tool restore
dotnet tool run kac validate

Only an error changes the exit code. Exit codes lists the three. Running it in CI has the whole workflow.

Known limits

Every check reads the corpus, and none reads the estate the corpus describes. A service deleted last month still validates cleanly. A green run says the corpus is consistent, not that it is right.

Discovery falls back to a directory walk where git cannot answer. kac walks a tree that is not a repository for *.md instead, and that walk obeys no exclude file. A Markdown file the corpus had ignored is then discovered and validated. Discovery says what else changes.

It reaches the network where your corpus consumes another. Every other check reads your working tree. This one asks each source in consumes: what it publishes. A source that does not answer within twenty seconds reports import-unreachable, and the run continues. A corpus with no consumes: block opens no connection at all.

A quotation of an imported clause is not checked. clause-quoted-faithfully compares a quoted span against the clause the same line cites. An export sends a record's ids and its fields, not its wording, so the words of an imported clause are not here to compare against and the check passes over the citation.

immutable-after-accepted is declared and does not run. Whether an accepted decision changed is a question about a diff, and this command reads a working tree.

generate writes the blocks this command checks a file still has.