• v6.1.0 11c400c670

    v6.1.0
    CI / verify (push) Successful in 55s
    Release / release (push) Successful in 41s
    Stable

    torben released this 2026-09-17 07:18:09 +00:00 | 0 commits to main since this release

    6.1.0 - 2026-09-17 - Upgrade-Pfad und Iteration-Budget-Gate gehaertet, wiki/-Pfadliterale bereinigt

    Author: Torben Nehmer

    High impact

    • Session-Id-Fallback: Harness-Variable schliesst die Luecke zwischen Telemetrie-Join und Iteration-Budget-Gate

    Medium impact

    • Upgrade-Prozedur als eigene Instruktion statt als Prosa in INSTALL.md
    • Migrationsdokument prueft gegen eine festgehaltene Vorher-Ausgabe, Beispielverweis auf die .template-Form
    • dist upgrade: --take-release nimmt fuer einen lokal geaenderten Pfad die Release-Fassung
    • version notes antwortet auf einer ausgelieferten Instanz aus dem Release-Feed
    • new: scaffold materializes a schema default only for a required field

    Low impact

    • Stale wiki/ path literals swept out of tools/ and types/, with a test guarding against the next rename
    • CHANGES.md/Guard-Docstring: die Zahl der nachgezogenen Pfadliterale korrigiert (33, nicht 27)

    Dieser Kandidat sammelt, was ein getraceter 5.0.0-auf-6.0.0-Upgrade-Lauf auf einer echten Instanz
    offengelegt hat: ein fehlender Upgrade-Leitfaden, zwei falsche Verweise im Migrationsdokument,
    eine fehlende dritte Antwort in dist upgrade fuer eine lokal geaenderte Datei, und
    version notes, das auf einer ausgelieferten Instanz nie antworten konnte. Im selben Lauf zerfiel
    die Sitzung durch einen PID-basierten Session-Id-Fallback in 21 Telemetrie-Buckets, wodurch das
    Iteration-Budget-Gate strukturell unerreichbar blieb - behoben durch eine Registry bekannter
    Harness-Session-Variablen, mit einem SIGPIPE-Nebenbefund im CLI-Emitter gleich mit. Dazu,
    unabhaengig vom getraceten Lauf: ein Scaffold-Fix, der obligation: required nicht mehr in jede
    neue Instruktion schreibt, und eine Bereinigung von 33 stehengebliebenen wiki/-Pfadliteralen aus
    der wiki/-nach-kb/-Umbenennung, mit einem Test-Guard gegen die naechste Umbenennung.

    Kein Grenzuebertritt: jede Aenderung ist in beide Richtungen ein Drop-in, additiv gegenueber
    6.0.1.

    Upgrade-Prozedur als eigene Instruktion statt als Prosa in INSTALL.md

    Der Upgrade-Pfad einer ausgelieferten Instanz stand nur in INSTALL.md § "Eine Instanz
    aktualisieren" - einem Dokument fuer Menschen, das AGENTS.md § File naming ausdruecklich als
    "never by an agent as instruction" fuehrt. Ausgefuehrt wird er aber von einer Agent-Sitzung,
    jedes Mal. Der getracete 5.0.0-auf-6.0.0-Lauf auf einer echten Instanz zeigt, was daraus folgt:
    der erste Tool-Call listete instructions/ mit, fand keine passende Instruktion, oeffnete die
    naechstliegende (private-instance.md, der falsche der beiden Wege) und fiel dann auf INSTALL.md
    zurueck. migrate verify --from <commit vor dem Tausch> - INSTALL.md Schritt 6, erster
    Pruefschritt - lief in 33 Werkzeugaufrufen kein einziges Mal, und die Agent-Sitzung wurde nie neu
    gestartet, obwohl AGENTS.md im selben Commit +44/-3 bekommen hatte. Die anschliessende Migration
    lief damit unter dem alten Kontrollplan.

    Dahinter lagen drei Reihenfolgen nebeneinander: die in INSTALL.md, die im Abschlussbericht von
    dist upgrade, und die tatsaechlich gelaufene. Genau der Zustand, den Invariante 8 verbietet.

    instructions/upgrade-instance.md ist jetzt die eine Fassung: dreizehn Schritte von der
    Sitzungs-Id bis zum zweiten Publish, mit dem Sitzungsneustart an der Stelle, an der der neue
    Kontrollplan zu gelten anfaengt - nach dem Publish der Maschinerie, vor der Migrationskette, und
    mit migrate status als Wiedereinstiegspunkt fuer die neue Sitzung. manual: true, weil die
    Prozedur einmal pro Release laeuft und nie implizit aufgegriffen werden darf; ein Skill wuerde
    seine description dafuer in jede Sitzung legen. Auffindbar ist sie ueber den Abschlussbericht
    von dist upgrade, der statt einer eigenen Schrittliste jetzt die Datei nennt und das Kommando,
    bei dem der Lauf weitergeht (instructions sync). INSTALL.md behaelt, was ein Mensch vorher
    entscheidet, und den einen Sonderfall, den die Instruktion nicht abdecken kann, weil es sie dort
    noch nicht gibt: den ersten Sprung auf 4.5.0.

    Zwei Schritte der Instruktion sagen ausdruecklich, dass sie eine Luecke umgehen, und was sie
    ueberfluessig macht. Schritt 2 liest die Release-Notes von der Release-Seite statt mit
    version notes, weil eine Instanz ihre CHANGES.md als Stub bekommt und dist upgrade sie nie
    ueberschreibt - der Befehl kann dort nicht heute und nicht spaeter antworten. Schritt 6 nimmt fuer
    eine lokal veraenderte stackeigene Datei die Release-Fassung von Hand, weil es zu --keep-local
    kein Gegenstueck gibt; dabei geht der noetige Commit ueber publish --no-push, nicht ueber
    git commit - Invariante 5 kennt keine Ausnahme fuer "ist ja nur eine Vorbedingung", und genau
    diese Ausnahme hat sich der beobachtete Lauf genommen.

    instructions/session-setup.md sagt jetzt, dass ein export nur traegt, solange die Shell
    traegt. Mehrere Harnesses starten pro Tool-Call eine frische Shell - das Arbeitsverzeichnis
    ueberlebt, Shell-State nicht - und dann faellt jeder Aufruf auf seine eigene Parent-PID zurueck.
    Im gemessenen Lauf wurde eine Sitzung so zu 21 Telemetrie-Buckets mit hoechstens drei Aufrufen
    pro Bucket: das Iteration-Budget-Gate (60) und der Loop-Breaker (3 identische in Folge) konnten
    strukturell nicht ausloesen. Die Anleitung nennt deshalb die Inline-Form pro Aufruf und den
    Einzeiler, mit dem sich beantworten laesst, welcher Fall vorliegt.

    Verifiziert: docs verify (73 ausgelieferte Dokumente, 58 Referenzdateien),
    instructions verify (23 Instruktionen, 7 Skills) und 1276 Tests gruen - einer davon neu und auf
    genau die Stelle gerichtet, an der die Doppelung wieder entstehen wuerde: der Abschlussbericht
    von dist upgrade muss die Instruktion und ihr Wiedereinstiegskommando nennen, nicht eine zweite
    Kopie der Liste.

    Kein Grenzuebertritt: eine neue Instruktionsdatei und ein geaenderter Meldungstext sind in beide
    Richtungen ein Drop-in. Eine Instanz, die zurueckgeht, behaelt die Datei als ueberzaehlige Datei,
    und nichts liest sie automatisch - manual: true heisst genau das.

    Migrationsdokument prueft gegen eine festgehaltene Vorher-Ausgabe, Beispielverweis auf die .template-Form

    instructions/migrations/6.0.0-type-guidance-split.md verlangte in seinem Verifikationsschritt,
    die Ausgabe von types describe <name> muesse "read the same as it did before this migration" -
    ohne dass ein Schritt davor dieses Vorher festhielt. Eine Pruefung gegen einen Zustand, den
    niemand aufgeschrieben hat, faellt auf das Gedaechtnis des Ausfuehrenden zurueck, und bei ueber
    150 Zeilen Ausgabe je Typ ist das keins. Der getracete 6.0.0-Lauf hat entsprechend durch
    | head -250 und | tail -80 geprueft und "structurally identical to before" geurteilt; was
    das uebersah, lag in der Mitte der source-Ausgabe. Das Dokument schreibt die Ausgabe jetzt in
    einem eigenen Schritt vor der Aenderung in eine Datei und diffed hinterher, mit
    grep -c '^## Authoring guidance' als Ein-Zahl-Probe: zwei Koepfe sind richtig - einen setzt
    types describe selbst, einen bringt die Guidance-Datei mit.

    Als generisches Muster steht dasselbe jetzt in instructions/migrate-corpus.md § "Writing the
    migration document", weil es nicht an diesem einen Dokument haengt: migrate verify traegt seine
    Baseline im letzten Commit, ob jemand daran denkt oder nicht - eine Migration an der Maschinerie
    statt an kb/ hat gar keine, und genau dort entsteht die Behauptung, die sich nicht widerlegen
    laesst.

    Zweiter Fehler im selben Dokument: der Beispielverweis auf types/entity.md zeigt in einer
    ausgelieferten Instanz auf die beim Setup adoptierte Kopie - also auf genau den Vorher-Zustand,
    den der Schritt entfernen laesst. Der Nachher-Zustand liegt dort unter
    types/entity.md.template, und im Ursprungs-Repo existiert diese Datei ueberhaupt nicht:
    dist export re-keyt types/<name>.md erst beim Export. Der Satz konnte in einer Instanz also
    nicht bloss unguenstig sein, er konnte dort nie stimmen. Dazu sagt der Schritt jetzt die Sprache
    des Pointer-Absatzes - englisch, weil Anleitungsprosa an einen Agenten Control Plane ist,
    unabhaengig davon, wem die Datei gehoert - und dass das auch fuer behaltene lokale Prosa gilt:
    die wird uebersetzt, nicht umbenannt. Die Tabelle dazu wird verlinkt statt kopiert
    (types/type-spec.md § "Who owns a type-spec"), und ein behaltener Abschnitt bekommt einen
    eigenen Namen statt der Ueberschrift, die types describe schon selbst setzt.

    Derselbe Defekt eine Ebene hoeher, gefunden beim Nachmessen: types/source.md trug hier im
    Ursprungs-Repo noch einen Rest-Abschnitt ## Authoring guidance mit einem einzigen Bullet, der
    die title_prefix-Frontmatter wiederholte - types describe source gab drei Koepfe aus, die
    anderen drei Typen zwei. Die Sprachzentralisierung hat den Abschnitt uebersetzt, der
    Guidance-Split den Rest der Prosa ausgelagert und diesen Bullet stehenlassen. Die Datei wird beim
    Export zu types/source.md.template, also haette ihn jede neu aufgesetzte Instanz mit adoptiert.
    Entfernt, geprueft mit genau dem Muster, das der Schritt oben jetzt vorschreibt: Vorher-Datei,
    Diff, vier entfernte Zeilen und sonst nichts, alle vier Typen komponieren jetzt mit zwei Koepfen.

    Verifiziert: docs verify (73 ausgelieferte Dokumente, 58 Referenzdateien),
    instructions verify (23 Instruktionen, 7 Skills) und 1276 Tests gruen. Kein neuer Test: die
    Aenderung ist Prosa in zwei Instruktionen und ein entfernter Abschnitt aus einem Type-Spec -
    was hier mechanisch pruefbar waere, prueft docs verify bereits als Type-Spec gegen sein Schema.

    Kein Grenzuebertritt: in beide Richtungen ein Drop-in. Die Korrektur gilt denen, die noch
    upgraden - eine Instanz, die das Angebot bereits genommen hat, liest das Dokument nicht noch
    einmal. Fuer sie lohnt der eine Befehl, mit dem der Schaden hier gefunden wurde:
    grep -c '^## Authoring guidance' ueber types describe <name> fuer alle vier Typen, drei
    bedeutet einen Rest-Abschnitt im eigenen Type-Spec.

    dist upgrade: --take-release nimmt fuer einen lokal geaenderten Pfad die Release-Fassung

    dist upgrade kannte zwei Antworten auf eine lokal geaenderte Datei und die dritte, die man
    eigentlich will, war keine davon. --keep-local behaelt die Aenderung - und weil der neue Stamp
    die Release-Digest trotzdem schreibt, wird dieselbe Datei bei jedem kuenftigen Upgrade erneut
    gemeldet. Fuer eine Datei, die der Instanz gar nicht gehoert, ist das der dauerhaft falsche
    Zustand. Der andere angebotene Weg, "reconcile them by hand first", hatte kein Werkzeug: im
    getraceten 5.0.0-auf-6.0.0-Lauf war eine kb/CONTRACT.md durch ein Format-on-Save um
    Tabellen-Whitespace verschoben, und das kostete eine Handkopie aus dem entpackten Tarball, einen
    Commit nur zur Herstellung der Clean-Tree-Vorbedingung des naechsten Kommandos - und damit einen
    rohen git commit, an AGENTS.md Invariante 5 vorbei, die fuer "ist ja nur eine Vorbedingung"
    keine Ausnahme kennt.

    --take-release <pfad> ist die fehlende Antwort: schreibe fuer diesen Pfad die Release-Fassung,
    statt abzubrechen. Wiederholbar, weil der Pfad die Entscheidung benennt - --keep-local verliert
    nichts, --take-release verwirft eine lokale Aenderung, und die zwei sind darum nicht symmetrisch
    genug fuer ein pauschales Flag. Beide gelten pro Pfad und komponieren auf einem Aufruf, was der
    gemischte Fall braucht: eine Datei zuruecksetzen, eine andere behalten. Ohne --keep-local bricht
    ein blockierter Pfad, zu dem nichts gesagt wurde, weiter ab; ein --take-release-Pfad, der gar
    nicht blockiert ist, wird abgelehnt - auch im --dry-run, denn das ist ein Fehler im Argument
    und nicht ein Zustand des Baums, und ein still ignorierter Tippfehler haette ein erfolgreiches
    Upgrade gemeldet und die Aenderung behalten, die verworfen werden sollte.

    Anders als bei --keep-local ist die Drift danach weg und nicht bloss uebergangen: die Datei
    stimmt wieder mit der Digest ueberein, die der Stamp fuehrt, und verschwindet aus der Meldung.

    Dazu die Abbruchmeldung selbst, die den Fehlgriff mitverursacht hat. Sie nannte --keep-local und
    "reconcile by hand", sagte aber nicht, dass es zu --keep-local kein Gegenstueck gibt - der Lauf
    kuendigte woertlich an, "I'll let the upgrade take the release's version", und rief das Kommando
    ohne Flag auf. Jetzt nennt sie alle drei Antworten mit fertig eingesetzter Kommandozeile, im Muster
    des Mass-Update-Gates, und sagt ausdruecklich, dass keine davon der Default ist.

    instructions/upgrade-instance.md Schritt 6 traegt entsprechend nicht mehr die Drei-Schritt-Handreparatur, sondern die Entscheidung und den Dry-Run, mit dem man sie vorher sieht.

    version notes antwortet auf einer ausgelieferten Instanz aus dem Release-Feed

    version notes liest die lokale CHANGES.md. Eine ausgelieferte Instanz bekommt die aber als
    neunzeiligen Stub ohne einen einzigen Versionseintrag, und CHANGES.md steht in
    chemenu.ownership.is_upgrade_preserved - dist upgrade ueberschreibt sie also nie. Der Stub
    bleibt der Stub, dauerhaft. Der Befehl konnte dort nicht nur heute nicht antworten, sondern nie,
    und das an genau der Stelle, an der die Antwort am meisten zaehlt: dem Grenzuebertritt, vor dem
    Breaking Change: und Migration: gelesen werden muessen. Der getracete
    5.0.0-auf-6.0.0-Lauf kam nur weiter, weil er die Release-Notes ueber einen MCP-Server holte - ein
    Weg, den die Anleitung nicht nannte und den eine Instanz ohne erreichbaren Server gar nicht hat.

    Fehlt der Eintrag lokal, fragt der Befehl jetzt den Feed aus update_url - denselben, den
    version check benutzt - und druckt den body des Release, den release.yml im Ursprungs-Repo
    ohnehin aus version notes baut. Drei Praezisierungen halten das von einem stillen Netzaufruf
    auseinander:

    • Nur mit Release-Stamp. Ein Baum ohne .wikitool-release.json ist ein Dev-Checkout und
      behaelt die alte Fehlermeldung. Damit kann der neue Pfad im Ursprungs-Repo und in CI nicht
      betreten werden - auch nicht von release.ymls eigenem version notes.
    • stdout traegt nur die Notes. Die Zeile, welcher Feed gefragt wird, und die, welche Version
      geantwortet hat, gehen nach stderr. release.yml leitet stdout in die Datei um, die es als
      Release-Body postet; alles andere dort waere Inhalt im Release.
    • --offline verweigert den Aufruf und scheitert mit der release_url aus dem Stamp. Dieselbe
      Seite nennt auch jeder Fehlerfall des Feeds, damit ein Lauf, der die Notes nicht lesen kann,
      wenigstens weiss, wo sie stehen. Ein leerer body ist ebenfalls ein Fehler: eine leere Antwort
      darf nicht als "dieses Release hat nichts zu melden" durchgehen.

    Gefragt werden kann nur das neueste Release: update_url ist die einzige URL, die der Stamp
    fuehrt, und eine /releases/tags/<tag>-URL daraus zusammenzusetzen waere eine geratene
    API-Form statt einer gelesenen (Invariante 7). Antwortet der Feed eine andere Version als die
    gefragte, wird das auf stderr benannt und die Notes werden trotzdem gedruckt - das ist nicht der
    Randfall, sondern der Hauptfall, weil die Notes vor dem Tausch gelesen werden, wenn VERSION
    noch das Release nennt, das verlassen wird.

    instructions/upgrade-instance.md Schritt 2 und INSTALL.md § "Version und Updates" tragen
    entsprechend nicht mehr den Hinweis, dass der Befehl auf einer Instanz nicht antwortet; damit ist
    auch die letzte der beiden Werkzeugluecken aus dieser Instruktion heraus, und ihr Vorwort nennt
    keine mehr.

    Bei der Gelegenheit zwei Eintraege aus tools/CONTRACT.md § "Future considerations (not
    implemented)" entfernt, die dort seit ihrer Umsetzung falsch standen: der MCP-Server-Wrapper und
    dist upgrade selbst. Beide sind im selben Dokument weiter oben als existierend beschrieben.

    Session-Id-Fallback: Harness-Variable schliesst die Luecke zwischen Telemetrie-Join und Iteration-Budget-Gate

    Gemessen an einem getracten Lauf (33 wikitool-Aufrufe, eine Sitzung): unter Claude Code, dessen
    Bash-Tool jeden Aufruf in einer frisch initialisierten Shell ausfuehrt, fiel chemenu.session
    ohne gesetztes WIKITOOL_SESSION_ID auf os.getppid() zurueck - eine neue "Sitzung" pro Aufruf.
    Der Lauf zerfiel so in 21 Telemetrie-Buckets (hoechster Bucket: 3 von 33 Aufrufen), und das
    Iteration-Budget-Gate (60 Aufrufe, Loop-Breaker bei 3 identischen in Folge) sah nie mehr als 3 von
    60 - strukturell unerreichbar, obwohl AGENTS.md es als eine der vier code-durchgesetzten
    Sicherungen fuehrt. Derselbe Bruch traf den Telemetrie-Join: Hook-Events (prompt.submitted)
    trugen die Harness-UUID, wikitool.call-Events die wechselnde PID - kein gemeinsamer Schluessel,
    und eval score bewertete 1-3 Aufrufe statt 33.

    chemenu.session bekommt eine dritte Stufe zwischen der expliziten Variable und dem
    PID-Fallback: eine kleine Registry bekannter Harness-Session-Variablen (HARNESS_ENV_VARS),
    heute mit einem verifizierten Eintrag, CLAUDE_CODE_SESSION_ID. Verifiziert heisst: gegen eine
    echte Sitzung gemessen, dass die Variable ueber Tool-Aufrufe hinweg stabil bleibt (anders als die
    Shell-PID) und exakt der Wert ist, den der UserPromptSubmit-Hook in die Trace schreibt - der
    Wert wird unveraendert als Schluessel uebernommen, kein Praefix, keine Umschreibung, sonst waere
    der Join wieder zerstoert. Ein Eintrag wird nur nach genau dieser Verifikation aufgenommen: ein
    Variablenname, der zufaellig existiert und etwas anderes bedeutet, waere ein stillerer Fehler als
    der PID-Fallback, den er ersetzt.

    run_budgets Zustandsdatei (budget.json) traegt je Eintrag jetzt die Herkunft seiner Id; faellt
    dieselbe Id-Zeichenkette unter eine andere Herkunft als die gespeicherte, beginnt ein neuer
    Zaehler statt einen fremden zu erben - ein Eintrag ohne das Feld (vor dieser Aenderung
    geschrieben) behaelt seinen Count unveraendert. doctor ist jetzt dreiwertig (OK fuer eine
    explizite Variable oder eine erkannte Harness-Variable, WARN nur noch fuer den reinen
    PID-Fallback), und sowohl budget status als auch der session.start-Event der wikitool-
    Telemetriequelle nennen die Herkunft der Id.

    Im selben Lauf gemessener Nebenbefund auf der Emitter-Seite: ein durch eine geschlossene Pipe
    abgebrochener, ansonsten erfolgreicher Aufruf (... | head) stand mit exit_code: 1 in der
    Trace - Click faengt BrokenPipeError selbst ab und erzwingt sys.exit(1), ununterscheidbar von
    einem echten Fehler. cli.py installiert jetzt vor jedem Dispatch einen Wrapper um
    stdout/stderr, der einen EPIPE-Schreibfehler schluckt, bevor Click ihn sieht, und markiert den
    Trace-Eintrag stattdessen mit stdout_truncated: true bei unveraendertem, dem tatsaechlichen
    Kommandoerfolg entsprechendem exit_code.

    Reproduziert mit Tests, die echte Subprozesse statt In-Process-Aufrufe verwenden - os.getppid()
    ist sonst ueber die Testlaufzeit hinweg konstant: 61 Aufrufe aus je eigenem Prozess mit nur der
    Harness-Variablen loesen das Gate jetzt aus, drei identische ebenso den Loop-Breaker; vor dieser
    Aenderung waeren beide Tests gruen und blind gewesen.

    --minor: additiv (ein neues optionales source-Feld in budget.json, die Id faellt weiterhin
    auf getppid() zurueck, wo keine Variable greift), keine der beiden Drop-in-Richtungen verletzt.

    new: scaffold materializes a schema default only for a required field

    tools/wikitool new instruction --name "x" schrieb bislang obligation: required in jede neue
    Instruktion. obligation: ist ein Migrationsfeld (instructions/CONTRACT.md
    § instructions/migrations/) - eine gewoehnliche Instruktion ist keine Migration und hat nichts,
    was laufen muesste. Ursache: new_page._build_frontmatter() materialisierte jedes
    Schema-default: unbesehen; ueber alle acht types/*.schema.yaml gibt es genau zwei
    (entity/concepts provenance, in required:; instructions obligation:, nicht).

    Die Regel jetzt: ein Schema-default: wird nur fuer ein Feld materialisiert, das das Schema auch
    in required: fuehrt. Auf einem optionalen Feld ist ein default: eine Lese-Annahme (was ein
    fehlendes Feld bedeutet), keine Schreib-Vorgabe - sie hinzuschreiben macht aus der stillen
    Annahme eine ausgesprochene Behauptung. instruction.obligations eigene Lese-Annahme steht
    unveraendert und unabhaengig in kb_state.py (frontmatter.get("obligation") or REQUIRED).
    Der array-Zweig direkt daneben (leere Liste fuer ein unbesetztes Array-Feld wie tags:) ist
    davon ausdruecklich nicht betroffen - er bleibt fuer optionale wie Pflichtfelder gleich, weil ein
    fehlender Schluessel sonst den Template-Filter-Suffix woertlich in den Body schreiben wuerde
    ({related|bullets} -> das Wort "bullets").

    --patch: kein Bestandsdokument aendert sich (obligation: stand bislang nur explizit oder auf
    den beiden Migrationsdokumenten), keine Migration noetig, und ein zurueckgerolltes Werkzeug
    schriebe das Feld nur wieder mit.

    Stale wiki/ path literals swept out of tools/ and types/, with a test guarding against the next rename

    Die Wissensschicht wurde am 2026-08-21 von wiki/ nach kb/ umbenannt. Das Verzeichnis zog um,
    die Zeichenkette nicht: 33 Stellen nannten weiter einen Pfad, den es nicht mehr gibt. Gemeldet
    war davon eine - die Kopfzeile des Lint-Reports (Scanned N pages under `wiki/` ) - als
    kosmetischer Einzelfall. Der Scan selbst war immer korrekt: run_lint(kb_dir) laeuft ueber
    kb/, gezaehlt wird, was dort liegt. Falsch waren ausschliesslich die Beschriftungen.

    Dreizehn davon sind nutzersichtbar. Die Fehlermeldungen von xref, cite, touch,
    move, rm, rename, raw accept und log status nannten wiki/, ebenso die --help-Texte
    von cite sync --all, provenance rebuild-index --dry-run und move --reconcile. Dazu die
    description:-Felder in types/type-spec.schema.yaml, die ueber types describe und ueber jede
    Schema-Validierungsmeldung bei einem Agenten landen. Zwei Stellen waren doppelt falsch:
    git_publish.py und run_budget.py verwiesen auf wiki/concepts/Mass-Update Gate.md, waehrend
    die Seite unter kb/concepts/workflows/Mass-Update Gate.md liegt - dort war auch die
    Collection-Ebene veraltet.

    Nicht angefasst: raw/ (unveraenderlich, was immer dort steht) und die Alteintraege dieser
    Datei. Beide sind Aufzeichnungen dessen, was zu ihrer Zeit galt, keine Wegweiser - dieselbe
    Unterscheidung, die instructions/dev/issue-tracking.md fuer den Tracker trifft.

    Dass es vier Wochen unbemerkt blieb, ist der eigentliche Befund: kein Check liest ein Pfadliteral
    in Quelltext. docs verify kam dafuer nicht in Frage, weil es shipped_prose() liest, also
    Markdown - der Grossteil des Defekts sass in .py-Zeichenketten. Der Guard ist deshalb ein Test:
    tools/chemenu/tests/test_source_hygiene.py scannt jede .py-Datei unter tools/chemenu/ sowie
    tools/wikitool gegen eine Tabelle stillgelegter Stufenpfade. Die naechste Umbenennung traegt
    dort eine Zeile nach und bekommt jede vergessene Stelle als Testfehler, statt als Zeichenkette,
    die ein Jahr lang niemand liest. Die zwei Ausnahmen stehen bewusst als Liste mit Begruendung und
    nicht als geschickteres Muster: eine Fixture-URL, in der wiki ein Repository-Name ist, und die
    Guard-Datei selbst, die die stillgelegten Pfade ja gerade deklariert.

    kb/entities/projects/Chemenu.md trug denselben Fehler in einer Kerndaten-Zeile und wurde ueber
    touch nachgezogen. "Dreilagig" blieb dort stehen: das deckt sich mit der Concept-Seite
    Three-Layer Architecture, die reports/ ausdruecklich als vierte Phase neben den drei
    Schichten fuehrt.

    --patch: keine Schnittstelle aendert sich, kein Verhalten, keine Migration. Ein
    zurueckgerolltes Werkzeug gibt nur wieder die alten Beschriftungen aus.

    CHANGES.md/Guard-Docstring: die Zahl der nachgezogenen Pfadliterale korrigiert (33, nicht 27)

    Der Eintrag darueber nannte 27 nachgezogene Stellen und "rund die Haelfte davon nutzersichtbar".
    Beides war falsch. Die 27 stammten aus einem wc -l, das nur tools/**/*.py gezaehlt hatte -
    tools/wikitool, types/type-spec.md und die vier description:-Felder in
    types/type-spec.schema.yaml fehlten darin. Nachgezaehlt am Commit selbst
    (git show <sha> | grep -c '^-.*wiki/'): 33, davon 32 im Stack und eine auf der Seite
    Chemenu. Nutzersichtbar sind davon dreizehn, also gut ein Drittel und nicht die Haelfte.

    Derselbe Zahlendreher stand im Docstring von tools/chemenu/tests/test_source_hygiene.py, wo er
    kuenftigen Lesern erklaert, wogegen der Guard schuetzt - dort ebenfalls korrigiert. Dass diese
    Korrektur einen eigenen Bump braucht, ist kein Formalismus: der Docstring liegt unter tools/,
    und das Version-Gate in .gitea/workflows/ci.yml ist nach Pfad geschnitten, nicht nach Absicht.

    --patch: reine Prosakorrektur, kein Verhalten, keine Schnittstelle.


    This note is a snapshot of the CHANGES.md entry as it stood when the tag was cut, and
    is never edited afterwards. The maintained version of this text - including any later
    correction - is the entry for this version in CHANGES.md in the repository.

    Downloads