Skip to content

Generation

What a corpus derives from its own records, where each derived thing lands, and why generation writes into hand-written files rather than beside them.

A corpus is one repository of knowledge records kept in git. A record is one Markdown document in it, filed under a type, with YAML frontmatter above its prose. A type is one kind of record, declared in the corpus's own .schema/.

The argument for generating any of it is Schema before prose. Where a machine could derive something and a person maintains it instead, the person's copy is the one that goes stale, and nothing says so.

What is generated, and from what

Artefact Built from
<type>/_index.md frontmatter across the folder
schema-<type> block in <type>.md _universal.yaml and the type's fields
checks-<type> block in <type>.md the checks the validator implements
schema-universal in knowledge-as-code/metadata.md _universal.yaml
types-metadata in knowledge-as-code/metadata.md the adopted types
five blocks in knowledge-as-code/taxonomy.md the adopted types
two blocks in knowledge-as-code/lineage.md the adopted types
types-index block in README.md the adopted types

kac regenerates the index whole. Its columns and its sort come from the schema's index block, and it gets a do-not-edit banner. A type with no records yet gets an index saying so, rather than a table with no rows. A type with no folder gets no index at all.

An index heads a table per folder. A type that declares a field with from: sub-path reads that field out of the folders below the type, and the index groups its rows on it. Discovery says how the value is read. Only the first folder heads a table, so standards/platform/dotnet/testing.md joins the Platform table instead of opening one of its own. A type whose records all sit directly in its folder gets a single table and no headings.

A column heading is its field name in sentence case, with an initialism in capitals. review-by heads Review by, and their-sla heads Their SLA. kac recognises id and sla. A field whose initialism is neither of those heads a column in sentence case, so open an issue on this repository where you declare one.

kac drops the derived column where the heading already gives it. Every row under a heading of Security holds security, so the column says nothing the reader has not read. A record filed deeper keeps it: platform/dotnet under a heading of Platform is the one place dotnet is written down.

The frontmatter reference lists the universal fields first, marked , then the type's own. Each row renders that field's description, falling back to notes where the schema declares none.

The checks table leaves out the rows a type cannot trip. Each row carries a predicate over the type's own declaration, so a rule it does not declare, a field shape it does not use and a parts source it does not have are all left out. Each page lists only its own checks.

schema-universal documents the universal fields once for the whole taxonomy, in knowledge-as-code/metadata.md, as the schema declares them. A type page lists them again under , narrowed to whatever that type made of them.

The taxonomy has five blocks. types-placement is the decision table, types-detail is the catalogue by tier, types-versus is the disambiguations, and types-graph and types-edges are the relation diagram and its edges.

Lineage has two. types-lineage records where each type's name came from, and types-collisions records where that name already means something else to a reader.

The corpus's own README block is the one a corpus may decline, by deleting the pair of markers around it. The file belongs to the corpus, so the choice does too.

GeneratedFiles holds this list, and validate checks a corpus against the same blocks. An _index.md has no markers, so nothing checks that a corpus has one.

The region between the markers

Generated content sits between markers inside otherwise hand-written files. You keep your prose and the generator keeps the tables current. kac preserves the rest of the file byte for byte.

kac regenerates every adopted type whether or not it has records, because the blocks derive from the schema alone. It writes an index for an empty type too, since every type page links to one and a withheld file would leave a dead link.

If a block loses its markers, generate no longer writes it. Discovery is the pass that catches that.

Which types are generated

Generation covers the types listed in types: in .corpus.yaml and no others. A corpus that has not declared the key yet has its adoption read off the folders instead, and a type counts there only where both its page and its folder are present.

kac leaves a declined type alone whatever .schema/ says about it, down to the hand-written text between the markers on a page left behind. Writing there would create an artefact no generated list of this corpus's types mentions, and generate --check would then hold the corpus to keeping it fresh. Standing a type up without adopting it is a defect validate reports.

The Mermaid subset

The types graph goes into a fenced Mermaid block, in the subset an Azure DevOps wiki renders. That subset is narrower than Mermaid's own, and a diagram that exceeds it renders nothing at all, with no error to say why.

So the generator writes graph rather than flowchart, uses no subgraphs, and keeps every arrow to -->. A corpus publishing somewhere more capable gets the same diagram, because one generator writes for every publishing target.

Those three are what the rule covers, and node shapes are not among them. Nobody has held a wiki to one, so the generator draws every node as [...] and the question stays unasked. A diagram on this site is free of the rule entirely, because no wiki reads one.

Byte-stable output

Generation is a pure function of frontmatter and schema, so running it twice produces no diff. kac pads tables to a width the content decides, escapes |, and writes files LF with a trailing newline.

That is what makes --check meaningful. It recomputes every generated file and compares, so a difference is real staleness rather than a formatting wobble.

Why a pipeline never commits

Where generated content is stale the build fails and names the command to run locally. Contributing says what a pipeline that pushed fixes would cost instead.

Where to go next

generate is the command that runs this, and --check is what CI runs instead.