Files
chemenu/kb/CONTRACT.md
T
torben 2c4c2b1c7c
CI / verify (push) Failing after 57s
Release / release (push) Successful in 37s
stack: TOC-Pflicht fuer Referenzdateien ueber 100 Zeilen (docs toc); session-setup.md/gates.md nennen die tatsaechliche Budget-Ausnahmeliste (schliesst #73, #76)
Files changed:
- AGENTS.md
- CHANGES.md
- VERSION
- instructions/CONTRACT.md
- instructions/capture-session.md
- instructions/claude-code-model-selection.md
- instructions/dev/issue-tracking.md
- instructions/dev/testing-conventions.md
- instructions/dev/version-parts.md
- instructions/evolve-subtypes.md
- instructions/gates.md
- instructions/german-terminology.md
- instructions/ingest-large-tree.md
- instructions/kb-profiles.md
- instructions/link-taxonomy.md
- instructions/mcp-read-server.md
- instructions/migrate-corpus.md
- instructions/migrations/3.0.0-authoring-conventions.md
- instructions/migrations/4.0.0-link-taxonomy.md
- instructions/private-instance.md
- instructions/session-setup.md
- instructions/setup-instance.md
- kb/CONTRACT.md
- kb/CONVENTIONS.md
- kb/concepts/COLLECTION.md
- raw/CONTRACT.md
- tools/CONTRACT.md
- tools/chemenu/commands/docs_verify.py
- tools/chemenu/commands/instructions_cmd.py
- tools/chemenu/tests/test_docs_verify.py
- tools/chemenu/tests/test_instructions_cmd.py
- tools/chemenu/tests/test_toc.py
- tools/chemenu/toc.py
- types/type-spec.md
2026-09-09 20:38:42 +02:00

19 KiB

kb/ - Knowledge Layer Contract

The compiled knowledge layer, and the third stage of the pipeline raw/ -> kb/ -> reports/. Everything here is written and maintained by the LLM from material in raw/, and is expected to stay correct without being re-derived.

Quality goal: a page should answer a future question without re-reading the source it came from. If answering still requires the raw file, the page is incomplete.

This file holds the rules that apply in every collection and in every instance. That second half is the cut: what is written here is enforced by tools/wikitool or follows from how it works, so it is identical everywhere and dist export ships it verbatim.

What an instance decides for itself is next door, in kb/CONVENTIONS.md - the language pages are written in, the headings its two generated regions render under, the naming forms, the tone, the confidence rubric. That file binds exactly as this one does; it is simply owned by the instance rather than by the stack, so the distribution ships only its .template and the instance writes the real one. Read both, plus the target collection's kb/<name>/COLLECTION.md (also instance-owned), before writing or editing a page.

The split is by who may change the sentence, not by what it is about. Language, tone and naming used to sit here, which meant every instance that answered "not German" to setup-instance.md was locally editing a file the stack also ships - and a merge from upstream would quietly hand it back.

Structural facts (which frontmatter fields exist, which are required, what the body skeleton looks like) are in neither - they belong to the type-specs and are printed by tools/wikitool types describe <type>. Never hand-write frontmatter; scaffold with tools/wikitool new <type> --name "<Name>" --set field=value ....

Contents

Collections

kb/ is a namespace, not a collection. It carries no COLLECTION.md of its own.

A directory under kb/ is a collection exactly when it contains a COLLECTION.md. That file is the local authoring contract for every page in the subtree, and it belongs to the instance: it declares in its frontmatter which profile from instructions/kb-profiles.md it adopted, and whether the stack resolves against it by name.

Field Means
profile: Which catalogue entry this contract started from, or none. Free text - the catalogue is a palette, not an enum, and a collection an instance invented has no entry to name
required_by_stack: Whether wikitool itself depends on this collection by name. Not the instance's to choose: docs verify checks it against the stack's own list. kb/sources/ is true - sources coverage, [^cite-id] resolution and kb/provenance.md all resolve against that name - and everything else is false
  • A subdirectory inside a collection is an area. It inherits the enclosing contract and must not carry a COLLECTION.md of its own - kb/entities/systems/ is an area of kb/entities/.
  • An area is as deep as a page goes. kb/<collection>/<page>.md and kb/<collection>/<area>/<page>.md are the two depths a page may sit at; nothing goes a level deeper. A further subdirectory is not a second-level area - it is invisible to the generated catalog, which reads exactly two path segments below kb/ and folds anything past them into the area's own table silently, with no location of its own. That is why wikitool lint's nested_pages finding is a hard error rather than an advisory one like misplaced_pages: a misplaced page still catalogs correctly from the wrong place, a nested one makes the catalog itself wrong. A grouping axis that does not come from a type-spec's layout: - project owner was the case that surfaced this - does not earn a second directory level; it goes into frontmatter instead.
  • A COLLECTION.md nested inside another collection is invalid.
  • COLLECTION.md appears nowhere outside kb/. raw/, types/, tools/, reports/ and instructions/ are not collections and carry a CONTRACT.md or a root type-spec instead.

tools/wikitool docs verify enforces all three.

Collection Holds Contract
kb/entities/ Concrete things: projects, deployed systems, tools, technologies, people entities/COLLECTION.md
kb/concepts/ Architectures, patterns, protocols, workflows, decisions, recurring problems concepts/COLLECTION.md
kb/sources/ One summary page per ingested source, carrying its raw_files: provenance sources/COLLECTION.md
kb/comparisons/ Structured comparisons of two or more existing pages comparisons/COLLECTION.md

The four rows above are this instance's collections, not a fixed set. Adding one: mkdir kb/<name> and write a kb/<name>/COLLECTION.md with the two fields above. Collections are discovered by contract presence, so no code change is needed. A collection only becomes writable once some type-spec declares a matching base_dir:. Renaming or dropping one is the instance's call too - except where required_by_stack: true says otherwise.

Where a page goes is decided by its type-spec, never by hand - see types/type-spec.md.

Generated files

Never hand-edit these; they are produced by tools/wikitool:

File Produced by
kb/index.md wikitool index rebuild - the catalog map: statistics, counts, links
kb/<collection>/INDEX.md and kb/<collection>/<area>/INDEX.md wikitool index rebuild - the page tables
kb/log.md wikitool log append
kb/provenance.md wikitool sources rebuild-index

To find a page, search rather than read the catalog: tools/wikitool search "<text>", or tools/wikitool search --field <predicate> for a structured query over frontmatter.

Titles are identifiers

The filename stem is the page title, and [[wikilinks]] must match it exactly. That is not a naming preference; it is the wiki's only way to address a page. wikitool lint reports an H1 that stops matching its title, rename/rm rewrite every reference to a stem, and a [^cite-id] resolves through one.

Which form those titles take - spaces or kebab-case, singular or plural, what prefixes a decision record - is the instance's, in kb/CONVENTIONS.md § Naming.

Every page should

  • Carry a clear, descriptive title and a summary near the top
  • Use consistent terminology with the rest of the wiki
  • Link to the entities and concepts it mentions, and declare an edge where the relationship is worth naming - in the direction this page asserts it, not in both
  • Cite its hard facts (see Provenance and citation)
  • Duplicate no existing page
  • Appear in the catalog (guaranteed by wikitool index rebuild)

Quotation cap

At most 2 blockquoted lines per page. wikitool lint reports overages as advisory, since exceeding the cap can be a legitimate judgment call - but the page should carry the knowledge itself, not delegate it to quotations. The cap is about how much of the page you let quotes carry; it does not apply to text you are citing verbatim from a source.

The register those lines are written in - what counts as a buzzword, what filler is refused - is the instance's, in kb/CONVENTIONS.md § Tone.

Language and identifiers

Which language pages are written in is kb/CONVENTIONS.md's to say. What follows here is the part that is not a choice, because the tool resolves against it.

Every line of a page is either prose or an identifier. Only prose is translated.

Prose: descriptions and definitions, ## Key Information values, ## Details body text, a source page's Summary / Key Takeaways / Action Items / Not Extracted, and summary:.

Identifiers - never translated, in any language:

Identifier Why
Page titles, and the H1 that repeats one A title is the wiki's only identifier for a page and follows the subject's own established name - see Titles are identifiers. wikitool lint reports an H1 that stops matching its title
The subtype value on the generated **Typ:** line It renders a schema enum value (technology, workflow), which search --field filters on. The label is prose; the value is not
tags: Search keys, not prose
Commands, paths, config keys, hostnames, code They are what they are
Quotations Quoted verbatim in the source's own language

Which foreign technical terms stay untranslated inside that prose is a judgment call the instance records - see kb/CONVENTIONS.md § Language.

A source in another language is still summarized in the KB language: a source page is evidence about a source, not a substitute for it. Quote verbatim in the original language and record the raw file's language in source_language:.

Generated regions

Two regions of a page body are generated, not authored: the links region xref owns and the footnotes region cite owns. Each sits between a marker pair:

<!-- wikitool:links -->
## Beziehungen

- **depends-on:** [[Hermes]]
<!-- /wikitool:links -->

The marker is what the tool locates the region by, and everything between the markers - heading included - is replaced wholesale on the next write. An author never edits inside them; anything left there is overwritten without warning, exactly as in kb/index.md. A region with nothing to show is absent rather than empty.

The heading is therefore a rendering value, taken from kb/CONVENTIONS.md's sections:. No heading text exists in the compiler, and nothing matches on it: changing the declaration re-renders the words on the next write and cannot split a page.

That is not how it used to work. The tool located these regions by matching their heading text, which made a translated heading a structural fact - and made the region's end a guess. It ran to the next heading, and before that to the end of the file, which silently deleted whatever sat after it on eight pages. Any other heading a page carries is ordinary prose.

Linking

An edge is authored in one direction, on the page that asserts it, and carries a label that is a machine value rather than prose:

related:
  - depends-on: Hermes

Created with tools/wikitool xref add --a "<A>" --b "<B>" --rel <label>, never by hand-editing related: or the rendered bullet. Say the sentence before choosing the label - [A] <label> [B] - and if it only reads true backwards, the edge belongs on the other page.

A reverse edge is a separate decision, not a mirror. Write one when it independently helps a reader at the other end; do not write one to make the graph symmetric. Navigation does not depend on it either way: index rebuild renders the inbound view from the graph, completely and without maintenance.

Which labels exist is instructions/link-taxonomy.md, a palette that binds nothing. Which of them a page may use is its own collection's outbound: block, per destination - the source collection decides, because the rules that govern an edge are the rules of the collection asserting it. xref add refuses an unauthorised label and lint reports one.

A page is expected to have at least one inbound edge; wikitool lint reports orphans. Comparison pages are exempt - they are reached through the catalog. Directional edges mean more pages qualify than under the old mirrored model, and that is the check measuring reachability rather than measuring whether xref ran.

Renaming a page, deleting one, or dropping a single reference are tool operations with their own procedure: see instructions/page-lifecycle.md.

Provenance and citation

Every claim is either traceable to a raw file or explicitly marked as not.

  • provenance: on every entity/concept page - sourced (every substantive claim traces to a raw file), general (LLM general knowledge, no raw backing), or mixed (both; put the unsourced part under a ## General Guidance (unsourced) heading).
  • raw_files: on every source page - concrete existing file paths under raw/, never a directory and never a bare URL. For an external article also set source_url:, but raw_files: must still point at the local copy under raw/articles/.
  • One source page may cover many raw files. A folder of related documents becomes a single page listing all of them, not one page per file.
  • A [^cite-id] footnote appended to any specific hard fact: an IP, port, version, path, command, or config value. tools/wikitool cite add --page "<Title>" --source "Source - X" [--file <qualifier>] mints the id, upserts its [[Source - X]] (or [[Source - X|storage-model.md]] for a multi-file source) definition in the page's trailing Footnotes block (named per Section headings), and adds Source - X to sources: - it prints the marker to paste at the fact; placing it is still manual. Never hand-type a cite-id (AGENTS.md invariant 1). This differs from a plain [[Source - X]] link, which only means "related to".
  • Notation inside code is notation, not a reference. A [^cite-id] or a [[wikilink]] written in backticks or a fenced block is read as an example: the citation does not count and the link does not exist. That is what lets a page document this stack's own syntax. It also means a marker appended to a line inside a fence cites nothing - put it on a source line under the block (<source-word>: [^cite-id], in the KB language), where it renders as a footnote instead of travelling with the command when someone copies it.
  • A source cited inline must also appear in the page's frontmatter sources: list; wikitool lint checks this in both directions, and hard-errors on a leftover pre-migration ^[[...]] marker, an undefined [^cite-id] reference, or an orphaned Footnotes definition. tools/wikitool cite sync reconciles a page's block after a prose edit changes which ids are actually referenced.
  • tools/wikitool xref link-source --source "Source - X" --entities A,B,C adds a new source to every page it backs in one pass.
  • Every raw file is expected to be claimed by some source page; tools/wikitool sources coverage lists the ones that are not.

If no raw file or existing page backs an answer, say so explicitly rather than synthesizing one - and never file the synthesized version back into the wiki.

Confidence

confidence_base is the undecayed score set when a page's content is last confirmed; confidence is derived from it by tools/wikitool confidence decay and must never be edited directly.

Re-assess a page with tools/wikitool touch --page "<Title>" --confidence-base <value>.

What the number means - the base score, what raises it and by how much, and how to hedge in prose below a threshold - is a rubric rather than a mechanism, so it is kb/CONVENTIONS.md § Confidence rubric's.

Confidence against source standing

The rubric already asks for an authority judgment ("+0.1 for official documentation, +0.05 for a reputable secondary source") without anywhere to record it - source count lives in sources:, recency in modified:, agreement is readable from the prose itself, but the authority term is thought, applied, and forgotten; the next person re-derives it from scratch. raw/CONTRACT.md's capture fields (fidelity, authority) make it a recorded fact instead: lint compares a page's confidence_base against the weakest capture standing of its cited sources and reports one when the page claims more than its sources can carry.

A ceiling, not a formula. confidence_base stays hand-set (see above); this only bounds it. The reasons are the same two, together: confidence is already derived from confidence_base by one mechanism (confidence decay), and a second derivation of the same number would leave two disagreeing answers to "what should this be" - and authority is a ceiling a page may fall under by independent verification, never a determinant a formula could compute outright. The check is therefore advisory, in the same class as unsharded_collections: it names a mismatch and asks a human to look, it never fails a build.

The ceiling is stack vocabulary (fidelity/authority are - see raw/CONTRACT.md), not an instance setting; an instance that disagrees with a specific finding overrides its judgment on that one page, rather than reconfiguring the rubric:

worst authority among a page's sources ceiling worst fidelity among a page's sources ceiling
normative (none) verbatim (none)
reporting 0.8 published (none)
opinion 0.6 secondhand 0.7
unknown no finding nontextual 0.7
unknown no finding

The effective ceiling is the minimum of both columns. unknown never triggers a finding on its own axis: a backfilled "we don't know" is not a claim about the source, and firing on it would report every page citing a source captured before these two fields existed at once, which is noise, not a finding.

What does not belong here

  • Raw source material - it stays immutable under raw/.
  • Type definitions, frontmatter contracts, or templates - those live in types/.
  • Procedures for operating the tooling - those live in instructions/.
  • Anything an instance would have to rewrite for itself - language, naming forms, tone, relationship labels, the confidence rubric. Those are kb/CONVENTIONS.md's, and a sentence of that kind here is a sentence the stack ships over the instance's own answer.
  • Rules that apply to only one collection - those belong in that collection's COLLECTION.md.
  • Hand-edited generated files - see Generated files.
  • Generated lint reports - they are written to reports/ and are not pages.
  • Answers with no source behind them.