-
v5.1.0 Stable
released this
2026-09-12 16:27:52 +00:00 | 4 commits to main since this release5.1.0 - 2026-09-12 - changelog: Kandidaten-Eintrag nach Impact gruppiert, version regrade zur Nachkorrektur, version release verlangt eine Zusammenfassung
Author: Torben Nehmer
High impact
- changelog: Kandidaten-Eintrag nach Impact gruppiert, version regrade zur Nachkorrektur, version release verlangt eine Zusammenfassung
Seit
4.4.0sammelt ein laufender Kandidat alle Bumps in einem Eintrag; bei5.0.0wurde das
mit 20 Bumps und ~1440 Zeilen unlesbar, weil die Liste chronologisch und ungewichtet war und die
Release-Seite genau diesen Eintrag 1:1 uebernimmt (version notes,release.yml). Der Eintrag
ist jetzt geschichtet statt einer einzigen Wand Text:version bump --impact high|medium|low
(Defaultmedium) graduiert jeden Bump, die Liste rendert nach High/Medium/Low gruppiert - ausser
wenn allesmediumist, dann bleibt sie flach wie bisher, damit jeder alte Eintrag und jeder
einfache Patch unveraendert bleibt.version regradekorrigiert eine Note im Nachhinein, gegen
einen einzelnen Lesevorgang der ganzen Liste, bevor der Kandidat geschlossen wird. Direkt unter
der Liste steht jetzt eine kurze Zusammenfassung, darunter je Bump ein eigener
### <Bump-Titel>-Changeset-Absatz;version releaseverweigert das Schliessen eines Kandidaten
mit zwei oder mehr Bumps, solange diese Zusammenfassung fehlt (ein Kandidat mit genau einem Bump
ist ausgenommen - sein Changeset ist bereits die Zusammenfassung, wie hier). Geschlossene
Eintraege wie der zu5.0.0bleiben in der alten Form stehen: die Release-Seiten sind laut
eigenem Footer unveraenderliche Snapshots, undinstructions/dev/version-parts.mdsowie
docs/version-model.mdzitieren den2.0.0-Eintrag mit Abschnittsnamen.--breaking/**Migration:**sitzen jetzt oberhalb der Bump-Liste statt darunter, damit die fuer
einen Operator wichtigste Zeile nicht unter einer moeglicherweise langen Liste verschwindet.
This note is a snapshot of the
CHANGES.mdentry 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 inCHANGES.mdin the repository.Downloads
-
v5.0.1 Stable
released this
2026-09-12 15:37:52 +00:00 | 5 commits to main since this release5.0.1 - 2026-09-12 - publish: ungeborene main ist kein detached HEAD, erster Push zu leerem Remote (schliesst #96, #97)
Author: Torben Nehmer
Zwei Defekte, die zusammen dazu führten, dass eine frisch aufgesetzte Instanz
sich über keinen dokumentierten Weg initial veröffentlichen ließ. Beide sitzen
inpublishund wurden erst beim Einrichten einer 5.0.0-Instanz aus dem
Release-Tarball sichtbar - der Pfad, deninstructions/setup-instance.md
Schritt 14 als den ersten schreibenden Aufruf einer neuen Instanz nennt.Ein ungeborener Branch ist kein detached HEAD.
current_branch()fragte
git rev-parse --abbrev-ref HEAD. Nachgit init -b main, vor dem ersten
Commit, zeigtHEADauf einen Ref, der sich nicht auflöst:rev-parseendet
mit Exit 128 und war damit von einem echten detached HEAD nicht zu
unterscheiden. Der erstepublisheiner neuen Instanz brach deshalb mit
"Refusing to pushmainfrom a detached HEAD" ab, und Invariante 5 schneidet
den naheliegenden Ausweg (git commitvon Hand) ab. Die Funktion liest jetzt
git symbolic-ref --short -q HEAD- wasHEADbenennt statt worauf es
zeigt. Damit beantwortet die Branch-Prüfung den ungeborenen Fall korrekt,
statt für ihn ausgesetzt werden zu müssen: sie vergleichtmainmitmain
und lässt durch. Ein echter detached HEAD wird unverändert abgelehnt, und ein
--branch, das nicht dem ausgecheckten entspricht, ebenfalls.Ein nie gepushter Branch ist nicht "nicht ahead".
_local_ahead_of_remote()
entscheidet, ob ein sauberer Working-Tree trotzdem etwas zu pushen hat, und
gabFalsezurück, sobald kein Tracking-Ref existierte. Genau das ist der Fall
bei einem frisch angelegten, leeren Remote-Repository: der lokale Commit stand,
publishmeldete "Nothing to commit" und pushte nie - beliebig oft
wiederholbar. Unterschieden wird jetzt übergit ls-remote --exit-code, dessen
Exit-Code die drei Lagen ohne Textvergleich trennt (0 = Ref vorhanden,
2 = Remote erreichbar und ohne diesen Ref, 128 = unerreichbar oder gar nicht
konfiguriert); auf die Meldung zu matchen schiede aus, weil git sie übersetzt.
Nur der mittlere Fall gilt als "ahead", und auch dort nur, wenn lokal
überhaupt ein Commit existiert. Ein unerreichbares Remote behält bewusst das
bisherige Verhalten, damit Offline- und Nur-lokal-Instanzen keine
Verhaltensänderung sehen.remote_ref_exists()bleibt unangetastet - sein
zweiter Aufruferreconcile()meint damit weiterhin richtig "nichts zum
Abgleichen da".tools/CONTRACT.mdzieht beides nach: diepublish-Zeile beschrieb den
Strandungsfall bisher als gelöst, was für einen nie gepushten Branch nicht
stimmte, und der Fehlerkontrakt benennt die Branch-Prüfung jetzt als
eigenständigen Exit-1-Grund.instructions/setup-instance.mdundINSTALL.md
blieben inhaltlich richtig - sie hatten den Umweg nie beschrieben, sondern den
Weg, der jetzt tatsächlich funktioniert.
This note is a snapshot of the
CHANGES.mdentry 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 inCHANGES.mdin the repository.Downloads
-
v5.0.0 Stable
released this
2026-09-11 17:25:49 +00:00 | 7 commits to main since this release5.0.0 - 2026-09-11 - TOC-Pflicht in docs verify, Konfidenz-Mechanismus ersatzlos entfernt
Author: Torben Nehmer
- status/incoming: menschliche Stubs werden ausgearbeitet, nie so umgesetzt
- page move: eine kb-Seite folgt ihrem Subtype ins Verzeichnis, das ihr Type-Spec berechnet
- kb/CONTRACT.md: Tiefe 1 als Grenze - Katalog liest nur eine Area-Ebene
- raw accept: incoming/ als abgeleiteter Rohablage-Eingang (schliesst #58)
- raw accept: Stem-Eindeutigkeit im Typverzeichnis erzwingen, --replaces als einziger Weg daran vorbei (schliesst #64)
- update entity naming conventions to use singular form for consistency
- kb/concepts/ bekommt Areas: layout: fuer concept, Area-Titel aus jedem Type-Spec, Schwellen-Empfehlung im lint (schliesst #59)
- source_type: Default streichen, unclassified als sichtbares Fach, layout: fuer source (schliesst #66)
- raw accept: Datums-Shard statt Typverzeichnis, fidelity/authority am Drop-Punkt (schliesst #67)
- source_type ist Instanzsache: Profilkatalog, Setup-Frage, evolve-subtypes-Instruction (#68)
- instructions/CONTRACT.md: Skill-H1, Referenztiefe und Begruendungsprosa praezisiert (#71, #72, #79)
- SKILL.md-Sanierung: Checklisten, Kommandolisten, wiki-status-Hard-Rule, wiki-query-Filingpruefung (#70, #74, #75, #78)
- Ausgelieferte Doku zitiert keine Issue-Nummern mehr, docs verify prueft es (schliesst #77)
- TOC-Pflicht fuer Referenzdateien ueber 100 Zeilen; session-setup.md/gates.md nennen die tatsaechliche Budget-Ausnahmeliste (schliesst #73, #76)
- tools/CONTRACT.md: docs toc im Fehlerkontrakt, docs-verify-Zeile nennt die TOC-Pruefung; tools/README.md korrigiert das --major-Kriterium
- dist export erzeugt die TOC-Region nach dem Marker-Strip neu (CI-Fund im Export-Replay)
- Konfidenz-Mechanismus ersatzlos entfernt
- Konfidenz-Mechanismus ersatzlos entfernt
- version-parts.md dokumentiert den --migration-required-Ruecknahmepfad
- wiki-status verweist auf session-setup.md (schliesst #84)
- CLAUDE.md-Importkette entdrifted, Modellwahl nach docs/ verschoben (schliesst #81)
- Telemetrie-Default nach Installationsform, Byte-Deckel und Session-Retention
- tools/CONTRACT.md: raw accept Doku auf Datums-Shard und Capture-Felder nachgezogen (schliesst #89)
- MCP submit-Tool: Quarantäne-Schreibpfad mit Upload Review Gate (schliesst #32)
- AGENTS.md-Changelog-Absatz korrigiert: Contract-Prosa ist Sitzungsarbeit, doc-pull-through-Instruction ergaenzt
- docs verify prueft die Kommando- und Fehlerkontrakttabelle in tools/CONTRACT.md getrennt, 10 fehlende Fehlerkontrakt-Zeilen nachgetragen (schliesst #91)
- tools/CONTRACT.md als Nachschlage-Dokument strukturiert: ###-Gruppen in beiden Tabellen, spiegelnde Reihenfolge, Lead-in-Regel
- incoming/.gitkeep als Datei- statt Verzeichnismuster trackbar (schliesst #88)
- dist export-Doku: Typverzeichnis-Behauptung nach #67 korrigiert (schliesst #93)
- raw/*/.gitkeep-Glob in dist-upgrade-Doku auf den flachen raw/.gitkeep-Anker korrigiert
- Breiten-Auslöser für Ingests: eine einzelne, thematisch breite Quelle bekommt einen Extract-Pass statt einer Seite pro Namen
- Breiten-Auslöser für Ingests: Extract-Pass statt einer Seite pro Namen, types/source.md nachgezogen
- stack-close Schritt 3: ein Doku-Nachzug in einen versionierten Pfad braucht seinen eigenen Bump
Breaking Change: docs verify verlangt eine aktuelle Inhaltsverzeichnis-Region () auf AGENTS.md, jedem Stage-/Collection-Contract und jeder flachen instructions/**.md-Datei ueber 100 Zeilen - eine bestehende Instanz mit einer eigenen instructions/*.md-Datei ueber 100 Zeilen ohne TOC sieht docs verify nach dem Tool-Update neu fehlschlagen, bis einmalig 'wikitool docs toc --apply' laeuft und der Diff committet wird. Ausserdem verschwinden wikitool confidence decay und confidence init-base ersatzlos, touch --confidence-base ebenso, und die Schemas fuer entity/concept verlieren confidence/confidence_base vollstaendig (additionalProperties: false) - jede bestehende Instanz muss den Korpus migrieren, sonst werden alle Entity-/Concept-Seiten beim Schema-Update sofort schemainvalide. Ablauf: instructions/migrations/5.0.0-confidence-removal.md
Das Label
status/incominggibt es seit heute in Gitea: der Mensch legt einen
Stub an — zwei Sätze, ein Verdacht, ein „wäre interessant" — und der Stack
komplettiert ihn.instructions/dev/issue-tracking.mdbeschrieb es nicht, und
das ist die gefährlichere Hälfte: ein Stub sieht aus wie ein Body, und der
Body ist genau das, was eine Sitzung nach Schritt 2 als Spec glaubt. Zwei
solche Issues lagen bereits offen auf dem Board (#60, #61).Neu in der Instruction ist deshalb ein eigener Abschnitt „Incoming stubs" mit
dem Verbot als erstem Satz — einstatus/incoming-Issue wird nie so umgesetzt,
wie es dasteht — und der Ausarbeitung als sechsschrittigem Ablauf: den Wortlaut
als Absichtserklärung lesen, gegen den Baum prüfen, den Originaltext wörtlich in
den Kommentar retten, bevor der Rewrite ihn überschreibt, offene Fragen benennen
statt beantworten (kind/decision), erst dann die vier Pflichtlabel, dann das
Flag entfernen. Zwei Ausgänge wie beistatus/unconfirmed: ausgearbeitet oder
mit Begründung geschlossen.Der interessante Punkt ist Schritt 4:
status/incomingist das einzige Flag,
das Schritt 4 nicht qualifiziert, sondern aussetzt. Die vier Pflichtachsen
fehlen einem Stub nicht, sie sind noch nicht fällig —kind/,prio/und
size/sind Antworten auf Fragen, die niemand gegen den Baum geprüft hat. Ein
Stub auf Sicht durchzulabeln ist der Fehler, nicht das Weglassen: es lässt
Ungeprüftes triagiert aussehen.Der Schaden, den das verhindert, ist derselbe wie bei #30, nur eine Stufe
früher: dort schrieb ein Body einen Mechanismus vor und bekam dessen Bugs
gebaut, hier schreibt ein Body gar nichts vor und bekommt die Lücke von
demjenigen gefüllt, der ihn am schnellsten gelesen hat — inklusive Close, womit
die Frage, für die der Stub stand, nie wieder gestellt wird.Keine neue Schrittnummer, bewusst:
stack-close/SKILL.mdund ältere
CHANGES.md-Einträge verweisen namentlich auf „Schritte 2-3 und 7". Eine
Umnummerierung hätte diese Verweise still falsch gemacht — genau der Zerfall,
den dieselbe Datei in § Renames beschreibt.Geändert: instructions/dev/issue-tracking.md
(Frontmatter, § When to run, Schritt 5, neuer § Incoming stubs, § What no tool
checks, § Decision points) und die Routing-Zeile in
instructions/dev/stack-dev/SKILL.md. Dev-only —dist exportschließt
instructions/dev/aus, eine ausgelieferte Instanz sieht davon nichts, deshalb
PATCH.Nachgezogen in derselben Sitzung, aber als eigenes Paket (#63, Commit
a0aecfc):kb/concepts/Issue Label Scheme.mdbeschrieb weiter nur die beiden
altenstatus/-Flags und zählte sechzehn statt siebzehn Labels. Das ist
kb/-Inhalt und brauchte nach Invariante 3 erst eine Quelle — die Threadkopie
von #62/#63 inraw/notes/— also einewiki-manage-Sitzung statt dieser
hier. Die Seite trägt jetzt die drittestatus/-Zeile samt Verbot und einen
Kernpunkt dazu, dass dieses Flag die Vier-Achsen-Pflicht aussetzt.Offen bleibt die erste tatsächliche Anwendung der Regel: #60 und #61 sind
weiterhin unausgearbeitete Stubs.wikitool move(#56): Nichts im Stack bewegte bisher eine Seite über eine
Verzeichnisgrenze —renameschreibt laut eigenem Docstring nie über
target.path.parenthinaus, undnew_page._target_dirrechnete die
Platzierung nur beim Anlegen. Ändert sichentity_typespäter, blieb die
Datei still am alten Ort liegen, und nichts prüfte das nach: weder
lint_core.pynochdoctor.pyverglichen den Ist-Ort einer Seite mit dem,
was ihr Type-Spec berechnen würde.Die Platzierungslogik selbst gibt es jetzt genau einmal:
TypeResolver.compute_target_dir(plusTypeResolver.subtype_dirfür den
layout:-Teil),new_page._target_dirdelegiert nur noch dorthin. Darauf
aufbauend zwei neue Stücke:wikitool move --page "<Titel>"bewegt eine Seite an den berechneten Ort;
--reconcilewendet dieselbe Regel auf den ganzen Bestand an und ist
idempotent (ein zweiter Lauf meldet nichts mehr zu tun). Beide Modi fassen
weder Body noch Frontmatter an, und der Titel — die einzige Identität einer
Seite — ändert sich nie, also folgt kein Referenz-Update. Ein bereits
belegtes Ziel (ein alter Stem-Kollisionsrest) wird verweigert statt still
überschrieben. Top-level registriert, wierename/rm, nicht unter einem
page-Unterbefehl.lintbekommt einen neuen Befund, Misplaced Pages, mit Ist- und
Soll-Pfad. Bewusst nicht inHARD_ERROR_KEYS: ein von Hand platzierter
Bestand ist keine kaputte Seite, nur eine, diemoveaufräumen könnte — auf
dieser Instanz sind das aktuell die dreikb/entities/projects/*/-Seiten,
die #57 separat behandelt.
Ein Fund unterwegs, der ohne #56 unsichtbar geblieben wäre:
migrate verify
schlüsselte Seiten über den repo-relativen Pfad. Ein reiner Move ergab
„N removed, N added, 0 compared" und lief grün durch — der eine mechanische
Check, für deninstructions/migrate-corpus.mdexistiert, hätte bei genau der
Operation nichts geprüft, die dieses Issue einführt. Behoben:PageShape
trägt jetzt zusätzlich den Pfad,_shapes_at_revision/_shapes_now
schlüsseln über den Titel (die einzige Identität einer Seite), und
CorpusDiff.movedmeldet einen reinen Ortswechsel separat — informativ,
niemals als Finding. Regressionstest deckt drei verschobene Seiten mit
compared == 3, added == 0, removed == 0ab.Bewusst nicht Teil dieses Pakets: die drei realen Seiten aus #57 bleiben
liegen (kein Korpus-Publish hier, nur Stack), undraw rename(#16) — das
git mv-mit-mv-Fallback aus #56s Entwurf war für Rohdateien gedacht;
rename/rm/movebewegen kb-Seiten über ein einfachesPath.rename, weil
publishohnehin übergit add -Astaged.Geändert:
tools/chemenu/type_resolver.py(compute_target_dir,
subtype_dir),tools/chemenu/commands/new_page.py(delegiert),
tools/chemenu/commands/page_ops.py(move_command),
tools/chemenu/lint_core.py(find_misplaced,misplaced_pages),
tools/chemenu/corpus_diff.pyundtools/chemenu/commands/migrate_cmd.py
(Titel-Schlüsselung,moved),tools/chemenu/commands/log_append.py(--op move),tools/CONTRACT.md,instructions/page-lifecycle.md,
instructions/publish-cycle.md. MINOR: rückwärts liest ein älterer Stack eine
verschobene Seite unverändert (Identität ist der Titel, nicht der Ort),
vorwärts reines Überkopieren.Katalogtiefe (#57):
index_build.group_pageslas bislang genau zwei
Pfadsegmente unterkb/(parts[0]als Collection,parts[1]als Area) und
faltete alles darunter still in die Level-1-Area. Real betroffen waren die
drei Seiten aus #56s Befund,kb/entities/projects/{kfchou,vanillaflava, yugasun}/*.md— im generierten Katalog nicht als eigener Ort sichtbar,
sondern als läge jede direkt inentities/projects/.kb/CONTRACT.md§
Collections beschrieb bis heute nur eine Ebene, ohne eine zweite
auszuschließen; der Baum hatte trotzdem eine, handplatziert, ohne
unterstützten Weg dorthin.Entscheidung war (b) aus dem Issue: die drei Verzeichnisse auflösen statt den
Katalog rekursiv zu machen. Die Gruppierungsachse dahinter — Owner
(kfchou/vanillaflava/yugasun) — kommt aus keinem Frontmatter-Feld und
aus keinem Type-Spec, sondern aus einer Ad-hoc-Entscheidung beim Anlegen; sie
verdient keine zweite Verzeichnisebene. Tiefe 1 ist jetzt geschriebene Regel
inkb/CONTRACT.md§ Collections, mit dieser Begründung.Vier Stücke setzen das um:
kb_scan.find_nested_pagesliefert(title, page, depth)für jede
Seite mehr als ein Verzeichnis unterhalb ihrer Collection — reine
Pfadtiefe, unabhängig davon, ob der Typ auflöst, damit auch eine Seite mit
kaputtemtype:nicht durchrutscht.lintbekommt den Befund Nested Pages, und anders als
misplaced_pageshart: eine fehlplatzierte Seite katalogisiert noch
korrekt von der falschen Stelle aus, eine verschachtelte macht den
generierten Katalog selbst falsch, und es gibt keine Version, ab der das
toleriert würde.index rebuildlehnt eine verschachtelte Seite nicht ab, sondern warnt
(Entscheidung aus der Session: melden statt verweigern, damit ein
Fremdinstanz-Upgrade mit handverschachtelten Seiten nicht hart bricht) —
group_pagesfaltet weiterhin wie zuvor, die Warnung ist die neue
Sichtbarkeit, nicht eine Verhaltensänderung der Faltung selbst.TypeResolver.get_layoutvalidiertlayout: {dir: ...}jetzt auf
einen einzelnen Pfadabschnitt (kein/, kein\, kein./.., nicht
leer) und schlägt fehl statt eine zweite Ebene über den einzigen
unterstützten Weg — ein Type-Spec — entstehen zu lassen.
Ein fünftes Stück, das das Issue selbst nicht explizit forderte, aber die
move-Mechanik aus #56 sonst mit toten Verzeichnissen zurückgelassen hätte:
move(--pagewie--reconcile) entfernt jetzt ein Verzeichnis, das es
durch den Wegzug seiner letzten Seite geleert hat — symmetrisch zum
mkdir(parents=True)auf der Zielseite. Ohne das hättenkfchou/,
vanillaflava/,yugasun/den eigenen Fix überlebt, leer und für git
unsichtbar, aber für einen verzeichnisbasierten Test sichtbar.Auf dieser Instanz angewendet:
wikitool move --reconcilehat die drei
Seiten nachkb/entities/projects/gezogen und die drei leeren
Owner-Verzeichnisse mitentfernt.migrate verify --from HEADbestätigt
compared == 182, added == 0, removed == 0, alle drei alsmovedmarkiert.
Titelkollision trat wie im Issue erwartet keine auf.Geändert:
tools/chemenu/kb_scan.py(find_nested_pages),
tools/chemenu/lint_core.py(nested_pages,HARD_ERROR_KEYS),
tools/chemenu/commands/index_build.py(Rebuild-Warnung),
tools/chemenu/type_resolver.py(get_layout-Validierung),
tools/chemenu/commands/page_ops.py(_rmdir_if_emptied),tools/CONTRACT.md,
kb/CONTRACT.md§ Collections, plus die drei realen Seiten unter
kb/entities/projects/. PATCH: reine Codeänderung ohne Schnittstellenwechsel,
gefaltet in den offenen4.8.0-Kandidaten (max-wins gegen die MINOR-Bewegung
aus #56); die drei bewegten Seiten sind Korpus dieser Instanz, kein
ausgelieferter Inhalt.Schließt #57.
raw accept(#58):raw/CONTRACT.mds Routing-Tabelle war bislang eine Regel für Menschen —
wer eine Datei ablegt, wähltarticles//documents//notes//assets/selbst, und mehrere
Dateien einer logischen Quelle waren im Dateisystem nicht als zusammengehörig erkennbar. Neu ist
ein gitignorierter Eingangincoming/, der dieselben vier Typverzeichnisse spiegelt: der Mensch
klassifiziert nur, indem er dort ablegt,tools/wikitool raw accept <datei> [<datei> ...] [--page "<Titel>"]berechnet die Beförderung nachraw/.Zwei Entscheidungen, gegen die ursprüngliche Skizze im Issue: ein Bundle-Verzeichnis
(raw/<typ>/<stamm>/, benannt nach der ersten Datei) entsteht erst ab der zweiten Datei, nie
einheitlich — damit sind die 29 heute flach liegenden Bestandsdateien keine Ausnahme, sondern
bereits die Regelform, und die Frage „was passiert mit dem Bestand" beantwortet sich von selbst.
Und der Typ wird über das Eingangs-Unterverzeichnis deklariert, nicht über ein--type-Flag: die
Erklärung wird abgegeben, wenn der Mensch die Datei in der Hand hat, statt im Moment des
accept-Aufrufs neu geraten werden zu müssen.--pagedeckt den Wachstumsfall ab: erweitertraw_files:einer bestehenden Source-Seite und
faltet deren schon abgelegte Einzeldatei ins neue Bundle, sobald das die Seite über eine Datei
hinaus wachsen lässt — ohne ein Fenster, in demraw_files:ins Leere zeigt. Die dafür nötige
Rückwärtssuche und der Mehrfach-Owner-Schutz sind keine neue Mechanik, sondern
provenance.source_pages_by_raw_file, daslintschon fürduplicate_raw_file_ownersbenutzt —
ein Owner-Konflikt lehnt die Beförderung ab, statt eine andere Seite unbemerkt zu brechen.incoming/ist fürsources coverageundlintunsichtbar (beide laufen ausschließlich über
config.iter_raw_files(config.RAW_DIR)), und dass keine Datei dort je committet werden kann, ist
überdocs_verify.REQUIRED_IGNORE_CANARIESbewiesen, nicht nur zugesichert.RAW_SUBDIRS
(dist_cmd.py) bleibt die einzige Quelle der Vier-Verzeichnis-Liste:docs verify
(check_raw_subdirs) hältraw/CONTRACT.mds Tabelle jetzt in beiden Richtungen dagegen, und
dist exportsätincoming/<typ>/.gitkeepnebenraw/<typ>/.gitkeep;instructions/bootstrap.md
legt den Eingang für einen bestehenden Klon nach, da er dort nie aus git kommt.Geändert:
tools/chemenu/commands/raw_cmd.py(neu,raw accept),tools/chemenu/cli.py,
tools/chemenu/commands/dist_cmd.py(RAW_SUBDIRS-Kommentar,incoming/*/.gitkeep),
tools/chemenu/commands/docs_verify.py(check_raw_subdirs,incoming/-Ignore-Kanarie),
.gitignore,raw/CONTRACT.md,tools/CONTRACT.md,instructions/bootstrap.md,
instructions/wiki-ingest/SKILL.md. MINOR: eine Umsortierung des Bestands wäre die Grenze
gewesen, findet aber unter der Bundle-erst-ab-zwei-Regel nicht statt — der Bestand bleibt
unangetastet, keine fremde Instanz muss migrieren, vorwärts wie rückwärts reines Überkopieren.Schließt #58.
raw acceptprüfte Kollisionen bisher nur auf einzelnen Dateipfaden
(dst.exists()), nie auf dem Bundle-Verzeichnis selbst. Weil der Bundle-Name
aus dem Stem der Primärdatei entsteht, konnte eine zweite, unabhängige Quelle
wortlos in das Bundle einer ersten wandern, sobald die Dateinamen zufällig
nicht kollidierten — verifiziert mit einem Wegwerf-Test:handbuch.txt+
anhang.mdohne--pagelandeten unbemerkt in einem bestehenden
raw/documents/handbuch/.lintmeldete nichts, weil beide Quellen ihre
Dateien korrekt abdeckten.Die Menge der Namen auf
raw/<typ>/-Ebene — Dateistämme plus
Bundle-Verzeichnisnamen — ist jetzt eindeutig erzwungen (_occupied_stemsin
raw_cmd.py), unter Ausnahme dessen, was der Aufruf selbst schon besitzt: ein
Bundle, das über--pagewächst, oder ein bereits registriertes, sich
fortsetzendes Bundle. Ein belegter Stem wird mit Exit 1 abgelehnt und nennt
beide Auswege, ohne einen zu empfehlen —--replacesoder Umbenennen in
incoming/.--replaces <raw-pfad>ist der einzige sanktionierte Weg, eine Rohdatei
wortwörtlich zu ersetzen: genau eine eingehende Datei, identischer Dateiname,
gleiches Typverzeichnis, Ablehnung bei mehr als einem Owner.raw_files:
bleibt unverändert, es wird keinekb/-Seite geschrieben, und die Altfassung
lebt ausschließlich ingit log --followweiter — kein Archivverzeichnis,
kein Hash im Dateinamen, kein neues Frontmatter-Feld. Nach einem Ersatz nennt
das Kommando die Source-Seite und ihre zitierenden Seiten, damit deren
Nachzug im selben Commit passiert wie die Ersetzung.raw/CONTRACT.md§ Rules trägt beide Regeln aus dieser Entscheidung
(unveränderlich, aber ersetzbar als Ganzes);instructions/wiki-ingest/SKILL.md
benennt den Kollisionsfall als Haltepunkt, an dem eine Sitzung die Meldung dem
Menschen vorlegt statt selbst zu entscheiden — dieselbe Klasse wie AGENTS.md
Invariante 6, auch ohne dass hier ein Exit-42-Gate greift.Geändert:
tools/chemenu/commands/raw_cmd.py(_occupied_stems,
_stem_collision_message,--replaces/_replace),
tools/chemenu/tests/test_raw_cmd.py(16 neue Tests),raw/CONTRACT.md,
tools/CONTRACT.md,instructions/wiki-ingest/SKILL.md. MINOR:raw accept
wurde nie released (letztes Releasev4.7.4), die Verschärfung kostet also
keine Kompatibilitätsfrage, solange sie vor4.8.0landet.Schließt #64.
kb/concepts/bekommt Areas (#59): Sharden ist längst automatisch —
index_build.SHARD_THRESHOLD = 50, hergeleitet aus der wikieigenen Seite
Index Scaling— aber es passiert pro Area, und eine Area legt niemand an.
kb/concepts/hatte keine, also war die Schwelle dort ein toter Wert: 80 Seiten
in einer einzigen Tabelle, weit über der eigenen Grenze, ohne dass je etwas
gefeuert hätte. Die Ursache war eine Asymmetrie in den Type-Specs —entity
deklarierte einlayout:,conceptnicht, obwohl das Subtype-Feld fertig dalag.types/concept.mddeklariert es jetzt für alle sechsconcept_type-Werte
(architectures/,patterns/,protocols/,workflows/,decisions/,
problems/). Fürsourcebewusst nicht: 25 von 29 Seiten sindnotes, die
Aufteilung ergäbe eine Area und vier Splitter, undkb/sources/liegt mit 29
Seiten ohnehin unter der Schwelle. Ein Subtype-Feld zu haben ist kein Grund, es
als Achse zu benutzen.Zwei Dinge im Code, beide Folgen desselben Befunds.
_area_titles()in
index_build.pylösteentityfest überfind_type_by_name("entity")auf und
las nur dessenlayout:— jeder zweite Typ mit einemlayout:hätte
.title()-Namen auf dem Verzeichnisnamen bekommen statt der deklarierten Titel.
Es liest jetzt jedes Type-Spec, und zwar pro Collection geschlüsselt, damit
zwei Typen denselben Area-Namen für Verschiedenes benutzen dürfen. Und der neue
lint-Befund meldet eine Collection über der Schwelle ohne Areas, mit der
Verteilung ihres Subtype-Felds — als Empfehlung, nicht als Failure, und nur
dann, wenn die Aufteilung jede entstehende Area unter die Schwelle drückt. Das
begrenzt sich selbst in beide Richtungen:kb/comparisons/mit einer Seite
feuert nie, und die schlechte Aufteilung nachsource_typeunterbleibt von
allein, ohne dass der Check etwas über Sources wüsste.Zwei Dinge fielen unterwegs an, die das Issue nicht vorhergesehen hatte.
lint_core.pydurfteSHARD_THRESHOLD/group_pagesnicht aus
commands/index_build.pyimportieren —test_api.pyprüft strukturell, dass
chemenu.apikein Modul unterchemenu.commandslädt, und der Import hätte den
ganzen CLI-Kopf mitgezogen. Die Gruppierung liegt deshalb neu in
tools/chemenu/catalog.py, entlang derselben Linie wielint_core.py:
Korpusform hier, Darstellung dort. Und_anchor()strich mit[^a-z0-9\s-]
jeden Nicht-ASCII-Buchstaben ersatzlos — die Karte verlinkte auf#ablufe,
während die Überschrift im Shard#abläufeheißt. Vorher fiel das keinem auf,
weil alle Entity-Area-Titel zufällig ASCII sind;Abläufeist der erste, der es
nicht ist.Auf dieser Instanz angewendet:
wikitool move --reconcilehat alle 80
Concept-Seiten in ihre Area gezogen,migrate verify --from HEADbestätigt
182 compared, 0 added, 0 removed, 80 moved, 0 findings— kein Titel, kein
Body, kein Frontmatter-Feld angefasst.index rebuilderzeugt sechs Areas
(Abläufe 28, Architekturen 20, Muster 17, Entscheidungen 7, Problemstellungen 5,
Protokolle 3); keine über der Schwelle, also kein eigener Shard, und die
Schwelle wirkt wieder als Schwelle.Geändert:
types/concept.md(layout:),types/type-spec.md(wann ein
layout:sich lohnt),tools/chemenu/catalog.py(neu),
tools/chemenu/commands/index_build.py(area_titles,_anchor),
tools/chemenu/lint_core.py(unsharded_collections),
tools/chemenu/tests/(Fixture-Concept liegt jetzt in seiner Area, plus neun
neue Tests),tools/CONTRACT.md,tools/README.md,README.md,
kb/concepts/COLLECTION.md, sowie die 80 bewegten Seiten unterkb/concepts/.MINOR, nicht MAJOR: der Umzug ist ein Angebot, kein Zwang. Eine
bestehende Instanz, diemove --reconcilenicht laufen lässt, bleibt
funktionsfähig —group_pagesliest das Dateisystem, nicht daslayout:, also
landen flache Bestandsseiten in der Area „All" und neu angelegte in ihrer
eigenen; beides rendert. Der gemischte Zustand meldet sich alslint-Befund
Misplaced Pages, der seit jeher advisory ist. Und ein Downgrade auf einen
Stack ohne dieseslayout:funktioniert weiter: die Verzeichnisse bleiben
Verzeichnisse, nur die Anzeigetitel fallen auf.title()zurück. Kosmetik, kein
Bruch der Austauschbarkeit in beiden Richtungen.Schließt #59.
Nachzug an
63b4bb8: die Umstellung der Namenskonvention aufHA Integration
hatte inREADME.mddas Gegenbeispiel verloren — die Zeile las
Use singular for entities: `HA Integration.md` (not `HA Integration.md`), beide
Seiten des „not" identisch, also eine Regel ohne Fall, an dem sie greift.
kb/CONVENTIONS.mdundkb/entities/COLLECTION.mdhatten im selben Commit das
korrekte Paar bekommen;README.mdzieht jetzt mitHA Integrations.mdnach.source_typehatte intypes/source.schema.yamleindefault: notes— der Compiler wählte
das Sammelbecken, sobald niemand widersprach, nicht ein Mensch. #59 hatte den Bestand deshalb
für lopsided gehalten (25 von 29 Seitennotes) undsourcebewusst flach gelassen; nachgezählt
nach dem, was die Seiten tatsächlich sind (Dateiname,author:, Rohdatei), waren es 16
Session-Transkripte, 4 LLM-Analysen, 2 Tracker-Exporte und nur 3 echte Notizen. Der Default war
der Fehler, nicht das Enum.Umgesetzt:
default:gestrichen,wikitool new sourceverweigert jetzt ohne expliziten Wert.
Enum neu:transcript,analysis,article,document,notes,tracker,unclassified—
specundimageentfallen (null Seiten, nie am echten Material bewährt).unclassifiedist
das neue, sichtbare Fach für eine Quelle, deren Kategorie noch nicht feststeht — eigene Area,
beratenderlint-Befund (unclassified_source_pages), keine harte Fehlerklasse.types/source.md
deklariert jetzt einlayout:für alle sieben Werte.Auf dieser Instanz angewendet, in einem eigenen
work/reclassify-source-types/-Lauf: 22 der 29
Source-Seiten perwikitool touch --set source_type=<wert>auf ihren tatsächlichen Wert
korrigiert (16 transcript, 4 analysis, 2 tracker), 7 unverändert.wikitool move --reconcilehat
alle 29 danach in ihre Area gezogen (transcripts 16, analyses 4, articles 3, notes 3, trackers 2,
documents 1, unclassified 0).migrate verify --from HEADbestätigt182 compared, 0 added, 0 removed, 29 moved, 22 findings— die 22 sind exakt die beabsichtigtensource_type-Änderungen,
kein Titel, kein Body, keine Wikilink- oder Zitatzahl angefasst.lint --fail-on-errorgrün,
insbesondere ohne Misplaced Pages, Nested Pages oder den neuen Unclassified Source Pages.Geändert:
types/source.schema.yaml(Enum, kein Default),types/source.md(layout:),
types/type-spec.md(sourceals Beispiel für ein bewusst fehlendeslayout:ersetzt —
ein lopsided Feld wird repariert, nicht dauerhaft flach gelassen),tools/chemenu/lint_core.py
(unclassified_source_pages, neu),tools/CONTRACT.md,instructions/wiki-ingest/SKILL.md
(--set source_type=,unclassifiedals Ausweg),instructions/dev/corpus-policy.md
(Floor-Ausnahme fürunclassified),kb/sources/COLLECTION.md(Areas, Autorschaft trennt
analysisvondocument), fünf Tests umgehängt (davon einer auf einen neuen
Fixture-Type-Spec, weilsourceals „hat Subtype-Feld, keinlayout:"-Beispiel wegfällt),
sowie die 29 bewegten und 22 reklassifizierten Seiten unterkb/sources/.MINOR, geprüft am Drop-in-Test: eine bestehende Instanz besitzt ihr eigenes
types/source.md(Auslieferung nur als.template), kopierttools//types//instructions/
über sich und bleibt unverändert funktionsfähig — kein Downgrade-Bruch, kein umgeschriebenes
maschinengelesenes Format. Kein--breaking, kein Migrationsdokument.Schließt #66.
raw accept: Datums-Shard statt Typverzeichnis,fidelity/authorityam Drop-Punkt (#67,
Paket B von vier — A ist #66 oben, C ist #68, D ist #69): zwei unabhängige Befunde, ein
Codepfad.Befund 1:
raw/CONTRACT.mds vier Typverzeichnisse (articles/,documents/,notes/,
assets/) lösten keinen der drei Gründe ein, die einen Verzeichnis-Split rechtfertigen —
raw/wird nie durchgeblättert, keine Klausel dieser Datei galt je pro Verzeichnis, alle vier
verrotten gleich (unveränderlich, nie gelöscht). Der Split kostete real: der Mensch trifft beim
Ablegen inincoming/<typ>/eine Routing-Entscheidung, die später blind nachsource_type:
abgeschrieben wird — genau darüber entstand der in #66 korrigierte Bias (raw/notes/hielt laut
altem Contract-Text „Gesprächsprotokolle", 16 der 25 Dateien dort waren tatsächlich Transkripte).Befund 2: was am Drop-Punkt bekannt ist und danach nirgends mehr — wie treu eine Erfassung ist
und was das Material über seinen Gegenstand behaupten darf. Zwei neue, unabhängige Achsen auf
types/source.md:fidelity(verbatim/published/secondhand/nontextual) undauthority
(normative/reporting/opinion), beide mitunknownals backfill-only-Wert.Umgesetzt:
raw acceptadressiert eine Datei jetzt überraw/<YYYY>/<MM>/, berechnet aus dem
Annahmedatum — eine reine Funktion von etwas Unveränderlichem, kann also nie rebalancieren und
keinen[^cite-id]-Anker brechen.incoming/wird flach; ein Unterverzeichnis wird toleriert
und ignoriert statt inspiziert (alteincoming/<typ>/-Skripte laufen unverändert weiter).
Bestandsdateien inraw/articles|documents|notes|assets/bleiben unbewegt und weiter gültige
--replaces-Ziele — das Layout war nirgends versioniert, es gibt also keine „zwei Korpusformen".
Wächst eine bereits promotete Einzeldatei zum Bündel, entsteht das Bündel an ihrem eigenen
Speicherort, nie im heutigen Shard — ein Bündel aus altem und neuem Datum hätte keine
eindeutig richtige Adresse.- Stem-Eindeutigkeit (#64) gilt jetzt global über
raw/, nicht mehr pro Typverzeichnis —
ohne Typverzeichnisse als Grenze wäre die Prüfung sonst wirkungslos gegen ein Bündel in einem
anderen Shard oder einem Alt-Verzeichnis. fidelity/authority: neu intypes/source.schema.yaml, ohnedefault:und bewusst
nicht inrequired:(sonst bricht jede bestehende Instanz an der Validierung — die
MINOR-Einstufung unten hängt daran). Erzwungen stattdessen im Werkzeug:raw acceptverlangt
beide Flags immer; trägt der Aufruf--page, schreibt es sie direkt auf die Zielseite, sonst
druckt es die fertigenew source --set fidelity=... --set authority=...-Folgezeile, und
new sourceverweigert seinerseits ohne beide Werte.unknownist backfill-only — weder
raw acceptnochnew sourcedürfen es schreiben.- Capture-Felder sind fill-once, nicht auf
touch.pysUNSETTABLE-Denylist: eine Denylist
hätte auch den ersten (Backfill-)Schreibzugriff verboten, den der Migrationslauf braucht.
touch --set <feld>=<wert>schreibt nur, solange das Feld fehlt, und verweist danach auf
raw accept --replacesals einzigen Korrekturweg — der einzige Aufruf, der einen bereits
gesetzten Capture-Wert überschreiben darf, weil eine korrigierte Erfassung eine neue Edition
der Quelle ist, keine Bearbeitung der Seite.types/source.mddeklariert die Feldliste selbst
(capture_fields:), gelesen überTypeResolver.get_capture_fieldsstatt an drei Stellen
hartkodiert. lintbekommt einen neuen beratenden Befund, Confidence Above Source Standing: die
Autoritätsbewertung, diekb/CONVENTIONS.mds Confidence-Rubrik seit je verlangt („+0.1 für
offizielle Doku"), aber nirgends festhielt. Eine stackseitige Obergrenzentabelle in
kb/CONTRACT.md(reporting0.8,opinion0.6,secondhand/nontextual0.7,normative/
verbatim/published/unknownohne Obergrenze) begrenztconfidence_base, ersetzt es aber
nicht — eine Formel hätte zwei widersprechende Ableitungen derselben Zahl, und Autorität ist
eine Obergrenze, kein Determinant. Nicht inHARD_ERROR_KEYS. Auf dieser Instanz meldet der
Befund aktuell nichts: kein Bestand trägt die neuen Felder, das ist erwartet, nicht geprüft.- Aufgeräumt:
dist_cmd.RAW_SUBDIRSund der darauf laufendedocs verify-Check
(check_raw_subdirs) entfallen ersatzlos,raw/CONTRACT.mds Routing-Tabelle beschreibt
stattdessen den Shard, die Ignore-Kanarie wandert vonincoming/documents/probe.pdfauf
incoming/probe.pdf.
MINOR,
4.8.0-beta.8desselben Kandidaten — drei geprüfte Bedingungen:raw acceptnimmt
weiterhin Dateien ausincoming/<irgendwas>/an, statt sie zu verweigern;fidelity/authority
stehen nicht inrequired:; die zwei neuen Pflichtflags anraw acceptsind eine
Verhaltensänderung, aber dieselbe Einstufung, die #66snew source-Verweigerung im selben
Kandidaten schon bekam. Kein--breaking, kein Migrationsdokument nötig — kein Bestand wird
durch diesen Bump ungültig.Geändert:
raw/CONTRACT.md,types/source.md,types/source.schema.yaml,
tools/chemenu/commands/raw_cmd.py(Neufassung),tools/chemenu/commands/new_page.py
(Capture-Feld-Pflicht),tools/chemenu/commands/touch.py(_capture_field_or_fail,
Fill-once),tools/chemenu/type_resolver.py(get_capture_fields),
tools/chemenu/commands/dist_cmd.py/docs_verify.py(Aufräumen),tools/chemenu/lint_core.py
(neuer Befund),kb/CONTRACT.md(Obergrenzentabelle),tools/CONTRACT.md,.gitignore,
instructions/bootstrap.md,instructions/wiki-ingest/SKILL.md(Schritt 1 und 6), zugehörige
Tests.Der Backfill über den Bestand lief als eigener
work/-Lauf (backfill-capture-fields,
Commit00220f8) hinterher, nachinstructions/migrate-corpus.mdund durch das
Mass-Update-Gate: 29 Source-Seiten, nicht 31 wie zwischenzeitlich im Issue notiert — die
höhere Zahl zählteINDEX.mdundCOLLECTION.mdmit.Die Regel dieses Laufs war enger als die des laufenden Betriebs, weil ein nachgetragener
Capture-Wert erschlossen ist und nicht erhoben: ein echter Wert nur dort, wo die Art des
Artefakts ihn aus dem Material selbst festlegt, sonstunknown. Ergebnis:verbatim+reporting
18 (16 Gesprächstranskripte, 2 Tracker-Exporte — beide wörtliche Mitschnitte, beide Protokoll
statt Festlegung),secondhand+opinion3 (LLM-Analysen),published+reporting2,
published+normative1 (Karpathys Idea-File, das definierende Dokument seines eigenen
Gegenstands),verbatim+normative1 (das qmd-README, dessen Rohdatei ihre Treue selbst
deklariert),unknown+normative1,unknown+reporting3.fidelity: unknownsteht auf 4 der 29 Seiten,authority: unknownauf keiner. Die
Asymmetrie ist der interessante Teil: wer für einen Gegenstand zuständig war, ließ sich überall
aus dem Material beantworten — wie treu ein selbstverfasstes Cheat Sheet oder ein Anweisungsdokument
„erfasst", nicht, weil der Enum für ein originär geschriebenes Artefakt keinen Wert hat. Das ist
kein Backfill-Fehler, sondern genau die Grenze, dieunknownmarkieren soll.Und der
lint-Befund hat einen Fall — 67 sogar: nach dem Backfill melden 67 von 152 Seiten
mitconfidence_basemehr Konfidenz, als die Quellenlage trägt (47 gegen die 0.8-Grenze für
reporting, 20 gegen die 0.6-Grenze füropinion). Das ist kein Fehlalarm und auch keine
Nacharbeit dieses Eintrags: der Korpus ist zu gut der Hälfte aus Gesprächstranskripten kompiliert,
und die Rubrik inkb/CONVENTIONS.mdlässt Quellenzahl und Aktualität allein bis 0.95 laufen,
während der Autoritätsterm der kleinste Summand ist. Ob daraus folgt, dass 67 Seiten überbewertet
sind oder dass die 0.8-Grenze für einen selbstdokumentierenden Korpus zu eng ist, ist eine
Entscheidung und keine Korrektur — sie hängt als Messung am Stub #60, der genau diesen Verdacht
ohne Zahlen aufgeschrieben hatte. Die Transkripte wurden ausdrücklich nicht aufnormative
hochgestuft, nur damit der Report leiser wird.Schließt #67.
source_typeist Instanzsache: Profilkatalog, Setup-Frage,evolve-subtypes-Instruction
(#68, Paket C von vier — A ist #66, B ist #67, D ist #69): reine Doku- und Instruction-Arbeit,
kein Korpus-Sweep.Befund:
source_types sieben Werte (transcript,analysis,article,document,notes,
tracker,unclassified) beschreiben diese Instanz, nicht den Stack — gegen drei
hypothetische Zielinstanzen (Handball-Verein, Produktentwicklung, Pen-&-Paper) hat die Liste
fast nichts gemeinsam, währendfidelity/authority(#67) in allen vieren dieselben Werte
bleiben. Architektonisch war das längst wahr (types/source.mdträgtroot: kb,dist export
liefert es nur als.template), nur stellte nichts die Frage:instructions/kb-profiles.md
riet imentities-Abschnitt „Adapt the area list first", sagte imsources-Abschnitt aber kein
Wort zusource_type. Zweiter Befund, aus #66 mitgenommen: dasunclassified-Fach bekam einen
beratendenlint-Befund, aber nie eine Prozedur, es wieder zu leeren.Umgesetzt:
kb-profiles.mdssources-Abschnitt behandeltsource_typejetzt wieentitiesseine Area-
Liste: als das, was zuerst anzupassen ist, mit zwei ausformulierten Domänenprofilen
(Handball-Verein, Pen-&-Paper) als Anschauung, und dem ausdrücklichen Gegenbeispiel
fidelity/authority— die sind Stack-Vokabular und stehen nicht zur Wahl.instructions/setup-instance.mdSchritt 5 bekommt einen neuen Unterschritt: nach dem
Anwendungsgebiet fragen,source_type-Vorschlag ableiten, Enum undlayout:in derselben
Bearbeitung setzen. Mit der Ansage, dass der Betreiber zum Setup-Zeitpunkt null Quellen hat und
seine Taxonomie vor jedem Material rät — das Ergebnis ist ein Startpunkt, keine Festlegung, und
unclassifiedbleibt in jedem Vorschlag erhalten.- Neue Instruction
instructions/evolve-subtypes.md,manual: true: benennt die
Weiterentwicklungsschleife, die werkzeugseitig schon vollständig existierte (Fach sehen →
Wert samtlayout:ergänzen →touch/move --reconcile→index rebuild/migrate verify),
über alle drei Subtype-Achsen (entity_type,concept_type,source_type— alle drei tragen
subtype_field:undlayout:;comparisonkeins von beidem). Zwei Regeln im Body: Wert
und Sweep sind untrennbar (ein deklarierter Wert ohne Seite lädt zum Raten ein — genau der
notes-Fall aus #66), und eine Aufnahmeschwelle von ≥3 Seiten, mitspec/imageaus #66 als
Gegenbeispiel und einer benannten-Ausnahme-Klausel für Fälle wietrackerbei zwei Seiten.
manual: trueverhindert, dass eine Taxonomie-Änderung in einen laufenden Ingest hineinstolpert
— erwähnt auskb-profiles.md,setup-instance.mdundkb/sources/COLLECTION.md, aus keinem
Skill, keiner AGENTS.md, keiner CLAUDE.md verlinkt. kb/sources/COLLECTION.mdbenenntevolve-subtypes.mdan derunclassified/-Zeile.
Die vom Vorbereitungs-Body übernommene, ursprünglich vierte Maßnahme entfiel: die
corpus-policy.md-Floor-Ausnahme fürunclassifiedsteht dort bereits seit #66.MINOR, geprüft gegen den Drop-in-Test: eine Instanz kopiert
instructions/undtypes/
über sich, nichts wird umbenannt oder entfernt, kein Kommando, kein Flag, kein
maschinengelesenes Format. Kein--breaking, kein Migrationsdokument.4.8.0-beta.9desselben
Kandidaten.Geändert:
instructions/kb-profiles.md,instructions/setup-instance.md,
instructions/evolve-subtypes.md(neu),kb/sources/COLLECTION.md.Schließt #68.
instructions/CONTRACT.md§ Writing an instruction: drei offene Fragen entschiedenDrei Issues aus der #65-Analyse zeigten auf denselben Abschnitt. Alle drei
enden dort, wo die Regel steht, nicht in einem Issue-Kommentar.Der Imperativ-Titel bindet eine Instruction, nicht ein
SKILL.md(#71).
Die Regel griff dem Wortlaut nach auf alle fünf Skills durch, deren H1
Nomenphrasen sind. Geprüft gegen die Primärquelle: Anthropic normiertname
unddescriptionund sagt zur Body-Überschrift nichts; die eigenen
Beispiel-Skills heißen# PDF Processing,# BigQuery Data Analysis. Dazu
das Sachargument — der H1 liegt auf keinem Retrieval-Pfad, weil über die
Aufnahme eines Skills diedescriptionentscheidet, die ab Sessionstart im
Kontext steht, während der Body erst beim Zugriff gelesen wird. In der Sitzung
kam ein Beleg dazu, den keines der Issues kannte: der vendorierte
commonplace-Korpus trägt dieselbe Imperativ-Titel-Regel, unabhängig
entstanden, und macht im selben Absatz dieselbe Ausnahme („for promoted skills,
the skill name is the title"). Die fünf Titel bleiben unverändert.Referenztiefe: Anthropics „one level deep" gilt gebündeltem Material (#72).
Weg 2 der drei zur Wahl stehenden. Der Beleg für die Reichweite steht im
vendoriertencodex-skill-creator/SKILL.md: die Beispiele der Regel sind
DOCX-JS.md,REDLINING.md,OOXML.md— alles Dateien im Skill-Bündel. Kein
Skill dieses Repos hat heute eine solche Datei, die Regel bindet hier also
wörtlich nichts. Für den Link von einem Skill auf einen repo-weiten Contract
fallen die beiden Hälften der Frage auseinander: die Mechanik (Zweit-Hop wird
womöglich nur angelesen) ist real und verzeichnisunabhängig, die Vorschrift ist
für diesen Fall von Anthropic nicht belegt. Die geteilten Contracts bleiben
geteilt — Invariante 8 hat sie dorthin gestellt, und § Frontload verlangt, dass
ein Schritt ohne Vorkontext entscheidbar ist, nicht dass jede Regel an ihm
wiederholt wird. Als Auflage bleibt das Billigere: ein Link sagt, was der
Schritt aus der Datei braucht.Ob die Mechanik hier überhaupt beißt, wurde vor der Entscheidung im Eval-Aufbau
nachgesehen, und die Antwort ist: nicht messbar. Die L2-Trajectory-Regeln lesen
ausschließlichwikitool.call,gate.*,publish.commitund
prompt.submitted— keine Dateizugriffe eines Agenten; auf Claude Code ist
überhaupt kein Tool-Hook verdrahtet, einhead -100hinterlässt also keine
Spur. Die zweite Hälfte der Behauptung, was am Ende im Kontextfenster stand,
erzeugt konstruktionsbedingt nirgends ein Event. Ein kausaler A/B-Vergleich
bräuchte den L3-Runner, der entworfen und nicht gebaut ist. Das steht jetzt im
Contract: eine Festlegung über Reichweite, keine Messung. Der Nebenfund — L2
sieht auf dem primären Harness gar keine Tool-Calls — ist ein eigenes Issue
wert und nicht Teil dieser Änderung.Wieviel Begründung ein Schritt tragen darf, ist jetzt messbar (#79). Der
alte Satz („Keep reasoning out of the body […] keep only enough reasoning to
decide edge cases") zog in zwei Richtungen, und die größte Instruction des
Repos lebte in der Lücke. Neu sind eine Keep/Cut-Tabelle und zwei Tests:
Substitution — die Passage streichen und den Schritt noch einmal lesen; rät
ein Agent ohne Vorkontext jetzt, war es eine Entscheidungshilfe und sie bleibt,
egal wie lang. Once — eine Entscheidungshilfe steht an dem Schritt, an dem die
Entscheidung fällt, und an genau einem solchen (Invariante 8). Danach gemessen
stand diesource_type/Capture-Asymmetrie inwiki-ingestzweimal; sie steht
jetzt einmal, in Schritt 1, und Schritt 6 trägt die Anweisung plus Verweis.
Die beiden anderen in #79 genannten Stellen — Namenskollision in Schritt 1,
## Not Extractedin Schritt 6 — bestehen den Substitutionstest und bleiben.
214 → 211 Zeilen; die Kürzung ist nicht der Zweck, die Eindeutigkeit ist es.PATCH, geprüft gegen den Drop-in-Test: eine Instanz kopiert
instructions/über sich, nichts wird umbenannt oder entfernt, kein Kommando,
kein Flag, kein maschinengelesenes Format, und der Rückweg funktioniert
genauso. Kein--breaking, kein Migrationsdokument.Geändert:
instructions/CONTRACT.md(§ Writing an instruction, drei neue
Unterabschnitte),instructions/wiki-ingest/SKILL.md(Schritte 1 und 6).Vier Befunde in der Skill-Prosa, ein Publish
Der Rest der #65-Analyse, soweit er die fünf
SKILL.mdselbst betrifft. Vier
Issues, fünf Dateien, kein Codeanteil.Die Hard Rule von
wiki-statuswar falsch (#70). Sie sagte „read-only.
Never writes, scaffolds, or modifies any file" — und Schritt 2 ruftlint,
schreibt also einen Report, was Schritt 2 sogar selbst beschreibt. Ein Agent,
der die Regel wörtlich nimmt, kann den Skill nicht ausführen; einer, der ihn
ausführt, hat die stärkste Aussage des Dokuments gebrochen, bevor er Schritt 5
erreicht. Das ist die teurere Sorte Widerspruch, weil die Hard Rule genau die
Stelle ist, an der ein Konflikt entschieden wird. Sie lautet jetzt wie die von
wiki-query— read-only gegenüber Wiki-Inhalt — und benennt den einen Write
mitsamt Grund:reports/ist gitignored und trägt keine Wiki-Seite. Schritt 5
behauptet nicht mehr, es sei keine Datei geschrieben worden, sondern sagt, was
mit der geschriebenen nicht passiert (Semantic Review bleibt leer, nichts
wird ausgetragen — das istwiki-lintSchritt 9). Der Decision Point „Never
publishes — nothing was written" trägt jetzt den wahren Grund: unterkb/hat
sich nichts geändert, und der Report kann gar nicht in einen Commit geraten.wiki-ingestundwiki-lintbekommen einen Abhak-Block (#74). Anthropics
Skill-Doku empfiehlt für „particularly complex workflows" eine Checkliste, die
der Agent in die Antwort kopiert und mitführt. Zwölf Schritte fallen
unzweifelhaft darunter. Der Ausschlag gibt aber nicht die Länge, sondern was
still ausfällt:## Not Extractedin Schritt 6, die Coverage-Prüfung in
Schritt 10, die Lint-Kadenz in Schritt 12 — keiner davon erzeugt eine
Fehlermeldung, wenn er ausbleibt.Die offene Frage des Issues — ob
wiki-lintdenselben Block bekommt — ist mit
ja beantwortet: neun Schritte, davon 3-6 reines Judgment, und ein Lauf, der
leise nur seine mechanische Hälfte gemacht hat, sieht aus wie ein
vollständiger. Damit haben zwei von fünf Skills einen Block und drei nicht, und
genau das wäre ohne festgeschriebenes Kriterium die nächste strukturelle
Ungleichheit im Sinne von #78.instructions/CONTRACT.md§ Writing an
instruction trägt sie deshalb jetzt: ein Block, wenn ein Ablauf acht
Schritte oder mehr hat und darin still ausfallende Schritte stehen. Beide
Hälften nötig — ein langer Ablauf aus reinen Tool-Calls meldet seine Lücken
selbst, weil der nächste Call ohne den vorigen scheitert. Der Abschnitt nennt
die drei anderen Skills mit ihren Schrittzahlen, damit niemand aus Symmetrie
einen vierten Block nachrüstet.wiki-queryprüfte nicht, bevor es filete (#75). Die drei Kriterien
(Synthese über mehrere Seiten, etwas noch nicht Dokumentiertes, wird wieder
gefragt) standen im Filing-Schritt selbst, und der Skill darf mehrere Seiten
je Sitzung anlegen — es gab also keine Stelle, an der jede geplante Seite
einzeln gemessen wurde. Neuer Schritt 5 vor dem erstennew: Kandidaten
benennen, jeden für sich gegen alle drei halten, ein Stapel wird nie als Stapel
beurteilt. Wer durchfällt, wird nicht angelegt, sondern in der Antwort mit
einem Satz genannt — der Nutzer kann ihn trotzdem verlangen. Der bisherige
Filing-Schritt ist Schritt 6,log appendSchritt 7, die Hard Rule zieht mit.
Der Mass-Update-Gate-Hinweis bleibt, sagt aber jetzt dazu, dass er keine
Ersatzprüfung ist: das Gate zählt Dateien und weiß nichts über Berechtigung,
und ein Stapel unter der Schwelle ist von ihm nicht freigegeben, nur nicht
angehalten worden. Dazu diesession-setup.md-Zeile in derselben Form wie in
den drei anderen — Schritt 7 läuft immer und der Filing-Pfad ziehtnew,
xref addund die Rebuilds nach sich.Die Kommandolisten gingen mit den Schritten auseinander (#78).
cite addfehlte inwiki-ingestundwiki-manage, obwohl beide es
ausdrücklich vorschreiben;types describefehlte inwiki-ingest, wo
Schritt 6 diesource_type-Werte daraus zieht;xref addfehlte in
wiki-lint, wo Schritt 1 das Umlabeln einer schwachen Kante darauf stützt;
publishfehlte inwiki-lintundwiki-query, wo je ein Decision Point es
beim Namen nennt. In die andere Richtung:rmstand inwiki-lints Liste,
ohne dass ein Schritt es begründet — die gefährlichere Richtung der Drift, weil
rmSeiten löscht. Dazulog status(entscheidet den Trigger, gelaufen wird
es vonwiki-ingest) und die nie benutzten Flagslint --markdownund
lint --json. Beide Streichungen stehen jetzt als Deliberately absent unter
der Liste, mit Grund — sonst trägt sie jemand aus Vollständigkeit wieder ein.Nicht als Drift gezählt und bewusst gelistet geblieben:
publish,
log append,index rebuildundsources rebuild-index, wo ein Skill sie
überpublish-cycle.mddelegiert. Ebensoxref removeinwiki-manage, das
zum Unlinking-Fall gehört, den der Skill als Ganzes anpage-lifecycle.md
abgibt; auch das steht jetzt als Satz dort, nicht als stille Annahme.Der zweite Teil von #78 ist die Symmetrie:
wiki-manageundwiki-status
tragen jetzt einen Beispielblock wie die drei anderen. Fünf Skills mit
demselben Aufbau sollten denselben Aufbau haben — ein fehlender Abschnitt liest
sich sonst als Aussage („hier gibt es keine typischen Fälle"), die niemand
gemeint hat.Die offene Frage aus #78 — ob
instructions verifydiesen Abgleich künftig
selbst macht — ist mit ja beantwortet und als #83 ausgelagert. Trivial ist er
nicht: die drei Ausnahmen oben (Delegation überpublish-cycle.md, benannte
Delegation an eine andere Instruction, „Deliberately absent") müsste ein Prüfer
alle kennen, sonst meldet er bei jedem Lauf dieselben Stellen. Nur die dritte
hat heute einen maschinenlesbaren Anker — den Absatz, den dieser Eintrag oben
eingeführt hat.PATCH, geprüft gegen den Drop-in-Test: eine Instanz kopiert
instructions/
über sich, nichts wird umbenannt oder entfernt, kein Kommando, kein Flag, kein
maschinengelesenes Format, und der Rückweg funktioniert genauso. Kein
--breaking, kein Migrationsdokument.Geändert:
instructions/wiki-status/SKILL.md,instructions/wiki-query/SKILL.md,
instructions/wiki-ingest/SKILL.md,instructions/wiki-lint/SKILL.md,
instructions/wiki-manage/SKILL.md,instructions/CONTRACT.md(§ Writing an
instruction, neuer Unterabschnitt „When a skill carries a copy-in checklist").
wiki-ingestbleibt mit 233 Zeilen unter Anthropics 500er-Schwelle.Schließt #70, #74, #75 und #78.
Issue-Nummern in ausgelieferter Doku (#77):
dist exportlieferte
Dateien aus, die im Fließtext auf Issue-Nummern dieses Trackers verwiesen —
„flat since Gitea #67", „new sourcerefuses without it (Gitea #66)". In einer
verteilten Instanz zeigt das auf nichts. Der Leser kann den Verweis weder
auflösen noch als unauflösbar erkennen, und eine Regel sieht damit so aus, als
stütze sie sich auf einen Beleg, den niemand beibringen kann. Das Board liegt
im Ursprungs-Repo, undinstructions/dev/issue-tracking.md— die einzige Datei,
die das überhaupt sagt — wird vondist exportmit dem Rest von
instructions/dev/weggeschnitten. Gegenprobe zum eigenen Anspruch aus
instructions/CONTRACT.md§ „Writing an instruction": „self-contained enough
for an agent with no prior context".Gemessen statt geschätzt: ein Export in ein leeres Verzeichnis,
grep -rn '#[0-9]', ergab 43 Treffer in 16 Dateien außerhalb vontools/**/*.py—
raw/CONTRACT.mdallein acht. Das Issue hatte zehn gelistet.Aufgelöst wurde nicht durch eine Markierung, sondern durch Umformulierung:
die Nummer fällt weg, die Datierung geht in Worte. Aus „flat since Gitea
#67" wird „flat since the addressing scheme dropped type directories", aus
„Pre-#67 files are not moved" wird „Files promoted under the old type
directories are not moved". Der Satz trägt sich damit selbst — es gibt keine
repoweite Notation zu definieren und an genau einer Stelle zu halten
(Invariante 8), und kein Leser vonREADME.mdmussAGENTS.mdgeladen haben,
um sie aufzulösen. Rückverfolgbar bleibt es hier übergit blame→ Commit-
Message; die tragen die Nummern ohnehin.Zwei Stellen, an denen der Zeiger der ganze Wert des Satzes war und in Worten
nichts übrig geblieben wäre, stehen jetzt in einem
<!-- dist:strip-start/end -->-Block: ininstructions/CONTRACT.md(was ein
Test der Kontextfenster-Behauptung kosten würde) und inEVALS.md(wo die
Coverage-Lücken geschlossen werden). Im Dev-Repo sichtbar, im Export weg — die
bestehende Konvention ausinstructions/CONTRACT.md§instructions/dev/, hier
zum zweiten Mal angewandt statt neu erfunden.docs verifyprüft es jetzt — die offene Frage des Issues, mit Ja
beantwortet.check_no_issue_referencesliest nicht den Arbeitsbaum, sondern
den Text, dendist_cmd.build_plan()schreiben würde: dort lebenROOT_FILES,
derinstructions/dev/-Ausschluss und das.template-Rekeying schon, und der
Text hat seine Marker-Blöcke bereits verloren. Deshalb ist ein Strip-Block
automatisch exemptiert, ohne dass der Check ihn kennen müsste.Der Einwand aus
instructions/dev/issue-tracking.md§ „What no tool checks" —
wikitoolsoll den Tracker nicht kennen — trägt hier nicht, und das ist die
Grenze, die der Abschnitt jetzt selbst zieht:re.compile(r"#\d+")hat keinen
Client, keine URL und keinen Begriff vom Zustand eines Issues. Der Check sieht
eine Eigenschaft des Dokuments, nicht des Boards. Gemessen: null False
Positives über den gesamten Export, weil Markdown-Anker aus Wortzeichen
bestehen (](#gates)matcht nicht). Der erste Fund war prompt der Satz, den
diese Sitzung selbst intools/CONTRACT.mdgeschrieben hatte, um die Regel zu
erklären.tools/**/*.pybleibt bewusst außen vor, mit ~90 Treffern in Docstrings und
Kommentaren. Ein Code-Kommentar adressiert, wer die Zeile editiert, und das
passiert ausschließlich im Ursprungs-Repo:dist exportschneidet den
stack-dev-Skill mitinstructions/dev/weg. Ein ausgeliefertestools/ist
Laufzeit-Maschinerie, keine Lektüre..gitignoreundtools/.coveragercsind
aus demselben Grund nicht im Check — von Hand mitgezogen wurden sie trotzdem,
sodass der Export heute in keiner Datei außerhalb.pyeine Nummer trägt.MINOR, geprüft gegen den Drop-in-Test: kein Kommando, kein Flag, kein
Dateiformat, keine Umbenennung; der Rückweg funktioniert unverändert, die alte
Version führt den Check schlicht nicht aus. Kein--breaking, kein
Migrationsdokument. Eine Konsequenz ist zu kennen: der Check liest auch die
instanzeigenenkb/CONVENTIONS.mdundkb/<collection>/COLLECTION.md, weil ein
Export sie als.templatemitnimmt. Eine Instanz, die dort ihre eigene
Ticket-Nummer zitiert, bekommt beim nächstendocs verifyein Finding. Das ist
kein Fehlalarm — ein Export dieser Instanz würde den Verweis weitergeben —
aber es ist neu.Geändert:
tools/chemenu/commands/docs_verify.py(neuer Check plus
shipped_prose()),tools/chemenu/tests/test_docs_verify.py(sechs Tests:
sauberer Baum, präparierte Datei, Anker-Nicht-Treffer,.pyaußerhalb des
Scans, Strip-Block unsichtbar,verifybricht ab),tools/CONTRACT.md
(Kommandotabelle und Fehlerkontrakt-Zeile),tools/README.md,
instructions/dev/issue-tracking.md(neuer § Citing an issue in the repo, und
§ What no tool checks zieht die Grenze zwischen „was dieses Repo über den
Tracker schreibt" und „dem Tracker selbst"), sowie die 16 Doku-Dateien:
raw/CONTRACT.md,kb/CONTRACT.md,tools/CONTRACT.md,types/type-spec.md,
types/source.schema.yaml,kb/sources/COLLECTION.md,
kb/concepts/COLLECTION.md,instructions/wiki-ingest/SKILL.md,
instructions/evolve-subtypes.md,instructions/bootstrap.md,
instructions/kb-profiles.md,instructions/mcp-read-server.md,
instructions/CONTRACT.md,README.md,EVALS.md,INSTALL.md,
docs/pipeline-rationale.md,.gitignore,tools/.coveragerc.Schließt #77.
Warum das eine MAJOR ist, obwohl
kb/unberührt bleibt (#73, #76): Anthropics
Skill-Authoring-Doku verlangt für Referenzdateien über 100 Zeilen ein
Inhaltsverzeichnis, damit ein Agent, der eine solche Datei nur mithead -100
anliest, trotzdem die volle Abschnittsübersicht sieht — dieselbe Vorschau-Mechanik,
die #72 schon für die Referenztiefe als real anerkannt hat. Ein von Hand
gepflegtes Inhaltsverzeichnis wäre die nächste Drift-Quelle; also ist es jetzt eine
dritte generierte Region nebenxrefs undcites (<!-- wikitool:toc -->...
<!-- /wikitool:toc -->,tools/chemenu/toc.py), erzeugt und geprüft wie jede
andere abgeleitete Kopie.wikitool docs toc [--apply]schreibt sie;docs verify
prüft jetzt, dass sie auf jeder Datei aktuell ist, dieAGENTS.md, ein
Stage-/Collection-Contract oder die flacheinstructions/**.md-Form abdeckt (25
Dateien in diesem Repo,instructions/dev/eingeschlossen — strukturell dieselbe
Dateiform, nur vondist exportausgenommen). Der Umfang ist berechnet, nie eine
Handliste: er folgt AGENTS.md § File naming, nicht einer Link-Traversierung ab den
fünf Content-Skills, und schließttypes/<name>.md-Einzelspecs bewusst aus — die
laufen überwikitool types describe, das den Inhalt neu rendert statt die Datei
roh auszugeben, sodass die Vorschau-Mechanik dort gar nicht greift.Das ist grenzüberschreitend, weil
docs verifydamit eine neue Pflichtprüfung
über bestehenden Inhalt bekommt: eine Instanz mit einer eigenen
instructions/*.md-Datei über 100 Zeilen, an der nichts geändert wurde, sieht
docs verifynach reinem Tool-Update neu fehlschlagen, bis einmalig
wikitool docs toc --applyläuft und der Diff committet wird — derselbe
Bruchtyp wie ein verschärftes Type-Spec-Pflichtfeld. Keine Migration nötig, weil
keinkb/-Inhalt betroffen ist; der einmaligedocs toc --apply-Lauf ist der
volle Reparaturweg.Zwei Nebenfunde beim Bauen der TOC-Regel, beide vor dem Bump behoben, weil sie
sonst denselben Bump falsch aussehen ließen:docs verifys
ISSUE_REFERENCE_RE(#\d+) hielt numerierte-Schritt-Anker wie
#2-fix-the-fidelity-before-writing-a-wordfür Issue-Zitate — die Regel nahm
bisher an, dass ein Anker immer mit einem Buchstaben beginnt, was für
nummerierte Überschriften (instructions/capture-session.md) nicht mehr gilt;
behoben durch einen Lookbehind, der genau die](#...-Linkfragment-Form
ausschließt, ohne ein echtes(#66)-Zitat zu übersehen. Und
instructions_cmd.dev_only_forbidden_referencesprüfte mit blankem
name in text: ein TOC-Anker wie#where-stack-development-happens
(instructions/private-instance.md) enthält „stack-dev" als reine Teilzeichenkette,
ohne den Skill zu meinen — behoben durch eine wortgrenzengebundene
Regex-Suche.Geändert:
tools/chemenu/toc.py(neu),tools/chemenu/commands/docs_verify.py
(check_toc_regions,docs toc-Kommando,ISSUE_REFERENCE_RE-Lookbehind),
tools/chemenu/commands/instructions_cmd.py
(dev_only_forbidden_referenceswortgrenzengebunden),tools/CONTRACT.md
(docs toc-Zeile),tools/chemenu/tests/test_toc.py(neu, 15 Tests),
tools/chemenu/tests/test_docs_verify.py(drei neue Tests: nummerierter Anker,
geklammertes echtes Zitat, TOC-Region auf dem realen Baum),
tools/chemenu/tests/test_instructions_cmd.py(ein neuer Test für die
Teilzeichenketten-Kollision), sowie die 25 Referenzdateien, die jetzt eine
TOC-Region tragen:AGENTS.md,kb/CONTRACT.md,kb/CONVENTIONS.md,
kb/concepts/COLLECTION.md,raw/CONTRACT.md,tools/CONTRACT.md,
types/type-spec.md,instructions/CONTRACT.md,instructions/gates.md,
instructions/setup-instance.md,instructions/private-instance.md,
instructions/link-taxonomy.md,instructions/kb-profiles.md,
instructions/ingest-large-tree.md,instructions/capture-session.md,
instructions/claude-code-model-selection.md,instructions/german-terminology.md,
instructions/evolve-subtypes.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/dev/issue-tracking.md,
instructions/dev/testing-conventions.md,instructions/dev/version-parts.md.Zusätzlich, unabhängig davon (#76):
instructions/session-setup.md§ Scope und
instructions/gates.mdbehaupteten, die Budget-Ausnahme richte sich danach, ob
ein Kommando das Wiki verändert. Tatsächlich zähltrun_budget.pyeine feste
Allowlist (SKIP_COMMANDS/SKIP_COMMAND_PATHS) —lintschreibt nur ins
gitignortereports/, sieht also lesend aus, steht aber nicht auf der Liste und
zählt wie jedes mutierende Kommando. Beide Dateien verweisen jetzt auf die Liste
intools/CONTRACT.md, statt sie mit einer falschen Faustregel zu umschreiben.
Kein Versionsbezug — reine Prosa-Korrektur, im selben Bump mitgeführt.Nachgezogen in
-beta.3:dist exporterzeugt die TOC-Region jetzt nach
dem Marker-Strip neu. Ein<!-- dist:strip-start/end -->-Block kann eine ganze
Sektion umschließen — der inAGENTS.mdumschließt## Developing this stack—,
sodass die ausgelieferte Datei eine Überschrift weniger hat, als das im
Arbeitsbaum erzeugte Inhaltsverzeichnis auflistet. Die frische Instanz wäre
damit beim allererstendocs verifyüber eine Datei gefallen, die niemand
angefasst hat. Gefunden hat das die CI im Export-Replay („The distribution works
as a fresh instance"), nichtpytestund nichtdocs verifyim Arbeitsbaum —
beide sehen den gestrippten Text nie. Der Regressionstest sitzt jetzt in
test_dist_cmd.py.Nachgezogen in
-beta.2, weildocs verifydie eigene Dokumentationstreue nur
für die Existenz einer Kommandozeile prüft, nicht für deren Inhalt: die
docs verify-Zeile intools/CONTRACT.mdnennt jetzt die TOC-Prüfung, die
Fehlerkontrakt-Tabelle bekommt die fehlendedocs toc-Zeile (Schritt 3 in
tools/README.md§ Adding a command verlangt beide Tabellen, geprüft wird nur
eine), undtools/README.md§ Adding a command Schritt 5 nannte als
--major-Kriterium „wenn bestehender Inhalt migriert werden muss" — was
instructions/dev/version-parts.mdausdrücklich verneint und was dieser Bump
selbst widerlegt: grenzüberschreitend mit--no-migration.Konfidenz-Mechanismus ersatzlos entfernt (schließt #60).
confidence,
confidence_base, der Zeit-Decay und die Konfidenz-Rubrik sind aus Schema,
Kommandos (confidence decay/init-base,touch --confidence-base), Lint
(confidence_exceeds_source_standing), Suche (--sort -confidence,
Konfidenzspalte) und jeder Doku-Stelle entfernt, die sie erwähnte.Der Mechanismus wurde gemessen, nicht nur für unschön befunden: 43 der 152
betroffenen Seiten trugen nie mehr als den Schema-Vorgabewert 0.5, der Decay
hat seit seiner Einführung keinen einzigen Wert bewegt (confidence_decay.py
übersprangconcept_type: decision,touch --confidence-basezog das
abgeleitete Feld nie mit), und kein Konsument im Stack hing außer über
corpus_diff.STRUCTURAL_FIELDS— eine Abhängigkeit von der Existenz des
Feldes, nicht von seinem Wert — überhaupt an ihm. Vier Entwürfe für eine
Reparatur (Rubrik nachjustieren, Quellenautorität anheben, aus
authority × fidelityberechnen, zwei Schubladen für Reifegrad und
Volatilität) scheiterten an denselben Messwerten. Die vollständige Studienlage
steht in #85, wo das Thema als zurückgestellt geführt wird, nicht als
verworfen.An die Stelle tritt nichts Neues:
kb/CONVENTIONS.mdbekommt eine
Prosa-Hedging-Regel — nach Quellenlage hedgen statt nach Schwellenwert, siehe
kb/CONVENTIONS.md § Hedging— und die Arbeitsliste ersetzt
--field 'confidence<0.6'durch Prädikate auf tatsächlich aufgezeichneten
Feldern (--field '!sources',--field provenance=general). Mit dem Feld
stirbt auchconfidence_exceeds_source_standing, der einzige automatische
Abgleich zwischen einer Seite und der Standing ihrer Quellen; ein Ersatz ohne
Zahl ist als Kandidat in #85 vorgemerkt, aber bewusst nicht Teil dieses Pakets.version bumpbekommt dabei ein neues Flag,--migration-required: der
laufende Kandidat hatte in einem früheren Bump--no-migrationerklärt, und
dieses Paket macht die Erklärung falsch. Die**Migration:** none required-Zeile ist maschinengeschrieben (Invariante 1 verbietet den
Handgriff), und bislang gab es keinen Weg, sie zurückzunehmen, sobald ein
späterer Bump doch eine Migration braucht. Das Flag entfernt die Zeile
stattdessen und verlangt ein Migrationsdokument, das die neue Basisversion
referenziert, bevor es das tut.Was das für eine bestehende Instanz bricht, steht in der
**Breaking Change:**-Zeile oben.instructions/migrations/5.0.0-confidence-removal.md
ist der mechanische Strip der zwei Felder über den betroffenen Korpus,
mitsamt der Invariante, die ein automatisierter Lauf einhalten muss (kein
modified:-Bump, byte-identischer Body, unveränderte Referenzarrays).Nachgezogen in
-beta.6:instructions/dev/version-parts.mdSchritt 6
beschreibt den Rücknahmepfad jetzt selbst.tools/CONTRACT.mdführte das neue
Flag bereits, aber die Instruction, die eine Sitzung vor einem Bump liest,
kannte den Fall nicht — dieselbe Sitzung ist genau darüber gestolpert. Der
Abschnitt nennt auch, warum ihn nichts meldet: die Zeile ist
maschinengeschrieben,docs verifygenügt ihr bloßes Vorhandensein, und die
Eskalationsprüfungen laufen nur auf dem Bump, der die Grenze zuerst
überschreitet.Nachgezogen in
-beta.7:instructions/wiki-status/SKILL.mdträgt jetzt den
session-setup.md-Verweis, den die anderen vier Content-Skills längst haben.
Der Skill ruft in Schritt 2lintauf, undlintsteht nicht auf der
Ausnahme-Allowlist inrun_budget.py— er zählt wie jedes mutierende
Kommando. Ohne exportierteWIKITOOL_SESSION_IDfällt die Zählung auf
getppid()zurück, die Sitzung erbt also den Stand irgendeiner fremden Shell.
Bis-beta.6war das Fehlen des Verweises durch die falsche Regel gedeckt, die
session-setup.md§ Scope selbst aufstellte („verändert das Wiki"); seit sie
die tatsächliche Allowlist nennt, ist es schlicht ein Loch.Die Alternative —
lintdurch etwas Befreites ersetzen undwiki-status
wirklich budgetfrei machen — scheidet an der Sache aus: der Skill liest aus dem
Report die Graph-Auswertung (Broken Links, Orphans, Most-Linked Pages,
uncovered raw files), und kein befreites Kommando liefert die.doctorprüft
die Installation, nicht den Korpus;searchist Retrieval. Einwiki-status
ohnelintwäre kein leichterer Skill, sondern ein leerer.Damit gilt über alle fünf Content-Skills dieselbe Aussage: ein Skill verlinkt
session-setup.mdgenau dann, wenn er mindestens ein nicht-befreites
wikitool-Kommando aufruft. Geprüft wird sie nicht —instructions verify
kennt weder die Kommandolisten der Skills noch die Allowlist. Das wäre ein
eigener Schnitt.Nachgezogen in
-beta.8:CLAUDE.mdimportierte bislangUSER.md,SOUL.md,
ENVIRONMENT.mdundinstructions/claude-code-model-selection.mdzusätzlich
zuAGENTS.md— eine Harness-Drift, denn dieselben drei
Personalisierungsdateien werden auf den anderen drei Harnesses (Codex CLI,
Copilot, Vibe) allein durchAGENTS.mds eigene Anweisung gelesen, nie
injiziert.CLAUDE.mdimportiert jetzt nur nochAGENTS.md; die Bedingung
„if the runtime has not already injected them" inAGENTS.md§
Personalization entfällt, weil kein Runtime mehr injiziert.instructions/claude-code-model-selection.mdist entfernt und als
docs/model-and-effort-selection.mdneu geschrieben, in Empfehlungsstimme
statt als Instruktion: die Datei beschrieb überwiegend Handlungen, die eine
Sitzung nicht selbst ausführen kann (das eigene Modell,/code-review-Stufen),
und wurde im ganzen Baum nur von den beiden dev-only Skillsstack-devund
stack-closereferenziert, deren Links jetzt dorthin zeigen. Eine
bestehende Instanz behält die entfernte Datei als Überbleibsel, bis sie
wikitool dist upgrade --prunelaufen lässt oder die Datei von Hand löscht —
instructions verifymeldet sie sonst neu als verwaist.AGENTS.md§§ Personalization, Environment und File naming sind an den
Stellen gekürzt, die eine zweite Kopie einer Regel waren, die
docs/ownership-and-templates.mdoder eine Invariante schon trägt; § File
naming verlinkt jetzt alle vierdocs/-Seiten namentlich, was vorher
nirgends geschah.USER.mdundSOUL.mdverlieren an derselben Stelle
Rahmen- bzw. Herkunftsprosa, dieUSER.md.templatebzw. ein Kommentar in
SOUL.mdselbst schon trägt.Telemetrie-Default nach Installationsform (#55):
chemenu.telemetry.writer.enabled()war
eine Zeile - immer an,WIKI_TRACE=0das einzige Opt-out. Richtig für dieses Repo, dessen
Traces das Messinstrument sind, mit dem der Stack sich selbst bewertet, aber die falsche
Voreinstellung für eine ausgelieferte Instanz: dort hat niemand Telemetrie bestellt, und
niemand liestEVALS.md, bevor die erste Datei geschrieben ist. Dazu kam eine zweite Lücke:
keine Mengenbegrenzung irgendeiner Art -reports/telemetry/<session>/trace.jsonlwächst,
solange die Instanz läuft, und nichts räumt je etwas weg.tools/chemenu/telemetry/policy.py(neu) löst jetzt beides an einer Stelle, gekeyt auf den
aufgelösten Root, damitwikitool doctor, der Writer und der MCP-Server-Start-Guard dieselbe
Antwort für denselben Checkout geben. Ein Git-Clone dieses Repos bleibt beim alten Verhalten
(an,WIKI_TRACE=0schaltet ab); eine perdist exportausgelieferte Instanz startet ab jetzt
mit Telemetrie aus - erkannt an der ohnehin vorhandenen, maschinengeschriebenen
.wikitool-release.json(Invariante 1). Wer sie dort anschalten will, legt eine
.wikitool-telemetry.jsonan (pro Checkout, gitignored, kein.template- wie
.wikitool-remotes.json);WIKI_TRACEüberschreibt weiterhin beide Richtungen und schlägt die
Datei.Zwei unabhängige, fail-silent durchgesetzte Mengendeckel greifen in beiden Installationsformen:
ein Byte-Deckel pro Session-Trace (Default 5 MiB, einstatvor jedem Append) und eine
Retention über die Anzahl der Session-Verzeichnisse (Default 250). Die Retention reserviert den
Platz der gerade entstehenden Session, statt sie mitzuzählen - sonst pendelt der Bestand
dauerhaft beikeep+1statt beikeep, weil jeder Lauf immer nur das räumt, was der vorige
Lauf über dem Limit gelassen hat. Am Byte-Limit schreibt ein weiterer Aufruf nichts mehr außer
einem einmaligentelemetry.limit-Event, per Exclusive-Create auf eine.limit-Sentinel-Datei
ausgelost - derselbe Ein-Schreiber-Trick wie beimsession.start-Header, für den Fall, dass
mehrere Prozesse gleichzeitig auf denselben Trace schreiben. Eine Retention-Runde löscht
ausschließlichtrace.jsonl/.limitder überzähligen Verzeichnisse undrmdirt nur, wenn
danach leer - niermtree, aus demselben Grund wie beiupstream merge(#30):
reports/telemetry/hält lokale, nicht rekonstruierbare Daten, dieeval scoreliest.wikitool doctorbekommt einen neuentelemetry-Check (an/aus, warum, Menge gegen beide
Deckel, nieFAIL, wiepublish-remotesundenvironment). Der MCP-Server-Start-Guard
(check_trace_destination) lasWIKI_TRACEbisher selbst statt den Writer zu fragen - eine
zweite Kopie derselben Regel, die Invariante 8 verletzte, bevor diese Änderung sie schließt; er
fragt jetzt dieselbe Policy.instructions/setup-instance.mdbekommt einen neuen
Entscheidungspunkt (Schritt 10).MINOR, kein neuer Boundary-Crossing: additiv in beide Richtungen - eine bestehende Instanz
kopiert die neue Maschinerie über sich und hört still auf zu schreiben, ohne Hand-Arbeit oder
Migration; die alte Version zurücklegen stellt den alten Default wieder her, weil sie die neue
Datei und die neuen Variablen schlicht ignoriert. Der Kandidat trägt seine--breaking-Zeile
bereits aus einem früheren Bump (Confidence-Entfernung); diese Änderung fügt keine neue hinzu.Geändert:
tools/chemenu/telemetry/policy.py(neu),tools/chemenu/telemetry/writer.py,
tools/chemenu/telemetry/schema.py,tools/chemenu/commands/doctor.py,
tools/chemenu/mcp/server.py,tools/chemenu/config.py,tools/chemenu/version.py,
.gitignore,tools/chemenu/tests/conftest.pyund die Telemetrie-/Doctor-/MCP-Server-Tests,
.gitea/workflows/ci.yml,EVALS.md,INSTALL.md,INSTALL-MCP.md,reports/CONTRACT.md,
tools/CONTRACT.md,instructions/setup-instance.md. Schließt #55.tools/CONTRACT.mdbeschriebraw acceptnoch vor #67: Typverzeichnisse statt Datums-Shard,
kein--fidelity/--authority, Stem-Eindeutigkeit "atraw/<type>/level" statt global. Vier
Zellen (beideraw accept-Zeilen der Kommandotabelle, beide im Fehlerkontrakt) waren an #67
vorbeigeschrieben worden -raw/CONTRACT.mdselbst war korrekt,docs verifyscheck_commands
prüft nur Kommandonamen gegen die Tabelle, nicht deren Prosa gegen den Code. Reine
Doku-Korrektur, kein Verhalten geändert. Schließt #89.Der MCP-Server bekommt ein sechstes, optionales Tool:
submit, ein Schreibpfad für Dokumente
von einem Aufrufer, der nicht dieses Terminal ist. #19s Eigenschaft "kein Tool schreibt" galt
strukturell - nichts unterchemenu.commandswar importierbar - und diese Formulierung wird mit
einem echten Schreibpfad falsch. Die tragfähige Ersatzformulierung ist eine Positiv-Liste
statt einer Abwesenheit: der Serverprozess darf in genau ein Verzeichnis schreiben,
mcp-upload/, erzwungen durch eine einzige Funktion (chemenu.upload._write_atomic_within),
die jeden aufgelösten Zielpfad gegen dieses eine Verzeichnis prüft - Tests decken..,
absolute Pfade und einen Symlink, der aus dem Verzeichnis hinausführt. Die alte
Abwesenheitseigenschaft bleibt daneben unverändert bestehen: die Reviewer-Kommandos
(upload accept/upload reject) liegen unterchemenu.commandsund sind vom Server aus nicht
erreichbar.Zwei Stufen vor
raw/, zwei verschiedene Grenzen.mcp-upload/<id>/hält Material, das
niemand geprüft hat;wikitool upload accept <id> --confirm <token>befördert es nach
incoming/, wo es sich nicht mehr von einer lokal abgelegten Datei unterscheidet und
wiki-ingestSchritt 1 unverändert greift. Ohne Token verweigertupload acceptmit Exit
42 - der vierte Gate des Stacks, Upload Review Gate, gleiche Form wie der
Mass-Update-Gate: ein Token, der Id, Dateiname, Größe, Sha256 und Einreicher digestet, wird
also ungültig, sobald sich das Manifest ändert.wikitool upload reject <id> --reason "<warum>"
braucht keinen Gate - Ablehnen braucht keine Freigabe, nur Annehmen tut das - und löscht das
Material, behält aber Grund und Sha256 im append-onlymcp-upload/ledger.jsonl.Die Einreicher-Identität kommt ausschließlich aus einem HTTP-Header, nie aus einem
Tool-Argument.identity_header(DefaultX-Forwarded-User) in.wikitool-upload.json
nennt den Header; fehlt er auf der Anfrage, wird ohne jeden Schreibvorgang verweigert - eine
unzurechenbare Einreichung ist damit unmöglich, nicht nur unerwünscht. Das Manifest hält neben
submitterauchsubmitter_source(den Headernamen), damit der Datensatz sagt, worauf die
Behauptung ruht, statt sie als Tatsache zu führen. Die Middleware muss den Header selbst setzen
und eine vom Client mitgeschickte Kopie verwerfen - eine Deployment-Pflicht, dokumentiert in
INSTALL-MCP.mdSchritt 5 undinstructions/ingest-queue.md, die der Prozess selbst nicht
erzwingen kann..wikitool-upload.jsonist ein struktureller Opt-in, nicht bloß eine Konfiguration. Fehlt
die Datei, wird dassubmit-Tool gar nicht erst registriert - anders als bei
.wikitool-remotes.json, wo Abwesenheit "unbeschränkt" heißt, heißt sie hier "der Schreibpfad
existiert nicht". Eine defekte Datei ist ein Startfehler des Servers (ValidationErrorbeim
Aufbau) und einFAILinwikitool doctors neuemupload-intake-Check - nie "keine
Beschränkung". Weitere Schutzschichten inchemenu/upload.py: eine Größenprüfung auf der
base64-Länge vor dem Dekodieren (mit einem Toleranzband von 2 Bytes für Padding, damit sie
keine an der Grenze liegende, legitime Einreichung fälschlich ablehnt - der Nachdekodier-Check
bleibt die exakte Durchsetzung), eine Endungs-Positivliste, ein rollierendes
24-Stunden-Kontingent pro Einreicher (Anzahl und Bytes, aus dem Ledger berechnet, nie aus
Verweigerungen), und eine Ablehnung doppelter Inhalte, solange die erste Einreichung noch
wartet - unter Nennung der wartenden Id.incoming/bleibt komplett unberührt als zweite, unabhängige Grenze. Ein Unterverzeichnis
dort wird seit #67 toleriert und ignoriert, sodass eine Fremdeinreichung darunter still
promotierbar gewesen wäre - deshalb ein eigenes Top-Level-Verzeichnismcp-upload/, gitignored,
ohne.gitkeep(die Schreibprimitive legt es selbst an).docs_verifys Ignore-Kanarien prüfen
jetzt auchmcp-upload/probe.pdfund.wikitool-upload.json.MINOR, kein neuer Boundary-Crossing: additiv in beide Richtungen geprüft - eine bestehende
Instanz kopiert die neue Maschinerie über sich und bekommt ein neues, standardmäßig
unregistriertes Tool, ohne Hand-Arbeit oder Migration; kein.wikitool-kb.json-Feld, kein
Type-Spec, kein umbenanntes Kommando oder Flag, kein geändertes Dateiformat. Die alte Version
zurücklegen verliert nichts - der Schreibpfad existiert dort schlicht nicht. Der Kandidat trägt
seine--breaking-Zeile bereits aus einem früheren Bump (Confidence-Entfernung); diese Änderung
fügt keine neue hinzu.Geändert:
tools/chemenu/upload.py(neu),tools/chemenu/commands/upload_cmd.py(neu),
tools/chemenu/tests/test_upload.py(neu),tools/chemenu/tests/test_upload_cmd.py(neu),
tools/chemenu/mcp/server.py,tools/chemenu/tests/test_mcp_server.py,
tools/chemenu/commands/doctor.py,tools/chemenu/tests/test_doctor.py,
tools/chemenu/commands/docs_verify.py,tools/chemenu/cli.py,tools/chemenu/config.py,
.gitignore,tools/CONTRACT.md,AGENTS.md,instructions/gates.md,
instructions/mcp-read-server.md,instructions/wiki-ingest/SKILL.md,
instructions/ingest-queue.md(neu),raw/CONTRACT.md,docs/why-gates-are-code.md,
INSTALL-MCP.md,README.md. Schließt #32.AGENTS.md behauptete,
docs verifyprüfe die Contract-Prosa selbst - das stimmt nicht (#90).
check_cli_readme()prüft nur, ob der gebacktickte Pfad einer Tabellenzeile registriert ist, nie
den Rest der Zelle: eine ausgetauschte Kommandobeschreibung oder ein neues--flagbleiben
unsichtbar, undTABLE_CELL_REläuft über das ganze Dokument statt über eine abgegrenzte
"Kommandotabelle".AGENTS.md§ Changelog zählte trotzdem "command tables, contracts, ignore
canaries" als maschinell geprüft auf und grenzte die Sitzungspflicht aufREADME.md/EVALS.md/
tools/README.mdein -tools/CONTRACT.mdund jedes<stage>/CONTRACT.mdstanden damit auf
keiner Liste, die je jemand nachzieht.Der Absatz nennt jetzt nur noch, was
docs verifys eigene Zeile intools/CONTRACT.md
tatsächlich auflistet (Verweis statt Kopie), und macht jede Zellenprosa - Kommandotabelle,
Fehlerkontrakt, Stage-Contract - ausdrücklich zur Sitzungsarbeit, neben den drei README-artigen
Dateien. Die Docstring voncheck_cli_readme()behauptet nicht mehr, sie prüfe
"tools/CONTRACT.md's command table" - sie beschreibt jetzt, dassTABLE_CELL_REdas ganze
Dokument scannt und nur den gebacktickten Pfad liest, nie die restliche Zelle.Neue Instruction instructions/dev/doc-pull-through.md
(dev-only) listet je berührter Fläche, welches Dokument eine Behauptung darüber trägt: beide
Tabellen intools/CONTRACT.md, der berührte<stage>/CONTRACT.md,AGENTS.mdbei
verschobener Regel/Gate/Invariante, die README-artigen Dateien,docs/bei verschobener
Begründung.stack-dev/SKILL.mdbekommt dafür einen neuen Schritt 5 zwischen Versionsbump und
Verify/Publish (jetzt Schritt 6) - ein Satz plus Link, die Liste bleibt in der Instruction; die
Katalog-Liste in Schritt 2 undstack-close/SKILL.mdSchritt 3 nennen die neue Instruction bzw.
die Contracts jetzt ebenfalls.version-parts.mdundtesting-conventions.mdkorrigieren dabei
zwei schon vorher falsche Schrittverweise aufstack-dev(Schritt 3 -> 4, Schritt 4 -> 6),
gefunden beim Nachziehen der Umnummerierung.MINOR, kein neuer Boundary-Crossing: additiv und drop-in in beide Richtungen - eine
bestehende Instanz kopiert die neue Instruction und die korrigierte Prosa über sich, ohne
Migration oder Hand-Arbeit; kein Feld, kein Kommando, kein Flag ändert sich. Der Kandidat trägt
seine--breaking-Zeile bereits aus einem früheren Bump; diese Änderung fügt keine neue hinzu.Geändert:
AGENTS.md,tools/chemenu/commands/docs_verify.py,
instructions/dev/doc-pull-through.md(neu),instructions/dev/stack-dev/SKILL.md,
instructions/dev/stack-close/SKILL.md,instructions/dev/version-parts.md,
instructions/dev/testing-conventions.md. Verifiziert:tools/wikitool docs verify,
tools/wikitool instructions verify(nachinstructions sync), vollepytest-Suite (1185
passed). Schließt #90.docs verifyprüfte § Commands und § Error contracts intools/CONTRACT.mdals einen Topf
(#91).TABLE_CELL_REsammelte das erste gebacktickte Wort jeder Tabellenzeile über das ganze
Dokument, ohne Abschnittsgrenze - eine aus § Commands gelöschte Zeile fiel nicht auf, solange
derselbe Name noch in § Error contracts stand, und § Error contracts wurde gegen nichts
erzwungen. Gemessen am Baum vor diesem Fix (55 registrierte Kommandos): § Commands war
vollständig, § Error contracts fehlten zehn Zeilen -budget reset,instructions list,
instructions verify,log append,migrate status,sources rebuild-index,sources trace,
types describe,upload show,version notes- unddocs verifyblieb grün.check_cli_readme()liest die beiden Tabellen jetzt über ihre##-Überschrift ab (neue
section_text()), unabhängig voneinander und je Tabelle in beide Richtungen; fehlt eine der
beiden Überschriften, meldet die Funktion das explizit statt stillschweigend auf "ganzes
Dokument" zurückzufallen. Ursache der zehn Lücken war überwiegend eine zweite, bisher unsichtbare
Untugend: mehrere Kommandos teilten sich in § Error contracts eine Zeile der Form "a/b" -
nur das erste Backtick-Wort einer Zeile zählte je als "dokumentiert", der Rest der Gruppe war für
den (ungeprüften) Check unsichtbar. Behoben, indem jede betroffene Gruppe in eigene Zeilen mit
eigenem, gegen den Code nachgesehenem Inhalt aufgeteilt wurde - dabei fielen nebenbei drei
sachlich falsche Zeilen auf:sources coverage,types list,instructions listundbudget statusschlagen nie fehl, die alte gemeinsame Zeile behauptete das Gegenteil, weil sie das
Verhalten des jeweils benachbarten Kommandos mit übernahm. Eine echte Formatierungslücke kam
dazu:log appends Zeile stand im Quelltext ohne Zeilenumbruch hinter der vonindex rebuild/
sources rebuild-indexverschmolzen - für Menschen als Tabelle kaum lesbar und für die
zeilenanfang-verankerte Regex unsichtbar.MINOR, kein neues Boundary-Crossing: additiv und in beide Richtungen drop-in - eine
bestehende Instanz kopiertdocs_verify.pyundtools/CONTRACT.mdüber sich, ohne Hand-Arbeit
oder Migration. Eine Ausnahme ist es wert, genannt zu werden: eine private Instanz mit einem
lokal abweichendentools/CONTRACT.md(instructions/private-instance.md) kann nach diesem
Update zum ersten Mal an der strengeren Prüfung scheitern, wenn ihr eigener Baum dieselbe
Fehlerkontrakt-Lücke trägt oder ihre Kommandotabelle anders benannte##-Überschriften
verwendet. Das ist kein Boundary-Crossing - kein Dateiformat ändert sich, keine bestehende
Funktion verschwindet -, sondern derselbe bereits akzeptierte Fall, den jede Verschärfung von
docs verifyseit jeher mit sich bringt: der Fix legt eine bereits vorhandene
Dokumentationslücke bloß, statt eine neue Anforderung einzuführen. Der Kandidat trägt seine
--breaking-Zeile bereits aus einem früheren Bump; diese Änderung fügt keine neue hinzu.Geändert:
tools/chemenu/commands/docs_verify.py(section_text,check_cli_readme),
tools/CONTRACT.md(diedocs verify-Zeile sowie zehn aufgeteilte bzw. nachgetragene
Fehlerkontrakt-Zeilen),tools/chemenu/tests/test_docs_verify.py(neue Tests für
Abschnittstrennung, fehlende/umbenannte Überschrift, § Error contracts in beiden Richtungen).
Verifiziert:tools/wikitool docs verify,tools/wikitool instructions verify, volle
pytest-Suite (1193 passed). Schließt #91.tools/CONTRACT.mdist jetzt ein Dokument zum Nachschlagen statt zum Durchlesen (#92).
Mit 70 KB war es das größte Dokument im Repo - größer alsAGENTS.md,kb/CONTRACT.mdund
raw/CONTRACT.mdzusammen -, und 89 % davon lagen in den zwei Kommandotabellen. Gleichzeitig
war es für gezielten Abruf bereits ideal gebaut, ohne dass es irgendwo stand: eine Zeile ist
ein Kommando, physisch einzeilig, also liefert ein einzigesgrep '^| `<kommando>'beide
Hälften seines Vertrags - was es tut und wie es fehlschlägt - und sonst nichts.AGENTS.md
routete stattdessen mit "Full command reference" dorthin, was sich als Vollread liest.Drei Änderungen: die Datei trägt die Nachschlage-Regel samt
grep-Zeile jetzt als Lead-in vor
dem Inhaltsverzeichnis (genau einmal, Invariante 8 -AGENTS.mds Routing-Zelle beschreibt nur
noch die Form und wiederholt die Zeile nicht); beide Tabellen sind in vierzehn###-Gruppen
gegliedert, wodurchdocs tocerstmals Anker unterhalb##erzeugt (vorher existierte im
ganzen Repo genau ein Anker-Link in diese Datei); und § Error contracts steht nun in derselben
Gruppen- und Zeilenreihenfolge wie § Commands, sodass die zwei Hälften eines Vertrags parallel
liegen - vorher saß etwalinks showeinmal zwischenxref link-sourceundcite id, einmal
zwischenversion releaseundmigrate list. Nebenbei repariert: diedist export-Zeile war
über vier physische Zeilen umgebrochen und damit kein gültiger GFM-Tabellen-Datensatz mehr.Die Umstellung lief per Skript mit einer Behauptung über die Multimenge der Erst-Zellen vor und
nach dem Schreiben, nicht per Augenschein - bei 117 Zeilen ist "keine verloren" nichts, was ein
Review zusieht. Dass###die Abschnittsgrenze aus #91 nicht bricht, ist jetzt ein eigener
Test:section_texts Lookahead(?=^#{1,2}[ \t]|\Z)verlangt nach ein bis zwei#ein
Space/Tab, was bei###fehlschlägt - ohne diese Eigenschaft wäre § Commands an seiner ersten
Gruppe abgeschnitten und jedes spätere Kommando als undokumentiert gemeldet worden.Bewusst nicht angefasst: die siebzehn Tabellenzellen über 900 Zeichen. Der naheliegende Schritt
wäre, ihren Begründungsanteil nachdocs/zu schieben; die Gegenprobe an der größten Zelle
(publish, 3.375 Zeichen) zeigt, dass die Kandidaten dafür - warum ein Token Dateiliste und
Inhalte digestiert, warum die Publish-Remote-Gate kein Flag hat - für eine handelnde Session
normativ verwertbar sind und nicht Hintergrund. Sie wegzukürzen hätte die Zelle verkleinert und
die Handlungsfähigkeit gesenkt. Die Zellen sind lang, weil die Verträge dicht sind.PATCH, kein neues Boundary-Crossing: reine Dokumentstruktur, kein Format, keine Funktion, keine
Hand-Arbeit bei Update oder Downgrade. Eine private Instanz mit eigenemtools/CONTRACT.md
bekommt beim Merge Konflikte in den beiden Tabellen, weil deren Zeilen umsortiert wurden - das
ist ein Merge-Konflikt in einer stack-eigenen Datei, denupstream mergeohnehin zugunsten der
Upstream-Seite auflöst, kein Kompatibilitätsbruch.Geändert:
tools/CONTRACT.md(Lead-in,###-Gruppen in beiden Tabellen, gespiegelte
Reihenfolge, TOC viadocs toc --apply),AGENTS.md(Routing-Zelle fürtools/),
tools/chemenu/tests/test_docs_verify.py(zwei Tests: Abschnitt läuft über eigene
Unterüberschriften hinweg, gruppierte Tabellen bleiben in beiden Richtungen geprüft).
Verifiziert:tools/wikitool docs verify,tools/wikitool instructions verify, volle
pytest-Suite (1195 passed). Typischer Zugriff: Median 229 statt 17.500 Tokens.
Schließt #92.incoming/.gitkeepist jetzt trackbar - Datei- statt Verzeichnismuster in.gitignore(#88).
/incoming/schloss bisher das Verzeichnis selbst aus, und Regel 2 im.gitignore-Kopf sagt
genau, warum das eine Falle ist: git kann keine Datei wieder einschließen, deren Elternverzeichnis
ausgeschlossen ist, also blieb ein!incoming/.gitkeepwirkungslos. Ein frischer Klon hatte den
Eingang deshalb nie -instructions/bootstrap.mdSchritt 2 (mkdir -p incoming) fing das für
einen Menschen ab, der die Instruction liest, aber nicht für einen Checkout, den eine Maschine
anlegt: der Korpus-Checkout des MCP-Read-Servers entsteht aus Klon plusfetch && reset --hard
und läuft durch keinen Bootstrap, also fehlte der Eingang dort still, bis das erste Kommando ihn
brauchte.Umgestellt auf
/incoming/*plus!/incoming/.gitkeep- jetzt ignoriert das Muster den Inhalt,
nicht das Verzeichnis, und die Negation greift tatsächlich. Gegengeprüft in beide Richtungen:
git check-ignore --no-index incoming/.gitkeepmeldet die Datei als nicht ignoriert,
incoming/probe.pdfweiterhin als ignoriert - die Kanarie aus
docs_verify.REQUIRED_IGNORE_CANARIEShält also unverändert.incoming/.gitkeepsteht jetzt
zusätzlich inREQUIRED_TRACKED_PATHS, damit eine künftige Rückkehr zur Verzeichnisform
docs verifylaut fehlschlagen lässt statt still zu wirken. Ein vollerdist export+
git init+git add -A-Replay bestätigt, dassincoming/.gitkeepim allerersten Commit einer
frischen Instanz landet, währendincoming/probe.pdfdort weiterhin ignoriert bleibt.instructions/bootstrap.mdSchritt 2 entfällt ersatzlos - ein frischer Klon braucht ihn nicht
mehr -, die übrigen Schritte rücken nach.mcp-upload/bleibt bewusst bei der Verzeichnisform
ohne.gitkeep: die Schreibprimitive dessubmit-Tools legt das Verzeichnis selbst an, sobald
sie gebraucht wird, und ein Checkout, der das Tool nie aktiviert, braucht das Verzeichnis auch
nie - anders als beiincoming/gibt es dort keinen Frisch-Klon-Fall abzudecken. Der
.gitignore-Kommentar zumcp-upload/benennt diesen Kontrast jetzt ausdrücklich, statt sich
nur noch mit dem alten (identischen) Verhalten vonincoming/zu vergleichen.Geändert:
.gitignore(beide Kommentarblöcke, neues Muster),incoming/.gitkeep(neu),
tools/chemenu/commands/docs_verify.py(REQUIRED_TRACKED_PATHS),instructions/bootstrap.md
(Schritt 2 entfernt, Rest umnummeriert),raw/CONTRACT.md,tools/CONTRACT.md(dist export-
Zeile),README.md(Architekturdiagramm-Kommentar). Verifiziert:tools/wikitool docs verify,
tools/wikitool instructions verify, vollepytest-Suite (1195 passed), manueller
dist export-Replay. Schließt #88.dist export-Doku nennt keine Typverzeichnisse mehr, die seit #67 nicht mehr angelegt werden
(#93).tools/CONTRACT.mdsdist export-Zeile und der--help-Docstring des Kommandos
selbst behaupteten wortgleich, ein frischer Export lege leere
raw/{articles,documents,notes,assets}/und die passendenincoming/-Typverzeichnisse an. #67
hat die Typverzeichnisse aus dem Adressierungsschema vonraw acceptentfernt (Datums-Shard
statt Typverzeichnis) und dabei den Export-Pfad auf zwei flache Anker verkürzt
(raw/.gitkeep,incoming/.gitkeep-run_export), ohne die zwei Prosa-Stellen nachzuziehen,
die noch die alte Struktur beschrieben. Nebenbefund aus der Sitzung zu #88.Beide Stellen beschreiben jetzt den tatsächlichen flachen Export. Gegengeprüft per
tools/wikitool dist exportin ein leeres Scratch-Verzeichnis:raw/enthält nach dem Export
nur.gitkeep(plus die verzeichniseigeneCONTRACT.md),incoming/nur.gitkeep- keine
Typunterverzeichnisse unter keinem der beiden Wurzeln.Außerhalb der Akzeptanzkriterien dieses Issues, aber derselbe Fund: dieselbe Prosa-Drift bei
raw/*/.gitkeepin derdist upgrade-Zeile (tools/CONTRACT.md), in
docs/ownership-and-templates.mdund imapply_upgrade-Docstring - andere Fundstelle, gleiche
Ursache. In derselben Sitzung gleich mitkorrigiert, siehe die nächste Eintragszeile unten.Geändert:
tools/CONTRACT.md(dist export-Zeile),tools/chemenu/commands/dist_cmd.py
(--help-Docstring vonexport). Verifiziert:tools/wikitool docs verify,
tools/wikitool instructions verify, vollepytest-Suite, manuellerdist export-Replay in
ein leeres Scratch-Verzeichnis. Schließt #93.raw/*/.gitkeep-Glob war seit #67 ebenfalls stale - korrigiert auf den flachen
raw/.gitkeep-Anker. Nebenbefund beim Nachziehen von #93 in derselben Sitzung: dieselbe
Typverzeichnis-Drift steckte noch an drei weiteren Stellen, die von einem mehrfachen
raw/<typ>/.gitkeepsprachen, obwohlraw/seit #67 flach ist und es nur noch den einen
raw/.gitkeep-Anker gibt -chemenu.ownership.is_export_stub/is_upgrade_preservedmatchen
ohnehin per Dateiname statt Pfadtiefe, das Verhalten war also nie falsch, nur die illustrative
Glob-Schreibweise in der Prosa. Betroffen:tools/CONTRACT.mdsdist upgrade-Zeile,
docs/ownership-and-templates.mds Aufzählung der seeded-once-Dateien, und der
apply_upgrade-Docstring indist_cmd.py.Geändert:
tools/CONTRACT.md(dist upgrade-Zeile),docs/ownership-and-templates.md,
tools/chemenu/commands/dist_cmd.py(--help-Docstring vondist upgrade). Verifiziert:
tools/wikitool docs verify,tools/wikitool instructions verify, vollepytest-Suite.Ingests haben jetzt zwei Größenachsen: Volumen und Breite (#61).
ingest-large-treelöste
bislang ausschließlich auf Volumen aus - mehr als ~20 Rohdateien, mehr als ~15raw_files:-
Einträge -, und sein § Scope sagte das auch so: „This is about volume, not difficulty."
Dazwischen lag eine Lücke: die einzelne, thematisch breite Quelle. Sie löst keinen
Volumen-Trigger aus,wiki-ingestkannte keinen anderen, und was sie anrichtet, meldet kein
lint-Befund.Der Korpus zeigt den Fall genau einmal, dafür deutlich.
Source - LLM Wiki v2: eine Rohdatei,
4 Entities + 26 Concepts, gegen einen Median von 6 über alle 29 Source-Seiten. Der naheliegende
Verdacht - die Source-Seite werde vage - trägt nicht: alle 26 Concepts haben eine eigene Seite,
die Breite wurde vollständig nachgezogen. Der Schaden sitzt eine Ebene tiefer und ist bimodal:
14 der 30 Subjektseiten liegen unter 150 Wörtern (die dünnsten bei 71 bis 78), die übrigen bei
541 bis 1.294, gegen einen Korpus-Median von 485. Eine Stub-Kohorte aus einem einzigen
Durchgang. Keine andere Quelle im Korpus hat eine: bei 18 Gegenständen (Source - LLM Wiki Pattern) sind es null, und darunter ebenfalls. Daher die Schwelle bei ~20 statt am
75.-Perzentil - ein Auslöser, der auf schadensfreien Seiten feuert, wird ignoriert.Breite wird nicht geschnitten, und das ist der Teil, der eine eigene Sektion bekommt. Zwei
Regeln schließen den Weg:raw/hält eine Datei „exactly as received", und eine Rohdatei hat
genau einen Besitzer (lintmeldet einen zweiten Anspruchsteller). Mehrere thematische
Source-Seiten über einer Datei wären also nicht bloß unüblich, sondern ließen bei einer neuen
Edition niemanden zuständig zurück. Der Schnitt vorraw acceptbleibt davon unberührt - er
ist das, wascapture-session§ 1 tut, und er ist nur möglich, weil das Transkript dort noch
gar nicht existiert. Diese Abgrenzung steht jetzt in § 1 selbst, weil genau dort der
Fehlschluss ansetzt, ein empfangenes Dokument ließe sich genauso zerlegen.Was die Breite stattdessen auslöst, ist der Extract-Pass vor dem Schreiben und die Regel, die
er beliefert: ein Gegenstand bekommt eine Seite, wenn die Quelle Material für eine trägt.
Eine beiläufige Erwähnung bekommt einen Wikilink von der Source-Seite und eine Zeile unter
## Not Extracted, keine eigene Seite. Eine Seite, die nur ihren eigenen Titel wiederholt, ist
schlechter als die Erwähnung, aus der sie entstand:lintmisst Struktur und nie Substanz, also
meldet sie niemand, und die nächste Sitzung liest sie als abgedecktes Terrain und schaut nicht
mehr in die Quelle.Der vendorierte
commonplace-Korpus stützt den Verzicht auf einen Source-Split unabhängig vom
Befund aus dem Baum, was ihn zum belastbareren Teil der Begründung macht: Atomizität ist dort
ausschließlich mit Co-Loading für Entdeckung begründet und gilt damit der Library-Schicht
(kb/entities/,kb/concepts/), nicht der Evidenz-Schicht; ein Mehr-Aussagen-Dokument ist per
title-as-claimReferenz statt Prämisse, weshalb Breite seiner Rolle nicht schadet; und die
Forderung, Forward-Lineage müsse den Betreiber unterbrechen, erreicht „eine Rohdatei, ein
Besitzer" auf eigenem Weg.Nicht gebaut: ein
lint-Befund gegen die Stub-Kohorte. Er wäre die Beobachtungsseite derselben
Sache, braucht aber eine eigene Schwellenwertdiskussion - Wortzahl ist ein grober Proxy für
Substanz - und liegt als eigenes Issue auf dem Board. Die Schwelle ~20 ruht auf einem einzigen
Schadenspunkt und ist entsprechend vorläufig.MINOR, kein neues Boundary-Crossing: additiv in beide Richtungen. Eine bestehende Instanz
bekommt einen zusätzlichen Auslöser und eine zusätzliche Regel in Dateien, die der Stack
besitzt; nichts ankb/, keinem Schema, keinem Kommando und keinem Flag ändert sich, und ein
Downgrade nimmt beides ersatzlos zurück. Der Kandidat steht ohnehin auf MAJOR, der Bump
erhöht also nur seinen Zähler.Geändert:
instructions/ingest-large-tree.md(§ When to run auf zwei Achsen, Tier-Tabelle,
neue § A broad source is not cut, § Scope, ein Decision Point,description),
instructions/wiki-ingest/SKILL.md(Schritt 2 verweist jetzt auf die Schwellenliste statt sie
zu wiederholen, Schritt 7 trägt die Seiten-Regel, Schritt 8 und ein Decision Point ziehen nach),
instructions/capture-session.md(§ 1 Abgrenzung),types/source.md(## Not Extractedist
jetzt auf beiden Achsen Pflicht;entities:+concepts:jenseits von ~20 als Gegenstück zum
bestehendenraw_files:-Signal). Verifiziert:tools/wikitool docs verify,
tools/wikitool instructions verify, vollepytest-Suite (1195 passed),docs toc --applyund
instructions syncfür die generierten Regionen und die veröffentlichten Kopien.
Schließt #61.stack-closeSchritt 3 sagt jetzt, dass ein Doku-Nachzug seinen eigenen Bump braucht.
Direkt aus dem vorigen Paket gelernt: dessen Abschlussphase zogtypes/source.mdnach, ohne
VERSIONzu bewegen, und CI-Run 249 fiel an der Version Gate um. Die Sitzung las den Nachzug
als Dokumentation -types/source.mdist aber beides, Dokument und ausgelieferte
Verhaltensbeschreibung, und die Gate ist nach Pfad gefasst, nicht nach Absicht
(^(tools/|types/|instructions/|AGENTS\.md$|[^/]+/CONTRACT\.md$)).Das ist kein Ausrutscher, sondern eine Reihenfolge, die sich wiederholt:
stack-devSchritt 4
bumpt die eigentliche Änderung, und Schritt 3 dieses Skills läuft danach - in genau den
Pfaden, die die Gate bewacht. Schritt 3 trägt den Satz deshalb jetzt selbst, samt der Falle
("nur Dokumentation") und des Auswegs (version bump --patchim selben Zug, was beim laufenden
Kandidaten ohnehin nur den Zähler bewegt). Nicht nachdoc-pull-through.mdgelegt, obwohl es
dort thematisch auch passte: die Entscheidung fällt in dem Schritt, der den Commit auslöst, und
zwei Kopien wären die Dopplung, die Invariante 8 verbietet.Geändert:
instructions/dev/stack-close/SKILL.md(Schritt 3). Verifiziert:
tools/wikitool docs verify,tools/wikitool instructions verify, vollepytest-Suite,
instructions syncfür die veröffentlichte Kopie. Kein Issue - direkt korrigiert.
This note is a snapshot of the
CHANGES.mdentry 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 inCHANGES.mdin the repository.Downloads
-
v4.7.4 Stable
released this
2026-09-04 18:51:38 +00:00 | 53 commits to main since this release4.7.4 - 2026-09-04 - bootstrap.md nennt den session-id-WARN nach frischem Bootstrap explizit als erwartet
Author: Torben Nehmer
- bootstrap.md nennt den session-id-WARN nach frischem Bootstrap explizit als erwartet
Nach einem frischen Clone plus
instructions/bootstrap.mdzeigtetools/wikitool doctor
durchgehendOK, außersession-id: WARN- ohne Einordnung, ob das ein Bootstrap-Defekt ist.
WIKITOOL_SESSION_IDwird lautinstructions/session-setup.mdbewusst pro Arbeitssitzung
gesetzt, nicht pro Clone; ein Export inbootstrap.mdselbst würde nur für den Bootstrap-Lauf
gelten, nicht für die tatsächliche Arbeitssitzung danach (die nach dem Neustart in Schritt 6 in
einer neuen Shell beginnt).bootstrap.mdbekommt deshalb einen neuen Schritt 7, der den WARN
als erwarteten Zustand benennt - analog zum bereits dokumentiertenpersonalization: FAILin
Schritt 4 - und aufsession-setup.mdverweist, statt den Export in Bootstrap nachzubauen.
Schließt #54.
This note is a snapshot of the
CHANGES.mdentry 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 inCHANGES.mdin the repository.Downloads
-
v4.7.3 Stable
released this
2026-09-04 18:31:10 +00:00 | 54 commits to main since this release4.7.3 - 2026-09-04 - eval: gate-not-self-opened prueft REMOVED_FLAGS gegen das eigene Kommando
Author: Torben Nehmer
Die Trajektorien-Regel
gate-not-self-opened(tools/chemenu/evals/trajectory.py) hat jedes
Argument jedeswikitool.callgegenREMOVED_FLAGS = {"--yes": "publish", "-y": "publish"}
geprüft, ohne je das eigenecommand-Feld des Aufrufs gegenzulesen.--yes/-ysind nur auf
publishentfernt worden - aufrm --page <Titel> --yessind sie ein gültiger, dokumentierter
Flag. Ergebnis: jederrm --yes-Aufruf wurde als Invarianten-Verstoß gemeldet ("an agent
inventing a flag the tool never accepts"), obwohl das Tool ihn akzeptiert hatte.In den vorhandenen Telemetrie-Traces unter
reports/telemetry/betraf das 111rm-Aufrufe
über 8 Sessions, davon 27 allein inpublish-cleanup/u3- jede davon fälschlichFAILED
gescort. Kein bestehender Test hätte das gefangen:tools/chemenu/tests/test_evals.pyprüfte
REMOVED_FLAGSausschließlich überpublish --yes, nie über ein anderes Kommando.Fix: die Bedingung liest jetzt
attrs.get("command") == REMOVED_FLAGS[arg]mit. Neuer
Regressionstesttest_yes_on_a_command_that_still_has_it_is_not_a_findingdeckt genau den
rm --yes-Fall ab und schlägt gegen den unfixed Code nachweislich fehl.Keine Verhaltensänderung an
wikitoolselbst - ausschließlich an der Scoring-Logik unter
tools/chemenu/evals/.- eval: gate-not-self-opened prueft REMOVED_FLAGS gegen das eigene Kommando
This note is a snapshot of the
CHANGES.mdentry 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 inCHANGES.mdin the repository.Downloads
-
v4.7.2 Stable
released this
2026-09-04 17:22:51 +00:00 | 56 commits to main since this release4.7.2 - 2026-09-04 - Coverage-Untergrenze bei 85 %, gegen beobachtete 87,0 %
Author: Torben Nehmer
- Coverage-Untergrenze 85 % in tools/.coveragerc
Die Suite hat jetzt einen Boden:
fail_under = 85intools/.coveragerc, gemessen gegen 87,0 %
(CI-Lauf 163, 6498 Statements, 975 Tests). Damit ist Gitea #10 geschlossen — das Issue, das die
Messung eingerichtet und die Schwelle danach absichtlich zurückgehalten hat, bis die Zahl
beobachtet war.Die Beobachtung ist der eigentliche Inhalt dieses Bumps. Zwischen der ersten Messung (86,9 % von
5105 Statements, 730 Tests, Lauf 87, Stack 1.8.1) und heute ist der gemessene Code um ein Viertel
gewachsen und die Suite um ein Drittel, über 38 grüne Läufe — und die Quote hat sich um einen
Zehntelpunkt bewegt. Eine Untergrenze, die auf dieser Beobachtung steht, ist etwas anderes als
eine gegriffene Zahl.85 und nicht 87, und das ist keine Bequemlichkeit. Der Coverage-Bericht unterscheidet drei
Sorten ungedeckter Zeilen, und nur eine davon bedeutet Arbeit (EVALS.md§ „How much of the
stack the suite reaches"). Ein neuer dünner Typer-Wrapper senkt den Gesamtwert, ohne dass
irgendetwas schlechter geworden wäre — seine Logik liegt daneben und ist dort getestet. Eine
Schwelle auf dem gemessenen Wert würde genau an diesem Commit rot, und eine Schwelle, die aus
einem Nicht-Grund rot wird, wird gesenkt statt verdient. Das ist die Fehlerweise, die #10
verhindern wollte, nur von der anderen Seite. Die zwei Punkte sind der Platz, den die Taxonomie
verlangt.fail_understeht in der Konfiguration und nicht als--cov-fail-underim CI-Schritt: so sitzt
die Zahl neben der Begründung, die sie erzeugt hat, und gilt für jeden--cov-Lauf statt nur für
den einen, den CI schreibt.Was der Boden nicht tut: die drei echten Lücken schließen (
provenance_cmd.py44 %,
migrate_cmd.py65 %,type_resolver.py79 %). Er friert den erreichten Stand ein. Diese Liste
ist die einzige, die sich nicht bewegt hat, während alles um sie herum wuchs —migrate_cmd.py
ist sogar von 71 % gefallen, weil das Modul gewachsen ist und die neuen Zeilen ungetestet ankamen.
Das ist Gitea #51.Mitgenommen, weil es dieselbe Frage beantwortet: der Coverage-Bericht ist als Artefakt
abrufbar, über die Run-Seite. Die Actions-Artefakt-Endpunkte melden dafürtotal_count: 0, weil
upload-artifact@v3über die ältere Artifact-API ablegt, die diese Endpunkte nicht lesen. Eine
leere Liste ist kein fehlgeschlagener Upload — steht jetzt inEVALS.mdund im Kommentar an der
Coverage report-Stufe, damit die naheliegende „Korrektur" auf v4 (hier eingeschränkt) niemandem
mehr einfällt.
This note is a snapshot of the
CHANGES.mdentry 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 inCHANGES.mdin the repository.Downloads
-
v4.7.1 Stable
released this
2026-09-04 16:49:59 +00:00 | 58 commits to main since this release4.7.1 - 2026-09-04 - redundant_see_also in tools/CONTRACT.md und wiki-lint dokumentiert; xref-remove-Falle benannt
Author: Torben Nehmer
- redundant_see_also in tools/CONTRACT.md und wiki-lint; xref-remove-Falle beim Aufräumen benannt
Die Doku-Hälfte von
4.7.0, beim Abschluss nachgezogen. Der Befund war ausgeliefert, aber
tools/CONTRACT.mdslint-Zeile zählt die Befunde auf und kannte ihn nicht - eine Instanz hätte
eine Sektion im Report gefunden, die ihr Contract nicht erklärt.docs verifyprüft, dass die
Kommandotabelle existiert, nie was in einer Zeile steht; genau die Lücke, für die AGENTS.md
„a stack change is not finished until the human docs describe it" geschrieben ist.Die eigentliche Änderung ist aber die Warnung in
wiki-lint, und sie ist keine Prosa-Politur.
Der neue Befund liest sich wie etwas, das Schritt 7 („repariere, was mechanisch ist") abräumt, und
der naheliegende Griff wärexref remove- das die Referenz beidseitig löscht. Angewandt auf
Wine see-also Wine GEnebenWine GE depends-on Winehätte das die schwache und die
spezifische Kante entfernt, und das Paar sagte danach gar nichts mehr. Ein Befund, dessen
offensichtliche Reparatur Daten zerstört, ist schlechter als kein Befund: Schritt 1 nennt die
Falle jetzt beim Namen und verweist aufxref add(fasst nur die Quellseite an) oder aufs
Berichten. Dieselbe Asymmetrie hat in #30 schon einmal Daten gekostet.
This note is a snapshot of the
CHANGES.mdentry 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 inCHANGES.mdin the repository.Downloads
-
v4.7.0 Stable
released this
2026-09-04 16:47:41 +00:00 | 59 commits to main since this release4.7.0 - 2026-09-04 - Link-Katalog: authored, alternative-to, addresses; entity→entity-Lineage; Lint-Befund gegen redundante see-also
Author: Torben Nehmer
- Link-Katalog: authored, alternative-to, addresses; Lint-Befund gegen redundante see-also
Drei neue Label, zwei geschlossene Autorisierungslücken und ein Lint-Befund - ausgelöst von der
anstehenden ersten produktiven Instanz. Katalog undCOLLECTION.mdgehen überdist exportin
jede neue Instanz; was hier fehlt, fehlt dort ab Tag eins, und nachträglich ist eine
Katalogerweiterung eine Korpus-Migration statt einer Datenzeile. Gitea #43 und #49.Die Messung, die den Zuschnitt bestimmt hat. Die 180
see-also-Kanten des Korpus zerfallen
exakt in drei Klassen: 57 (32 %) sind Spiegel einer bereits typisierten Gegenkante, 70 (39 %)
sind wechselseitigesee-also-Paare, 53 (29 %) stehen einseitig. Die erste Klasse ist kein
Vokabularproblem -Wine see-also Wine GEsteht nebenWine GE depends-on Wine,RAG see-also NotebookLMnebenNotebookLM implements RAG. Der Katalog war für ein Drittel der Fälle längst
ausreichend; es hat sie nur nichts gemeldet. Genau dafür ist der Lint-Befund unten da, und er ist
der Grund, warum diese Version mehr ist als zwei Katalogzeilen.authored(operationales Register). „hat das Ziel als einmaligen Akt geschaffen." Der Katalog
kannte fortlaufende Rechenschaft (owns) und fortlaufende Arbeit (maintains), aber nicht den
historischen Ursprung - Urheberschaft stand im Korpus deshalb in vier unvereinbaren Formen
nebeneinander:source.authorals Freitext,owns,see-alsound ein Prosa-Bullet. Eine davon
war sachlich falsch:Vannevar Bush owns Memexbehauptet laufende Rechenschaft für einen 1974
Verstorbenen, und eine falsche maschinenlesbare Kante ist schlechter als eine schwache, weil sie
geglaubt wird. Geschrieben wird das Label auf der Entity-Seite (Andrej Karpathy authored LLM Wiki Pattern) - die Gegenrichtungauthored-byauf der Concept-Seite hättexref remove
gebraucht, das beidseitig abräumt, stattxref add, das relabelt. Vier Kanten im Korpus
umgestellt, keine verloren.alternative-to(operationales Register, selbst-dual). „erfüllt denselben Zweck wie das Ziel,
so dass ein Leser, der zwischen beiden wählt, beide will." Belegt durch rund 30 Paare, darunter
die sieben Agent-CLIs, die untereinander ausschließlichsee-alsotragen - keine einzige
typisierte Kante. Abgegrenzt gegencontrasts(behauptet einen lesenswerten Unterschied) und
compares-with(wiegt auf benannten Dimensionen ab und führt in dieser Instanz auf eine
kb/comparisons/-Seite): zwei Agent-CLIs sind austauschbar, zwei gegensätzliche Entwurfsprinzipien
sind es nicht.Der Katalog sagt jetzt ausdrücklich, dass ein selbst-duales Label einmal pro Paar geschrieben
wird. Ohne diesen Satz wäre aus einer 22-Kanten-see-also-Clique eine 22-Kanten-
alternative-to-Clique geworden und nichts gewonnen: sieben austauschbare Werkzeuge sind 21
Paare, beidseitig deklariert 42 Kanten, von denen die zweiten 21 nichts sagen.addresses(konzeptionelles Register). „ist eine Antwort auf das Problem, das das Ziel
beschreibt."types/concept.mddeklariertproblemunddecisionals eigene Subtypen, und der
Katalog hatte kein Label, das eine Entscheidung mit dem Problem verbindet, das sie löst - eine
Collection konnte ein Problem benennen und nie sagen, was dagegen unternommen wurde. Abgegrenzt
gegenrests-on, das das Ziel als Prämisse nimmt statt als zu lösendes Problem. Im Korpus nur
vier belegte Paare, also dünn nach dem sonst geltenden „erst der Anwendungsfall"-Maßstab; die
Ausnahme ist bewusst und gilt dem Auslieferungszeitpunkt, der die Kosten umdreht.Zwei Autorisierungslücken entity→entity.
kb/entities/COLLECTION.mderlaubte bisher keine
Lineage zwischen zwei Entities - ein Fork, eine Neuimplementierung, ein Nachbau war nicht
ausdrückbar;derived-fromundadapted-fromsind jetzt freigegeben. Ebensoimplements, für
eine Entity, die eine als Entity geführte Konvention umsetzt. Ein autorisiertes Label ohne
Live-Nutzung ist ausdrücklich in Ordnung (instructions/dev/corpus-policy.md).Lint-Befund
redundant_see_also. Meldet einesee-also-Kante, deren Gegenrichtung bereits
ein typisiertes Label trägt. Gegen den Korpus dieser Instanz meldet er genau die gemessenen 57.
Advisory, nicht hart, aus zwei Gründen zugleich: eine schwache Kante neben einer spezifischen
ist redundant, nicht kaputt - und der Befund kommt lange nach den Korpora, die er beurteilt, also
würde eine harte Einstufung jede bestehende Instanz mit dem Upgrade rot schalten, das ihn
ausliefert. Anders alsunlabelled_edgesist er auch nicht migrations-gegatet: es gibt keine
Version, ab der die Redundanz zum Fehler wird, nur einen Sweep, zu dem jemand kommt oder nicht.links.SEE_ALSOist damit das einzige Katalog-Label, das das Werkzeug beim Namen kennt. Das ist
eine begründete Ausnahme, keine Aufweichung:see-alsoist der erklärte letzte Ausweg des
Katalogs und behauptet nur, dass nichts Besseres passte - was der einzige Grund ist, warumlint
eine Kante als schwächer als eine andere über demselben Paar beurteilen kann. Alles andere am
Vokabular bleibt ininstructions/link-taxonomy.mdund denoutbound:-Blöcken.Nicht dabei, bewusst. Der Sweep der 180 bestehenden Kanten (#48) - diese Version ändert außer
den vier Urheberschaftskanten keine Korpus-Kante. Verworfen wurden außerdemvariant-of(die
Wine-Forks tragen bereitsdepends-on Wine),implemented-by(Spiegel vonimplements, den die
Inbound-View rendert),sibling-offür die Concept-Cliquen (Über-Verlinkung, kein fehlendes Wort)
undbuilds-on(Vokabularkollision mitextends/derived-from/adapted-from).
This note is a snapshot of the
CHANGES.mdentry 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 inCHANGES.mdin the repository.Downloads
-
v4.6.1 Stable
released this
2026-09-04 13:58:09 +00:00 | 60 commits to main since this release4.6.1 - 2026-09-04 - DEVELOPMENT.md im Kommandotabellen-Check, selbstbeschriftete Release-Notes, Prosa-Korrekturen (#47 Block 3)
Author: Torben Nehmer
- DEVELOPMENT.md im Kommandotabellen-Check, selbstbeschriftete Release-Notes, Prosa-Korrekturen (#47 Block 3)
Block 3 aus #47 - die beiden Nebenbefunde entschieden und umgesetzt - plus drei Prosa-Korrekturen
an4.6.0, die eine Bewertung des eigenen Ergebnisses gefunden hat.DEVELOPMENT.mdgehört indocs_verify.STAGE_READMES(entschieden: ja). Das Gegenargument
bei der Aufnahme war, die Liste führe bisher nur ausgelieferte Dokumente, undDEVELOPMENT.md
wird vondist_cmd.ROOT_FILESbewusst nicht ausgeliefert. Beim Hinsehen löst es sich auf:
check_readmes_have_no_command_tableüberspringt eine Datei, die nicht existiert. In einer
ausgelieferten Instanz ist der Eintrag damit schlicht wirkungslos, im Entwicklungs-Checkout - dem
einzigen Ort, an dem die Datei existiert und also driften kann - greift er. Dafür spricht der
Anlass: genau diese Datei trug einmal eine Tabelle, die für jeden Verify-Befehl ein zweites Mal
beschrieb, was er prüft, und sie musste von Hand entfernt werden, weil nichts sie mit etwas
verglich. Zwei Tests: einer, der die Tabelle inDEVELOPMENT.mdmeldet, und einer, der
festhält, dass eine fehlende gelistete Datei übersprungen und nicht als Fund gemeldet wird - der
Instanz-Fall, an dem die Entscheidung hing. Der Konstantenname ist jetzt enger als sein Inhalt;
das steht als Kommentar daneben, statt eine Umbenennung durch zwei Aufrufstellen zu ziehen.Veröffentlichte Release-Notes veralten weiter - sie sagen es jetzt selbst (entschieden:
Schnappschuss akzeptieren, statt einen Korrekturweg zu bauen). Eine nach dem Tag korrigierte
CHANGES.mderreicht die Release-Seite nicht:gitea-mcpkennt kein Release-Edit, und
Löschen-und-neu-Anlegen würde die angehängten Tarball-Assets vernichten, auf dieINSTALL.mdund
version checkzeigen. Beiv4.4.0ist das real eingetreten. Statt eines Korrekturwegs für einen
Text, den niemand editieren kann, trägt der Schnappschuss jetzt eine Fußzeile, die sagt, dass er
einer ist und wo die gepflegte Fassung liegt - eine veraltete Notiz kostet einen Leser damit einen
Klick statt einer falschen Überzeugung. Angehängt inrelease.ymlund nicht inversion notes:
das Kommando ist ein allgemeiner Extraktor, dessen andere Aufrufer (lokale Vorschau, eine Pipe)
keine Release-Seiten-Fußzeile erben sollen..gitea/-Änderung, also ohne eigenen Bump-Anspruch -
sie fährt hier mit.Drei Prosa-Korrekturen an
4.6.0. Der4.6.0-Eintrag und der Docstring von
touches_stack_machinerybehaupteten, das Prädikat prüfe „denselben Pfad-Umfang, den der
CI-Versions-Gate selbst verwendet". Das stimmt nicht: CI matcht[^/]+/CONTRACT\.md$, also genau
eine Pfadebene, das Prädikat matchtCONTRACT.mdin jeder Tiefe. Folgenlos im Verhalten - ein
Über-Match druckt eine Zeile zu viel, nie eine zu wenig -, aber es war eine behauptete Äquivalenz,
die keine ist, geschrieben in genau der ungeprüften Prosa-Phase, um die #47 sich dreht. Docstring
undtools/CONTRACT.mdbenennen die Differenz jetzt und begründen sie (bei einer Erinnerung ist
Über-Matchen die richtige Richtung). Drittens:stack-closebeschrieb den eigenen Skill-Schnitt
zu stark („es gibt keinen nächsten Schritt mehr, an dem vorbei zu rutschen wäre"). Wahr für die
Prozedur, die nicht mehr im Kontext steht; nicht wahr für den Auslöser -stack-devs „invoke
it now" ist weiterhin ein Satz, und diepublish-Notiz nennt den Skill bewusst nicht beim Namen.
Zwei der drei Kettenglieder bleiben Selbstdisziplin. Der Skill sagt das jetzt selbst, statt sich
als Garantie zu verkaufen, die er nicht ist.Verifiziert:
tools/wikitool docs verify,tools/wikitool instructions verify,
.venv/bin/python -m pytest -q(969 passed, 2 davon neu),release.ymlgegen den YAML-Parser
und das Heredoc als Trockenlauf gegen eine Beispiel-Notiz.
This note is a snapshot of the
CHANGES.mdentry 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 inCHANGES.mdin the repository.Downloads
-
v4.6.0 Stable
released this
2026-09-04 13:33:22 +00:00 | 62 commits to main since this release4.6.0 - 2026-09-04 - stack-dev/stack-close skill split, publish stack-machinery note, model-selection fix (#47 Block 2)
Author: Torben Nehmer
- stack-dev/stack-close skill split, publish stack-machinery note, model-selection fix (#47 Block 2)
Block 2 aus #47 (Vorschlag E, am 2026-09-04 entschieden): die ungeprüfte Schlussphase einer
Stack-Sitzung - Issue-Body-Rewrite,docs/-Veralterung, Changelog-Prosa - hatte bisher keinen
eigenen Haltepunkt, sondern einen Prosa-Break instack-devSchritt 6. Der ist zweimal
hintereinander verschluckt worden (#42, #30), beide Male mit echtem Fund im nachgeholten
Durchgang. Ein dritter Prosa-Haltepunkt hätte dieselbe Wette verloren, die
docs/why-gates-are-code.mdfür Gates schon verliert - also keine Prosa-Lösung mehr, sondern ein
struktureller Schnitt.Neuer Skill
stack-close, dev-only wiestack-dev.stack-devendet nachtools/wikitool publishmit einem Stop statt mit einem sechsten Schritt; die Schlussphase existiert nur noch als
eigener Skill, den eine Sitzung aufrufen muss - es gibt keinen „nächsten Schritt" mehr, an dem
vorbei sie rutschen könnte.stack-closeträgt drei Dinge: den Modell-Rückwechsel-Hinweis (wie
zuvor), die Body-Rewrite-Disziplin ausissue-tracking.mdSchritte 2-3 und 7, und neu die
Handover-Pflicht über die ganze Sitzung: benannt wird das Modell für Design/Versionsstelle
(Schritt 3), für die mechanische Mitte, und für diese Schlussphase - alle drei, auch wenn sie
identisch sind. Eine Handover-Zeile, die nur eine billige Schlussphase meldet, schweigt genau
dann, wenn die ebenso ungeprüfte Design-Phase auch billig lief und niemand dort gewechselt hat.Ein Agenten-Zuschnitt (Schlussphase als eigener Subagent mit eigenem Modell) wurde geprüft und
verworfen: ein Fork erbt in Claude Code zwingend das Elternmodell, ein frischer Subagent den
Sitzungskontext nicht - die Kombination, die der Zuschnitt bräuchte, gibt es nicht, und selbst
wenn: der Input der Schlussphase ist das akkumulierte Sitzungswissen, das ein kalter Agent aus
Diff und Issue neu ableiten müsste. Volle Begründung im Body von #47.instructions/claude-code-model-selection.mdkorrigiert, im dist-strip-Block: die
Übersicht „stack-dev Schritt 3 und 6" ist falsch geworden, seit Schritt 6 nicht mehr existiert.
Sie benennt jetzt beide Haltepunkte an ihrem tatsächlichen Ort - Schritt 3 instack-dev,
der zweite am Anfang vonstack-close.stack-devSchritt 3 ehrlicher formuliert (Vorschlag C): nicht mehr „ab hier alles
mechanisch", sondern mit benannter Ausnahme - Changelog-Prosa (Schritt 4), eine berührte
docs/-Seite, neue Menschendoku, der Prosa-Anteil einer Instruction. Dazu die Einschränkung aus
#30: „durch Tests abgedeckt" gilt nur für das, was die Tests treffen - zwei
datenvernichtende Bugs inupstream mergeliefen an einem grünenpytest/docs verify/instructions verify/CI vorbei, weil kein Test den Fall traf, nicht weil ein
schwächeres Modell schlechteren Code für den getesteten Fall geschrieben hätte.Neu:
tools/wikitool publishselbst erinnert an die Phasengrenze. Berührt das Changeset
tools/,types/,instructions/,AGENTS.mdoder ein<stage>/CONTRACT.md- derselbe
Umfang, den ein Versions-Bump selbst abdeckt -, drucktpublishnach der Erfolgsmeldung eine
Zeile, dass die folgende Phase von keinem der drei Checks abgedeckt ist. Kein Gate, keine
Änderung am Exit-Code, für eine gewöhnliche Content-Publish stumm; harness- und
instanzneutral formuliert, ohne jede Erwähnung eines Trackers, weilpublishvon jedem
Skill genutzt wird, nicht nur vonstack-dev.git_publish.touches_stack_machinery()plus
vier neue Tests (test_git_publish.py): zwei für die reine Klassifikationsfunktion
(positiv/negativ), zwei Integrationstests gegen einen echten Publish - die Notiz erscheint genau
einmal bei einerinstructions/-Änderung und bleibt aus bei einer gewöhnlichenkb/-Änderung.
tools/CONTRACT.mdspublish-Zeile trägt die Kurzfassung, absichtlich ohne den Dateinamen
version-parts.mdzu nennen - die Datei liegt unterinstructions/dev/und würde in einer
ausgelieferten Instanz ins Leere zeigen, währendtools/CONTRACT.mdselbst ausgeliefert wird.Verifiziert:
tools/wikitool instructions sync(7 Skills,stack-closeneu),tools/wikitool docs verify,tools/wikitool instructions verify,.venv/bin/python -m pytest -q(967
passed, 4 davon neu).#47 bleibt offen für Block 3 (
DEVELOPMENT.mdinSTAGE_READMES, veraltete Release-Notes).Downloads