• v4.0.0 177c7e9ce8

    v4.0.0
    CI / verify (push) Successful in 55s
    Release / release (push) Successful in 38s
    Stable

    torben released this 2026-09-02 16:42:22 +00:00 | 90 commits to main since this release

    4.0.0 - 2026-09-02 - Prosa ist kein Identifier: Link-Taxonomie als Enum, generierte Regionen mit Markern

    Author: Torben Nehmer

    Breaking Change: Beziehungslabel sind Enum-Werte in related: statt Freitext im Body-Bullet, toolgefuehrte Abschnitte liegen zwischen Marker-Paaren statt hinter ihrer Ueberschrift, und xref add schreibt nur noch eine Kante statt beider Richtungen. tools/chemenu/sections.py ist geloescht. Eine bestehende Instanz muss sections: in kb/CONVENTIONS.md auf links/footnotes umstellen, outbound: in jede COLLECTION.md eintragen, die {section.*}-Variablen aus ihren Page-Type-Templates entfernen und den Korpus umstellen - sonst scaffoldet new die Variablen woertlich in neue Seiten. Ablauf: instructions/migrations/4.0.0-link-taxonomy.md

    Der Stack benutzte an drei Stellen Prosa als Identifier, und jede hat messbar etwas
    gekostet. Die Überschrift eines Abschnitts war seine Adresse (^## Beziehungen$), was die
    KB-Sprache zu einer Compiler-Konstante machte und das Ende der Region zur Schätzung - sie lief
    bis zur nächsten Überschrift, davor bis zum Dateiende, und hat auf acht Seiten still Inhalt
    gelöscht. Das Beziehungslabel stand nur im Body-Bullet, also konnte nichts das Vokabular prüfen:
    gemessen am Korpus 152 distinkte Label in 337 Bullets gegen dreizehn dokumentierte, 102 davon
    genau einmal vorkommend. Und xref add spiegelte jede Kante, was ## Siehe auch mit 555
    Bullets ohne Label füllte - 353 davon beweisbar redundant.

    Was jetzt Identifier ist. Eine Region liegt zwischen <!-- wikitool:links --> bzw.
    <!-- wikitool:footnotes --> und wird vollständig aus dem Frontmatter gerendert, Überschrift
    eingeschlossen. Ein Label ist ein Maschinenwert in related: (- depends-on: Hermes), gezogen
    aus instructions/link-taxonomy.md und pro Ziel autorisiert von der Quell-Collection
    (outbound: im COLLECTION.md, Commonplaces ADR-019). Der Body-Bullet ist eine Darstellung
    dieser Daten, nicht ihr zweiter Aufbewahrungsort.

    Gelöscht, ersatzlos: tools/chemenu/sections.py komplett, heading_re, der
    Alias-Mechanismus, PRE_CONVENTIONS_NAMES, cite_block_heading, provenance.__getattr__, die
    {section.*}-Template-Variablen, xrefs Abschnittssuche. Kein Überschriftentext liegt mehr in
    Python - bis auf zwei kosmetische Fallbacks, und die sind harmlos geworden: der Marker trägt die
    Identität, also rendert ein falscher Default falsche Wörter statt Struktur zu zerlegen, und der
    nächste Write repariert es.

    Kanten sind direktional, und das war keine Geschmacksfrage. Die per-Collection-Autorisierung
    ist mit einer automatisch gespiegelten Gegenkante logisch unverträglich: die Spiegelhälfte
    entsteht in einer Collection, deren Regeln der Autor nie gelesen hat. Entweder schriebe das
    Werkzeug unautorisierte Kanten, oder die Regel "die Quellcollection entscheidet" löst sich auf.
    Der Navigationseinwand wird dabei besser beantwortet als vorher: wikitool links show --page
    berechnet die Eingangssicht über den Korpus, vollständig und ohne Pflege, und das gerenderte
    Bullet ist ein gewöhnlicher [[wikilink]] - ein Backlink-Panel zeigt es ohnehin. Die erzwungene
    Gegenkante garantierte nie Vollständigkeit, nur dass jemand daran gedacht hat.

    Der Orphan-Check meldet dadurch mehr, und das ist die Prüfung bei der Arbeit: sie misst jetzt
    Erreichbarkeit statt "ist xref gelaufen".

    obligation: trennt zwei Achsen, die vorher eine waren. migration_kind: sagt wie
    gearbeitet wird, neu obligation: required|offered ob überhaupt. Eine offered-Migration ist
    ein Angebot für eine Datei, die der Instanz gehört - sie blockiert nie, steht nicht in der Kette,
    und migrate done verbucht sie im Ledger, ohne kb_version zu bewegen. Genau daran hing ein
    Entwurfsfehler, den erst der Test gezeigt hat: Offers gegen kb_version zu filtern hätte jede
    Offer verschwinden lassen, sobald irgendein unbeteiligter Pflichtschritt lief. Dazu ist die
    Erkennungshälfte aktiviert, die seit ihrer Einführung ungelesen dalag - die sha256 pro Datei in
    .wikitool-release.json beantwortet jetzt "editiert oder nur empfangen", also ob eine Offer
    kopiert werden darf oder von Hand abgeglichen werden muss.

    types/ teilt sich entlang root:. root: kb heißt Wissensseite heißt Instanz: die vier
    Page-Type-Specs samt Schemas gehen als .template, instruction/lint-report/type-spec
    verbatim. Damit ist die deutsche Prosa in jenen vier Dateien korrekt statt Migrationsschuld -
    es war die richtige Sprache an einem Ort mit falsch deklariertem Eigentümer. Was der Stack von
    der Type-Schicht noch verlangt, ist eine Zeile: ein Type-Spec name: source, dessen Schema
    raw_files fordert. STACK_REQUIRED_COLLECTIONS entfällt als separate Liste - die pflichtige
    Collection wird aus dem base_dir dieses Typs abgeleitet.

    Warum das MAJOR ist. Vorwärts: sections: hat eine andere Form, outbound: fehlt, und die
    in 3.0.0 übernommenen Page-Type-Templates enthalten {section.*}-Variablen, die es nicht mehr
    gibt - new schriebe sie wörtlich in neue Seiten. Rückwärts: 4.0.0 schreibt gelabelte Kanten,
    die 3.0.0s Schema als type: string ablehnt. Beide Hälften des Drop-in-Tests fallen.

    Der Korpus dieser Instanz ist noch nicht umgestellt. Diese Version liefert die Maschinerie;
    lint meldet die 480 noch ungelabelten Kanten als Findings, nicht als Fehler, weil das genau das
    Fenster ist, für das .wikitool-kb.json existiert. malformed_edges und unbalanced_markers
    sind dagegen sofort hart - keines beschreibt eine unkonvertierte Seite, nur eine kaputte. Die
    Beförderung der beiden anderen kommt, wenn der Korpus sie bestehen kann.

    Downloads