kb/CONTRACT.md mischt Stack-Regel und Instanz-Konvention: nach Eigentum schneiden, nicht nach Ort #39
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Der Befund
kb/CONTRACT.mdist eine Datei mit zwei Autoritäten. Ein Teil ist code-erzwungen und darf nie abweichen; ein Teil ist Konvention, die jede Instanz für sich entscheidet — und wird trotzdem als bindender Contract ausgeliefert.docs verify), Generated-Files-Tabelle,provenance:-Enum, Cite-/Footnote-Mechanik,confidence_basevs. abgeleitetesconfidence, Orphan-Check, Blockquote-Caphängt ab von,verwendet, …), Ton-Beispiele und Buzzword-Liste (deutsch), Confidence-Rubrik (0.5 / +0.2 pro Quelle / …), ADR-PräfixDie Sprachentscheidung ist der Extremfall: sie steckt nicht in einer Datei, sondern in vier Schichten.
setup-instance.mdSchritt 5 wusste das und löste es als Handarbeit an fünf Stellen über drei Schichten.Warum das mehr war als Unordnung
Zwei Dokumente widersprachen sich für jede nicht-deutsche Instanz. Wer bei Schritt 5 „Englisch" antwortete, hatte lokal
kb/CONTRACT.md, vier Type-Specs undsections.pygeändert.private-instance.mds Decision Point sagt für so einen Merge-Konflikt: „Conflict intools/,types/orinstructions/? You changed the stack locally — take the upstream side and re-file the change as an issue there." Die Upstream-Seite nehmen hieß für diese Instanz: KB-Sprache zurück auf Deutsch.Und die Merge-Prozedur aus 2.2.1 hatte dadurch ein zweites Leck, in die andere Richtung als das in #30 beschriebene.
git checkout HEAD -- kb rawholt alles unter beiden Stages auf den Vor-Merge-Stand — auch diese sechs Maschinerie-Dateien:Änderte der Upstream einen davon, warf die Prozedur das Update still weg. Die Kontrollzeile
git diff --name-only $BEFORE HEAD -- kb rawmeldete dann leer, also „nachweislich funktioniert", während sie gerade eine Contract-Änderung gefressen hatte. Die Kontrolle, die der wichtigste Teil sein soll, bestätigte hier den Fehler.dist_cmd.pykannte die Unterscheidung bereits (CONTRACT_ONLY_STAGESplus jedeskb/*/COLLECTION.mdwerden explizit kopiert). Die Prosa-Prozedur hatte sie nicht.Vorbild: Commonplace
Gemessen an
commonplace/(vendored, sieheinstructions/dev/commonplace-kb.md), drei zusammengehörige Mechanismen:COLLECTION.mdstays authoritative for the collection's actual contract; a profile entry here is a proven starting point, not a binding source." Das Ausgelieferte ist unverbindliche Referenz, das Instanz-eigene bindet. Bei uns war es andersherum..commonplace): bei Drift wird nicht überschrieben, sondern verweigert.Dazu der Begriff, der die Diagnose trägt: system-definition artifact (bindet) vs. knowledge artifact (informiert).
Disanalogie, die die Kopie begrenzt: ADR-021s Namespace (
kb/commonplace/) löst „ausgelieferter Inhalt kollidiert mit Nutzerinhalt" — 195 Methodik-Notizen imkb/notes/des Nutzers. Chemenu liefert null Seiten aus. Unser Problem war nicht ausgelieferter Inhalt, sondern ausgelieferter Contract. Der Namespace war deshalb die falsche Hälfte; die richtige sind Punkt 1 und 2.Entscheidung
Der Schnitt lief bis dahin nach Ort — alles über
kb/steht inkb/CONTRACT.md. Er läuft seit 3.0.0 danach, wer den Satz ändern darf.kb/CONTRACT.mdkb/CONVENTIONS.md.templatekb/*/COLLECTION.md.templateje Default-Collection, adoptiert ein Profil per Referenzinstructions/kb-profiles.mdkb/CONVENTIONS.mdträgt: KB-Sprache samt Abschnittsnamen, Beziehungslabel-Vokabular, Ton-Beispiele und Buzzword-Liste, Confidence-Rubrik, ADR-Präfix. Die Distribution liefert nurkb/CONVENTIONS.md.template— exakt derUSER.md/SOUL.md-Split, denROOT_FILESunddoctors Sentinel-Check schon konnten. Kein neuer Mechanismus, ein vorhandener ein Verzeichnis tiefer.COLLECTION.mdbekam FrontmatterBis dahin wurde eine Collection rein an der Dateipräsenz erkannt (
kb_collections.py). Die Deklaration brauchte einen Träger, sonst hätten wir den Ortsschnitt durch einen Prosaschnitt ersetzt:required_by_stack: trueheißt: Autorenkonventionen darf die Instanz umschreiben, löschen oder umbenennen nicht. Das trifftsources(sources coverage, Cite-Auflösung,provenance.md), nichtentities. Geprüft vondocs verify.Nachtrag aus 4.0.0:
required_by_stack:wird nicht auf Treu und Glauben genommen, sondern gegenstack_required_collections()geprüft, das die Menge aus dembase_dir:dessource-Type-Specs ableitet. Eine separate Liste im Code gibt es nicht.Die Stelle, an der es sonst Prosa-Umschichtung geblieben wäre
RELATIONSHIPS = "Beziehungen"insections.pywar der Punkt, an dem die Konvention in Code übergelaufen war. Solange sie dort stand, konnte kein Template die Sprache umstellen. Das Modul las die drei Namen ab 3.0.0 aus den Instanz-Konventionen; der Alias-Mechanismus war der Migrationspfad. In 4.0.0 (#40) istsections.pyersatzlos entfallen — Marker-Regionen tragen die Identität, der Überschriftentext ist reine Anzeige.Verworfen:
sources/auskb/herausziehenErwogen, weil
sourcessich strukturell vonentitiesunterscheidet. Verworfen, gemessen:kb_scan.iter_kb_pages(kb_dir)macht einkb_dir.rglob("*.md"); darauf sitzenbuild_link_graph,inbound_links,find_duplicate_title_paths, Orphan-Erkennung,index rebuild,search. Source-Seiten sind der dichteste Knotentyp darin — jedes[^cite-id]löst gegen eine auf. Ein Hoist macht jede Graph-Operation dauerhaft zweiwurzelig, um ein Verzeichnis umzubenennen.kb_collectionserzwingt „keinCOLLECTION.mdaußerhalbkb/". Ein Top-Level-sources/verlöre entweder seinen Contract oder diese Invariante bekäme eine Ausnahme.kb_collections.py: „mkdir kb/<name> && $EDITOR kb/<name>/COLLECTION.mdadd a collection without a code change";types/type-spec.md:45: „Adding a type must require no code change"). Eine selbst angelegte Collection liegt im selbenkb/wie die Defaults. Eigentum ist eine deklarierte, keine Orts-Eigenschaft — daher das Frontmatter oben.Sichtbar halten, ohne es zu nehmen:
types/type-spec.mdkennt bereitsroot: kb | repo. Eine Seite außerhalbkb/ist schema-seitig vorgesehen; es fehlt der zweiwurzelige Graph-Walker. Wenn je eine Stage außerhalbkb/Seiten tragen soll, ist das die Stelle — kein Sonderfall fürsources.Umsetzung
kb/CONTRACT.mdschneiden;kb/CONVENTIONS.md+.template; Eintrag indist_cmd.py;doctor-Check analog zum Personalization-Sentinel.COLLECTION.md-Frontmatter (profile:,required_by_stack:) inkb_collections.py+docs verify; die vier Default-Contracts als Templates.sections.pyund die vier Type-Specs von den hartkodierten deutschen Namen lösen.instructions/kb-profiles.mdschreiben;setup-instance.mdSchritt 5 auf „Profil wählen" umstellen;private-instance.mds Decision Point korrigieren; Version-Bump.Akzeptanzkriterien
kb/CONTRACT.md, den eine Instanz ändern müsstekb/CONVENTIONS.mdist nicht im Distributions-Dateiplan; nur das Template ist esdoctorFAILt auf fehlendeCONVENTIONS.mdund auf eine mit Template-Sentineldocs verifyprüftprofile:undrequired_by_stack:auf jedemCOLLECTION.mdtools/odertypes/setup-instance.mdSchritt 5 nennt eine Entscheidung, keine fünf Editierstellenprivate-instance.mds Decision Point widerspricht dem nicht mehrinstructions/dev/version-parts.md; der Verdacht auf Boundary-Crossing hat sich bestätigt und wurde als3.0.0mit Migrationsdokument gelöstVorgeschichte
Entschieden in der Sitzung vom 2026-09-02, aus #30 heraus. Die Alternativen (
/sources-Hoist, Marker-Blöcke im bestehenden Text, nur die Pfadmenge maschinenlesbar machen) sind oben mit Begründung verworfen.Nach Abschluss geht es weiter bei
#28 — der kuratierte Fixture-Korpus. Er muss gegen die hier entschiedene Contract-Form autorisiert werden; vorher geschrieben, wird er zweimal geschrieben. Danach #30.
Changelog: Body auf den Abschlussstand gezogen — Status-Zeile vorangestellt, alle Umsetzungsschritte und Akzeptanzkriterien abgehakt, Tempus von Plan auf Befund umgestellt. Zwei sachliche Nachträge aus 4.0.0 eingearbeitet:
required_by_stack:wird gegenstack_required_collections()geprüft statt auf Treu und Glauben genommen, undsections.pyist inzwischen ersatzlos entfallen statt nur von den deutschen Namen gelöst. Das letzte Akzeptanzkriterium präzisiert: der Boundary-Crossing-Verdacht hat sich bestätigt und wurde als3.0.0gelöst.Abschluss
Umgesetzt und ausgeliefert als
3.0.0, Commit502971d. Am 2026-09-03 gegen den laufenden Baum nachverifiziert.Was implementiert wurde, entlang der vier Umsetzungspunkte:
kb/CONVENTIONS.mdexistiert samt.template, ist nicht im Distributions-Dateiplan,doctorprüft sie (conventions: present, language de, sections Beziehungen, Fußnoten)COLLECTION.mdtragenprofile:undrequired_by_stack:;docs verifyprüft beide,required_by_stack:gegen die aus demsource-Type-Spec abgeleitete Mengeinstructions/german-terminology.mddeklariert sich selbst als Bestandteil desgerman-Sprachprofils und nicht des Stacks; die vier Page-Type-Specs gehören seit 4.0.0 vollständig der Instanzinstructions/kb-profiles.mdexistiert;setup-instance.mdSchritt 5 stellt eine Frage und wählt ein Profil;private-instance.mdlistetkb/CONTRACT.md,kb/CONVENTIONS.md.templateundraw/CONTRACT.mdnamentlich als die drei Dateien, die der Merge ausMERGE_HEADzurückholt, und die Kontrollzeile filtert genau diese drei herausWas bewusst nicht umgesetzt wurde: der
/sources-Hoist aus dem Abschnitt Verworfen. Die Begründung steht im Body und hat sich seither bestätigt — 4.0.0 hatSTACK_REQUIRED_TYPESeingeführt undSTACK_REQUIRED_COLLECTIONSals separate Liste gestrichen, also ist Eigentum jetzt an genau einer Stelle deklariert statt an zwei. Der zweiwurzelige Graph-Walker bleibt der offene Vorbehalt, falls je eine Stage außerhalbkb/Seiten tragen soll; ein eigenes Issue hat er nicht, weil es dafür heute keinen Anlass gibt.Verifiziert:
docs verify,instructions verify,doctorundlint --fail-on-errorgrün; 863 Tests;dist exporterzeugt 191 Dateien mitkb/CONVENTIONS.md.templateund je einemCOLLECTION.md.template, undfind_leaksverweigert die gefüllten Namen. Der vollesetup-instance.md-Replay gegen einen frischen Export lief bei der Umsetzung durch — eine englische Instanz scaffoldet## Relationships/## See Alsoaus ihrer eigenenkb/CONVENTIONS.mdund ändert dabei keine Datei untertools/odertypes/.Anschluss laut Body: #28 (kuratierter Fixture-Korpus), danach #30.