-
v4.5.1 Stable
released this
2026-09-04 13:02:36 +00:00 | 63 commits to main since this release4.5.1 - 2026-09-04 - issue-tracking - destructive-step invariants, comment-vs-body authority, rename sweep
Author: Torben Nehmer
- issue-tracking - destructive-step invariants, comment-vs-body authority, rename sweep
Block 1 aus #47 (gemeinsam mit #29): drei Ergänzungen an
instructions/dev/issue-tracking.md, ausgelöst durch zwei Fehlerklassen, die
in derselben Sitzung am Stack aufgetreten waren.- Schritt 1 trägt jetzt, dass destruktive Schritte im Body die Invariante
nennen müssen, die sie nicht verletzen dürfen, und dass ein
Akzeptanzkriterium eine prüfbare Eigenschaft ist, keine Tätigkeit. Auslöser
war #30: der Body schrieb wörtlich "Arbeitsverzeichnis entfernen" für den
upstream merge-Ablauf, und genau das wurde zum datenvernichtenden Bug -
einshutil.rmtreeauf eine Stage mit gitignorierten, nicht
rekonstruierbaren Daten. - Schritt 2 trägt jetzt die Lesesicht auf Body und Kommentare, die es
bisher nur aus Autorensicht gab: der Body ist der Stand, Kommentare sind
Historie; ein erkennbar veralteter Body wird richtiggestellt statt
umgangen; widersprüchliche Kommentare werden nach Beleg aufgelöst, nicht
nach Datum. Auslöser war ebenfalls #30 (ein Kommentar empfahl das Gegenteil
dessen, was der Body später festlegte) und #10 (ein seit Tagen veralteter
Body gegen drei widersprechende Kommentare, zwei davon sich selbst
widersprechend). - Neuer Abschnitt "Renames and other decay in the tracker" nach Schritt 7:
ein Rename ist erst fertig, wenn auch die offenen Issues nachgezogen sind,
weilwikitooldiesen Tracker nicht kennt und nicht kennenlernen soll. Mit
der Wegweiser-vs-Beleg-Unterscheidung aus #29 und dem Hinweis, dass auch
verschwundenekb/-Seiten und private Infrastrukturangaben Issue-Texte
altern lassen. Ein neuer Trigger in "When to run" verweist darauf.
Keine der drei Ergänzungen verschiebt die bestehende Nummerierung der
Schritte 1-7 - die Querverweise darauf (u. a. aus
instructions/dev/stack-dev/SKILL.mdauf Schritt 7, aus
kb/concepts/Issue Label Scheme.mdauf Schritt 2) bleiben also gültig, ohne
angefasst zu werden.Verifiziert:
tools/wikitool instructions verify,tools/wikitool docs verify, beide grün (Prosa-only, kein Interface geändert, PATCH).#47 bleibt offen (Block 2: der Skill-Schnitt aus Vorschlag E; Block 3: die
beiden Nebenbefunde). #29 bleibt ebenfalls offen: dieser Block deckte nur den
Regelabsatz, nicht den noch ausstehenden Pfad-Durchgang durch #4, #5, #15,
#21, #16, #26 - der war nicht Teil des Auftrags für diesen Block.Downloads
-
v4.5.0 Stable
released this
2026-09-04 10:51:12 +00:00 | 65 commits to main since this release4.5.0 - 2026-09-04 - Beide Update-Wege in Code: upstream merge fuer Clones, dist upgrade fuer Tarball-Instanzen
Author: Torben Nehmer
- Faktenkorrekturen in der 4.4.0-Prosa; DEVELOPMENT.md ohne zweite Kommandobeschreibung
- wikitool upstream merge/verify: code procedure for taking a stack update, ownership.py as the shared stack/instance boundary
- upstream merge: combined-commit regression test (edit+add+delete+contract+template+contract-delete in one commit)
- upstream merge: keep gitignored local data under a content stage, refuse a merge git never opened, report what actually changed
- dist upgrade: apply a stack update, not just detect one (#7)
- dist upgrade: Stamp-Semantik nach --keep-local benannt, docs/ownership-and-templates.md auf drei Eigentumsklassen nachgezogen
Die Prosa zu 4.4.0 - Changelog-Eintrag,
docs/version-model.md,instructions/dev/version-parts.md,
DEVELOPMENT.md- entstand ungeprüft: kein maschineller Check liest sie, und die Sitzung, die sie
schrieb, hat den dafür vorgesehenen Modellwechsel ausstack-devSchritt 6 übersprungen. Ein
nachgeholter Durchgang hat drei Fehler gefunden, von denen der erste der teuerste war.Der Befund war an einer Stelle schlicht falsch. Changelog und
docs/version-model.md
behaupteten, unter dem alten Modell seien Nummern vergeben worden, die „nie ausgeliefert" wurden -
im Changelog sogar mit einer erfundenen Zahl („fünf Minor-Bumps ... vier nie ausgeliefert"). Das
Gegenteil stimmt: weilrelease.ymlauf jedeVERSION-Bewegung feuerte, wurde jeder Bump zu
einem echten, getaggten Release. Der 2026-09-03 hat vier davon in sechs Stunden erzeugt (4.3.0
bis4.3.3), zwei für reine Prosa-Änderungen. Der Schaden war nie ein Phantom-Release, sondern
dass „Release" aufhörte, etwas zu bedeuten - vier Upgrades an einem Nachmittag sind für einen
Konsumenten kein kleineres Versprechen, sondern ein unleserliches. Beide Stellen tragen jetzt den
tatsächlichen Vorgang samt Beleg.Dazu zwei kleinere Korrekturen:
version-parts.mdnannte den Stack weiterhin2.x(er steht bei
4.x), undDEVELOPMENT.mdtrug eine Tabelle, die für jeden Verify-Befehl ein zweites Mal
beschrieb, was er prüft - eine Kopie dessen, wastools/CONTRACT.mdhält unddocs verifydort
gegen die CLI prüft. Die Tabelle ist raus; dass die Datei selbst außerhalb der von diesem Check
abgedeckten Dokumente liegt, steht jetzt an ihrer Stelle. Der Code-seitige Teil davon - ob
DEVELOPMENT.mdindocs_verify.STAGE_READMESgehört - hängt an #47, zusammen mit der Lücke im
Skill, die den übersprungenen Modellwechsel überhaupt erst unauffällig gemacht hat.Kein Verhalten geändert, nur Prosa - und bewusst als laufender Kandidat gelassen statt fixiert:
CIs Version-Gate verlangt dieVERSION-Bewegung, ein Release verlangt sie nicht. Genau dafür gibt
es das Modell aus 4.4.0.Zweiter Bump auf demselben Kandidaten (#30):
git merge upstream/mainbehandelt einen
bewegten Korpus asymmetrisch - eine gelöschte, upstream-geänderte Seite meldet sich als Konflikt,
eine neu angelegte Seite wird still gestaged, nur eine beidseitig gelöschte Seite ist harmlos. Die
Prosa-Prozedur inprivate-instance.md§ "Taking a stack update" hat das geschlossen, aber mit
vier eigenen Fehlern: der Pfadsatz stand dreifach (im Skript, im Kontroll-Grep, implizit in
dist_cmd.py); eine vom Upstream gelöschte Maschinerie-Datei wurde vongit checkout MERGE_HEAD -- <pfad>still ignoriert, weil das Skript keinset -ehatte; ein echter Konflikt in
tools//types//instructions/endete in einem von der Prosa nirgends erwähnten offenen Merge;
und ein neuer Maschinerie-Pfad unter einer Content-Stage hätte die Literal-Liste nie erreicht.Die Eigentumsgrenze ist jetzt ein Prädikat statt einer Liste:
chemenu/ownership.py,
is_stack_owned(relative), wahr für<stage>/CONTRACT.mddirekt unter einer Content-Stage
(kb,raw,work,reports) und für jeden Pfad, der dort auf.templateendet - nicht
rekursiv,kb/<collection>/COLLECTION.mdbleibt seit #39 instanzeigen.dist_cmd.pyliest das
Modul jetzt statt einer eigenen_CONTENT_ALLOWED_NAMES-Liste zu pflegen, und
CONTRACT_ONLY_STAGESleitet sich ausownership.CONTENT_STAGESab statt die drei Stage-Pfade
ein zweites Mal aufzuschreiben - ein Test hält fest, dass beide Sichten nicht auseinanderlaufen
können.Neu:
wikitool upstream merge [--remote upstream] [--branch main] [--no-fetch]undwikitool upstream verify --since <rev> [--until HEAD].mergeprüft Vorbedingungen (sauberer Baum, kein
laufendes Merge, Remote löst auf), warnt statt zu blockieren, wenn.wikitool-remotes.jsonfehlt,
hält den Merge offen (--no-commit --no-ff), zwingt jede Content-Stage auf die lokale Seite
zurück, holt dann über die Vereinigungsmenge der Bäume vonMERGE_HEADundHEADgenau die
stack-eigenen Pfade zurück - inklusive einer Löschung, falls der Upstream einen Maschinerie-Pfad
entfernt hat -, verweigert bei verbliebenen unaufgelösten Pfaden ohne zu committen, committet
sonst und verifiziert den entstandenen Commit mit derselben Logik wieverify- ein Fund dort
wird laut gemeldet und nicht automatisch zurückgerollt. Nicht idempotent (AGENTS.md § Tool
error contract), nicht budget-exempt;verifyliest nur und ist wiemigrate verifyvon der
Budget-Gate ausgenommen. Die Mass-Update-Gate greift bei einem Merge-Commit strukturell nicht -
das steht jetzt als eigener Absatz ininstructions/gates.md, mitupstream merges eigener
Nachkontrolle als der Sicherung, die hier tatsächlich trägt.private-instance.md§ "Taking a stack update" verweist jetzt auf den Befehl statt das Skript
auszuschreiben; die Pfadtabelle bleibt als Erklärung stehen, ist aber nicht mehr die operative
Liste. Vorschlag B (eigenes Demo-Repo) bleibt zurückgestellt, siehe #30.23 neue Tests unter
test_upstream_cmd.py(35 Fälle mit der parametrisierten
is_stack_owned-Tabelle) decken die Fälle aus der Spezifikation ab:
gelöschte vs. geänderte vs. neu angelegte Seiten,kb/CONTRACT.md- und Template-Änderungen,
kb/entities/COLLECTION.mdbleibt lokal, eine gelöschteraw/CONTRACT.mdlandet, ein neuer
Template-Pfad landet, ein offenerwork/-Lauf landet nicht, ein echtertools/-Konflikt lässt
den Merge offen, ein schmutziger Baum wird unberührt abgewiesen, "bereits aktuell" ist ein No-op,
die Publish-Remote-Gate-Warnung,verifygegen einen von Hand verpfuschten Merge, und die
dist_cmd/ownership-Konsistenz.Dritter Bump auf demselben Kandidaten: die im Akzeptanzkriterium geforderte Kombinationsprobe
fehlte noch - ein einzelner Upstream-Commit, der Editieren, Anlegen, Löschen einer Seite, eine
Contract-Änderung, eine Template-Änderung und eine Contract-Löschung gleichzeitig bewegt. Jetzt
alstest_one_upstream_commit_mixing_every_case_at_oncenachgetragen; alle sechs Erwartungen in
einemupstream merge-Aufruf verifiziert.Vierter Bump: zwei Fehler, die ein Review-Durchgang nach dem Publish gefunden hat. Beide
waren in der ersten Fassung enthalten, beide hätten Daten vernichtet, und keiner der bestehenden
Tests hat sie berührt.Erstens: die Content-Stage wurde als Ganzes gelöscht.
_restore_stage_to_localhieß in der
ersten Fassungshutil.rmtree(stage_dir)— die wörtliche Übersetzung desrm -rf kb rawaus der
Prosa-Prozedur. Fürkb/undraw/ist das harmlos, weil dort nichts Ignoriertes liegt. Für die
beiden Stages, die dieses Issue neu in den Satz aufgenommen hat, ist es das nicht:reports/
ist bis auf seinen Contract komplett gitignored und trägt genau die Daten, die nirgends sonst
existieren — die Telemetrie-Traces, aus deneneval scoreliest, gespeicherte Eval-Berichte,
alte Lint-Reports. In dieser Instanz standen zum Zeitpunkt des Fundes 497 Trace-Verzeichnisse
unterreports/telemetry/; ein einzigerupstream mergehätte sie alle gelöscht, und zwar
stillschweigend, weil git von ignorierten Dateien nichts meldet. Die Stage wird jetzt über die
getrackten Pfade beider Bäume zurückgesetzt statt über das Verzeichnis; ignorierte lokale
Daten bleiben unberührt. Leergewordene Verzeichnisse werden aufgeräumt, aber nur wirklich leere.Zweitens: ein Merge, den git nie eröffnet hat, hätte die Maschinerie gelöscht. Der Exit-Code
vongit merge --no-commit --no-ffwird bewusst ignoriert (Konflikte unter den Content-Stages
sind erwartet). Nur: wenn git das Merge gar nicht erst eröffnet — unverwandte Historien, eine
ignorierte Datei im Weg —, gibt es keinMERGE_HEAD,_tree_paths("MERGE_HEAD")liefert die
leere Menge, und jeder stack-eigene Pfad inHEADfällt damit in den Zweig „der Upstream hat
ihn gelöscht":kb/CONTRACT.md,raw/CONTRACT.mdund sämtliche Templates werden entfernt. Der
Kommando-Ablauf prüft jetzt nach dem Merge-Aufruf, dass tatsächlich ein Merge offen ist, und
bricht sonst ab, ohne den Baum angefasst zu haben. Beide Fehler haben je einen Regressionstest,
und beide Tests wurden gegen die alte Fassung laufen gelassen, um zu zeigen, dass sie sie
tatsächlich fangen.Dazu eine Ehrlichkeitskorrektur an der Erfolgsmeldung: sie zählte die wiederhergestellten
Pfade, nicht die geänderten — ein Merge, der eine Datei bewegt, meldete vier oder fünf. Sie fragt
jetztgit diffzwischen Vor- und Nach-Commit, kennzeichnet Löschungen, und stimmt damit mit dem
überein, was ein Leser nachprüfen würde.docs/ownership-and-templates.mdhat einen Abschnitt
bekommen, warum die Grenze ein Prädikat und keine Liste ist — die Begründung, die dieses Issue
erarbeitet hat, gehörte in die Hintergrunddoku und nicht nur in einen Changelog-Eintrag.Fünfter Bump:
wikitool dist upgrade(#7), der zweite der beiden Update-Wege.upstream mergeoben bedient eine Instanz mit gemeinsamer Git-History;dist upgradebedient eine
Instanz aus einem Tarball, ohne History, die bislang eine rein manuelle Prozedur in
INSTALL.mddurchlaufen musste - Schritt 4 verlangte einen sha256-Vergleich von Hand gegen den
files-Block der alten.wikitool-release.json.Die tragende Regel: die Schreibmenge ist genau der
files-Block der neuen
.wikitool-release.json, minus was ein Export aus einer leeren Vorlage neu sät
(chemenu.ownership.is_export_stub, wie bisher schon fürkb/log.md/.gitkeep) oder einmalig
sät und danach der Instanz gehört (chemenu.ownership.is_upgrade_preserved, neu für
.wikitool-kb.jsonundCHANGES.md), plus der Stamp selbst. Jeder Kandidatpfad wird gegen die
alte Instanz-Summe klassifiziert: unverändert wird geräuschlos überschrieben, neu im Release
wird angelegt, lokal verändert oder gelöscht wird nie still überschrieben - der Lauf bricht
mit der vollständigen Liste ab, außer--keep-localsagt ausdrücklich, dass die Dateien liegen
bleiben sollen.--pruneentfernt zusätzlich aus dem Release entfallene Dateien, aber nur
solche, die seit der Installation unverändert sind.Die Migrationskette nach dem Tausch wird aus den
instructions/migrations/des neuen Baums
ermittelt (kb_state.load_migrationsbekam dafür einendirectory-Parameter) und nur
gemeldet, nie ausgeführt - es gibt bewusst keinmigrate run. Eine bereits gegen die
installierte Maschinerie offene Kette lässt den Befehl abbrechen, bevor er die Quelle
überhaupt öffnet.kb_state.divergent_files()(bisher nur vonmigrate statusgelesen) ist
jetzt eine dünne Hülle um das neue, zwei-Baum-fähigecompare_against_stamp()- gleiches
Verhalten für den bestehenden Aufrufer, wiederverwendbar fürdist upgrades eigenen Vergleich.Quelle ist immer ein bereits vorhandenes Verzeichnis oder
.tar.gz- kein Download, das bleibt
alleinversion checks Sache. Ein Tarball muss genau ein Top-Level-Verzeichnis enthalten (die
Form, in derrelease.ymles baut) und wird gegen eine.sha256-Beidatei geprüft, falls eine
danebenliegt (fehlt sie: WARN, kein Abbruch). Weitere Abbruchgründe vor jedem Schreiben: fehlende
lokaleVERSION/.wikitool-kb.json/Stamp mitfiles-Block, ein schmutziger Arbeitsbaum (kein
Git-Repo ist ein WARN, keine Sperre), eine Vorab-Version (-beta.N) ohne--pre, sowie ein
Downgrade; Gleichstand ist ein No-op. Ein Grenzübertritt der Kompatibilität wird laut gemeldet,
blockiert aber nicht. Committet und pusht nichts (Invariante 5).Gegenüber dem ersten Entwurf des Issues zwei Korrekturen, die dort auch nachgetragen sind: der
files-Block wurde entgegen der ursprünglichen Annahme bereits vor diesem Bump gelesen
(divergent_files/migrate status), und die Migrationskette war ursprünglich falsch begründet- sie kann nur aus dem neuen Baum kommen, nicht durch eine andere Abfragereihenfolge aus der
alten Instanz. 24 neue Tests untertest_dist_upgrade.pydecken die Klassifikation, alle
Abbruchgründe,--keep-local,--pruneund beide Quellformen (Verzeichnis und Tarball,
inklusive der sha256- und Top-Level-Prüfung) ab.
Bewusst nicht angetastet:
instructions/private-instance.md(der Clone-Weg ändert sich nicht,
INSTALL.mdbenennt jetzt beide Wege nebeneinander) und die Frage, wiedist upgrademit
Collection-Templates umgeht, deren Namen eine fremde Instanz gar nicht hat - es verhält sich wie
upstream mergeund schreibt sie, was ein eigenes Issue gegen den Export wäre, keins gegen das
Upgrade.Sechster Bump: die ungeprüfte Phase nachgeholt. Der Abschluss des vorigen Bumps lief auf
Sonnet, undstack-devSchritt 6 verlangt dort genau zwei Dinge, die kein Check erzwingt:
Issue-Body unddocs/-Veralterung. Der Body war gemacht, diedocs/-Prüfung nicht - sie wurde
benannt statt durchgeführt. Nachgeholt auf Opus, mit einem Fund.docs/ownership-and-templates.md§ „The consequence in practice" beschrieb ein Upgrade als
Zweiteilung: verbatim überschreiben,.template-gestützte Dateien liegen lassen - und begründete
den ersten Teil damit, dass verbatim ausgelieferte Dateien „safe to replace wholesale" seien,
weil sie „never instance-specific to begin with" waren. Genau diese Annahme trifftdist upgrade
nicht: eine Instanz kann eine verbatim ausgelieferte Datei angefasst haben, und die sha256 je
Datei existiert, um das zu erkennen, statt es vorauszusetzen. Dazu fehlte die dritte Klasse ganz -
die einmalig gesäten, danach instanzeigenen Pfade (.wikitool-kb.json,CHANGES.md,
kb/log.md,raw/*/.gitkeep), die im Stamp stehen wie jede andere Datei und deshalb aktiv
ausgeschlossen werden müssen. Die Seite nennt jetzt drei Klassen und die engere praktische Regel:
überschreibe die verbatim ausgelieferten Dateien, die diese Instanz nicht angefasst hat.Dazu eine Präzisierung in
tools/CONTRACT.md, die vorher nirgends stand: nach--keep-local
wird der neue Stamp trotzdem vollständig geschrieben, trägt also die Release-Summe auch für
Dateien, die bewusst nicht geschrieben wurden. Der Stamp ist die Vergleichsbasis für den
nächsten Lauf, kein wörtliches Inventar der Platte - und genau das hält eine übersprungene Datei
bei jedem weiteren Lauf als abweichend gemeldet, statt sie nach einmaligem Überspringen still als
aktuell zu führen.Downloads
-
v4.4.0 Stable
released this
2026-09-03 20:22:38 +00:00 | 72 commits to main since this release4.4.0 - 2026-09-03 - Versionskandidat statt Bump-pro-Release: VERSION traegt -beta.N, version release fixiert
Author: Torben Nehmer
- Versionskandidat statt Bump-pro-Release: VERSION traegt -beta.N, version release fixiert
Bisher bekam jeder
version bumpsofort eine fixierte Nummer, und weilrelease.ymlauf jede
VERSION-Bewegung feuert, wurde daraus sofort ein Release: Nummern entstanden in
Commit-Granularität statt in Release-Granularität. Der 2026-09-03 hat so vier Releases in sechs
Stunden erzeugt (4.3.0bis4.3.3), zwei davon für reine Prosa-Änderungen - alle vier echt,
keines davon eine Einheit, an der ein Konsument sich hätte orientieren können.VERSIONträgt
jetzt zwischen zwei Releases einen laufenden Kandidaten (X.Y.Z-beta.N):
--major/--minor/--patcheskaliert diesen Kandidaten max-wins gegen den letzten Release, statt
eine neue Nummer danebenzustellen, und geht dabei nie zurück.Versionversteht den Suffix, mit einer expliziten Ordnung
(4.4.0-beta.1 < 4.4.0-beta.2 < 4.4.0, numerisch nachN, nicht lexikografisch).CHANGES.md
trägt genau einen offenen Eintrag pro Kandidat: der erste Bump eröffnet ihn, jeder weitere
aktualisiert Heading und die maschinenverwaltete Bump-Titel-Liste in
<!-- wikitool:bumps -->(Marker-Konvention ausblocks.py, aber bewusst nicht in
blocks.BLOCKS- diese Region gehört zuCHANGES.md, nicht zu einer Seite).version release
ist neu und fixiert einen Kandidaten: Suffix weg, Eintrag geschlossen, committet und pusht nichts.Vier Stellen am Bestand angepasst, die das Kandidatenmodell sonst still beschädigt hätten:
release.ymlüberspringt einen suffixbehaftetenVERSION-Push sauber, bevor die Releases-API
gefragt wird, statt jeden Beta-Bump zu veröffentlichen; die Grenzübertritts-Checks in
docs verify(check_migration_for_boundary,check_breaking_change_for_boundary) messen jetzt
gegen den letzten Release (version_mod.last_release) statt gegen den zweitobersten Eintrag,
der zwischen zwei Betas keine Grenze mehr hergibt;kb_state.chain()/next_link()vergleichen
gegen die Kandidatenbasis, weil eine Migration mit Ziel4.4.0sonst bei installiertem
4.4.0-beta.1aus dem Intervall fällt (4.4.0-beta.1 < 4.4.0);read_kb_version()verweigert
einen Prerelease, weil eine Inhaltsform kein Beta kennt.dist exportschreibtVERSIONund den
Stamp weiterhin ehrlich mit Suffix, aber.wikitool-kb.jsonbekommt die Basis.Menschendoku für die Erzeuger-Seite:
DEVELOPMENT.mdim Repo-Root, bewusst nicht in
dist_cmd.ROOT_FILES(Begründung als Kommentar dort), mit Zeile inAGENTS.md§ File naming und
Zeiger ausREADME.md.docs/version-model.mdhat einen neuen Abschnitt, warum eine Nummer erst
durch ein Release verbraucht wird.Downloads
-
v4.3.3 Stable
released this
2026-09-03 19:33:51 +00:00 | 73 commits to main since this release4.3.3 - 2026-09-03 - Modellwahl nach Pruefbarkeit statt nach Aufgabenname; stack-dev bricht an den Phasenwechseln fuer den Model-Switch
Author: Torben Nehmer
instructions/claude-code-model-selection.mdroutete bisher nach Skill: eine Zeile "Stack
development -> Opus/high" fuer alles, wastools/,types/oderinstructions/anfasst. Das ist
zu grob in beide Richtungen - es verteuert die lange, mechanische Mitte einer Stack-Sitzung, und es
sagt nichts darueber, dass Anfang und Ende derselben Sitzung anders zu behandeln sind.Die neue Achse ist "was faengt hier einen Fehler ab". Wo ein Check in Code steht -
pytest,
docs verify,instructions verify, CI, die Gates - kostet der Fehler eines schwaecheren Modells
eine Runde und faellt auf. Wo die einzige Durchsetzung eine Sitzung ist, die Prosa liest, faellt
derselbe Fehler gar nicht auf: er wird ausgeliefert und bleibt stehen. Das ist dasselbe Argument,
dasdocs/why-gates-are-code.mdfuer Gates fuehrt, angewandt auf die Modellwahl.Stack-Entwicklung ist damit nicht mehr eine Zeile, sondern drei:
Phase Was einen Fehler faengt Modell Design, Versionsstelle, Grenzuebertritts-Urteil nichts Opus/high Code, Tests, mechanische Doku-Synchronisation pytest, CI, docs verifySonnet/high Issue-Abschluss, docs/-Veralterung, Changelog-Prosanichts, per Konstruktion Opus/high Die Mitte ist die lange Phase und die mit den Checks - dort liegt die Ersparnis. Die beiden
Raender sind kurz (Minuten, nicht Stunden), haben aber keinen maschinellen Waechter:wikitool
kennt den Issue-Tracker bewusst nicht, und einedocs/-Seite traegt keinen normativen Satz, also
gibt es dort nichts zu verifizieren. Sie oben zu lassen ist billig und schuetzt genau die Arbeit,
die still scheitert.Zwei Praezisierungen dazu: Effort ist der billigere Hebel als das Modell -
mediumsteht fuer
Stack-Arbeit bewusst in keiner Zeile, weil Mehrdatei-Konsistenz das ist, was ein reduzierter
Effort zuerst aufgibt;highist die Untergrenze, sobald mehr als eine Datei oder ein Contract
betroffen ist. Und die Asymmetrie ist benannt: eine unnoetige Opus-Phase kostet einmal Geld, eine
ungepruefte Sonnet-Phase kann etwas ausliefern, das nie wieder jemand ansieht.Damit die Tabelle ueberhaupt wirksam wird, braucht sie Haltepunkte. Eine Sitzung kann ihr
eigenes Modell nicht wechseln - das ist/modelund gehoert dem Nutzer. Eine Empfehlung, die
niemand zum richtigen Zeitpunkt ausspricht, aendert nichts.instructions/dev/stack-dev/SKILL.md
bekommt deshalb zwei ausdrueckliche Breaks:- Neuer Schritt 3 - "Settle the design before building", mit dem Angebot zum Wechsel nach
unten, sobald der Plan steht und die Arbeit mechanisch wird. Einmal aussprechen, dann so oder
so weiterarbeiten. - Schritt 6 (Abschluss) bricht in die Gegenrichtung - ab dort greift wieder kein Check. Mit
der ausdruecklichen Auflage, die Arbeit unabhaengig von der Antwort zu tun: nach dem Publish
auf einen Modellwechsel zu blockieren wuerde genau den Zustand hinterlassen, den Schritt 6
verhindern soll. Lief die Phase auf dem billigeren Modell, gehoert das in die Uebergabe statt
ins Schweigen.
Ein auftauchender Grenzuebertritt ist unter den Decision points ebenfalls als Anlass zum Wechsel
nach oben benannt:docs verifyprueft, dass ein Uebertritt sich dokumentiert, nie dass die
Stelle richtig gewaehlt war.Die uebrigen Schritte sind unveraendert und nur umnummeriert (alt 3-5 -> neu 4-6).
Downloads
- Neuer Schritt 3 - "Settle the design before building", mit dem Angebot zum Wechsel nach
-
v4.3.2 Stable
released this
2026-09-03 18:49:07 +00:00 | 74 commits to main since this release4.3.2 - 2026-09-03 - stack-dev: Issue-Abschluss ist ein nummerierter Schritt, kein Zeiger in einer Routing-Liste
Author: Torben Nehmer
Nachfassen zu 4.1.2 (#44), das die Regel geschaerft, aber den Weg zu ihr nicht geaendert hat.
instructions/dev/issue-tracking.mdbekam damals Schritt 7 ("Closing is the last body update,
not a comment");instructions/dev/stack-dev/SKILL.mdbekam nur eine umformulierte Zeile in
seiner Routing-Liste. Eine Stunde spaeter schloss #45 auf exakt dieselbe Weise: gruendlicher
Abschlusskommentar ueber einem Body mit unangehakten Kriterien.Die Ursache lag nicht am Text der Regel, sondern an ihrer Erreichbarkeit. Die nummerierten
Schritte des Skills endeten bei "Verify before publishing". Ein Issue zu schliessen war ueberhaupt
kein Schritt - es hing an einem Zeiger innerhalb von Schritt 2, und Schritt 2 ist eine
Routing-Tabelle aus fuenf "read X before Y"-Eintraegen, keine Checkliste. Eine Sitzung folgt dem
Spine, den sie im Kontext hat; was nur hinter einem Link steht, wird genau in dem Moment nicht
aufgeschlagen, in dem es greift - am Ende einer langen Sitzung, wenn der Kontext am vollsten und
die verbleibende Instruktionsflaeche am duennsten ist.Verschaerfend arbeitete der Blurb gegen seine eigene Regel: fett gesetzt war "keep it current as
the state moves, not at the end". Wer den Body unterwegs ungefaehr gepflegt hatte, las daraus
Konformitaet - der eigentliche Abschlusstest stand nur in der verlinkten Datei.Geaendert:
- Neuer Schritt 5 in
stack-dev/SKILL.md- "Close the issue with a body rewrite, not a
comment", mit dem Test inline (Kriterien abgehakt oder mit Begruendung gestrichen,
Entscheidungen als entschieden formuliert, kein Praesens ueber einen behobenen Defekt,
Verifikation benannt) und dem Verweis auf Schritt 7 fuer die volle Form. Damit steht der
Abschluss auf dem Spine. - Schritt-2-Blurb rebalanciert - beide Haelften binden jetzt sichtbar: fortlaufende Pflege
und der Rewrite vor dem Schliessen, mit Verweis auf Schritt 5.
Nichts davon ist maschinell pruefbar, und das bleibt richtig so:
wikitoolkennt den Tracker
nicht und darf ihn nicht lernen, weil es an Instanzen ausliefert, die kein Board haben
(issue-tracking.md§ "What no tool checks"). Der Skill-Spine ist die einzige Durchsetzung, die
es geben kann - was der Grund ist, den Schritt zu nummerieren statt ihn zu verlinken.Verallgemeinerbar: eine Regel, die in eine verlinkte Instruction geschrieben wird, erreicht
Sitzungen nur, wenn die nummerierten Schritte des zustaendigen Skills sie in dem Moment
ansteuern, in dem sie greift.Downloads
- Neuer Schritt 5 in
-
v4.3.1 Stable
released this
2026-09-03 17:43:36 +00:00 | 75 commits to main since this release4.3.1 - 2026-09-03 - docs/ befuellt - Stack-Hintergrund fuer vier Themen, Pflegeklausel in AGENTS.md ergaenzt
Author: Torben Nehmer
Gitea #45: die von #38 angelegte, bis dahin leere
docs/bekommt ihre ersten vier Seiten - frisch
geschrieben, nicht durch Umzug auskb/befuellt, jede ohne normativen Satz und mit Verweis auf
das bindende Dokument statt einer Wiederholung seiner Regeln:docs/pipeline-rationale.md- warumraw -> types/tools -> kb -> reportsvier getrennte Stufen
sind und was "never re-derive, always compile" praktisch bedeutetdocs/why-gates-are-code.md- warum Mass-Update-, Publish-Remote- und Iteration-Budget-Gate in
tools/wikitoolstatt in einer Instruktion stehendocs/ownership-and-templates.md- der Unterschied zwischen stack-eigenen, verbatim
ausgelieferten Dateien und instanz-eigenen.template-Dateiendocs/version-model.md- warum Drop-in-Kompatibilitaet und Migrationsbedarf zwei unabhaengige
Fragen sind, illustriert an der 2.0.0-Fallstudie
AGENTS.md § Changelog: neue Klausel zur Pflege von
docs/, ergaenzt neben der bestehenden
Regel zuREADME.md/EVALS.md/tools/README.md. Einedocs/-Seite veraltet nicht wie ein
README bei jedem neuen Flag, sondern nur, wenn die aufgeschriebene Begruendung selbst nicht mehr
traegt - per Konstruktion ungeprueft, da die Seite keinen normativen Satz enthaelt, den
docs verifypruefen koennte.Downloads
-
v4.3.0 Stable
released this
2026-09-03 17:04:16 +00:00 | 76 commits to main since this release4.3.0 - 2026-09-03 - docs/ als ausgelieferter Hintergrund-Ort; Decision-Seiten bleiben in kb/, Decay-Skip fuer concept_type: decision
Author: Torben Nehmer
Gitea #38:
dist exportlieferte bislang keine einzigekb/-Seite aus - eine frische Instanz
bekam den Stack, aber keinen Grund für seine Form. Die dokumentierteadr-NNN--Konvention in
kb/concepts/COLLECTION.mdexistierte zudem nur auf Papier: keine der sieben
concept_type: decision-Seiten folgte ihr, undconfidence_decay()lief bedingungslos über sie- ein Kategorienfehler, weil Zeitablauf eine Entscheidung nicht falscher macht, nur Supersession
tut das.
Neu:
docs/- ein inertes Verzeichnis für Stack-Hintergrund (warum der Stack so gebaut ist,
nicht was diese Instanz entschieden hat). Keine Frontmatter, kein Typ, kein Index, kein Lint,
keine Decay, keine Provenance, keineCOLLECTION.md.dist exportliefert es verbatim aus, wie
instructions/undtypes/. Befüllung folgt in Gitea #45.Verworfen, nach Prüfung: ein Umzug der sieben Decision-Seiten nach
decisions/. Der
Subtyp-Floor aus #28 verlangt mindestens eine Seite je deklariertemconcept_type, und ein
Umzug hättedecisionauf null gebracht; dazu zeigen 89 Wikilinks auskb/sowie
tool-eigene Frontmatter-Arrays auf die sieben, undlinks.py/xref addkennen kein Ziel
außerhalbkb/. Die sieben bleiben inkb/concepts/, ebenso ein zweiter, separat erwogener
Rename (docs verify→parity verify) - der wäre nur nötig gewesen, wenn ein Befehl auf das
Verzeichnisdocs/wirkt, und keiner tut das.Geändert:
confidence_decay()überspringtconcept_type: decisionstrukturell (kategorische Ausnahme,
nicht als Brücke gebaut - Begründung im Docstring).kb/concepts/COLLECTION.md§ Decisions ersetzt die tote ADR-Vorlage durch die real gelebte
Form: eine Entscheidung ist eine gewöhnliche Concept-Seite, organische Prosa, kein
adr-NNN--Präfix,**Status:**optional, Supersession persupersedes-Link.kb/CONVENTIONS.md§ Naming undinstructions/kb-profiles.md(Profilgerman) korrigiert -
beide dokumentierten noch die verworfeneadr-NNN--Namensregel.AGENTS.md§ File naming und § Routing:docs/-Zeile, plus die Regel, dassdocs/keinen
normativen Satz trägt (das hält Invariante 8 heil - was binden würde, gehört in einen
Contract).tools/CONTRACT.md: Klarstellung, dassdocs verifyDokumentations-Parität prüft, nicht das
docs/-Verzeichnis, sowiedocs/in derdist export-Zeile ergänzt.
Additiv und in beide Richtungen drop-in: eine bestehende Instanz ohne
docs/exportiert
weiterhin identisch (leerer_copy_tree-Treffer), eine Instanz mitdocs/bekommt es ab jetzt
mitgeliefert. Kein Feld, kein Kommando ändert sein Verhalten für bestehenden Inhalt.Migration: none required.
Berührt:
tools/chemenu/commands/confidence_decay.py,tools/chemenu/commands/dist_cmd.py,
tools/chemenu/tests/test_confidence_decay.py,tools/chemenu/tests/test_dist_cmd.py,
kb/concepts/COLLECTION.md,kb/CONVENTIONS.md,instructions/kb-profiles.md,AGENTS.md,
tools/CONTRACT.md.Downloads
- ein Kategorienfehler, weil Zeitablauf eine Entscheidung nicht falscher macht, nur Supersession
-
v4.2.0 Stable
released this
2026-09-03 06:14:11 +00:00 | 77 commits to main since this release4.2.0 - 2026-09-03 - Korpus-Kuratierungsrichtlinie: Untergrenzen und Leitplanke für reaktive Fixes
Author: Torben Nehmer
Ein Demo-Korpus will klein und stabil sein, ein Testbett groß, unordentlich und in Bewegung -
dieses Repo verlangt seit der Veröffentlichung beides vom selbenkb/(Gitea #28). Die Sitzung
vom 2026-09-02 hatte Fixture,--with-demound ein zweites Repo bereits verworfen; offen blieb
nur, wie kuratiert "kuratiert genug" heißt und welche Leitplanke reaktive Fixes bekommen.Neu:
instructions/dev/corpus-policy.md. Fünf Untergrenzen, jede mit einer bestehenden
wikitool-Prüfung messbar, keine davon durch neuen Tool-Code: jeder Seitentyp und jeder
deklarierte Subtyp mit mindestens einer Seite, mindestens fünf Seiten mit mindestens drei
Quellen, ein bis zehn Orphan-Seiten, im Schnitt mindestens vier ausgehende Wikilinks pro Seite.
Gemessen am 2026-09-03: 181 Seiten, alle Typ-/Subtyp-Floors erfüllt, 12 Seiten mit ≥3 Quellen, 3
Orphans, Ø 6,2 ausgehende Links - der Korpus war bereits groß genug, ohne dass eine einzige
Seite eigens dafür angelegt werden musste. Eine Untergrenze wird nie durch eine erfundene Seite
gefüllt, sondern durch eine echte Quelle beim nächsten passenden Ingest - Invariante 3 gilt
unverändert.Die Leitplanke für reaktive Fixes unterscheidet drei Stufen: punktuelle Änderungen (immer
erlaubt, gewöhnliche Arbeit), korpusweite Änderungen (nur geplant, mit eigenem Issue und
work/-Run - trifft eine Session das Mass-Update-Gate während sie etwas anderes tat, holt sie
sich nicht den--confirm-Token, sondern stoppt und legt ein Issue an) und reaktive Eingriffe
in Korpusinhalt, um einen Test grün zu machen oder einen Tool-Bug zu umgehen (nie erlaubt,
Invariante 7). Das Verhältnis zukb_dir/raw_dirundtest_pipeline_l0.pybleibt wie im
ursprünglichen Befund: kleiner, isolierter Fall in der Fixture, großer, vernetzter Fall in
kb/- keine Fixture-Extraktion aus dem Korpus.Dev-only und rein additiv - kein Feld, kein Kommando, keine Datei außerhalb von
instructions/dev/ändert sich, daher--minorohne--breaking.Migration: none required.
Berührt:
instructions/dev/corpus-policy.md(neu),
instructions/dev/stack-dev/SKILL.md(Schritt 2, Routing-Zeile).Downloads
-
v4.1.2 Stable
released this
2026-09-03 04:42:13 +00:00 | 78 commits to main since this release4.1.2 - 2026-09-03 - Issue-Abschluss ist ein Body-Rewrite, nicht nur ein Kommentar
Author: Torben Nehmer
Aufgefallen beim Schließen von #44: der Abschlussbericht stand als Kommentar da, der Body
darunter weiterhin als offene Arbeit — Abschnitt „Zu entscheiden" über eine längst getroffene
Entscheidung, ungehakte Checkliste, Präsens über einen Defekt, den es nicht mehr gab.Die Regel gab es dafür schon: Schritt 2 von
instructions/dev/issue-tracking.mdsagt, der Body
ist die aktuelle Wahrheit und wird umgeschrieben, wenn sich der Stand ändert. Nur ließ die
Formulierung offen, wann — und Schritt 7 („Close with what actually happened") war vollständig
erfüllbar, ohne den Body anzufassen. Ein Abschlussbericht im Kommentar fühlt sich beim Schreiben
vollständig an; dass der Body dabei zurückbleibt, merkt erst der nächste Leser.Schritt 2 ist deshalb schärfer geworden: der Body ist das Plan-File dieses Stacks. Dasselbe,
was das Plan-Dokument eines Harness ist, und genauso gepflegt — fortlaufend, sobald etwas darin
nicht mehr stimmt, nicht am Ende. Der Maßstab ist der Abbruch, nicht der Meilenstein: eine
Session kann jederzeit enden, und was der Body in diesem Moment sagt, ist die vollständige
Übergabe. Eine frische Session muss zu jedem Zeitpunkt allein aus dem Body weiterarbeiten
können, ohne Kommentare rückwärts zu lesen und ohne einen Menschen, der es neu erklärt. Entschieden
ersetzt die Frage, erledigt hakt das Kriterium ab, verworfen steht mit Begründung dort, wo das
Kriterium stand.Schritt 7 ist damit kein Sonderakt mehr, sondern die letzte dieser Aktualisierungen: erst Body
auf den Endstand, dann schließen, dann die Changelog-Zeile aus Schritt 3. Wer Schritt 2 befolgt
hat, ist fast fertig; wer nicht, zahlt die ganze Schuld im schlechtesten Moment — der
geschlossene Body ist die Fassung, die danach alle lesen und niemand mehr aufsucht. #44 steht
als Beispiel drin.Schritt 3 zieht die Konsequenz: ein Kommentar pro Session-Umfang, nicht pro Edit. Ein
fortlaufend gepflegter Body mit einem Changelog-Kommentar je Änderung wäre Lärm; triviale Pflege
braucht gar keinen. Derstack-dev-Skill sagt es beim Aufgreifen mit, weil dort die Entscheidung
fällt, ob eine Session den Body überhaupt anfasst.Und die ehrliche Antwort auf die Frage nach dem Tooling: es gibt keins, und es soll keins
geben.wikitoolkennt diesen Tracker nicht. Es wird an Instanzen ausgeliefert, die unter
dieser URL keine Issues haben, währendinstructions/dev/vondist exportgepruned wird —
ein Gitea-Client im ausgelieferten Tool wäre eine Dev-Abhängigkeit, die jede Instanz mitträgt,
um ein Board zu prüfen, das keine von ihnen hat. Der Tracker ist ausschließlich über
gitea-mcperreichbar, also in einer Session, durch einen Agenten.Kein
docs verifyfängt also einen geschlossenen Issue, dessen Body offen klingt, einen Body,
der seinen eigenen Kommentaren widerspricht, oder ein fehlendes Pflichtlabel. Das steht jetzt
als eigener Abschnitt „What no tool checks" in der Instruktion — nicht als Bedauern, sondern als
Begründung dafür, warum die Reihenfolge in Schritt 7 ausgeschrieben ist statt aus Schritt 2
erschlossen zu werden.Downloads
-
v4.1.1 Stable
released this
2026-09-03 04:36:40 +00:00 | 79 commits to main since this release4.1.1 - 2026-09-03 - Testisolation: kb_dir repointet config.ROOT, lint löst Kollektionen gegen den übergebenen Baum auf
Author: Torben Nehmer
Issue #44, gefunden beim Bau der Migrations-Gate-Tests für 4.1.0: die
kb_dir-Fixture baute
ihren Baum untertmp_path, ließconfig.ROOTaber auf dem echten Checkout stehen. Jeder
Codepfad, der eine Datei überconfig.ROOT/config.KB_DIRauflöst statt über das übergebene
Verzeichnis, traf damit das echte Repository.Der laute Fall war ein Test, der
kb_state.write_kb_state()rief und dabei das
.wikitool-kb.jsondes Repos überschrieb — Applied-Ledger leer statt zwei Einträgen. In
git statussofort sichtbar und reversibel; bei einer gitignorierten Datei wäre es das nicht
gewesen.Der stillere Fall ist der teurere.
lints Kollektions-Lookup löste eine Seite gegen
config.KB_DIRauf. Für eine Seite untertmp_path/kb/warf dasValueError, die Funktion
antwortete „keine Kollektion", und die Label-Autorisierung übersprang die Kante wortlos.
unauthorised_labelswar damit faktisch ungetestet — jeder Test, der das Finding hätte
auslösen können, bekam eine leere Liste und behauptete nichts. Ein grüner Lauf, der wie eine
Zusicherung aussah.Der Fix ist der Codepfad, nicht die Fixture.
run_lint()bekommt ein Verzeichnis
übergeben und löst jetzt auch intern dagegen auf;authorised_labels()bekommt denselben Baum
gereicht, statt aufconfig.KB_DIRzurückzufallen. Der Regressionstest lintet einen Baum, von
demROOTbewusst wegzeigt — genau der Fall, den die alte Auflösung verschluckte. Eine Funktion,
die ein Verzeichnis entgegennimmt, löst dagegen auf: keine Fixture kann diese Form von außen
reparieren.Beide Korpus-Fixturen repointen jetzt.
kb_dirtut, wasraw_dirlängst tat —ROOTauf
das eigenetmp_path, plususe_shipped_type_specs(). Der Suite-Lauf kippte dadurch keinen
einzigen Test. Die lokalerooted_kb-Umgehung aus 4.1.0 entfällt damit; die Auswahl zwischen
zwei fast gleichen Fixturen war Wissen, das nirgends stand.Und ein Wächter für die ganze Klasse.
repository_tree_guard(session-scoped, autouse)
vergleichtgit status --porcelainvor und nach dem Lauf und lässt die Suite scheitern, wenn
sich im Checkout etwas bewegt hat — zweigit status-Aufrufe pro Lauf, deshalb per Default an.
Er vergleicht vorher gegen nachher statt einen sauberen Baum zu verlangen, sagt also nichts über
die unveröffentlichte Arbeit des Entwicklers. Den Verursacher benennt er nicht;
CHEMENU_TREE_GUARD=eachprüft nach jedem Test und tut es. Ohne git oder außerhalb eines
Repositorys sind beide still.Was der Wächter nicht sieht: eine Prüfung, die unter Test nichts tut, schreibt keine Datei.
Dagegen hilft nur ein Test, der das Finding tatsächlich auslöst — der neue tut das.instructions/dev/testing-conventions.mdhat dafür einen eigenen Abschnitt („Which tree a test
writes into"), einen Schritt in der Checkliste und die Regel für neue Fixturen.Downloads