What the schema is held to¶
Before kac validates a single record, it validates the schema. A declaration the tool does nothing with is not
harmlessly inert. rules: reads as behaviour the validator applies, and id.style: reads as a spelling every id must
match. Either one can sit in a file for a year while nobody notices it does nothing.
This matters most in a corpus that copied the framework from somewhere else. A corpus is one repository of knowledge
records kept in git, and if you hold a copied .schema/ you cannot ask what a key was meant to do. The pass answers for
you, and every finding names the file and the key.
What the pass reports¶
| Reported | Check |
|---|---|
A key at any level the loader never reads, notes: excepted |
schema-unknown-key |
An expr: that will not compile, or that names no severity: or message: |
schema-unreadable |
A required-when: outside its three forms |
schema-unreadable |
values: $enums.x where _enums.yaml declares no x |
schema-unreadable |
shape: x where _shapes.yaml declares no x, or a shape: beside an entry: block |
schema-unreadable |
A rule claiming a severity: that neither an expr: nor a rule class answers |
schema-dispatch |
A rule class reporting under a check id _checks.yaml does not declare |
schema-dispatch |
values: on any field that is not an enum |
schema-dispatch |
min-items: or min-records: on any field that is not a list |
schema-dispatch |
A type: or an of: naming a value no check reads, at any depth of an entry: block |
schema-dispatch |
An of: on any field that is not a list |
schema-dispatch |
An entry: block or a shape: on a field whose value is never an object |
schema-dispatch |
type: object or of: object with neither an entry: block nor a shape: |
schema-shape |
An index.order: that is neither ascending nor descending |
schema-dispatch |
A tier: no _tiers.yaml declares, or a tier only one of the two files knows |
schema-shape |
A tier declaring no label: or no behaviour: (both head its section in the taxonomy) |
schema-shape |
An id.style with no code behind the value |
schema-dispatch |
An id.width span on a numbered type, which pads to one width so that ids sort |
schema-shape |
An id.width span beside a filename still carrying the id, or a min: above its max: |
schema-shape |
A filename.carries-id: false on a slug type, whose id is the filename stem |
schema-shape |
A from: naming a source no derivation reads |
schema-dispatch |
A from: on a field that is also required: true |
schema-shape |
A type declaring no folder: |
schema-shape |
A mirrors-section: at a section the type's sections: block does not declare |
schema-shape |
A mirrors-citations: on a field with no ref:, so its ids resolve against nothing |
schema-shape |
An export.sections: key at a section the type's sections: block does not declare |
schema-shape |
An export.fields: entry naming a field neither the type nor _universal.yaml declares |
schema-shape |
An export: block declaring no version:, which a consumer reads its files at |
schema-shape |
An export.parts: on a type carrying no parts: block |
schema-shape |
An export.parts: with no line: beneath it, or a line: key naming no source |
schema-shape |
A line: source outside the vocabulary the exporter fills |
schema-dispatch |
A front.<field> naming a field neither the type nor _universal.yaml declares |
schema-shape |
A column.<Header> at a header the type's parts.columns: does not declare |
schema-shape |
A part.lead or part.aside on a type sourcing its parts from a table |
schema-shape |
A part.level on a type declaring no binding or advisory modals |
schema-shape |
A parts.source: outside the sources the tool extracts |
schema-dispatch |
A parts.section: at a section the type's sections: block does not declare |
schema-shape |
A table-sourced parts: block declaring no binding: |
schema-shape |
| An export entry declaring no fidelity at all | schema-shape |
| A fidelity no export carries | schema-dispatch |
A missing label-plural:, summary:, goes-here:, detail: or lineage.prior-art: |
schema-shape |
A label-plural:, summary: or goes-here: past 120 characters (they render as cells) |
schema-shape |
A rule description: past 120 characters, for the same reason |
schema-shape |
A versus: against the declaring type itself, or one both sides declare |
schema-shape |
The rows are grouped by what trips them, where the catalogue holds one entry per check, so the table is written by hand. A test compares the check ids in it against the catalogue in both directions, which catches one renamed, retired or introduced. What it cannot catch is an id growing a second way to fail, because nothing in the code tells one arm from another.
Whether code acts on the value¶
The question is whether code acts on the value, not whether the key is spelled correctly. style: mnemonic is a
real style and would pass a spelling test. What makes it sound is the branch that reads it. kac reads each vocabulary
in the table above out of the code that dispatches it, so adding a name with no branch beneath it is the mistake this
pass exists to prevent.
There is no list of permitted keys anywhere. The loader records what it asked each mapping for, and reports whatever is left over. So a key gains its meaning and its admission in the same edit, and a key that stops being read stops being admitted without anyone having to remember.
meta/type.schema.json does
hold a list, and that list can be behind, which is why it advises an author and gates nothing.
A derived field¶
from: sub-path says the field's value comes from the folders a record sits in. The source is a vocabulary, so a name
no derivation reads is schema-dispatch. The field would then be empty on every record, and the page would show a
column the corpus never fills in.
Declaring it required: true as well is schema-shape. The two declarations contradict each other. The author cannot
write the field, so they cannot meet the requirement, and writing the line to try trips derived-key instead.
Discovery says how the value is read.
What schema-shape asks¶
The schema-shape rows ask a different question. There the tool acts on whatever the value says: any section is
reconciled, any folder is read, any sentence is rendered. What makes one sound is a second declaration in the same file,
or the shape of the page the value lands on. A sections: block sits beside a mirrors-section:, and the width of a
table cell bounds the summary: that becomes it.
ref: and versus:¶
Both are outside the pass. A field's ref: names the type its ids point at, and a type's versus: names the type it is
most often confused with. A corpus adopts as many of the framework's types as it has use for, so either one may name a
type this corpus turned down.
kac reports nothing when it does. The disambiguation renders nothing, and kac update --add-type starts the reference
without an edit to .schema/. kac also drops a required field that points only at types nothing here supplies. No
record can fill one, so required-field says nothing until the corpus adopts one of those types or imports a corpus
publishing them. A required-when: on such a field is quiet for the same reason. A field taking a word under
allow-literal: is fillable without any of those types, so its obligation stands.
What a record does write is held to the same standard. ref-resolves still asks that a cited id exists, and it refuses
one of a type the field never named. A field whose every type this corpus declined admits nothing at all, and says which
types it wanted.
The cost is a misspelled name, which reads the same way from inside a corpus that holds a subset of the types. kac
reports it nowhere. What catches it is a test over the authored .schema/, held in the repository that writes the
framework, where every type is present. This repository runs one. A fork writing its own framework schema needs its own,
and the shape to copy is tooling/kac.tests/SchemaReferenceTests.cs.
values: $corpus.x¶
The corpus answers this one, not the schema. $enums.x draws a field's range from _enums.yaml, and a name declared
nowhere is a schema fault, because the declaration says the values are written down and they are not. $corpus.x draws
it from enums: in .corpus.yaml instead, for a field whose range belongs to the estate rather than to the framework.
What a service is built on is one list in a library and another in a payments platform, so one .schema/ above both can
state neither.
A name the corpus answers nothing to is outside this pass. The schema is right and the corpus has not written its list
yet, so validate reports corpus-enum-undeclared once against .corpus.yaml, from the first record with the field.
The descriptor reference says what to write there.
A rule with no severity:¶
A rule you have not built yet keeps its description:, drops its severity:, and the type page renders it under
Declared, not yet enforced. A rule naming a severity that nothing dispatches would read as enforced from every angle
and not be, so the load fails on it.
Where to go next¶
Checks is the page for adding a check, or for deciding whether the one you want already exists.