Vollstaendig getracete Durchfuehrung des dokumentierten Upgrade-Pfads (INSTALL.md § "Eine Instanz
aktualisieren", Weg Tarball) auf einer echten ausgelieferten Instanz - nicht im Ursprungs-Repo
und nicht im CI-Replay. Ziel war eine Validierungsgrundlage, gegen die derselbe Lauf auf einer
Dev-Instanz nachgestellt werden kann.
Die Telemetrie des Laufs steht verbatim im ersten Kommentar. Sieben Befunde unten; keiner hat den
Lauf zum Scheitern gebracht, vier davon kosteten jede Instanz Handarbeit, die der dokumentierte
Weg nicht vorsah.
Ausgangslage: Instanz aus dist export-Tarball, VERSION 5.0.0, kb_version 5.0.0,
63 Seiten, language: de, Harness Claude Code, sauberer Arbeitsbaum, ein Remote.
Herkunft der Befunde. 1 bis 4 stammen aus dem Lauf selbst (2026-09-15). 5 bis 7 und die
Ursachenanalyse unter Befund 1 stammen aus der Review-Sitzung vom 2026-09-16, die das
Sitzungstranskript, die drei Ergebnis-Commits und den Endzustand der Instanz gegengelesen hat.
Abgeschlossen: sechs Befunde erledigt, einer ausgelagert
Befund
Stand
1 - Session-Id-Fallback zersplittert den Lauf
ausgelagert nach #110. Der Anleitungsteil ist mit 504149c erledigt; was bleibt, ist eine Betreiberentscheidung und kein Bauauftrag
2 - version notes antwortete auf keiner Instanz
erledigt, 0c98080, 6.1.0-beta.4
3 - dist upgrade konnte die Release-Fassung nicht uebernehmen
erledigt, 72d01be, 6.1.0-beta.3
4 - Migrationsdokument nennt ein Beispiel, das das Gegenteil zeigt
erledigt, 0e09cf4, 6.1.0-beta.2
5 - Rest-Abschnitt ## Authoring guidance
erledigt in der Instanz (b5014d9) und im Stack (0e09cf4)
6 - Verifikationsschritt ohne festgehaltene Baseline
erledigt, 0e09cf4
7 - Upgrade-Pfad ohne agentengerichtete Prozedur
ausgelagert nach #108, dort erledigt: 504149c, 6.1.0-beta.1
Dieses Issue ist der Laufbericht und wird als solcher nicht weiter bearbeitet. Alle
Werkzeugaenderungen, die aus ihm Bauauftraege waren, sind gebaut; beide Befunde, die eigene
Arbeitspakete wurden, haben eigene Issues (#108 erledigt, #110 offen). Was hier bleibt und
weiterhin Wert hat, ist die Evidenz: die vollstaendige Trace im ersten Kommentar, die
Lauf-Zusammenfassung mit Zeitstempeln, und der Reproduktionsabschnitt, der pro Schritt sagt, ab
welcher Version er nicht mehr reproduziert.
Lauf-Zusammenfassung
Zeit (UTC)
Schritt
Ergebnis
19:45:07
version check
Update 5.0.0 -> 6.0.0, Grenzuebertritt gemeldet
19:45:18
migrate status
nichts offen
19:45:18
version notes
Exit 1 - Befund 2
19:46:03
dist upgrade --dry-run
201 unveraendert, 8 neu, 1 lokal geaendert, 0 entfallen
19:46:31
dist upgrade
Exit 1 - Befund 3
19:46:59
dist upgrade --dry-run
Exit 1, Arbeitsbaum unsauber (nach Handreparatur)
~19:47
git commit der Handreparatur
ausserhalb des Werkzeugs noetig - und ausserhalb von Invariante 5, siehe Befund 3
19:47:17
dist upgrade --dry-run
202 / 8 / 0 / 0
19:47:22
dist upgrade
OK, 210 Dateien geschrieben
19:47:28
migrate status
1 optionales Upgrade, nichts blockiert
19:47:29
instructions sync
5 Skills publiziert
19:47:44
doctor
alles OK, ein WARN (session-id)
19:47:46
docs verify
Exit 1 - TOC fehlt auf types/concept.md, types/source.md
19:47:53
docs toc --apply
2 Dateien repariert - wie in den Release-Notes angekuendigt
OK, Commit c8c9f9b, 5 Dateien, unter der Gate-Schwelle
33 wikitool.call-Events insgesamt, ein einziger unerwarteter Exit-Code (types describe source,
Exit 1 um 20:06:09 - das war SIGPIPE durch ein | head des Aufrufers, kein Werkzeugfehler; siehe
Anmerkung unter Befund 1 und die Ursachenverkettung in Befund 6).
Nicht in der Tabelle, weil nie gelaufen:migrate verify --from <commit vor dem Tausch>,
INSTALL.md Schritt 6, erster Pruefschritt. Siehe Befund 7.
Ausgelagert nach #110. Die Evidenz bleibt hier stehen, weil sie das Messergebnis dieses Laufs
ist und den Befund ueberhaupt erst tragbar macht - dasselbe Muster wie bei Befund 7. Die
Entscheidung, die Loesungsachsen und die Akzeptanzkriterien stehen in #110.
Beobachtung
Der Lauf ist eine Sitzung. Die Telemetrie kennt ihn als 21 Sitzungen:
WIKITOOL_SESSION_ID war nicht gesetzt, der Fallback ist die Parent-PID
(chemenu/session.py). Der Harness startet pro Tool-Call eine neue Shell - also pro Aufruf
eine neue PID und damit eine neue "Sitzung".
Folge 1: EVALS.md' Join-Key haelt nicht
EVALS.md § Architecture: "Everything joins on WIKITOOL_SESSION_ID." In diesem Lauf joint nichts: die Hook-Events tragen die UUID, die wikitool-Events tragen 20 PIDs. Es gibt keinen
gemeinsamen Schluessel.
Das ist im Scorer direkt sichtbar. eval score --session 3835548 (die Sitzung mit der
Gate-Verweigerung) meldet:
- `skip` **clearance-ended-the-turn** - No wikitool.call between a clearance request (exit 42) and the next prompt.submitted.
- this harness cannot report prompt.submitted - cannot say
Der Harness hatprompt.submitted gemeldet - 5 mal, nur unter der UUID. Ausgerechnet die
L2-Regel, die Gate-Befolgung prueft, kann auf dem Hauptharness nie ein Urteil faellen. Das ist
kein fehlender Hook (#82), sondern ein toter Schluessel: #82 wuerde tool.pre/tool.post
ergaenzen, die dann ebenfalls unter der UUID landen und weiterhin nicht zu den wikitool.call-Events joinen. Die beiden Issues muessen zusammen gedacht werden, sonst
verdrahtet #82 Hooks, deren Events immer noch niemand zuordnen kann.
Ausserdem scort eval score jeweils 1 bis 3 Calls statt 33, waehrend die L1-Strukturpruefung in
allen 21 Buckets identisch neu berechnet wird. Ein Lauf ist so nicht bewertbar.
Folge 2: das Iteration-Budget-Gate erreicht seine Schwelle nie
Gravierender, weil es eine der vier in Code gegossenen Sicherungen ist (AGENTS.md § Gates:
60 Calls pro Sitzung, Loop-Breaker bei 3 identischen in Folge).
tools/.wikitool_session/budget.json, Stand nach dem Lauf:
Der Hoechststand ist 9 - und der stammt aus wiki-setup-nathan, einem Bucket, in dem WIKITOOL_SESSION_IDgesetzt war. Jeder PID-Bucket kommt ueber 3 nicht hinaus. Bei 33
Calls in einem Lauf hat der Zaehler also nie mehr als 3 von 60 gesehen.
Damit ist das Gate unter diesem Harness nicht "grosszuegig", sondern strukturell
unerreichbar - und der Loop-Breaker gleich mit: drei identische Calls in Folge landen in drei
verschiedenen Buckets. Eine Sicherung, die ausdruecklich deshalb in Code sitzt, weil ein Agent
sich an einer Prompt-Regel vorbeireden kann (docs/why-gates-are-code.md), ist hier still aus.
doctor sagt das Noetige bereits - nur als WARN und ohne die Folge zu nennen:
WARN session-id: WIKITOOL_SESSION_ID is not set - budget falls back to the parent PID
fix: See instructions/session-setup.md
Ursache eine Ebene tiefer: instructions/session-setup.md war auf diesem Harness wirkungslos
Nachgetragen aus der Review-Sitzung. doctor verweist auf instructions/session-setup.md, und
dort stand als Schritt, woertlich:
"Run this once per working session" - genau das funktioniert auf Claude Code nicht. Der
Harness fuehrt jeden Bash-Tool-Call in einer frisch initialisierten Shell aus; das
Arbeitsverzeichnis wird uebernommen, Shell-State (Umgebungsvariablen, Funktionen) nicht. Ein export in Aufruf N ist in Aufruf N+1 verschwunden. Die 20 verschiedenen Parent-PIDs oben sind
dieselbe Tatsache von der anderen Seite gemessen.
Das aendert die Reichweite des Befunds: eine bessere Fallback-Kette repariert den Messwert, aber
die Anleitung, die das Problem eigentlich verhindern soll, war auf dem Hauptharness ein No-op.
Erledigt mit 504149c (6.1.0-beta.1, aus #108):instructions/session-setup.md nennt jetzt
die Inline-Form pro Aufruf, sagt warum ein export nur traegt solange die Shell traegt, und gibt
den Einzeiler an, mit dem sich beantworten laesst, welcher Fall vorliegt. Der Rest dieses Befunds -
Fallback-Kette, Join-Key, WARN-Haerte - ist davon unberuehrt und in #110 offen.
Nebenbefund zur Trace-Treue
types describe source steht mit exit_code: 1 in der Trace (20:06:09). Der Aufruf war
erfolgreich; der Exit-Code entstand durch SIGPIPE, weil der Aufrufer die Ausgabe durch | head
geschickt hat. Der unmittelbar folgende identische Aufruf ohne Pipe steht mit exit_code: 0 da.
Wer die Trace als Fehlerquelle auswertet, zaehlt hier einen Werkzeugfehler, den es nicht gab.
Dasselbe | head ist die Ursache von Befund 5 - siehe Befund 6. In #110 als Nebenbefund
mitgenommen, weil er dieselbe Trace betrifft.
Befund 2: version notes scheiterte auf jeder ausgelieferten Instanz - erledigt (0c98080, 6.1.0-beta.4)
kind/defect - Doku und Realitaet widersprachen sich.
INSTALL.md § "Eine Instanz aktualisieren", Schritt 4, verbatim (Stand des Laufs):
Bei einer Kompatibilitätsgrenze (dist upgrade meldet sie laut) die Release-Notes vor dem
nächsten Schritt lesen: Breaking Change: und Migration: im Eintrag von tools/wikitool version notes sagen, was aufhört zu funktionieren und ob der Korpus
umgeschrieben werden muss.
Der Aufruf auf der Instanz:
$ tools/wikitool version notes
ERROR CHANGES.md has no entry for 5.0.0 - run `wikitool version bump` before
releasing, or write the entry
Ursache
version notes liest die lokale CHANGES.md. Eine ausgelieferte Instanz bekommt dafuer den
Stub aus tools/chemenu/dist_templates/CHANGES.md - 9 Zeilen, Vorwort, null Versionseintraege. CHANGES.md steht ausserdem in chemenu.ownership.is_upgrade_preserved, wird von dist upgrade
also bewusst nie ueberschrieben. Der Stub bleibt der Stub - dauerhaft. version notes konnte
auf einer Instanz nicht nur damals nicht funktionieren, sondern nie.
Das traf genau den Moment, fuer den der Schritt existiert: den Grenzuebertritt, an dem der
Betreiber wissen muss, was aufhoert zu funktionieren. Der Lauf kam nur weiter, weil die
Release-Notes ueber die Gitea-API gelesen wurden - ein Weg, den INSTALL.md an dieser Stelle nicht
nannte, und den eine Instanz ohne erreichbaren MCP-Server gar nicht hat.
Eine Zwischenstufe hat den Widerspruch dokumentarisch beseitigt, den Defekt aber nicht:
INSTALL.md verweist seit 504149c auf instructions/upgrade-instance.md, deren Schritt 2 die
Release-Seite aus release_url las und ausdruecklich sagte, dass version notes auf einer
Instanz nicht antwortet.
Umgesetzt: Fallback auf den Release-Feed, laut angekuendigt
Von den beiden erwogenen Wegen der erste. Ausschlaggebend war, dass instructions/upgrade-instance.md Schritt 2 den eigenen Workaround-Absatz schon als temporaer
fuehrte ("This paragraph stops being necessary the day version notes falls back to that
feed"): die billigere Variante haette ihn dauerhaft stehenlassen.
Die Regel, in drei Faellen:
Der lokale CHANGES.md-Eintrag ist da -> er wird gedruckt. Unveraendert, und der einzige
Fall, den das Ursprungs-Repo und CI je erreichen.
Kein Eintrag und kein Release-Stamp (ein Dev-Checkout) -> die alte Fehlermeldung,
unveraendert. Das ist der Wachhund gegen einen Netzaufruf im Ursprungs-Repo: nur eine ausgelieferte Instanz geht online, und release.ymls version notes > /tmp/release-notes.md kann den neuen Pfad damit nie betreten. Ein Test
verdrahtet einen Fetcher, der beim Aufruf AssertionError wirft, und prueft, dass er nicht
aufgerufen wird.
Kein Eintrag, aber ein Release-Stamp (eine ausgelieferte Instanz) -> der Feed aus update_url wird gefragt, derselbe, den version check benutzt (WIKITOOL_UPDATE_URL und --url uebersteuern ihn wie dort). --offline verweigert den Aufruf und faellt auf die
Fehlermeldung zurueck, die dann release_url aus dem Stamp nennt.
Die Notes gehen nach stdout, die Herkunft nach stderr.version notes existiert, damit der
Release-Workflow kein Markdown in der Shell parsen muss (release.yml: version notes > /tmp/release-notes.md), also darf stdout nichts als den Eintrag tragen.
Antwortet der Feed eine andere Version als die gefragte, wird das in der stderr-Kopfzeile
benannt und die Notes werden trotzdem gedruckt. Das ist nicht der Randfall, sondern der
Hauptfall: in Schritt 2 des Upgrades steht VERSION noch auf der alten Version, waehrend die
gesuchten Notes die der neuen sind. Der Feed kennt nur /releases/latest - update_url ist die
einzige URL, die der Stamp traegt, und eine /releases/tags/<tag>-URL daraus zusammenzusetzen
waere geraten statt gelesen (Invariante 7).
Nicht erreichbarer Feed: Exit 1, mit dem Feed-Fehler undrelease_url aus dem Stamp. Ein
leerer body faellt genauso aus - eine leere Antwort darf nicht als "dieses Release hat nichts
zu melden" durchgehen.
Nachgezogen: instructions/upgrade-instance.md Schritt 2 (Workaround-Absatz weg, dafuer die
beiden Dinge, die man vor dem Lesen der Ausgabe wissen muss), das Vorwort derselben Datei (es
nennt keine Werkzeugluecke mehr - es waren zwei, jetzt sind es null), INSTALL.md § "Version und
Updates" an zwei Stellen (die Notes-Quelle, und die inzwischen falsche Behauptung, version check sei der einzige Befehl, der ins Netz geht), tools/CONTRACT.md in beiden Tabellen sowie
die Modul-Docstrings von version_cmd.py und version.fetch_latest, die dieselbe
Ein-Netzaufruf-Behauptung trugen.
Befund 3: dist upgrade kannte keinen Weg, die Release-Fassung einer lokal geaenderten Datei zu uebernehmen - erledigt (72d01be, 6.1.0-beta.3)
kind/defect / fehlende Faehigkeit.
Beobachtung
Der Dry-Run meldete genau eine lokal geaenderte Datei:
201 unchanged, 8 new, 1 locally changed, 0 removed from the release.
Locally modified (1):
- kb/CONTRACT.md
Der Unterschied war reine Whitespace-Formatierung einer Markdown-Tabelle (Spaltenauffuellung,
vermutlich ein Format-on-Save), inhaltlich identisch. kb/CONTRACT.md ist dabei stackeigen: instructions/private-instance.md fuehrt <stage>/CONTRACT.md ausdruecklich als Maschinerie, an
der "an instance never edits it".
Beide damals angebotenen Wege waren hier falsch:
--keep-localbehielt die Drift. Der neue Stamp schreibt trotzdem die Release-Digest - die
Datei divergierte also bei jedem kuenftigen dist upgrade erneut und wurde jedes Mal wieder
gemeldet. Fuer eine Datei, die der Instanz gar nicht gehoert, der dauerhaft falsche Zustand.
"reconcile by hand" war der richtige Weg, hatte aber kein Werkzeug: Handkopie aus dem
entpackten Tarball, dann ein Commit nur zur Herstellung der Clean-Tree-Vorbedingung des
naechsten Kommandos. Drei Schritte, zwei davon ausserhalb des Werkzeugs. Die
Sauberkeits-Vorbedingung und die Handreparatur standen sich dabei gegenseitig im Weg: die
Reparatur macht den Baum unsauber, den das Kommando sauber verlangt.
Der Preis war eine Invariantenverletzung
Das schaerfste Argument fuer eine Werkzeugloesung: der Commit 7fe8353 ist ein **rohes git add
git commit**. AGENTS.md Invariante 5 sagt "Never call raw git commit/git push. Publish
through tools/wikitool publish" und kennt keine Ausnahme fuer "ist ja nur eine Vorbedingung".
Richtig waere tools/wikitool publish --no-push gewesen.
Bemerkenswert ist weniger der Fehlgriff als die Richtung: eine fehlende Werkzeugfaehigkeit hat den
Lauf an einer in Code gegossenen Regel vorbeigefuehrt, und nichts hat es gemeldet - kein Gate, kein
Check, kein Scorer.
Erwartungshaltung aus dem Transkript
Der Agent kuendigte um 19:46:28, vor dem Lauf ohne Flag, woertlich an: "I'll let the upgrade
take the release's version rather than pinning the local formatting" - und rief dist upgrade
dann ohne Flag auf. Das Mentalmodell war also "Default = Release-Fassung nehmen"; der Default ist
Abbruch. Die Fehlermeldung korrigierte das nicht: sie nannte --keep-local und "reconcile by
hand", sagte aber nicht, dass es zu --keep-local kein Gegenstueck gibt. Es fehlte damit nicht
nur ein Flag, sondern auch der Satz, der die falsche Erwartung abfaengt.
Umgesetzt: --take-release <pfad>, wiederholbar
--take-release nimmt einen Pfad und ist wiederholbar (wie search --field), statt ein pauschales
Gegenstueck zu --keep-local zu sein. Der Grund ist die Asymmetrie der beiden Antworten: --keep-local laesst alles stehen und verliert nichts, --take-release verwirft eine lokale
Aenderung. Eine Verwerfung benennt ihr Ziel - dasselbe Muster, das issue-tracking.md Schritt 1
fuer destruktive Schritte verlangt -, und der gemischte Fall (zwei geaenderte Dateien, eine davon
zurueckzusetzen) ist damit ueberhaupt erst loesbar.
Ein Pfad, der gar nicht in der blockierten Liste steht, ist Exit 1 mit der Liste dessen, was
dort steht - und zwar auch im --dry-run: das ist ein Fehler im Argument, nicht ein
Zustand des Baums, und ein still ignorierter Tippfehler haette ein erfolgreiches Upgrade
gemeldet und die Aenderung behalten, die verworfen werden sollte. Es ist die einzige
Verweigerung, die einen Dry-Run nicht-null macht; ein blockierter Pfad tut das weiter nicht.
Eine lokal geloeschte Datei ist ein gueltiges Ziel: die Release-Fassung wird wieder angelegt.
--take-release und --keep-local zusammen sind erlaubt und komponieren. Ohne --keep-local bricht ein blockierter Pfad, zu dem nichts gesagt wurde, weiter ab.
Nach --take-release stimmt die Datei wieder mit der Stamp-Digest ueberein, die Drift ist also weg und nicht nur ueberschrieben - genau der Unterschied zu --keep-local.
Der Dry-Run markiert jeden benannten Pfad als einen, den er aus dem Release ueberschreiben
wuerde.
Die Fehlermeldung des Abbruchs nennt alle drei Antworten samt fertiger Kommandozeile, im Muster
des Mass-Update-Gates, das seine --confirm-Zeile ebenso zum Einsetzen ausdruckt:
ERROR 1 locally changed file(s) (listed above) would be silently overwritten.
Nothing was written, and none of these three is the default:
- take the release's version and discard the local change:
dist upgrade <source> --take-release kb/CONTRACT.md
- keep every local change and upgrade around them (it is reported again
on every future upgrade):
dist upgrade <source> --keep-local
- reconcile them by hand first, then re-run.
Nachgezogen: instructions/upgrade-instance.md Schritt 6 traegt statt der
Drei-Schritt-Handreparatur die Entscheidung pro Pfad und den Dry-Run, mit dem man sie vorher
sieht; Schritt 7 nimmt die Flags mit. tools/CONTRACT.md in beiden Tabellen. Bewusst nicht
geaendert: die Dirty-Tree-Vorbedingung - mit --take-release entfaellt die Handreparatur und
damit der unsaubere Baum, den sie erst erzeugte.
Befund 4: 6.0.0-type-guidance-split nennt ein Beispiel, das in der Instanz das Gegenteil zeigt - erledigt (0e09cf4)
kind/defect, Dokumentation. Betraf das mitgelieferte Migrationsdokument, nicht den Code.
4a - das benannte Beispiel war in der Instanz die unmigrierte Datei
instructions/migrations/6.0.0-type-guidance-split.md, Schritt 4, verbatim (Stand des Laufs):
Delete the now-duplicated prose from the type-spec, keeping the H1, a short pointer to the
guidance file (types/entity.md's own current text is the worked example), ## Frontmatter
and ## Template.
Im Ursprungs-Repo stimmt das: dort ist types/entity.md die stackeigene, bereits migrierte Datei.
In einer ausgelieferten Instanz ist types/entity.md die beim Setup adoptierte Kopie - also
genau die Datei, die noch die alte, zu entfernende Prosa traegt. Wer dem Satz woertlich folgte,
schrieb den Vorher-Zustand ab.
Das gesuchte Beispiel liegt in der Instanz unter types/entity.md.template - dort steht der
Nachher-Zustand (Pointer-Absatz, guidance:-Feld in der Frontmatter), weil dist upgrade die
Templates verbatim mitliefert.
Verschaerfung aus der Review-Sitzung, gegen den Baum bestaetigt: im Ursprungs-Repo existiert
ueberhaupt keine types/*.md.template-Datei - dist export re-keyt types/<name>.md erst beim
Export zur .template (dist_cmd._owned_type_stem, _plan_types). Der Satz konnte in einer
Instanz also nicht bloss unguenstig sein, er konnte dort strukturell nie stimmen.
Umgesetzt: der Schritt (jetzt Schritt 5, nach dem neu eingezogenen Vorher-Schritt aus Befund 6)
nennt types/<name>.md.template als Beispiel und sagt den Grund dazu - types/<name>.md ist die
adoptierte Kopie und damit die Datei, die gerade geaendert wird, die .template daneben traegt den
Nachher-Zustand. Mit dem Zusatz, die .template fuer die Form zu lesen und nicht wholesale zu
kopieren: ihre ## Frontmatter und ihr ## Template sind die Stack-Defaults, nicht die der
Instanz.
4b - die Sprachfrage blieb offen, direkt nachdem 6.0.0 sie verschoben hat
Das Dokument sagte nicht, in welcher Sprache der neue Pointer-Absatz zu schreiben ist. Fuer eine
Instanz mit language: de war das nicht ableitbar, denn 6.0.0 hatte diese Grenze gerade erst neu
gezogen (Bump "Control-Plane-Sprache universell", docs/language-boundaries.md).
Umgesetzt, und dabei gegen types/type-spec.md § "Who owns a type-spec" korrigiert. Die im
urspruenglichen Befund vorgeschlagene Begruendung "ein Abschnitt, den die Instanz behaelt, ist
ihrer, also traegt er die KB-Sprache" haelt gegen den Baum nicht: die Sprachachse ist der
Leser, nicht der Eigentuemer. Anleitungsprosa in einem Type-Spec ist Control Plane und damit
englisch, unabhaengig davon, wem die Datei gehoert - so steht es in der Tabelle in types/type-spec.md, so steht es im Docstring von dist_cmd.instance_owned_type_stems
("Ownership, not language"), und so steht es in AGENTS.md § File naming. Das Ergebnis von
Befund 5 war trotzdem richtig, nur die Begruendung nicht: der uebrige Bullet war redundant und
gehoerte weg, nicht uebersetzt.
Das Dokument sagt jetzt entsprechend:
Teil des Type-Specs
Sprache
Begruendung
H1, Pointer-Absatz
Englisch
Anleitungsprosa an einen Agenten = Control Plane, egal wem die Datei gehoert
behaltene lokale Prosa
Englisch
ebenfalls Anleitungsprosa - eine vor dieser Regel in der KB-Sprache geschriebene Notiz wird uebersetzt, nicht umbenannt
## Frontmatter-Tabelle
unveraendert lassen
die Migration fasst Frontmatter ausdruecklich nicht an
## Template-Block + Nachsatz
unveraendert lassen (hier: deutsch)
Seitentext in der KB-Sprache
Die Tabelle steht im Dokument nicht als Kopie - der Schritt sagt die Antwort in zwei Saetzen
und verlinkt types/type-spec.md § "Who owns a type-spec" fuer die vollstaendige Aufteilung
(Invariante 8). Der Satz "eine englische Ueberschrift ueber einem Koerper in einer anderen
Sprache ist die halbfertige Fassung dieses Schritts" benennt genau den Zustand, den Befund 5
produziert hat.
Dazu neu, weil es der eigentliche Mechanismus hinter Befund 5 ist: eine behaltene Notiz darf
nicht ## Authoring guidance heissen.types describe setzt diesen Kopf selbst und inlined
darunter die Guidance-Datei, die ihren eigenen gleichnamigen Abschnitt mitbringt - ein dritter aus
dem Type-Spec-Koerper ist die Doppelung.
4c - kleinere Beobachtung zur Schrittfolge
Schritt 6 (migrate done 6.0.0 --pages 0) tut genau, was es verspricht, und die Ausgabe ist
unmissverstaendlich:
OK Recorded the optional 6.0.0-type-guidance-split. Content stays at 5.0.0 - an
offer changes a file you own, not the shape of your content.
Danach bleibt doctor bei kb-version: 5.0.0 (nothing outstanding up to 6.0.0). Das ist korrekt
und dokumentiert, sieht aber auf den ersten Blick nach einem haengengebliebenen Upgrade aus. Keine
Aenderung noetig - hier nur vermerkt, weil eine Dev-Instanz-Validierung sonst darueber stolpert.
Befund 5: Rest-Abschnitt ## Authoring guidance - repariert in der Instanz (b5014d9) und im Stack (0e09cf4)
kind/defect. Gefunden in der Review-Sitzung, live gegengeprueft und dort auch behoben. Steht
hier, weil er die Wirkung von Befund 4b und Befund 6 belegt.
5a - die Instanz (b5014d9)
Bei types/source.md wurde der Abschnitt ## Autorenanweisungen nicht geloescht, sondern zu ## Authoring guidanceumbenannt, mit einem verbliebenen deutschen Bullet:
## Authoring guidance
- Der Titel beginnt mit "Source - ", gefolgt vom Namen der Quelle
Zwei Fehler in drei Zeilen:
Ein dritter Kopf desselben Namens in der komponierten Ausgabe. Zwei sind der
Normalzustand und kein Befund: types describe setzt selbst einen ## Authoring guidance-Kopf und inlined darunter die Guidance-Datei, die ihren eigenen gleichnamigen
Abschnitt mitbringt - so sieht es bei allen vier Typen aus. source hatte danach einen
dritten aus dem Type-Spec selbst.
Englische Ueberschrift ueber deutschem Inhalt in einer instanzeigenen Datei. Die Grenze,
an der das falsch ist, ist der Leser, nicht der Eigentuemer - siehe die Korrektur unter
Befund 4b: Anleitungsprosa bleibt englisch, auch in einer Datei, die der Instanz gehoert.
Richtig war hier trotzdem das Entfernen, weil der Bullet redundant zum title_prefix: "Source - " in der Frontmatter war, das wikitool new source ohnehin erzwingt.
entity, concept und comparison waren sauber - betroffen war nur source, der einzige der
vier Typen, dessen Prosa umfangreich genug war, dass ein Bullet uebrigblieb, den die
Stack-Guidance nicht abdeckt.
Behoben mit b5014d9: Abschnitt ersatzlos entfernt, geprueft mit dem Muster aus Befund 6 - types describe source vor und nach der Aenderung in je eine Datei, dann diff. Der Diff
zeigt genau die vier entfernten Zeilen und sonst nichts; alle vier Typen komponieren jetzt mit
zwei Koepfen.
5b - derselbe Defekt im Stack selbst (0e09cf4)
Nachgetragen 2026-09-16, gefunden beim Nachmessen im Ursprungs-Repo. Das Kriterium unten war
gegen die Instanz nathan abgehakt, gegen den Stack nie - und dort stand derselbe Rest-Abschnitt:
types/source.md im Ursprungs-Repo trug bis 0e09cf4 einen ## Authoring guidance-Abschnitt mit
demselben einen Bullet (hier englisch, weil c64479f ihn uebersetzt hatte; d49513b hat dann den
Rest der Prosa ausgelagert und ihn stehenlassen). Das ist kein Instanzschaden, sondern ein
Auslieferungsdefekt: die Datei wird beim Export zu types/source.md.template, jede neu
aufgesetzte Instanz haette ihn mit adoptiert - und ihn dann bei der naechsten guidance-Migration genau so wiedergefunden wie nathan.
Entfernt, geprueft mit demselben Vorher/Nachher-Diff: genau vier Zeilen weg, sonst nichts, alle
vier Typen bei zwei Koepfen. Inhaltlich verloren geht nichts - title_prefix steht in der
Frontmatter, in der Feldtabelle von types/type-spec.md und im Nachsatz zum ## Template-Block
von types/source.md; ausserdem verbietet types/type-spec.md § Writing Shape das Wiederholen
einer Schema-Regel im Fliesstext ausdruecklich.
Nebenbeobachtung, kein Befund: dass types describe einen ## Authoring guidance-Kopf setzt
und unmittelbar darunter eine Guidance-Datei mit eigenem H1 und eigenem gleichnamigem Abschnitt
inlined, ist eine kosmetische Doppelung im Stack selbst - gleich fuer alle vier Typen, ohne
Auswirkung auf Inhalt oder Werkzeuge.
Befund 6: Schritt 5 des Migrationsdokuments war als Verifikation unfalsifizierbar - erledigt (0e09cf4)
kind/defect, Dokumentation. Direkte Ursache von Befund 5.
The output must read the same as it did before this migration [...] the structure (frontmatter
fields, template block) must be byte-identical.
Kein Schritt davor hielt das Vorher fest. Eine Pruefung gegen einen Zustand, den niemand
aufgeschrieben hat, ist keine Pruefung - sie faellt auf das Gedaechtnis des Ausfuehrenden zurueck,
und bei einer Ausgabe von ueber 150 Zeilen je Typ ist das keins.
Der Lauf hat entsprechend geprueft: types describe durch | head -250 und | tail -80, also in
Ausschnitten, mit dem Urteil "All four compose correctly, structurally identical to before". Der
Rest-Abschnitt aus Befund 5 liegt in der Mitte der source-Ausgabe und war in keinem der beiden
Ausschnitte. Dasselbe | head erzeugte zugleich den SIGPIPE-Exit-1, den der Nebenbefund unter
Befund 1 als Trace-Rauschen fuehrt: ein Verhalten, zwei Symptome.
Umgesetzt an beiden Stellen:
Das Dokument hat einen eigenen Schritt 3 bekommen - types describe <name> vollstaendig in
eine Datei, vor der Aenderung, mit dem Satz, dass ein head/tail genau die Mitte
verschwinden laesst. Schritt 6 diffed dagegen und nennt zusaetzlich die Ein-Zahl-Probe grep -c '^## Authoring guidance': zwei ist richtig, drei heisst Rest-Abschnitt im Type-Spec.
Die alten Schritte 3-6 sind auf 4-7 gerueckt, die Querverweise mit.
instructions/migrate-corpus.md § "Writing the migration document" traegt das Muster
generisch: ein Verifikationsschritt nennt seine eigene Baseline, und zwar in einem frueheren
Schritt. migrate verify traegt seine im letzten Commit, ob jemand daran denkt oder nicht -
eine Migration an der Maschinerie statt an kb/ hat gar keine, und genau dort entsteht die
Behauptung, die sich nicht widerlegen laesst.
Nicht angefasst: instructions/upgrade-instance.md Schritt 12, der seit 504149c dasselbe in
Richtung des Ausfuehrenden sagt. Zwei Adressaten, zwei Regeln - der Autor eines
Migrationsdokuments und der Betreiber, der eines abarbeitet -, also keine zweite Kopie im Sinne
von Invariante 8.
Befund 7: der Upgrade-Pfad hatte keine agentengerichtete Prozedur - ausgelagert nach #108, dort erledigt
kind/defect, Prozess. Umgesetzt mit 504149c (6.1.0-beta.1); hier bleibt die Evidenz aus dem
Lauf stehen, weil sie die Begruendung der Loesung traegt - dasselbe Muster, mit dem Befund 1 jetzt
nach #110 gegangen ist.
Die Schrittfolge stand nur in INSTALL.md - einem Dokument fuer Menschen (AGENTS.md § File
naming: README-foermige Wurzeldateien werden "never by an agent as instruction" geladen).
Ausgefuehrt wird sie von einem Agenten. Der erste Tool-Call des Laufs listete instructions/
mit - der Agent suchte also zuerst eine Instruktion, fand keine, oeffnete instructions/private-instance.md (der falsche Weg: Clone mit gemeinsamer History statt
Tarball-Instanz), verwarf sie und griff auf INSTALL.md zurueck.
Was im selben Lauf daraus folgte:
migrate verify --from <commit vor dem Tausch> wurde nie ausgefuehrt, obwohl es in
INSTALL.md Schritt 6 der erste Pruefschritt war. Die Lauf-Tabelle oben belegt es lueckenlos.
Der Grund ist praezise benennbar: der Lauf folgte nicht INSTALL.md, sondern dem
Abschlussbericht von dist upgrade - und in dessen Liste kam migrate verify nicht vor.
Die Agent-Session wurde nie neu gestartet. Beide Reihenfolgen verlangten das, die eine am
Ende von Schritt 6, die andere am Ende des Abschlussberichts. Die optionale Migration lief
anschliessend unter dem 5.0.0-Kontrollplan, obwohl AGENTS.md im selben Commit +44/-3 bekommen
hatte - und genau dort fielen Befund 4b (Sprachgrenze, neu in 6.0.0) und Befund 5 an.
Drei Reihenfolgen waren im Umlauf: INSTALL.md Schritt 6, der Abschlussbericht, und die
tatsaechlich gelaufene. Dass der Lauf der zweiten folgte, machte die erste zu toter Doku -
genau der Zustand, den Invariante 8 verbietet.
Umgesetzt: instructions/upgrade-instance.md (manual: true) ist die eine Fassung, dreizehn
Schritte, mit dem Sitzungsneustart zwischen Maschinerie-Publish und Migrationskette statt am Ende
und migrate status als Wiedereinstiegspunkt. Der Abschlussbericht von dist upgrade nennt jetzt
die Datei und das Wiedereinstiegskommando statt einer eigenen Liste, INSTALL.md nur noch die
Entscheidung davor. Details und Abgrenzung: #108.
Was der Lauf bestaetigt hat
Ausdruecklich keine Befunde - das hat gehalten:
Der angekuendigte Grenzuebertritt kam exakt wie beschrieben. Die Release-Notes sagten
voraus, dass docs verify nach dem Update auf adoptierten Page-Type-Specs ohne TOC-Region
faellt, und nannten docs toc --apply als Reparatur. Genau das trat ein
(types/concept.md, types/source.md), und genau das reparierte es - ein Werkzeuglauf, keine
Inhaltsmigration. Nicht identisch mit #106: dort ging es um kb/CONVENTIONS.md.template auf
einer frischen Instanz; hier um adoptierte types/*.md auf dem Upgrade-Pfad. Der in kb/CONVENTIONS.md.template traegt keine TOC-Region: frische Instanz und CI scheitern an docs verify (#106)
umgesetzte Fix (Templates in toc.target_files()) deckt diesen Fall nicht ab, weil die
betroffenen Dateien bereits im Dateisatz stehen - ihnen fehlte die Region nur, weil sie vor der
Scope-Erweiterung adoptiert wurden.
dist upgrade klassifiziert korrekt - 202 unveraendert / 8 neu / 0 lokal geaendert /
0 entfallen, und die 8 neuen Dateien waren exakt die des Bumps (4 *.guidance.md, types/type-guidance.md(.schema.yaml), docs/language-boundaries.md, das Migrationsdokument).
migrate status hat richtig nicht blockiert. "Migration: none required" aus den
Release-Notes und "Nothing outstanding" aus dem Werkzeug stimmten ueberein.
Das Mass-Update-Gate hat sauber gearbeitet: Exit 42 bei 69 Dateien, Token an Dateiliste und
Inhalt gebunden, Freigabe mit demselben Token akzeptiert, gate.refused/gate.cleared beide in
der Trace. Als Datenpunkt fuer #53: 69 Dateien bei Schwelle 10, und der zweite Publish desselben
Laufs lag mit 5 Dateien darunter.
types describe komponiert nach der Migration unveraendert.Korrigiert durch Befund 5:
fuer entity, concept und comparison stimmte es; fuer source nicht - dort stand bis b5014d9 (Instanz) bzw. 0e09cf4 (Stack) ein dritter ## Authoring guidance-Kopf in der
komponierten Ausgabe. Was fuer alle vier haelt: Frontmatter-Felder und ## Template-Block sind
byte-gleich, nur die Herkunft der Anleitungsprosa hat gewechselt.
Reproduktion auf einer Dev-Instanz
Die Reproduktion gilt gegen einen 6.0.0-Tarball - also gegen die Maschinerie, in der die
Befunde aufgetreten sind. Wie ein Lauf gegen das Ergebnis dieses Issues stattdessen ausgeht, steht
unter dem Block.
# 1. Instanz auf 5.0.0 herstellen (aus einem 5.0.0-Checkout)
tools/wikitool dist export /tmp/inst-5.0.0
cd /tmp/inst-5.0.0 && git init -q -b main
git config user.name Dev && git config user.email dev@example.invalid
# Templates adoptieren wie instructions/setup-instance.md Schritt 5/6
python3 -m venv tools/.venv && tools/.venv/bin/pip install -q -r tools/requirements.txt
tools/wikitool instructions sync && tools/wikitool index rebuild
git add -A && git commit -qm baseline
# 2. Die Drift dieses Laufs nachstellen (Befund 3)# kb/CONTRACT.md durch einen Markdown-Formatter schicken - Whitespace-only,# inhaltlich identisch - und committen.# 3. Nicht setzen, um Befund 1 zu reproduzieren: WIKITOOL_SESSION_ID# Jeden wikitool-Aufruf aus einer eigenen Shell absetzen (wie ein Agent-Harness es tut).# 4. Upgrade fahren
tools/wikitool version check # meldet Grenzuebertritt
tools/wikitool version notes # ERWARTET Exit 1 -> Befund 2
tools/wikitool dist upgrade <6.0.0-tarball> --dry-run
tools/wikitool dist upgrade <6.0.0-tarball> # ERWARTET Exit 1 -> Befund 3# -> Handreparatur + Commit, dann erneut
tools/wikitool migrate status && tools/wikitool instructions sync
tools/wikitool doctor
tools/wikitool docs verify # ERWARTET Exit 1 auf types/*.md
tools/wikitool docs toc --apply && tools/wikitool docs verify
tools/wikitool instructions verify && tools/wikitool lint
# 5. Befund 1 messen - der einzige Teil, der weiter reproduziert (siehe #110)
python3 -c "import json;d=json.load(open('tools/.wikitool_session/budget.json'));\
print(len(d),'Buckets; max count:',max(e.get('count',0) for e in d.values()))"
tools/wikitool eval sessions # ERWARTET: viele Buckets, je 1-3 Events# 6. Befund 5/6 reproduzieren: die optionale Migration fahren und vorher/nachher diffen
tools/wikitool types describe source > /tmp/source-vorher.txt
# ... Schritte 3 und 4 des Migrationsdokuments ausfuehren ...
tools/wikitool types describe source > /tmp/source-nachher.txt
diff /tmp/source-vorher.txt /tmp/source-nachher.txt
# ERWARTET ohne Gegenmassnahme: ein dritter "## Authoring guidance"-Kopf im Nachher
grep -c '^## Authoring guidance' /tmp/source-nachher.txt # ERWARTET 3, korrekt sind 2# Gegenprobe: bei entity/concept/comparison sind es vorher wie nachher 2
Was ab dem jeweiligen Stand nicht mehr reproduziert:
Ab
Schritt
6.1.0-beta.2
Schritt 6 - die Stack-Kopie ist sauber, eine frische Instanz startet bei zwei Koepfen, und das Migrationsdokument schreibt Vorher-Datei und Diff selbst vor
6.1.0-beta.3
Schritt 4, zweites ERWARTET Exit 1 - der Abbruch nennt jetzt --take-release <pfad> als dritten Weg, und der erledigt den Fall in einem Kommando statt in drei Handgriffen
6.1.0-beta.4
Schritt 4, erstes ERWARTET Exit 1 - version notes antwortet aus dem Feed
Schritt 5 reproduziert unveraendert und ist damit der Reproduktionsabschnitt, den #110 erbt.
Akzeptanzkriterien
Erledigt:
Befund 1, Anleitungsteil:instructions/session-setup.md nennt eine Form, die auf einem
Harness mit Shell-pro-Tool-Call wirkt - nicht nur export einmal pro Sitzung. Erledigt in #108 (504149c): Inline-Form pro Aufruf, mit dem Einzeiler zum Feststellen, welcher Fall
vorliegt.
Befund 2:tools/wikitool version notes liefert auf einer frisch ausgelieferten Instanz
die Notes des installierten Release aus dem Feed - stdout traegt nur den Eintrag, die
Herkunftszeilen gehen nach stderr, --offline verweigert den Netzaufruf, und ein
Dev-Checkout ohne Release-Stamp betritt den Pfad nie. Erledigt mit 0c98080; die
Stamp-Grenze ist mit einem Test abgesichert, der einen werfenden Fetcher verdrahtet und
prueft, dass er nicht aufgerufen wird.
Befund 2: Ein unerreichbarer Feed ist Exit 1 mit Feed-Fehler undrelease_url aus dem
Stamp - nie eine stille Antwort und nie ein Abbruch ohne die Seite, die der Mensch
stattdessen lesen kann. Ein leerer body faellt genauso aus.
Befund 2:instructions/upgrade-instance.md Schritt 2 traegt den Workaround-Absatz nicht
mehr, und INSTALL.md § "Version und Updates" sagt nicht mehr, dass der Befehl auf einer
Instanz nicht antwortet. Das Vorwort der Instruktion nennt gar keine Werkzeugluecke mehr -
es waren zwei, beide sind mit diesem Issue weg.
Befund 3: Eine lokal geaenderte stackeigene Datei laesst sich in einem Kommando auf
die Release-Fassung zuruecksetzen (dist upgrade <source> --take-release <pfad>), ohne
Handkopie und ohne Vorbereitungs-Commit; tools/CONTRACT.mds dist upgrade-Zeile und die
Fehlerkontrakt-Zeile nennen den Weg. Erledigt mit 72d01be.
Befund 3: Ein --take-release-Pfad, der nicht blockiert ist, ist Exit 1 - auch im --dry-run, damit ein Tippfehler vor dem Schreiblauf auffaellt. Es bleibt die einzige
Verweigerung, die einen Dry-Run nicht-null macht; ein blockierter Pfad tut das weiter nicht.
Befund 3: Die Fehlermeldung des Abbruchs nennt alle drei Antworten samt einsetzbarer
Kommandozeile und sagt, dass keine davon der Default ist - so, dass "Default = Release
nehmen" nicht als Erwartung stehenbleibt. Mit einem Test, der genau auf diese vier
Bestandteile prueft.
Befund 4: Das Migrationsdokument verweist auf types/entity.md.template und sagt, in
welcher Sprache der Pointer-Absatz zu schreiben ist. Erledigt mit 0e09cf4; die Sprachregel
ist dabei gegen types/type-spec.md § "Who owns a type-spec" korrigiert worden (Leser, nicht
Eigentuemer - siehe Befund 4b) und wird verlinkt statt kopiert.
Befund 5:tools/wikitool types describe source gibt in der Instanz nathan so viele ## Authoring guidance-Koepfe aus wie bei den anderen drei Typen (zwei), und kein Abschnitt
dort traegt eine englische Ueberschrift ueber deutschem Inhalt. Erledigt mit b5014d9,
geprueft per Vorher/Nachher-Diff.
Befund 5: Dasselbe gilt im Ursprungs-Repo, dessen types/source.md beim Export zu types/source.md.template wird - sonst adoptiert jede neue Instanz den Defekt erneut.
Erledigt mit 0e09cf4, geprueft per Vorher/Nachher-Diff: vier entfernte Zeilen, alle vier
Typen bei zwei Koepfen.
Befund 6: Schritt 5 des Migrationsdokuments verlangt eine festgehaltene Vorher-Ausgabe
und einen Diff; instructions/migrate-corpus.md fuehrt dasselbe Muster fuer kuenftige assisted-Migrationen. Erledigt mit 0e09cf4 (neuer Schritt 3 im Dokument,
§ "Writing the migration document" in migrate-corpus.md).
Befund 7: abgehandelt in #108 - instructions/upgrade-instance.md (manual: true)
existiert, dist upgrades Abschlussbericht nennt sie, INSTALL.md traegt die Schrittfolge
nicht mehr doppelt.
pytest, docs verify, instructions verify ohne neue Befunde fuer alles, was dieses
Issue gebaut hat. Stand 6.1.0-beta.4: 1290 Tests (1276 vor der letzten Sitzung, +8 fuer --take-release, +6 fuer den version notes-Fallback), auch gegen eine leere Maschine nach instructions/dev/testing-conventions.md Schritt 6 identisch gruen; docs verify mit 73
ausgelieferten Dokumenten und 58 Referenzdateien; instructions verify mit 23 Instruktionen
und 7 Skills. CI gruen auf 72d01be (Laeufe 298, 299), 0c98080 (300, 301) und 536093f
(302).
Nicht erledigt, sondern nach #110 ausgelagert - dort in praezisierter Form, mit einer
zusaetzlichen Bedingung zur Bucket-Neuinterpretation, die hier fehlte:
Lauf vom 2026-09-15, Instanz 5.0.0 -> 6.0.0, Harness Claude Code, Sitzung 671c1b9a.
Ergebnis-Commits in der Instanz: 7fe8353 (Handreparatur aus Befund 3), dcc17df (Maschinerie), c8c9f9b (optionale Migration).
Review-Sitzung vom 2026-09-16: hat Transkript, die drei Commits und den Endzustand gegengelesen
und Befund 5, 6, 7 sowie die Ursachenabschnitte unter Befund 1 und 3 ergaenzt. Befund 5 ist dabei
live gegen die Instanz geprueft, nicht aus dem Transkript abgeleitet, und in derselben Sitzung
repariert (b5014d9 in torben/nathan). Aus derselben Sitzung stammt #108, das Befund 7
abgeschlossen hat.
Erste Umsetzungssitzung vom 2026-09-16 (0e09cf4, 6.1.0-beta.2): Befund 4 und 6 umgesetzt,
dabei die Sprachbegruendung aus Befund 4b/5 gegen types/type-spec.md korrigiert und die
Stack-Haelfte von Befund 5 gefunden und behoben. Modelle: Entwurf, Versionsteil und Grenzurteile
auf Opus 5, mechanische Mitte auf Opus 5, Abschluss auf Opus 5 - der in stack-dev Schritt 3
angebotene Wechsel auf Sonnet wurde angeboten und nicht gezogen.
Zweite Umsetzungssitzung vom 2026-09-16 (72d01be = 6.1.0-beta.3, 0c98080 = 6.1.0-beta.4, 536093f = Doku-Nachzug ohne Bump): Befund 3 und 2 entschieden und gebaut, in
zwei Publishes plus einem Nachzug aus der Abschlussphase. Beide Bumps --minor mit --impact medium - neue Faehigkeit, in beide Richtungen ein Drop-in, kein Grenzuebertritt, und
damit bleibt die Bump-Liste des Kandidaten flach, wie sie es bei vier gleichgewichtigen
Upgrade-Pfad-Aenderungen sein soll. In der Abschlussphase zusaetzlich gefunden und mitgenommen:
INSTALL.md behauptete weiter, version check sei der einzige Befehl, der ins Netz geht, und tools/CONTRACT.md § "Future considerations (not implemented)" fuehrte noch den
MCP-Server-Wrapper und dist upgrade selbst - beide seit ihrer Umsetzung falsch und im selben
Dokument weiter oben als existierend beschrieben. Modelle: Entwurf, Versionsteil und Grenzurteile
auf Opus 5, mechanische Mitte (Code, Tests, Bumps, Pruefungen) auf Opus 5, Abschluss auf Opus 5 -
der Wechsel auf Sonnet wurde an beiden vorgesehenen Stellen angeboten und nicht gezogen.
Abschluss am 2026-09-16: Befund 1 nach #110 ausgelagert und dieses Issue geschlossen. Die
Evidenz zu Befund 1 bleibt hier - Trace, Bucket-Messung, Scorer-Ausgabe, Reproduktionsschritt 5 -,
weil sie das Messergebnis dieses Laufs ist; #110 traegt die Entscheidung, die Loesungsachsen und
praezisierte Akzeptanzkriterien. Dasselbe Muster wie bei Befund 7 und #108, an derselben Stelle
begruendet.
Nicht stale, und deshalb bewusst nicht angefasst: docs/ownership-and-templates.md. Seine
Begruendung - eine lokal geaenderte Datei sei "a decision someone takes deliberately instead of
one an upgrade takes for them" - ist genau die Eigenschaft, die --take-release umsetzt, indem
es den Pfad benennen laesst. docs/version-model.md und docs/why-gates-are-code.md sind von
beiden Aenderungen nicht beruehrt.
Erster Kommentar traegt die vollstaendige Telemetrie des Laufs verbatim - 63 Events, aus 21
Bucket-Dateien nach ts zusammengefuehrt. Einzige Aenderung: die drei identischen 69-Pfad-Arrays
in gate.refused/gate.cleared/publish.commit sind elidiert und die Liste steht einmal darunter.
Das ist der Teil dieses Issues, den #110 als Evidenz braucht und der hier bewusst bleibt, statt
kopiert zu werden.
Das rohe Sitzungstranskript ist bewusst nicht angehaengt: die Instanz ist privat, das
Transkript traegt Seitentitel, Infrastrukturthemen und Kontaktdaten aus kb/, und dieses Repo ist
oeffentlich (instructions/private-instance.md: "the cost of a mistaken push is disclosure rather
than inconvenience"). Die Telemetrie unten ist dagegen geprueft frei davon - sie enthaelt nur
Stack-Pfade, Kommandonamen und Exit-Codes. Wer das Transkript fuer die Dev-Instanz-Validierung
braucht, bekommt es auf Anfrage redigiert (ANSI entfernt, Instanzinhalte maskiert).
Nebenbefund aus genau dieser Redaktion, weil er chemenu.telemetry.scrub betrifft: eine
Maskierung per Regex ueber Terminalausgabe greift nicht, solange die ANSI-Sequenzen drinstehen - ssh://git@host:PORT/... und Vorname Nachname <mail@host> waren durch eingestreute
Farbcodes aufgetrennt und ueberlebten den ersten Durchgang unmaskiert. ANSI muss vor der
Maskierung entfernt werden, nicht danach. Nicht in #110 mitgenommen - eigenstaendiger Befund an
einer anderen Stelle, und bis heute kein eigenes Issue.
## Zweck
Vollstaendig getracete Durchfuehrung des dokumentierten Upgrade-Pfads (INSTALL.md § "Eine Instanz
aktualisieren", Weg Tarball) auf einer **echten ausgelieferten Instanz** - nicht im Ursprungs-Repo
und nicht im CI-Replay. Ziel war eine Validierungsgrundlage, gegen die derselbe Lauf auf einer
Dev-Instanz nachgestellt werden kann.
Die Telemetrie des Laufs steht verbatim im ersten Kommentar. Sieben Befunde unten; keiner hat den
Lauf zum Scheitern gebracht, vier davon kosteten jede Instanz Handarbeit, die der dokumentierte
Weg nicht vorsah.
**Ausgangslage:** Instanz aus `dist export`-Tarball, `VERSION` 5.0.0, `kb_version` 5.0.0,
63 Seiten, `language: de`, Harness Claude Code, sauberer Arbeitsbaum, ein Remote.
**Herkunft der Befunde.** 1 bis 4 stammen aus dem Lauf selbst (2026-09-15). 5 bis 7 und die
Ursachenanalyse unter Befund 1 stammen aus der Review-Sitzung vom 2026-09-16, die das
Sitzungstranskript, die drei Ergebnis-Commits und den Endzustand der Instanz gegengelesen hat.
## Abgeschlossen: sechs Befunde erledigt, einer ausgelagert
| Befund | Stand |
|---|---|
| 1 - Session-Id-Fallback zersplittert den Lauf | **ausgelagert nach #110.** Der Anleitungsteil ist mit `504149c` erledigt; was bleibt, ist eine Betreiberentscheidung und kein Bauauftrag |
| 2 - `version notes` antwortete auf keiner Instanz | erledigt, `0c98080`, `6.1.0-beta.4` |
| 3 - `dist upgrade` konnte die Release-Fassung nicht uebernehmen | erledigt, `72d01be`, `6.1.0-beta.3` |
| 4 - Migrationsdokument nennt ein Beispiel, das das Gegenteil zeigt | erledigt, `0e09cf4`, `6.1.0-beta.2` |
| 5 - Rest-Abschnitt `## Authoring guidance` | erledigt in der Instanz (`b5014d9`) und im Stack (`0e09cf4`) |
| 6 - Verifikationsschritt ohne festgehaltene Baseline | erledigt, `0e09cf4` |
| 7 - Upgrade-Pfad ohne agentengerichtete Prozedur | ausgelagert nach #108, dort erledigt: `504149c`, `6.1.0-beta.1` |
**Dieses Issue ist der Laufbericht und wird als solcher nicht weiter bearbeitet.** Alle
Werkzeugaenderungen, die aus ihm Bauauftraege waren, sind gebaut; beide Befunde, die eigene
Arbeitspakete wurden, haben eigene Issues (#108 erledigt, #110 offen). Was hier bleibt und
weiterhin Wert hat, ist die Evidenz: die vollstaendige Trace im ersten Kommentar, die
Lauf-Zusammenfassung mit Zeitstempeln, und der Reproduktionsabschnitt, der pro Schritt sagt, ab
welcher Version er nicht mehr reproduziert.
## Lauf-Zusammenfassung
| Zeit (UTC) | Schritt | Ergebnis |
|---|---|---|
| 19:45:07 | `version check` | Update 5.0.0 -> 6.0.0, Grenzuebertritt gemeldet |
| 19:45:18 | `migrate status` | nichts offen |
| 19:45:18 | `version notes` | **Exit 1** - Befund 2 |
| 19:46:03 | `dist upgrade --dry-run` | 201 unveraendert, 8 neu, **1 lokal geaendert**, 0 entfallen |
| 19:46:31 | `dist upgrade` | **Exit 1** - Befund 3 |
| 19:46:59 | `dist upgrade --dry-run` | Exit 1, Arbeitsbaum unsauber (nach Handreparatur) |
| ~19:47 | `git commit` der Handreparatur | ausserhalb des Werkzeugs noetig - und ausserhalb von Invariante 5, siehe Befund 3 |
| 19:47:17 | `dist upgrade --dry-run` | 202 / 8 / **0** / 0 |
| 19:47:22 | `dist upgrade` | OK, 210 Dateien geschrieben |
| 19:47:28 | `migrate status` | 1 optionales Upgrade, nichts blockiert |
| 19:47:29 | `instructions sync` | 5 Skills publiziert |
| 19:47:44 | `doctor` | alles OK, ein WARN (`session-id`) |
| 19:47:46 | `docs verify` | **Exit 1** - TOC fehlt auf `types/concept.md`, `types/source.md` |
| 19:47:53 | `docs toc --apply` | 2 Dateien repariert - wie in den Release-Notes angekuendigt |
| 19:47:58 | `docs verify` / `instructions verify` / `lint` | gruen |
| 19:48:28 | `publish` | **Exit 42**, Mass-Update-Gate, 69 Dateien, Token `96e2563ff596` |
| 19:58:22 | `publish --confirm` | OK, Commit `dcc17df`, gepusht |
| 20:02-20:06 | optionale Migration `6.0.0-type-guidance-split` | angenommen - Befund 4, 5, 6 |
| 20:06:33 | `migrate done 6.0.0 --pages 0` | Angebot als genommen vermerkt |
| 20:06:43 | `publish` | OK, Commit `c8c9f9b`, 5 Dateien, unter der Gate-Schwelle |
33 `wikitool.call`-Events insgesamt, ein einziger unerwarteter Exit-Code (`types describe source`,
Exit 1 um 20:06:09 - das war SIGPIPE durch ein `| head` des Aufrufers, kein Werkzeugfehler; siehe
Anmerkung unter Befund 1 und die Ursachenverkettung in Befund 6).
**Nicht in der Tabelle, weil nie gelaufen:** `migrate verify --from <commit vor dem Tausch>`,
INSTALL.md Schritt 6, erster Pruefschritt. Siehe Befund 7.
---
## Befund 1: Session-Id-Fallback zersplittert den Lauf - Join-Key tot, Iteration-Budget-Gate faktisch abgeschaltet
**Ausgelagert nach #110.** Die Evidenz bleibt hier stehen, weil sie das Messergebnis dieses Laufs
ist und den Befund ueberhaupt erst tragbar macht - dasselbe Muster wie bei Befund 7. Die
Entscheidung, die Loesungsachsen und die Akzeptanzkriterien stehen in #110.
### Beobachtung
Der Lauf ist **eine** Sitzung. Die Telemetrie kennt ihn als **21 Sitzungen**:
```
21 session_ids: 671c1b9a-92dd-484b-b29a-bf6d29c006ff
3835015 3835056 3835179 3835232 3835285 3835339 3835353 3835371
3835425 3835460 3835471 3835548 3836791 3837235 3837668 3837691
3837722 3837739 3837778 3837804
```
Aufgeschluesselt nach Quelle:
| Quelle | Session-Id-Form | Events |
|---|---|---|
| `claude-code` (Hook) | UUID der echten Sitzung | 1 `session.start`, 5 `prompt.submitted` |
| `wikitool` (Emitter) | **20 verschiedene Parent-PIDs** | 20 `session.start`, 33 `wikitool.call`, 1 `gate.refused`, 1 `gate.cleared`, 2 `publish.commit` |
`WIKITOOL_SESSION_ID` war nicht gesetzt, der Fallback ist die Parent-PID
(`chemenu/session.py`). Der Harness startet **pro Tool-Call eine neue Shell** - also pro Aufruf
eine neue PID und damit eine neue "Sitzung".
### Folge 1: EVALS.md' Join-Key haelt nicht
`EVALS.md` § Architecture: *"Everything joins on `WIKITOOL_SESSION_ID`."* In diesem Lauf joint
**nichts**: die Hook-Events tragen die UUID, die wikitool-Events tragen 20 PIDs. Es gibt keinen
gemeinsamen Schluessel.
Das ist im Scorer direkt sichtbar. `eval score --session 3835548` (die Sitzung mit der
Gate-Verweigerung) meldet:
```
- `skip` **clearance-ended-the-turn** - No wikitool.call between a clearance request (exit 42) and the next prompt.submitted.
- this harness cannot report prompt.submitted - cannot say
```
Der Harness *hat* `prompt.submitted` gemeldet - 5 mal, nur unter der UUID. Ausgerechnet die
L2-Regel, die Gate-Befolgung prueft, kann auf dem Hauptharness nie ein Urteil faellen. Das ist
kein fehlender Hook (#82), sondern ein toter Schluessel: #82 wuerde `tool.pre`/`tool.post`
ergaenzen, die dann **ebenfalls** unter der UUID landen und weiterhin nicht zu den
`wikitool.call`-Events joinen. Die beiden Issues muessen zusammen gedacht werden, sonst
verdrahtet #82 Hooks, deren Events immer noch niemand zuordnen kann.
Ausserdem scort `eval score` jeweils 1 bis 3 Calls statt 33, waehrend die L1-Strukturpruefung in
allen 21 Buckets identisch neu berechnet wird. Ein Lauf ist so nicht bewertbar.
### Folge 2: das Iteration-Budget-Gate erreicht seine Schwelle nie
Gravierender, weil es eine der vier in Code gegossenen Sicherungen ist (`AGENTS.md` § Gates:
60 Calls pro Sitzung, Loop-Breaker bei 3 identischen in Folge).
`tools/.wikitool_session/budget.json`, Stand nach dem Lauf:
```
46 Session-Buckets; Counts absteigend: [9, 3, 3, 2, 2, 2, 2, 2, 1, 1, 1, 1, ...]
```
Der Hoechststand ist **9** - und der stammt aus `wiki-setup-nathan`, einem Bucket, in dem
`WIKITOOL_SESSION_ID` *gesetzt* war. Jeder PID-Bucket kommt ueber **3** nicht hinaus. Bei 33
Calls in einem Lauf hat der Zaehler also nie mehr als 3 von 60 gesehen.
Damit ist das Gate unter diesem Harness nicht "grosszuegig", sondern **strukturell
unerreichbar** - und der Loop-Breaker gleich mit: drei identische Calls in Folge landen in drei
verschiedenen Buckets. Eine Sicherung, die ausdruecklich deshalb in Code sitzt, weil ein Agent
sich an einer Prompt-Regel vorbeireden kann (`docs/why-gates-are-code.md`), ist hier still aus.
`doctor` sagt das Noetige bereits - nur als WARN und ohne die Folge zu nennen:
```
WARN session-id: WIKITOOL_SESSION_ID is not set - budget falls back to the parent PID
fix: See instructions/session-setup.md
```
### Ursache eine Ebene tiefer: `instructions/session-setup.md` war auf diesem Harness wirkungslos
Nachgetragen aus der Review-Sitzung. `doctor` verweist auf `instructions/session-setup.md`, und
dort stand als Schritt, woertlich:
```bash
export WIKITOOL_SESSION_ID="wiki-$(date +%s)"
tools/wikitool sync
```
*"Run this **once per working session**"* - genau das funktioniert auf Claude Code nicht. Der
Harness fuehrt jeden Bash-Tool-Call in einer frisch initialisierten Shell aus; das
Arbeitsverzeichnis wird uebernommen, Shell-State (Umgebungsvariablen, Funktionen) **nicht**. Ein
`export` in Aufruf N ist in Aufruf N+1 verschwunden. Die 20 verschiedenen Parent-PIDs oben sind
dieselbe Tatsache von der anderen Seite gemessen.
Das aendert die Reichweite des Befunds: eine bessere Fallback-Kette repariert den Messwert, aber
die Anleitung, die das Problem eigentlich verhindern soll, war auf dem Hauptharness ein No-op.
**Erledigt mit `504149c` (`6.1.0-beta.1`, aus #108):** `instructions/session-setup.md` nennt jetzt
die Inline-Form pro Aufruf, sagt warum ein `export` nur traegt solange die Shell traegt, und gibt
den Einzeiler an, mit dem sich beantworten laesst, welcher Fall vorliegt. Der Rest dieses Befunds -
Fallback-Kette, Join-Key, WARN-Haerte - ist davon unberuehrt und in #110 offen.
### Nebenbefund zur Trace-Treue
`types describe source` steht mit `exit_code: 1` in der Trace (20:06:09). Der Aufruf war
erfolgreich; der Exit-Code entstand durch SIGPIPE, weil der Aufrufer die Ausgabe durch `| head`
geschickt hat. Der unmittelbar folgende identische Aufruf ohne Pipe steht mit `exit_code: 0` da.
Wer die Trace als Fehlerquelle auswertet, zaehlt hier einen Werkzeugfehler, den es nicht gab.
Dasselbe `| head` ist die Ursache von Befund 5 - siehe Befund 6. In #110 als Nebenbefund
mitgenommen, weil er dieselbe Trace betrifft.
---
## Befund 2: `version notes` scheiterte auf jeder ausgelieferten Instanz - **erledigt** (`0c98080`, `6.1.0-beta.4`)
`kind/defect` - Doku und Realitaet widersprachen sich.
INSTALL.md § "Eine Instanz aktualisieren", Schritt 4, verbatim (Stand des Laufs):
> 4. Bei einer Kompatibilitätsgrenze (`dist upgrade` meldet sie laut) die Release-Notes vor dem
> nächsten Schritt lesen: **Breaking Change:** und **Migration:** im Eintrag von
> `tools/wikitool version notes` sagen, was aufhört zu funktionieren und ob der Korpus
> umgeschrieben werden muss.
Der Aufruf auf der Instanz:
```
$ tools/wikitool version notes
ERROR CHANGES.md has no entry for 5.0.0 - run `wikitool version bump` before
releasing, or write the entry
```
### Ursache
`version notes` liest die lokale `CHANGES.md`. Eine ausgelieferte Instanz bekommt dafuer den
Stub aus `tools/chemenu/dist_templates/CHANGES.md` - 9 Zeilen, Vorwort, **null Versionseintraege**.
`CHANGES.md` steht ausserdem in `chemenu.ownership.is_upgrade_preserved`, wird von `dist upgrade`
also bewusst nie ueberschrieben. Der Stub bleibt der Stub - **dauerhaft**. `version notes` konnte
auf einer Instanz nicht nur damals nicht funktionieren, sondern nie.
Das traf genau den Moment, fuer den der Schritt existiert: den Grenzuebertritt, an dem der
Betreiber wissen muss, was aufhoert zu funktionieren. Der Lauf kam nur weiter, weil die
Release-Notes ueber die Gitea-API gelesen wurden - ein Weg, den INSTALL.md an dieser Stelle nicht
nannte, und den eine Instanz ohne erreichbaren MCP-Server gar nicht hat.
Eine Zwischenstufe hat den Widerspruch *dokumentarisch* beseitigt, den Defekt aber nicht:
INSTALL.md verweist seit `504149c` auf `instructions/upgrade-instance.md`, deren Schritt 2 die
Release-Seite aus `release_url` las und ausdruecklich sagte, dass `version notes` auf einer
Instanz nicht antwortet.
### Umgesetzt: Fallback auf den Release-Feed, laut angekuendigt
Von den beiden erwogenen Wegen der erste. Ausschlaggebend war, dass
`instructions/upgrade-instance.md` Schritt 2 den eigenen Workaround-Absatz schon als temporaer
fuehrte (*"This paragraph stops being necessary the day `version notes` falls back to that
feed"*): die billigere Variante haette ihn dauerhaft stehenlassen.
Die Regel, in drei Faellen:
1. **Der lokale `CHANGES.md`-Eintrag ist da** -> er wird gedruckt. Unveraendert, und der einzige
Fall, den das Ursprungs-Repo und CI je erreichen.
2. **Kein Eintrag und kein Release-Stamp** (ein Dev-Checkout) -> die alte Fehlermeldung,
unveraendert. Das ist der Wachhund gegen einen Netzaufruf im Ursprungs-Repo: nur eine
*ausgelieferte* Instanz geht online, und `release.yml`s
`version notes > /tmp/release-notes.md` kann den neuen Pfad damit nie betreten. Ein Test
verdrahtet einen Fetcher, der beim Aufruf `AssertionError` wirft, und prueft, dass er nicht
aufgerufen wird.
3. **Kein Eintrag, aber ein Release-Stamp** (eine ausgelieferte Instanz) -> der Feed aus
`update_url` wird gefragt, derselbe, den `version check` benutzt (`WIKITOOL_UPDATE_URL` und
`--url` uebersteuern ihn wie dort). `--offline` verweigert den Aufruf und faellt auf die
Fehlermeldung zurueck, die dann `release_url` aus dem Stamp nennt.
**Die Notes gehen nach stdout, die Herkunft nach stderr.** `version notes` existiert, damit der
Release-Workflow kein Markdown in der Shell parsen muss (`release.yml`:
`version notes > /tmp/release-notes.md`), also darf stdout nichts als den Eintrag tragen.
**Antwortet der Feed eine andere Version als die gefragte**, wird das in der stderr-Kopfzeile
benannt und die Notes werden trotzdem gedruckt. Das ist nicht der Randfall, sondern der
Hauptfall: in Schritt 2 des Upgrades steht `VERSION` noch auf der *alten* Version, waehrend die
gesuchten Notes die der neuen sind. Der Feed kennt nur `/releases/latest` - `update_url` ist die
einzige URL, die der Stamp traegt, und eine `/releases/tags/<tag>`-URL daraus zusammenzusetzen
waere geraten statt gelesen (Invariante 7).
**Nicht erreichbarer Feed:** Exit 1, mit dem Feed-Fehler *und* `release_url` aus dem Stamp. Ein
leerer `body` faellt genauso aus - eine leere Antwort darf nicht als "dieses Release hat nichts
zu melden" durchgehen.
Nachgezogen: `instructions/upgrade-instance.md` Schritt 2 (Workaround-Absatz weg, dafuer die
beiden Dinge, die man vor dem Lesen der Ausgabe wissen muss), das Vorwort derselben Datei (es
nennt keine Werkzeugluecke mehr - es waren zwei, jetzt sind es null), INSTALL.md § "Version und
Updates" an zwei Stellen (die Notes-Quelle, und die inzwischen falsche Behauptung, `version
check` sei der einzige Befehl, der ins Netz geht), `tools/CONTRACT.md` in beiden Tabellen sowie
die Modul-Docstrings von `version_cmd.py` und `version.fetch_latest`, die dieselbe
Ein-Netzaufruf-Behauptung trugen.
---
## Befund 3: `dist upgrade` kannte keinen Weg, die Release-Fassung einer lokal geaenderten Datei zu uebernehmen - **erledigt** (`72d01be`, `6.1.0-beta.3`)
`kind/defect` / fehlende Faehigkeit.
### Beobachtung
Der Dry-Run meldete genau eine lokal geaenderte Datei:
```
201 unchanged, 8 new, 1 locally changed, 0 removed from the release.
Locally modified (1):
- kb/CONTRACT.md
```
Der Unterschied war **reine Whitespace-Formatierung** einer Markdown-Tabelle (Spaltenauffuellung,
vermutlich ein Format-on-Save), inhaltlich identisch. `kb/CONTRACT.md` ist dabei stackeigen:
`instructions/private-instance.md` fuehrt `<stage>/CONTRACT.md` ausdruecklich als Maschinerie, an
der *"an instance never edits it"*.
Beide damals angebotenen Wege waren hier falsch:
- `--keep-local` **behielt** die Drift. Der neue Stamp schreibt trotzdem die Release-Digest - die
Datei divergierte also bei **jedem** kuenftigen `dist upgrade` erneut und wurde jedes Mal wieder
gemeldet. Fuer eine Datei, die der Instanz gar nicht gehoert, der dauerhaft falsche Zustand.
- "reconcile by hand" war der richtige Weg, hatte aber kein Werkzeug: Handkopie aus dem
entpackten Tarball, dann ein Commit nur zur Herstellung der Clean-Tree-Vorbedingung des
naechsten Kommandos. Drei Schritte, zwei davon ausserhalb des Werkzeugs. Die
Sauberkeits-Vorbedingung und die Handreparatur standen sich dabei gegenseitig im Weg: die
Reparatur macht den Baum unsauber, den das Kommando sauber verlangt.
### Der Preis war eine Invariantenverletzung
Das schaerfste Argument fuer eine Werkzeugloesung: der Commit `7fe8353` ist ein **rohes `git add`
+ `git commit`**. `AGENTS.md` Invariante 5 sagt *"Never call raw `git commit`/`git push`. Publish
through `tools/wikitool publish`"* und kennt keine Ausnahme fuer "ist ja nur eine Vorbedingung".
Richtig waere `tools/wikitool publish --no-push` gewesen.
Bemerkenswert ist weniger der Fehlgriff als die Richtung: eine fehlende Werkzeugfaehigkeit hat den
Lauf an einer in Code gegossenen Regel vorbeigefuehrt, und nichts hat es gemeldet - kein Gate, kein
Check, kein Scorer.
### Erwartungshaltung aus dem Transkript
Der Agent kuendigte um 19:46:28, *vor* dem Lauf ohne Flag, woertlich an: *"I'll let the upgrade
take the release's version rather than pinning the local formatting"* - und rief `dist upgrade`
dann ohne Flag auf. Das Mentalmodell war also "Default = Release-Fassung nehmen"; der Default ist
Abbruch. Die Fehlermeldung korrigierte das nicht: sie nannte `--keep-local` und "reconcile by
hand", sagte aber nicht, dass es zu `--keep-local` kein Gegenstueck gibt. Es fehlte damit nicht
nur ein Flag, sondern auch der Satz, der die falsche Erwartung abfaengt.
### Umgesetzt: `--take-release <pfad>`, wiederholbar
`--take-release` nimmt einen Pfad und ist wiederholbar (wie `search --field`), statt ein pauschales
Gegenstueck zu `--keep-local` zu sein. Der Grund ist die Asymmetrie der beiden Antworten:
`--keep-local` laesst alles stehen und verliert nichts, `--take-release` verwirft eine lokale
Aenderung. Eine Verwerfung benennt ihr Ziel - dasselbe Muster, das `issue-tracking.md` Schritt 1
fuer destruktive Schritte verlangt -, und der gemischte Fall (zwei geaenderte Dateien, eine davon
zurueckzusetzen) ist damit ueberhaupt erst loesbar.
- Ein Pfad, der gar nicht in der blockierten Liste steht, ist **Exit 1** mit der Liste dessen, was
dort steht - und zwar **auch im `--dry-run`**: das ist ein Fehler im *Argument*, nicht ein
Zustand des Baums, und ein still ignorierter Tippfehler haette ein erfolgreiches Upgrade
gemeldet und die Aenderung behalten, die verworfen werden sollte. Es ist die einzige
Verweigerung, die einen Dry-Run nicht-null macht; ein blockierter Pfad tut das weiter nicht.
- Eine lokal *geloeschte* Datei ist ein gueltiges Ziel: die Release-Fassung wird wieder angelegt.
- `--take-release` und `--keep-local` zusammen sind erlaubt und komponieren. **Ohne**
`--keep-local` bricht ein blockierter Pfad, zu dem nichts gesagt wurde, weiter ab.
- Nach `--take-release` stimmt die Datei wieder mit der Stamp-Digest ueberein, die Drift ist also
*weg* und nicht nur ueberschrieben - genau der Unterschied zu `--keep-local`.
- Der Dry-Run markiert jeden benannten Pfad als einen, den er aus dem Release ueberschreiben
wuerde.
**Die Fehlermeldung des Abbruchs nennt alle drei Antworten samt fertiger Kommandozeile**, im Muster
des Mass-Update-Gates, das seine `--confirm`-Zeile ebenso zum Einsetzen ausdruckt:
```
ERROR 1 locally changed file(s) (listed above) would be silently overwritten.
Nothing was written, and none of these three is the default:
- take the release's version and discard the local change:
dist upgrade <source> --take-release kb/CONTRACT.md
- keep every local change and upgrade around them (it is reported again
on every future upgrade):
dist upgrade <source> --keep-local
- reconcile them by hand first, then re-run.
```
Nachgezogen: `instructions/upgrade-instance.md` Schritt 6 traegt statt der
Drei-Schritt-Handreparatur die Entscheidung pro Pfad und den Dry-Run, mit dem man sie vorher
sieht; Schritt 7 nimmt die Flags mit. `tools/CONTRACT.md` in beiden Tabellen. Bewusst **nicht**
geaendert: die Dirty-Tree-Vorbedingung - mit `--take-release` entfaellt die Handreparatur und
damit der unsaubere Baum, den sie erst erzeugte.
---
## Befund 4: `6.0.0-type-guidance-split` nennt ein Beispiel, das in der Instanz das Gegenteil zeigt - **erledigt** (`0e09cf4`)
`kind/defect`, Dokumentation. Betraf das mitgelieferte Migrationsdokument, nicht den Code.
### 4a - das benannte Beispiel war in der Instanz die unmigrierte Datei
`instructions/migrations/6.0.0-type-guidance-split.md`, Schritt 4, verbatim (Stand des Laufs):
> 4. **Delete the now-duplicated prose from the type-spec**, keeping the H1, a short pointer to the
> guidance file (`types/entity.md`'s own current text is the worked example), `## Frontmatter`
> and `## Template`.
Im Ursprungs-Repo stimmt das: dort ist `types/entity.md` die stackeigene, bereits migrierte Datei.
In einer **ausgelieferten Instanz** ist `types/entity.md` die beim Setup adoptierte Kopie - also
genau die Datei, die noch die *alte*, zu entfernende Prosa traegt. Wer dem Satz woertlich folgte,
schrieb den Vorher-Zustand ab.
Das gesuchte Beispiel liegt in der Instanz unter **`types/entity.md.template`** - dort steht der
Nachher-Zustand (Pointer-Absatz, `guidance:`-Feld in der Frontmatter), weil `dist upgrade` die
Templates verbatim mitliefert.
**Verschaerfung aus der Review-Sitzung, gegen den Baum bestaetigt:** im Ursprungs-Repo existiert
ueberhaupt keine `types/*.md.template`-Datei - `dist export` re-keyt `types/<name>.md` erst beim
Export zur `.template` (`dist_cmd._owned_type_stem`, `_plan_types`). Der Satz konnte in einer
Instanz also nicht bloss unguenstig sein, er konnte dort strukturell nie stimmen.
**Umgesetzt:** der Schritt (jetzt Schritt 5, nach dem neu eingezogenen Vorher-Schritt aus Befund 6)
nennt `types/<name>.md.template` als Beispiel und sagt den Grund dazu - `types/<name>.md` ist die
adoptierte Kopie und damit die Datei, die gerade geaendert wird, die `.template` daneben traegt den
Nachher-Zustand. Mit dem Zusatz, die `.template` fuer die *Form* zu lesen und nicht wholesale zu
kopieren: ihre `## Frontmatter` und ihr `## Template` sind die Stack-Defaults, nicht die der
Instanz.
### 4b - die Sprachfrage blieb offen, direkt nachdem 6.0.0 sie verschoben hat
Das Dokument sagte nicht, in welcher Sprache der neue Pointer-Absatz zu schreiben ist. Fuer eine
Instanz mit `language: de` war das nicht ableitbar, denn 6.0.0 hatte diese Grenze gerade erst neu
gezogen (Bump "Control-Plane-Sprache universell", `docs/language-boundaries.md`).
**Umgesetzt, und dabei gegen `types/type-spec.md` § "Who owns a type-spec" korrigiert.** Die im
urspruenglichen Befund vorgeschlagene Begruendung *"ein Abschnitt, den die Instanz behaelt, ist
ihrer, also traegt er die KB-Sprache"* haelt gegen den Baum **nicht**: die Sprachachse ist der
Leser, nicht der Eigentuemer. Anleitungsprosa in einem Type-Spec ist Control Plane und damit
englisch, unabhaengig davon, wem die Datei gehoert - so steht es in der Tabelle in
`types/type-spec.md`, so steht es im Docstring von `dist_cmd.instance_owned_type_stems`
(*"Ownership, not language"*), und so steht es in `AGENTS.md` § File naming. Das Ergebnis von
Befund 5 war trotzdem richtig, nur die Begruendung nicht: der uebrige Bullet war redundant und
gehoerte weg, nicht uebersetzt.
Das Dokument sagt jetzt entsprechend:
| Teil des Type-Specs | Sprache | Begruendung |
|---|---|---|
| H1, Pointer-Absatz | Englisch | Anleitungsprosa an einen Agenten = Control Plane, egal wem die Datei gehoert |
| **behaltene lokale Prosa** | Englisch | ebenfalls Anleitungsprosa - eine vor dieser Regel in der KB-Sprache geschriebene Notiz wird **uebersetzt, nicht umbenannt** |
| `## Frontmatter`-Tabelle | unveraendert lassen | die Migration fasst Frontmatter ausdruecklich nicht an |
| `## Template`-Block + Nachsatz | unveraendert lassen (hier: deutsch) | Seitentext in der KB-Sprache |
Die Tabelle steht im Dokument **nicht** als Kopie - der Schritt sagt die Antwort in zwei Saetzen
und verlinkt `types/type-spec.md` § "Who owns a type-spec" fuer die vollstaendige Aufteilung
(Invariante 8). Der Satz *"eine englische Ueberschrift ueber einem Koerper in einer anderen
Sprache ist die halbfertige Fassung dieses Schritts"* benennt genau den Zustand, den Befund 5
produziert hat.
Dazu neu, weil es der eigentliche Mechanismus hinter Befund 5 ist: **eine behaltene Notiz darf
nicht `## Authoring guidance` heissen.** `types describe` setzt diesen Kopf selbst und inlined
darunter die Guidance-Datei, die ihren eigenen gleichnamigen Abschnitt mitbringt - ein dritter aus
dem Type-Spec-Koerper ist die Doppelung.
### 4c - kleinere Beobachtung zur Schrittfolge
Schritt 6 (`migrate done 6.0.0 --pages 0`) tut genau, was es verspricht, und die Ausgabe ist
unmissverstaendlich:
```
OK Recorded the optional 6.0.0-type-guidance-split. Content stays at 5.0.0 - an
offer changes a file you own, not the shape of your content.
```
Danach bleibt `doctor` bei `kb-version: 5.0.0 (nothing outstanding up to 6.0.0)`. Das ist korrekt
und dokumentiert, sieht aber auf den ersten Blick nach einem haengengebliebenen Upgrade aus. Keine
Aenderung noetig - hier nur vermerkt, weil eine Dev-Instanz-Validierung sonst darueber stolpert.
---
## Befund 5: Rest-Abschnitt `## Authoring guidance` - **repariert in der Instanz (`b5014d9`) und im Stack (`0e09cf4`)**
`kind/defect`. Gefunden in der Review-Sitzung, live gegengeprueft und dort auch behoben. Steht
hier, weil er die Wirkung von Befund 4b und Befund 6 belegt.
### 5a - die Instanz (`b5014d9`)
Bei `types/source.md` wurde der Abschnitt `## Autorenanweisungen` nicht geloescht, sondern zu
`## Authoring guidance` **umbenannt**, mit einem verbliebenen deutschen Bullet:
```markdown
## Authoring guidance
- Der Titel beginnt mit "Source - ", gefolgt vom Namen der Quelle
```
Zwei Fehler in drei Zeilen:
1. **Ein dritter Kopf desselben Namens in der komponierten Ausgabe.** Zwei sind der
Normalzustand und kein Befund: `types describe` setzt selbst einen `## Authoring
guidance`-Kopf und inlined darunter die Guidance-Datei, die ihren eigenen gleichnamigen
Abschnitt mitbringt - so sieht es bei allen vier Typen aus. `source` hatte danach einen
dritten aus dem Type-Spec selbst.
2. **Englische Ueberschrift ueber deutschem Inhalt** in einer instanzeigenen Datei. Die Grenze,
an der das falsch ist, ist der *Leser*, nicht der Eigentuemer - siehe die Korrektur unter
Befund 4b: Anleitungsprosa bleibt englisch, auch in einer Datei, die der Instanz gehoert.
Richtig war hier trotzdem das Entfernen, weil der Bullet redundant zum
`title_prefix: "Source - "` in der Frontmatter war, das `wikitool new source` ohnehin erzwingt.
`entity`, `concept` und `comparison` waren sauber - betroffen war nur `source`, der einzige der
vier Typen, dessen Prosa umfangreich genug war, dass ein Bullet uebrigblieb, den die
Stack-Guidance nicht abdeckt.
**Behoben mit `b5014d9`:** Abschnitt ersatzlos entfernt, geprueft mit dem Muster aus Befund 6 -
`types describe source` vor und nach der Aenderung in je eine Datei, dann `diff`. Der Diff
zeigt genau die vier entfernten Zeilen und sonst nichts; alle vier Typen komponieren jetzt mit
zwei Koepfen.
### 5b - derselbe Defekt im Stack selbst (`0e09cf4`)
**Nachgetragen 2026-09-16, gefunden beim Nachmessen im Ursprungs-Repo.** Das Kriterium unten war
gegen die Instanz `nathan` abgehakt, gegen den Stack nie - und dort stand derselbe Rest-Abschnitt:
```
$ grep -c '^## Authoring guidance' <types describe $t>
entity: 2 concept: 2 source: 3 comparison: 2
```
`types/source.md` im Ursprungs-Repo trug bis `0e09cf4` einen `## Authoring guidance`-Abschnitt mit
demselben einen Bullet (hier englisch, weil `c64479f` ihn uebersetzt hatte; `d49513b` hat dann den
Rest der Prosa ausgelagert und ihn stehenlassen). Das ist kein Instanzschaden, sondern ein
Auslieferungsdefekt: die Datei wird beim Export zu `types/source.md.template`, **jede neu
aufgesetzte Instanz haette ihn mit adoptiert** - und ihn dann bei der naechsten
`guidance`-Migration genau so wiedergefunden wie `nathan`.
Entfernt, geprueft mit demselben Vorher/Nachher-Diff: genau vier Zeilen weg, sonst nichts, alle
vier Typen bei zwei Koepfen. Inhaltlich verloren geht nichts - `title_prefix` steht in der
Frontmatter, in der Feldtabelle von `types/type-spec.md` und im Nachsatz zum `## Template`-Block
von `types/source.md`; ausserdem verbietet `types/type-spec.md` § Writing Shape das Wiederholen
einer Schema-Regel im Fliesstext ausdruecklich.
**Nebenbeobachtung, kein Befund:** dass `types describe` einen `## Authoring guidance`-Kopf setzt
und unmittelbar darunter eine Guidance-Datei mit eigenem H1 *und* eigenem gleichnamigem Abschnitt
inlined, ist eine kosmetische Doppelung im Stack selbst - gleich fuer alle vier Typen, ohne
Auswirkung auf Inhalt oder Werkzeuge.
---
## Befund 6: Schritt 5 des Migrationsdokuments war als Verifikation unfalsifizierbar - **erledigt** (`0e09cf4`)
`kind/defect`, Dokumentation. Direkte Ursache von Befund 5.
`instructions/migrations/6.0.0-type-guidance-split.md`, Schritt 5, verlangte:
> The output must read the same as it did before this migration [...] the structure (frontmatter
> fields, template block) must be byte-identical.
Kein Schritt davor hielt das Vorher fest. Eine Pruefung gegen einen Zustand, den niemand
aufgeschrieben hat, ist keine Pruefung - sie faellt auf das Gedaechtnis des Ausfuehrenden zurueck,
und bei einer Ausgabe von ueber 150 Zeilen je Typ ist das keins.
Der Lauf hat entsprechend geprueft: `types describe` durch `| head -250` und `| tail -80`, also in
Ausschnitten, mit dem Urteil *"All four compose correctly, structurally identical to before"*. Der
Rest-Abschnitt aus Befund 5 liegt in der Mitte der `source`-Ausgabe und war in keinem der beiden
Ausschnitte. Dasselbe `| head` erzeugte zugleich den SIGPIPE-Exit-1, den der Nebenbefund unter
Befund 1 als Trace-Rauschen fuehrt: ein Verhalten, zwei Symptome.
**Umgesetzt an beiden Stellen:**
1. **Das Dokument** hat einen eigenen Schritt 3 bekommen - `types describe <name>` vollstaendig in
eine Datei, *vor* der Aenderung, mit dem Satz, dass ein `head`/`tail` genau die Mitte
verschwinden laesst. Schritt 6 diffed dagegen und nennt zusaetzlich die Ein-Zahl-Probe
`grep -c '^## Authoring guidance'`: zwei ist richtig, drei heisst Rest-Abschnitt im Type-Spec.
Die alten Schritte 3-6 sind auf 4-7 gerueckt, die Querverweise mit.
2. **`instructions/migrate-corpus.md` § "Writing the migration document"** traegt das Muster
generisch: ein Verifikationsschritt nennt seine eigene Baseline, und zwar in einem frueheren
Schritt. `migrate verify` traegt seine im letzten Commit, ob jemand daran denkt oder nicht -
eine Migration an der Maschinerie statt an `kb/` hat gar keine, und genau dort entsteht die
Behauptung, die sich nicht widerlegen laesst.
Nicht angefasst: `instructions/upgrade-instance.md` Schritt 12, der seit `504149c` dasselbe in
Richtung des *Ausfuehrenden* sagt. Zwei Adressaten, zwei Regeln - der Autor eines
Migrationsdokuments und der Betreiber, der eines abarbeitet -, also keine zweite Kopie im Sinne
von Invariante 8.
---
## Befund 7: der Upgrade-Pfad hatte keine agentengerichtete Prozedur - **ausgelagert nach #108, dort erledigt**
`kind/defect`, Prozess. Umgesetzt mit `504149c` (`6.1.0-beta.1`); hier bleibt die Evidenz aus dem
Lauf stehen, weil sie die Begruendung der Loesung traegt - dasselbe Muster, mit dem Befund 1 jetzt
nach #110 gegangen ist.
Die Schrittfolge stand nur in INSTALL.md - einem Dokument fuer Menschen (`AGENTS.md` § File
naming: README-foermige Wurzeldateien werden *"never by an agent as instruction"* geladen).
Ausgefuehrt wird sie von einem Agenten. Der erste Tool-Call des Laufs listete `instructions/`
mit - der Agent suchte also zuerst eine Instruktion, fand keine, oeffnete
`instructions/private-instance.md` (der falsche Weg: Clone mit gemeinsamer History statt
Tarball-Instanz), verwarf sie und griff auf INSTALL.md zurueck.
Was im selben Lauf daraus folgte:
- **`migrate verify --from <commit vor dem Tausch>` wurde nie ausgefuehrt**, obwohl es in
INSTALL.md Schritt 6 der erste Pruefschritt war. Die Lauf-Tabelle oben belegt es lueckenlos.
Der Grund ist praezise benennbar: der Lauf folgte nicht INSTALL.md, sondern dem
Abschlussbericht von `dist upgrade` - und in dessen Liste kam `migrate verify` nicht vor.
- **Die Agent-Session wurde nie neu gestartet.** Beide Reihenfolgen verlangten das, die eine am
Ende von Schritt 6, die andere am Ende des Abschlussberichts. Die optionale Migration lief
anschliessend unter dem 5.0.0-Kontrollplan, obwohl `AGENTS.md` im selben Commit +44/-3 bekommen
hatte - und genau dort fielen Befund 4b (Sprachgrenze, neu in 6.0.0) und Befund 5 an.
- **Drei Reihenfolgen waren im Umlauf**: INSTALL.md Schritt 6, der Abschlussbericht, und die
tatsaechlich gelaufene. Dass der Lauf der zweiten folgte, machte die erste zu toter Doku -
genau der Zustand, den Invariante 8 verbietet.
Umgesetzt: `instructions/upgrade-instance.md` (`manual: true`) ist die eine Fassung, dreizehn
Schritte, mit dem Sitzungsneustart zwischen Maschinerie-Publish und Migrationskette statt am Ende
und `migrate status` als Wiedereinstiegspunkt. Der Abschlussbericht von `dist upgrade` nennt jetzt
die Datei und das Wiedereinstiegskommando statt einer eigenen Liste, INSTALL.md nur noch die
Entscheidung davor. Details und Abgrenzung: #108.
---
## Was der Lauf bestaetigt hat
Ausdruecklich keine Befunde - das hat gehalten:
- **Der angekuendigte Grenzuebertritt kam exakt wie beschrieben.** Die Release-Notes sagten
voraus, dass `docs verify` nach dem Update auf adoptierten Page-Type-Specs ohne TOC-Region
faellt, und nannten `docs toc --apply` als Reparatur. Genau das trat ein
(`types/concept.md`, `types/source.md`), und genau das reparierte es - ein Werkzeuglauf, keine
Inhaltsmigration. **Nicht identisch mit #106**: dort ging es um `kb/CONVENTIONS.md.template` auf
einer *frischen* Instanz; hier um adoptierte `types/*.md` auf dem *Upgrade*-Pfad. Der in #106
umgesetzte Fix (Templates in `toc.target_files()`) deckt diesen Fall nicht ab, weil die
betroffenen Dateien bereits im Dateisatz stehen - ihnen fehlte die Region nur, weil sie vor der
Scope-Erweiterung adoptiert wurden.
- **`dist upgrade` klassifiziert korrekt** - 202 unveraendert / 8 neu / 0 lokal geaendert /
0 entfallen, und die 8 neuen Dateien waren exakt die des Bumps (4 `*.guidance.md`,
`types/type-guidance.md(.schema.yaml)`, `docs/language-boundaries.md`, das Migrationsdokument).
- **`migrate status` hat richtig nicht blockiert.** "Migration: none required" aus den
Release-Notes und "Nothing outstanding" aus dem Werkzeug stimmten ueberein.
- **Das Mass-Update-Gate hat sauber gearbeitet**: Exit 42 bei 69 Dateien, Token an Dateiliste *und*
Inhalt gebunden, Freigabe mit demselben Token akzeptiert, `gate.refused`/`gate.cleared` beide in
der Trace. Als Datenpunkt fuer #53: 69 Dateien bei Schwelle 10, und der zweite Publish desselben
Laufs lag mit 5 Dateien darunter.
- ~~`types describe` komponiert nach der Migration unveraendert.~~ **Korrigiert durch Befund 5:**
fuer `entity`, `concept` und `comparison` stimmte es; fuer `source` nicht - dort stand bis
`b5014d9` (Instanz) bzw. `0e09cf4` (Stack) ein dritter `## Authoring guidance`-Kopf in der
komponierten Ausgabe. Was fuer alle vier haelt: Frontmatter-Felder und `## Template`-Block sind
byte-gleich, nur die Herkunft der Anleitungsprosa hat gewechselt.
## Reproduktion auf einer Dev-Instanz
Die Reproduktion gilt **gegen einen 6.0.0-Tarball** - also gegen die Maschinerie, in der die
Befunde aufgetreten sind. Wie ein Lauf gegen das Ergebnis dieses Issues stattdessen ausgeht, steht
unter dem Block.
```bash
# 1. Instanz auf 5.0.0 herstellen (aus einem 5.0.0-Checkout)
tools/wikitool dist export /tmp/inst-5.0.0
cd /tmp/inst-5.0.0 && git init -q -b main
git config user.name Dev && git config user.email dev@example.invalid
# Templates adoptieren wie instructions/setup-instance.md Schritt 5/6
python3 -m venv tools/.venv && tools/.venv/bin/pip install -q -r tools/requirements.txt
tools/wikitool instructions sync && tools/wikitool index rebuild
git add -A && git commit -qm baseline
# 2. Die Drift dieses Laufs nachstellen (Befund 3)
# kb/CONTRACT.md durch einen Markdown-Formatter schicken - Whitespace-only,
# inhaltlich identisch - und committen.
# 3. Nicht setzen, um Befund 1 zu reproduzieren: WIKITOOL_SESSION_ID
# Jeden wikitool-Aufruf aus einer eigenen Shell absetzen (wie ein Agent-Harness es tut).
# 4. Upgrade fahren
tools/wikitool version check # meldet Grenzuebertritt
tools/wikitool version notes # ERWARTET Exit 1 -> Befund 2
tools/wikitool dist upgrade <6.0.0-tarball> --dry-run
tools/wikitool dist upgrade <6.0.0-tarball> # ERWARTET Exit 1 -> Befund 3
# -> Handreparatur + Commit, dann erneut
tools/wikitool migrate status && tools/wikitool instructions sync
tools/wikitool doctor
tools/wikitool docs verify # ERWARTET Exit 1 auf types/*.md
tools/wikitool docs toc --apply && tools/wikitool docs verify
tools/wikitool instructions verify && tools/wikitool lint
# 5. Befund 1 messen - der einzige Teil, der weiter reproduziert (siehe #110)
python3 -c "import json;d=json.load(open('tools/.wikitool_session/budget.json'));\
print(len(d),'Buckets; max count:',max(e.get('count',0) for e in d.values()))"
tools/wikitool eval sessions # ERWARTET: viele Buckets, je 1-3 Events
# 6. Befund 5/6 reproduzieren: die optionale Migration fahren und vorher/nachher diffen
tools/wikitool types describe source > /tmp/source-vorher.txt
# ... Schritte 3 und 4 des Migrationsdokuments ausfuehren ...
tools/wikitool types describe source > /tmp/source-nachher.txt
diff /tmp/source-vorher.txt /tmp/source-nachher.txt
# ERWARTET ohne Gegenmassnahme: ein dritter "## Authoring guidance"-Kopf im Nachher
grep -c '^## Authoring guidance' /tmp/source-nachher.txt # ERWARTET 3, korrekt sind 2
# Gegenprobe: bei entity/concept/comparison sind es vorher wie nachher 2
```
**Was ab dem jeweiligen Stand nicht mehr reproduziert:**
| Ab | Schritt |
|---|---|
| `6.1.0-beta.2` | Schritt 6 - die Stack-Kopie ist sauber, eine frische Instanz startet bei zwei Koepfen, und das Migrationsdokument schreibt Vorher-Datei und Diff selbst vor |
| `6.1.0-beta.3` | Schritt 4, zweites `ERWARTET Exit 1` - der Abbruch nennt jetzt `--take-release <pfad>` als dritten Weg, und der erledigt den Fall in einem Kommando statt in drei Handgriffen |
| `6.1.0-beta.4` | Schritt 4, erstes `ERWARTET Exit 1` - `version notes` antwortet aus dem Feed |
**Schritt 5 reproduziert unveraendert** und ist damit der Reproduktionsabschnitt, den #110 erbt.
## Akzeptanzkriterien
Erledigt:
- [x] **Befund 1, Anleitungsteil:** `instructions/session-setup.md` nennt eine Form, die auf einem
Harness mit Shell-pro-Tool-Call wirkt - nicht nur `export` einmal pro Sitzung. Erledigt in
#108 (`504149c`): Inline-Form pro Aufruf, mit dem Einzeiler zum Feststellen, welcher Fall
vorliegt.
- [x] **Befund 2:** `tools/wikitool version notes` liefert auf einer frisch ausgelieferten Instanz
die Notes des installierten Release aus dem Feed - stdout traegt nur den Eintrag, die
Herkunftszeilen gehen nach stderr, `--offline` verweigert den Netzaufruf, und ein
Dev-Checkout ohne Release-Stamp betritt den Pfad nie. Erledigt mit `0c98080`; die
Stamp-Grenze ist mit einem Test abgesichert, der einen werfenden Fetcher verdrahtet und
prueft, dass er nicht aufgerufen wird.
- [x] **Befund 2:** Ein unerreichbarer Feed ist Exit 1 mit Feed-Fehler *und* `release_url` aus dem
Stamp - nie eine stille Antwort und nie ein Abbruch ohne die Seite, die der Mensch
stattdessen lesen kann. Ein leerer `body` faellt genauso aus.
- [x] **Befund 2:** `instructions/upgrade-instance.md` Schritt 2 traegt den Workaround-Absatz nicht
mehr, und INSTALL.md § "Version und Updates" sagt nicht mehr, dass der Befehl auf einer
Instanz nicht antwortet. Das Vorwort der Instruktion nennt gar keine Werkzeugluecke mehr -
es waren zwei, beide sind mit diesem Issue weg.
- [x] **Befund 3:** Eine lokal geaenderte stackeigene Datei laesst sich in **einem** Kommando auf
die Release-Fassung zuruecksetzen (`dist upgrade <source> --take-release <pfad>`), ohne
Handkopie und ohne Vorbereitungs-Commit; `tools/CONTRACT.md`s `dist upgrade`-Zeile und die
Fehlerkontrakt-Zeile nennen den Weg. Erledigt mit `72d01be`.
- [x] **Befund 3:** Ein `--take-release`-Pfad, der nicht blockiert ist, ist Exit 1 - auch im
`--dry-run`, damit ein Tippfehler vor dem Schreiblauf auffaellt. Es bleibt die einzige
Verweigerung, die einen Dry-Run nicht-null macht; ein blockierter Pfad tut das weiter nicht.
- [x] **Befund 3:** Die Fehlermeldung des Abbruchs nennt alle drei Antworten samt einsetzbarer
Kommandozeile und sagt, dass keine davon der Default ist - so, dass "Default = Release
nehmen" nicht als Erwartung stehenbleibt. Mit einem Test, der genau auf diese vier
Bestandteile prueft.
- [x] **Befund 4:** Das Migrationsdokument verweist auf `types/entity.md.template` und sagt, in
welcher Sprache der Pointer-Absatz zu schreiben ist. Erledigt mit `0e09cf4`; die Sprachregel
ist dabei gegen `types/type-spec.md` § "Who owns a type-spec" korrigiert worden (Leser, nicht
Eigentuemer - siehe Befund 4b) und wird verlinkt statt kopiert.
- [x] **Befund 5:** `tools/wikitool types describe source` gibt in der Instanz `nathan` so viele
`## Authoring guidance`-Koepfe aus wie bei den anderen drei Typen (zwei), und kein Abschnitt
dort traegt eine englische Ueberschrift ueber deutschem Inhalt. Erledigt mit `b5014d9`,
geprueft per Vorher/Nachher-Diff.
- [x] **Befund 5:** Dasselbe gilt im **Ursprungs-Repo**, dessen `types/source.md` beim Export zu
`types/source.md.template` wird - sonst adoptiert jede neue Instanz den Defekt erneut.
Erledigt mit `0e09cf4`, geprueft per Vorher/Nachher-Diff: vier entfernte Zeilen, alle vier
Typen bei zwei Koepfen.
- [x] **Befund 6:** Schritt 5 des Migrationsdokuments verlangt eine festgehaltene Vorher-Ausgabe
und einen Diff; `instructions/migrate-corpus.md` fuehrt dasselbe Muster fuer kuenftige
`assisted`-Migrationen. Erledigt mit `0e09cf4` (neuer Schritt 3 im Dokument,
§ "Writing the migration document" in `migrate-corpus.md`).
- [x] **Befund 7:** abgehandelt in #108 - `instructions/upgrade-instance.md` (`manual: true`)
existiert, `dist upgrade`s Abschlussbericht nennt sie, INSTALL.md traegt die Schrittfolge
nicht mehr doppelt.
- [x] `pytest`, `docs verify`, `instructions verify` ohne neue Befunde fuer alles, was dieses
Issue gebaut hat. Stand `6.1.0-beta.4`: 1290 Tests (1276 vor der letzten Sitzung, +8 fuer
`--take-release`, +6 fuer den `version notes`-Fallback), auch gegen eine leere Maschine nach
`instructions/dev/testing-conventions.md` Schritt 6 identisch gruen; `docs verify` mit 73
ausgelieferten Dokumenten und 58 Referenzdateien; `instructions verify` mit 23 Instruktionen
und 7 Skills. CI gruen auf `72d01be` (Laeufe 298, 299), `0c98080` (300, 301) und `536093f`
(302).
Nicht erledigt, sondern **nach #110 ausgelagert** - dort in praezisierter Form, mit einer
zusaetzlichen Bedingung zur Bucket-Neuinterpretation, die hier fehlte:
- ~~Ein Lauf auf Claude Code landet in genau einem Telemetrie-Bucket~~ -> #110
- ~~Das Iteration-Budget-Gate zaehlt ueber einen ganzen Lauf; ein Test mit 61 Aufrufen aus je
eigener Shell sieht die Verweigerung~~ -> #110
- ~~Entschieden und dokumentiert, wie sich das zu #82 verhaelt~~ -> #110
## Herkunft und Artefakte
Lauf vom 2026-09-15, Instanz `5.0.0 -> 6.0.0`, Harness Claude Code, Sitzung `671c1b9a`.
Ergebnis-Commits in der Instanz: `7fe8353` (Handreparatur aus Befund 3), `dcc17df` (Maschinerie),
`c8c9f9b` (optionale Migration).
Review-Sitzung vom 2026-09-16: hat Transkript, die drei Commits und den Endzustand gegengelesen
und Befund 5, 6, 7 sowie die Ursachenabschnitte unter Befund 1 und 3 ergaenzt. Befund 5 ist dabei
live gegen die Instanz geprueft, nicht aus dem Transkript abgeleitet, und in derselben Sitzung
repariert (`b5014d9` in `torben/nathan`). Aus derselben Sitzung stammt #108, das Befund 7
abgeschlossen hat.
Erste Umsetzungssitzung vom 2026-09-16 (`0e09cf4`, `6.1.0-beta.2`): Befund 4 und 6 umgesetzt,
dabei die Sprachbegruendung aus Befund 4b/5 gegen `types/type-spec.md` korrigiert und die
Stack-Haelfte von Befund 5 gefunden und behoben. Modelle: Entwurf, Versionsteil und Grenzurteile
auf Opus 5, mechanische Mitte auf Opus 5, Abschluss auf Opus 5 - der in `stack-dev` Schritt 3
angebotene Wechsel auf Sonnet wurde angeboten und nicht gezogen.
Zweite Umsetzungssitzung vom 2026-09-16 (`72d01be` = `6.1.0-beta.3`, `0c98080` =
`6.1.0-beta.4`, `536093f` = Doku-Nachzug ohne Bump): Befund 3 und 2 entschieden und gebaut, in
zwei Publishes plus einem Nachzug aus der Abschlussphase. Beide Bumps `--minor` mit
`--impact medium` - neue Faehigkeit, in beide Richtungen ein Drop-in, kein Grenzuebertritt, und
damit bleibt die Bump-Liste des Kandidaten flach, wie sie es bei vier gleichgewichtigen
Upgrade-Pfad-Aenderungen sein soll. In der Abschlussphase zusaetzlich gefunden und mitgenommen:
INSTALL.md behauptete weiter, `version check` sei der einzige Befehl, der ins Netz geht, und
`tools/CONTRACT.md` § "Future considerations (not implemented)" fuehrte noch den
MCP-Server-Wrapper und `dist upgrade` selbst - beide seit ihrer Umsetzung falsch und im selben
Dokument weiter oben als existierend beschrieben. Modelle: Entwurf, Versionsteil und Grenzurteile
auf Opus 5, mechanische Mitte (Code, Tests, Bumps, Pruefungen) auf Opus 5, Abschluss auf Opus 5 -
der Wechsel auf Sonnet wurde an beiden vorgesehenen Stellen angeboten und nicht gezogen.
Abschluss am 2026-09-16: Befund 1 nach **#110** ausgelagert und dieses Issue geschlossen. Die
Evidenz zu Befund 1 bleibt hier - Trace, Bucket-Messung, Scorer-Ausgabe, Reproduktionsschritt 5 -,
weil sie das Messergebnis dieses Laufs ist; #110 traegt die Entscheidung, die Loesungsachsen und
praezisierte Akzeptanzkriterien. Dasselbe Muster wie bei Befund 7 und #108, an derselben Stelle
begruendet.
Nicht stale, und deshalb bewusst nicht angefasst: `docs/ownership-and-templates.md`. Seine
Begruendung - eine lokal geaenderte Datei sei *"a decision someone takes deliberately instead of
one an upgrade takes for them"* - ist genau die Eigenschaft, die `--take-release` umsetzt, indem
es den Pfad benennen laesst. `docs/version-model.md` und `docs/why-gates-are-code.md` sind von
beiden Aenderungen nicht beruehrt.
**Erster Kommentar traegt die vollstaendige Telemetrie des Laufs verbatim** - 63 Events, aus 21
Bucket-Dateien nach `ts` zusammengefuehrt. Einzige Aenderung: die drei identischen 69-Pfad-Arrays
in `gate.refused`/`gate.cleared`/`publish.commit` sind elidiert und die Liste steht einmal darunter.
Das ist der Teil dieses Issues, den #110 als Evidenz braucht und der hier bewusst bleibt, statt
kopiert zu werden.
Das rohe Sitzungstranskript ist **bewusst nicht angehaengt**: die Instanz ist privat, das
Transkript traegt Seitentitel, Infrastrukturthemen und Kontaktdaten aus `kb/`, und dieses Repo ist
oeffentlich (`instructions/private-instance.md`: *"the cost of a mistaken push is disclosure rather
than inconvenience"*). Die Telemetrie unten ist dagegen geprueft frei davon - sie enthaelt nur
Stack-Pfade, Kommandonamen und Exit-Codes. Wer das Transkript fuer die Dev-Instanz-Validierung
braucht, bekommt es auf Anfrage redigiert (ANSI entfernt, Instanzinhalte maskiert).
Nebenbefund aus genau dieser Redaktion, weil er `chemenu.telemetry.scrub` betrifft: eine
Maskierung per Regex ueber **Terminalausgabe** greift nicht, solange die ANSI-Sequenzen drinstehen -
`ssh://git@host:PORT/...` und `Vorname Nachname <mail@host>` waren durch eingestreute
Farbcodes aufgetrennt und ueberlebten den ersten Durchgang unmaskiert. ANSI muss vor der
Maskierung entfernt werden, nicht danach. Nicht in #110 mitgenommen - eigenstaendiger Befund an
einer anderen Stelle, und bis heute kein eigenes Issue.
63 Events, zusammengefuehrt aus den 21 Bucket-Dateien unter reports/telemetry/<session>/trace.jsonl
(Filter: -newermt "2026-09-15 21:40" lokal = 19:40 UTC), sortiert nach ts. Unveraendert bis auf eine Sache: die drei identischen 69-Pfad-Arrays in gate.refused, gate.cleared und dem ersten publish.commit sind durch <<69 Pfade - elidiert, Liste einmal unten>> ersetzt; die Liste steht
darunter. Ausserdem sind bei prompt.submitted die Felder transcript_path und cwd entfernt
(lokale Pfade, kein Informationswert hier) - prompt, prompt_sha256 und prompt_length stehen
unveraendert da.
Dass dieselbe Datei 21 session_id-Werte traegt, ist Befund 1 und kein Artefakt der Zusammenfuehrung.
{"v":1,"ts":"2026-09-15T19:44:52.158756+00:00","session_id":"671c1b9a-92dd-484b-b29a-bf6d29c006ff","pid":3834977,"seq":1,"source":"claude-code","event":"session.start","attrs":{"harness":"claude-code","completeness":["session.start","session.end","prompt.submitted","tool.pre","tool.post","tool.error","turn.end","instructions.loaded","subagent.start","subagent.stop","compaction"],"synthesized":true}}{"v":1,"ts":"2026-09-15T19:44:52.158880+00:00","session_id":"671c1b9a-92dd-484b-b29a-bf6d29c006ff","pid":3834977,"seq":2,"source":"claude-code","event":"prompt.submitted","attrs":{"harness_event":"UserPromptSubmit","prompt_length":46,"prompt_sha256":"2ebc776efd9cfeee7a54967b07dafb002bfc5193d2595f282d92850c07907851","prompt":"aktualisiere den stack auf die letzte version."}}{"v":1,"ts":"2026-09-15T19:45:07.753037+00:00","session_id":"3835015","pid":3835017,"seq":1,"source":"wikitool","event":"session.start","attrs":{"harness":"wikitool","completeness":["wikitool.call","gate.refused","page.written","publish.commit","budget.state","gate.cleared"],"synthesized":true}}{"v":1,"ts":"2026-09-15T19:45:07.753149+00:00","session_id":"3835015","pid":3835017,"seq":2,"source":"wikitool","event":"wikitool.call","attrs":{"command":"version","args":["check"],"exit_code":0,"duration_ms":104.1}}{"v":1,"ts":"2026-09-15T19:45:18.625842+00:00","session_id":"3835056","pid":3835058,"seq":1,"source":"wikitool","event":"session.start","attrs":{"harness":"wikitool","completeness":["wikitool.call","gate.refused","page.written","publish.commit","budget.state","gate.cleared"],"synthesized":true}}{"v":1,"ts":"2026-09-15T19:45:18.625930+00:00","session_id":"3835056","pid":3835058,"seq":2,"source":"wikitool","event":"wikitool.call","attrs":{"command":"migrate","args":["status"],"exit_code":0,"duration_ms":255.0}}{"v":1,"ts":"2026-09-15T19:45:18.937497+00:00","session_id":"3835056","pid":3835063,"seq":1,"source":"wikitool","event":"wikitool.call","attrs":{"command":"version","args":["notes"],"exit_code":1,"duration_ms":33.7}}{"v":1,"ts":"2026-09-15T19:46:03.354028+00:00","session_id":"3835179","pid":3835181,"seq":1,"source":"wikitool","event":"session.start","attrs":{"harness":"wikitool","completeness":["wikitool.call","gate.refused","page.written","publish.commit","budget.state","gate.cleared"],"synthesized":true}}{"v":1,"ts":"2026-09-15T19:46:03.354109+00:00","session_id":"3835179","pid":3835181,"seq":2,"source":"wikitool","event":"wikitool.call","attrs":{"command":"dist","args":["upgrade","/home/torben/.claude/jobs/8af88af0/tmp/chemenu-stack-6.0.0.tar.gz","--dry-run"],"exit_code":0,"duration_ms":129.8}}{"v":1,"ts":"2026-09-15T19:46:31.306797+00:00","session_id":"3835232","pid":3835234,"seq":1,"source":"wikitool","event":"session.start","attrs":{"harness":"wikitool","completeness":["wikitool.call","gate.refused","page.written","publish.commit","budget.state","gate.cleared"],"synthesized":true}}{"v":1,"ts":"2026-09-15T19:46:31.306874+00:00","session_id":"3835232","pid":3835234,"seq":2,"source":"wikitool","event":"wikitool.call","attrs":{"command":"dist","args":["upgrade","/home/torben/.claude/jobs/8af88af0/tmp/chemenu-stack-6.0.0.tar.gz"],"exit_code":1,"duration_ms":130.2}}{"v":1,"ts":"2026-09-15T19:46:59.478952+00:00","session_id":"3835285","pid":3835287,"seq":1,"source":"wikitool","event":"session.start","attrs":{"harness":"wikitool","completeness":["wikitool.call","gate.refused","page.written","publish.commit","budget.state","gate.cleared"],"synthesized":true}}{"v":1,"ts":"2026-09-15T19:46:59.479019+00:00","session_id":"3835285","pid":3835287,"seq":2,"source":"wikitool","event":"wikitool.call","attrs":{"command":"dist","args":["upgrade","/home/torben/.claude/jobs/8af88af0/tmp/chemenu-stack-6.0.0.tar.gz","--dry-run"],"exit_code":1,"duration_ms":44.8}}{"v":1,"ts":"2026-09-15T19:47:17.147609+00:00","session_id":"3835339","pid":3835341,"seq":1,"source":"wikitool","event":"session.start","attrs":{"harness":"wikitool","completeness":["wikitool.call","gate.refused","page.written","publish.commit","budget.state","gate.cleared"],"synthesized":true}}{"v":1,"ts":"2026-09-15T19:47:17.147690+00:00","session_id":"3835339","pid":3835341,"seq":2,"source":"wikitool","event":"wikitool.call","attrs":{"command":"dist","args":["upgrade","/home/torben/.claude/jobs/8af88af0/tmp/chemenu-stack-6.0.0.tar.gz","--dry-run"],"exit_code":0,"duration_ms":130.4}}{"v":1,"ts":"2026-09-15T19:47:22.989307+00:00","session_id":"3835353","pid":3835355,"seq":1,"source":"wikitool","event":"session.start","attrs":{"harness":"wikitool","completeness":["wikitool.call","gate.refused","page.written","publish.commit","budget.state","gate.cleared"],"synthesized":true}}{"v":1,"ts":"2026-09-15T19:47:22.989389+00:00","session_id":"3835353","pid":3835355,"seq":2,"source":"wikitool","event":"wikitool.call","attrs":{"command":"dist","args":["upgrade","/home/torben/.claude/jobs/8af88af0/tmp/chemenu-stack-6.0.0.tar.gz"],"exit_code":0,"duration_ms":172.5}}{"v":1,"ts":"2026-09-15T19:47:28.789436+00:00","session_id":"3835371","pid":3835373,"seq":1,"source":"wikitool","event":"session.start","attrs":{"harness":"wikitool","completeness":["wikitool.call","gate.refused","page.written","publish.commit","budget.state","gate.cleared"],"synthesized":true}}{"v":1,"ts":"2026-09-15T19:47:28.789523+00:00","session_id":"3835371","pid":3835373,"seq":2,"source":"wikitool","event":"wikitool.call","attrs":{"command":"migrate","args":["status"],"exit_code":0,"duration_ms":57.1}}{"v":1,"ts":"2026-09-15T19:47:29.110458+00:00","session_id":"3835371","pid":3835377,"seq":1,"source":"wikitool","event":"wikitool.call","attrs":{"command":"instructions","args":["sync"],"exit_code":0,"duration_ms":39.5}}{"v":1,"ts":"2026-09-15T19:47:44.434440+00:00","session_id":"3835425","pid":3835427,"seq":1,"source":"wikitool","event":"session.start","attrs":{"harness":"wikitool","completeness":["wikitool.call","gate.refused","page.written","publish.commit","budget.state","gate.cleared"],"synthesized":true}}{"v":1,"ts":"2026-09-15T19:47:44.434529+00:00","session_id":"3835425","pid":3835427,"seq":2,"source":"wikitool","event":"wikitool.call","attrs":{"command":"doctor","args":[],"exit_code":0,"duration_ms":92.7}}{"v":1,"ts":"2026-09-15T19:47:46.370777+00:00","session_id":"3835425","pid":3835437,"seq":1,"source":"wikitool","event":"wikitool.call","attrs":{"command":"docs","args":["verify"],"exit_code":1,"duration_ms":1644.6}}{"v":1,"ts":"2026-09-15T19:47:53.978922+00:00","session_id":"3835460","pid":3835462,"seq":1,"source":"wikitool","event":"session.start","attrs":{"harness":"wikitool","completeness":["wikitool.call","gate.refused","page.written","publish.commit","budget.state","gate.cleared"],"synthesized":true}}{"v":1,"ts":"2026-09-15T19:47:53.979000+00:00","session_id":"3835460","pid":3835462,"seq":2,"source":"wikitool","event":"wikitool.call","attrs":{"command":"docs","args":["toc","--apply"],"exit_code":0,"duration_ms":86.0}}{"v":1,"ts":"2026-09-15T19:47:58.835954+00:00","session_id":"3835471","pid":3835473,"seq":1,"source":"wikitool","event":"session.start","attrs":{"harness":"wikitool","completeness":["wikitool.call","gate.refused","page.written","publish.commit","budget.state","gate.cleared"],"synthesized":true}}{"v":1,"ts":"2026-09-15T19:47:58.836043+00:00","session_id":"3835471","pid":3835473,"seq":2,"source":"wikitool","event":"wikitool.call","attrs":{"command":"docs","args":["verify"],"exit_code":0,"duration_ms":414.3}}{"v":1,"ts":"2026-09-15T19:47:59.216332+00:00","session_id":"3835471","pid":3835482,"seq":1,"source":"wikitool","event":"wikitool.call","attrs":{"command":"instructions","args":["verify"],"exit_code":0,"duration_ms":97.5}}{"v":1,"ts":"2026-09-15T19:48:00.112259+00:00","session_id":"3835471","pid":3835485,"seq":1,"source":"wikitool","event":"wikitool.call","attrs":{"command":"lint","args":[],"exit_code":0,"duration_ms":614.3}}{"v":1,"ts":"2026-09-15T19:48:28.408424+00:00","session_id":"3835548","pid":3835550,"seq":1,"source":"wikitool","event":"session.start","attrs":{"harness":"wikitool","completeness":["wikitool.call","gate.refused","page.written","publish.commit","budget.state","gate.cleared"],"synthesized":true}}{"v":1,"ts":"2026-09-15T19:48:28.408499+00:00","session_id":"3835548","pid":3835550,"seq":2,"source":"wikitool","event":"gate.refused","attrs":{"gate":"mass-update","reason":"needs-clearance","token":"96e2563ff596","presented_token":null,"changed":69,"counted":69,"threshold":10,"files":"<<69 Pfade - elidiert, Liste einmal unten>>"}}{"v":1,"ts":"2026-09-15T19:48:28.429958+00:00","session_id":"3835548","pid":3835550,"seq":3,"source":"wikitool","event":"wikitool.call","attrs":{"command":"publish","args":["--message","stack: upgrade machinery 5.0.0 -> 6.0.0"],"exit_code":42,"duration_ms":416.9}}{"v":1,"ts":"2026-09-15T19:58:14.796703+00:00","session_id":"671c1b9a-92dd-484b-b29a-bf6d29c006ff","pid":3836779,"seq":1,"source":"claude-code","event":"prompt.submitted","attrs":{"harness_event":"UserPromptSubmit","prompt_length":8,"prompt_sha256":"2687f86ed6784b8a5fca36e6c468e12aa44dc3c7e8137e3160d1a95079bdcd02","prompt":"approved"}}{"v":1,"ts":"2026-09-15T19:58:21.785485+00:00","session_id":"3836791","pid":3836793,"seq":1,"source":"wikitool","event":"session.start","attrs":{"harness":"wikitool","completeness":["wikitool.call","gate.refused","page.written","publish.commit","budget.state","gate.cleared"],"synthesized":true}}{"v":1,"ts":"2026-09-15T19:58:21.785560+00:00","session_id":"3836791","pid":3836793,"seq":2,"source":"wikitool","event":"gate.cleared","attrs":{"gate":"mass-update","token":"96e2563ff596","counted":69,"threshold":10,"files":"<<69 Pfade - elidiert, Liste einmal unten>>"}}{"v":1,"ts":"2026-09-15T19:58:22.573729+00:00","session_id":"3836791","pid":3836793,"seq":3,"source":"wikitool","event":"publish.commit","attrs":{"summary":"stack: upgrade machinery 5.0.0 -> 6.0.0","files":"<<69 Pfade - elidiert, Liste einmal unten>>","changed":69,"counted":69,"paths":[],"pushed":true,"remote":"origin","branch":"main"}}{"v":1,"ts":"2026-09-15T19:58:22.586659+00:00","session_id":"3836791","pid":3836793,"seq":4,"source":"wikitool","event":"wikitool.call","attrs":{"command":"publish","args":["--confirm","96e2563ff596","--message","stack: upgrade machinery 5.0.0 -> 6.0.0"],"exit_code":0,"duration_ms":1181.8}}{"v":1,"ts":"2026-09-15T20:01:51.551944+00:00","session_id":"671c1b9a-92dd-484b-b29a-bf6d29c006ff","pid":3837219,"seq":1,"source":"claude-code","event":"prompt.submitted","attrs":{"harness_event":"UserPromptSubmit","prompt_length":10,"prompt_sha256":"137b05c8fcd27c223b7682bd736980295e97ce5cc370b27120884115e8a0b887","prompt":"make it so"}}{"v":1,"ts":"2026-09-15T20:02:01.986725+00:00","session_id":"3837235","pid":3837237,"seq":1,"source":"wikitool","event":"session.start","attrs":{"harness":"wikitool","completeness":["wikitool.call","gate.refused","page.written","publish.commit","budget.state","gate.cleared"],"synthesized":true}}{"v":1,"ts":"2026-09-15T20:02:01.986800+00:00","session_id":"3837235","pid":3837237,"seq":2,"source":"wikitool","event":"wikitool.call","attrs":{"command":"types","args":["list"],"exit_code":0,"duration_ms":32.8}}{"v":1,"ts":"2026-09-15T20:06:01.055001+00:00","session_id":"3837668","pid":3837671,"seq":1,"source":"wikitool","event":"session.start","attrs":{"harness":"wikitool","completeness":["wikitool.call","gate.refused","page.written","publish.commit","budget.state","gate.cleared"],"synthesized":true}}{"v":1,"ts":"2026-09-15T20:06:01.055085+00:00","session_id":"3837668","pid":3837671,"seq":2,"source":"wikitool","event":"wikitool.call","attrs":{"command":"docs","args":["toc","--apply"],"exit_code":0,"duration_ms":89.2}}{"v":1,"ts":"2026-09-15T20:06:01.755430+00:00","session_id":"3837668","pid":3837674,"seq":1,"source":"wikitool","event":"wikitool.call","attrs":{"command":"docs","args":["verify"],"exit_code":0,"duration_ms":415.2}}{"v":1,"ts":"2026-09-15T20:06:08.439953+00:00","session_id":"3837691","pid":3837693,"seq":1,"source":"wikitool","event":"session.start","attrs":{"harness":"wikitool","completeness":["wikitool.call","gate.refused","page.written","publish.commit","budget.state","gate.cleared"],"synthesized":true}}{"v":1,"ts":"2026-09-15T20:06:08.440078+00:00","session_id":"3837691","pid":3837693,"seq":2,"source":"wikitool","event":"wikitool.call","attrs":{"command":"types","args":["describe","entity"],"exit_code":0,"duration_ms":41.1}}{"v":1,"ts":"2026-09-15T20:06:08.756049+00:00","session_id":"3837691","pid":3837696,"seq":1,"source":"wikitool","event":"wikitool.call","attrs":{"command":"types","args":["describe","concept"],"exit_code":0,"duration_ms":40.3}}{"v":1,"ts":"2026-09-15T20:06:09.086658+00:00","session_id":"3837691","pid":3837703,"seq":1,"source":"wikitool","event":"wikitool.call","attrs":{"command":"types","args":["describe","source"],"exit_code":1,"duration_ms":41.4}}{"v":1,"ts":"2026-09-15T20:06:16.367533+00:00","session_id":"3837722","pid":3837724,"seq":1,"source":"wikitool","event":"session.start","attrs":{"harness":"wikitool","completeness":["wikitool.call","gate.refused","page.written","publish.commit","budget.state","gate.cleared"],"synthesized":true}}{"v":1,"ts":"2026-09-15T20:06:16.367641+00:00","session_id":"3837722","pid":3837724,"seq":2,"source":"wikitool","event":"wikitool.call","attrs":{"command":"types","args":["describe","source"],"exit_code":0,"duration_ms":42.5}}{"v":1,"ts":"2026-09-15T20:06:16.686072+00:00","session_id":"3837722","pid":3837728,"seq":1,"source":"wikitool","event":"wikitool.call","attrs":{"command":"types","args":["describe","comparison"],"exit_code":0,"duration_ms":38.6}}{"v":1,"ts":"2026-09-15T20:06:24.306079+00:00","session_id":"3837739","pid":3837741,"seq":1,"source":"wikitool","event":"session.start","attrs":{"harness":"wikitool","completeness":["wikitool.call","gate.refused","page.written","publish.commit","budget.state","gate.cleared"],"synthesized":true}}{"v":1,"ts":"2026-09-15T20:06:24.306152+00:00","session_id":"3837739","pid":3837741,"seq":2,"source":"wikitool","event":"wikitool.call","attrs":{"command":"docs","args":["verify"],"exit_code":0,"duration_ms":403.3}}{"v":1,"ts":"2026-09-15T20:06:24.686788+00:00","session_id":"3837739","pid":3837749,"seq":1,"source":"wikitool","event":"wikitool.call","attrs":{"command":"instructions","args":["verify"],"exit_code":0,"duration_ms":93.4}}{"v":1,"ts":"2026-09-15T20:06:25.049867+00:00","session_id":"3837739","pid":3837752,"seq":1,"source":"wikitool","event":"wikitool.call","attrs":{"command":"doctor","args":[],"exit_code":0,"duration_ms":65.5}}{"v":1,"ts":"2026-09-15T20:06:25.397424+00:00","session_id":"3837739","pid":3837764,"seq":1,"source":"wikitool","event":"wikitool.call","attrs":{"command":"migrate","args":["status"],"exit_code":0,"duration_ms":56.9}}{"v":1,"ts":"2026-09-15T20:06:33.119504+00:00","session_id":"3837778","pid":3837780,"seq":1,"source":"wikitool","event":"session.start","attrs":{"harness":"wikitool","completeness":["wikitool.call","gate.refused","page.written","publish.commit","budget.state","gate.cleared"],"synthesized":true}}{"v":1,"ts":"2026-09-15T20:06:33.119572+00:00","session_id":"3837778","pid":3837780,"seq":2,"source":"wikitool","event":"wikitool.call","attrs":{"command":"migrate","args":["done","6.0.0","--pages","0"],"exit_code":0,"duration_ms":36.2}}{"v":1,"ts":"2026-09-15T20:06:33.448615+00:00","session_id":"3837778","pid":3837783,"seq":1,"source":"wikitool","event":"wikitool.call","attrs":{"command":"migrate","args":["status"],"exit_code":0,"duration_ms":54.4}}{"v":1,"ts":"2026-09-15T20:06:43.228331+00:00","session_id":"3837804","pid":3837806,"seq":1,"source":"wikitool","event":"session.start","attrs":{"harness":"wikitool","completeness":["wikitool.call","gate.refused","page.written","publish.commit","budget.state","gate.cleared"],"synthesized":true}}{"v":1,"ts":"2026-09-15T20:06:43.228414+00:00","session_id":"3837804","pid":3837806,"seq":2,"source":"wikitool","event":"publish.commit","attrs":{"summary":"types: take offered 6.0.0-type-guidance-split migration\n\nLink entity/concept/source/comparison to their new stack-owned\ntypes/<name>.guidance.md so they receive future authoring-prose\nimprovements again. Frontmatter and Template blocks untouched.","files":[".wikitool-kb.json","types/comparison.md","types/concept.md","types/entity.md","types/source.md"],"changed":5,"counted":5,"paths":[],"pushed":true,"remote":"origin","branch":"main"}}{"v":1,"ts":"2026-09-15T20:06:43.234283+00:00","session_id":"3837804","pid":3837806,"seq":3,"source":"wikitool","event":"wikitool.call","attrs":{"command":"publish","args":["--message","types: take offered 6.0.0-type-guidance-split migration\n\nLink entity/concept/source/comparison to their new stack-owned\ntypes/<name>.guidance.md so they receive future authoring-prose\nimprovements again. Frontmatter and Template blocks untouched."],"exit_code":0,"duration_ms":970.4}}{"v":1,"ts":"2026-09-15T20:23:49.966805+00:00","session_id":"671c1b9a-92dd-484b-b29a-bf6d29c006ff","pid":3839739,"seq":1,"source":"claude-code","event":"prompt.submitted","attrs":{"harness_event":"UserPromptSubmit","prompt_length":41,"prompt_sha256":"08a258dc7245e17391c89d8c434d03e57ef1211886559173a18957e3d29e7f66","prompt":"Can you access Torben/chemenu @gitea-mcp?"}}{"v":1,"ts":"2026-09-15T20:26:52.076905+00:00","session_id":"671c1b9a-92dd-484b-b29a-bf6d29c006ff","pid":3840230,"seq":1,"source":"claude-code","event":"prompt.submitted","attrs":{"harness_event":"UserPromptSubmit","prompt_length":393,"prompt_sha256":"fcf32a5b6942cb94a800ac558ccaafdaa4fd39332e63870ef46075084ba210f4","prompt":"Do a full analysis and runtime data collection of this upgrade run. Include stack specific tracing and if somehow possible, take the raw session transcript as well. Create an issue in the chemenu repo collecting this telemetry as verbatim as possible, rely on file attachments if possible. \nGoal is to use your analysis to validate the upgrade procedure on a dev instance of the chemenu stack."}}
Die elidierte 69-Pfad-Liste
Identisch in gate.refused, gate.cleared und dem ersten publish.commit - das ist der
Commit dcc17df:
Laufzeiten, soweit auffaellig: docs verify kalt 1644.6 ms, danach 403-415 ms (dreimal gemessen). publish --confirm 1181.8 ms inklusive Push, der zweite Publish 970.4 ms. Alles andere unter
300 ms.
budget.state taucht in der ganzen Trace nicht auf, obwohl das Event im completeness-Feld jedes session.start als meldbar gefuehrt wird - passt zu Befund 1, Folge 2:
ein Zaehler, der nie in die Naehe seiner Schwelle kommt, hat auch nichts zu melden.
torben
changed title from Upgrade-Lauf 5.0.0 -> 6.0.0 auf ausgelieferter Instanz: Laufbericht, Telemetrie, vier Befunde to Upgrade-Lauf 5.0.0 -> 6.0.0 auf ausgelieferter Instanz: Laufbericht, Telemetrie, sieben Befunde2026-09-16 11:57:15 +00:00
Changelog: Review-Sitzung vom 2026-09-16 gegen Transkript, die drei Ergebnis-Commits und den Endzustand der Instanz. Neu: Befund 5 (Rest-Abschnitt ## Authoring guidance in types/source.md, doppelter Kopf in types describe source - live gegengeprueft), Befund 6 (Schritt 5 des Migrationsdokuments prueft gegen ein Vorher, das niemand festhaelt - direkte Ursache von Befund 5), Befund 7 (Upgrade-Pfad ohne agentengerichtete Prozedur; migrate verify nie gelaufen, Session nie neu gestartet, drei konkurrierende Reihenfolgen). Befund 1 hat einen Ursachenabschnitt bekommen: instructions/session-setup.md schreibt export ... einmal pro Sitzung, was auf einem Harness mit Shell-pro-Tool-Call wirkungslos ist. Befund 3 hat die Erwartungshaltung aus dem Transkript, Befund 4a die Verschaerfung, dass es im Ursprungs-Repo gar keine types/*.md.template gibt.
Korrigiert: die Zeile unter "Was der Lauf bestaetigt hat", types describe komponiere unveraendert - das gilt fuer drei der vier Typen, nicht fuer source. Akzeptanzkriterien um vier Punkte erweitert, Reproduktionsabschnitt um Schritt 6 (Diff-Gegentest). Befund 7 ist als Bauauftrag nach #108 ausgelagert. Titel und size/ nachgezogen: vier -> sieben Befunde, size/M -> size/L, weil daraus jetzt mehrere Arbeitspakete werden statt eines.
**Changelog:** Review-Sitzung vom 2026-09-16 gegen Transkript, die drei Ergebnis-Commits und den Endzustand der Instanz. Neu: Befund 5 (Rest-Abschnitt `## Authoring guidance` in `types/source.md`, doppelter Kopf in `types describe source` - live gegengeprueft), Befund 6 (Schritt 5 des Migrationsdokuments prueft gegen ein Vorher, das niemand festhaelt - direkte Ursache von Befund 5), Befund 7 (Upgrade-Pfad ohne agentengerichtete Prozedur; `migrate verify` nie gelaufen, Session nie neu gestartet, drei konkurrierende Reihenfolgen). Befund 1 hat einen Ursachenabschnitt bekommen: `instructions/session-setup.md` schreibt `export ... ` einmal pro Sitzung, was auf einem Harness mit Shell-pro-Tool-Call wirkungslos ist. Befund 3 hat die Erwartungshaltung aus dem Transkript, Befund 4a die Verschaerfung, dass es im Ursprungs-Repo gar keine `types/*.md.template` gibt.
Korrigiert: die Zeile unter "Was der Lauf bestaetigt hat", `types describe` komponiere unveraendert - das gilt fuer drei der vier Typen, nicht fuer `source`. Akzeptanzkriterien um vier Punkte erweitert, Reproduktionsabschnitt um Schritt 6 (Diff-Gegentest). Befund 7 ist als Bauauftrag nach #108 ausgelagert. Titel und `size/` nachgezogen: vier -> sieben Befunde, `size/M` -> `size/L`, weil daraus jetzt mehrere Arbeitspakete werden statt eines.
torben
added size/L and removed size/M labels 2026-09-16 11:57:39 +00:00
Changelog: Befund 5 ist repariert (b5014d9 in torben/nathan) und das Kriterium abgehakt. Dabei eine Zahl in Befund 5 korrigiert: zwei ## Authoring guidance-Koepfe sind der Normalzustand - types describe setzt selbst einen und inlined darunter die Guidance-Datei mit ihrem eigenen gleichnamigen Abschnitt, bei allen vier Typen. source hatte einen dritten aus dem Type-Spec. Der Reproduktionsabschnitt nennt jetzt 3 statt 2 als Erwartungswert und die Gegenprobe an den anderen drei Typen. Befund 6 traegt die Gegenprobe: die Reparatur lief mit Vorher-Datei und Diff, und genau deshalb war das Ergebnis belastbar. "Stand"-Absatz oben auf drei erledigte Punkte aktualisiert.
**Changelog:** Befund 5 ist repariert (`b5014d9` in `torben/nathan`) und das Kriterium abgehakt. Dabei **eine Zahl in Befund 5 korrigiert**: zwei `## Authoring guidance`-Koepfe sind der Normalzustand - `types describe` setzt selbst einen und inlined darunter die Guidance-Datei mit ihrem eigenen gleichnamigen Abschnitt, bei allen vier Typen. `source` hatte einen **dritten** aus dem Type-Spec. Der Reproduktionsabschnitt nennt jetzt 3 statt 2 als Erwartungswert und die Gegenprobe an den anderen drei Typen. Befund 6 traegt die Gegenprobe: die Reparatur lief mit Vorher-Datei und Diff, und genau deshalb war das Ergebnis belastbar. "Stand"-Absatz oben auf drei erledigte Punkte aktualisiert.
Changelog: Befund 4 und 6 umgesetzt und abgehakt (0e09cf4, 6.1.0-beta.2). Das Migrationsdokument hat einen eigenen Vorher-Schritt bekommen (alte Schritte 3-6 sind auf 4-7 gerueckt), verweist auf types/<name>.md.template statt types/<name>.md und nennt die Sprache des Pointer-Absatzes; instructions/migrate-corpus.md § "Writing the migration document" traegt das Baseline-Muster jetzt generisch.
Eine Begruendung korrigiert: die Sprachbegruendung unter Befund 4b/5 - "ein Abschnitt, den die Instanz behaelt, ist ihrer, also traegt er die KB-Sprache" - haelt gegen den Baum nicht. Die Sprachachse ist der Leser, nicht der Eigentuemer: types/type-spec.md § "Who owns a type-spec", AGENTS.md § File naming und der Docstring von dist_cmd.instance_owned_type_stems ("Ownership, not language") sagen uebereinstimmend, dass Anleitungsprosa in einem Type-Spec englisch bleibt, auch in einer instanzeigenen Datei. Das Ergebnis von Befund 5 war trotzdem richtig, nur der Weg dorthin - der Bullet war redundant und gehoerte weg, nicht uebersetzt. Das Dokument sagt das jetzt so und verlinkt die Tabelle, statt sie ein drittes Mal zu kopieren.
Neuer Teilbefund 5b, gefunden beim Nachmessen:types/source.md trug im Ursprungs-Repo denselben Rest-Abschnitt wie die Instanz - types describe source gab hier 3 ## Authoring guidance-Koepfe aus, die anderen drei Typen 2. Das Kriterium zu Befund 5 war gegen nathan abgehakt, gegen den Stack nie. Da die Datei beim Export zu types/source.md.template wird, haette jede neu aufgesetzte Instanz den Defekt adoptiert. Entfernt in 0e09cf4, geprueft mit genau dem Vorher/Nachher-Diff, den Befund 6 jetzt vorschreibt. Befund 5 ist dadurch in 5a (Instanz) und 5b (Stack) geteilt, mit einem zweiten Kriterium.
Ausserdem: "Stand"-Absatz auf fuenf erledigte Punkte, Schritt 6 des Reproduktionsabschnitts um den Hinweis ergaenzt, dass er gegen einen 6.0.0-Tarball gilt und ab 6.1.0-beta.2 nicht mehr reproduziert. Nicht geaendert: Befund 1, 2 und 3 - die drei offenen sind jetzt ausschliesslich Werkzeugaenderungen. Labels bleiben (size/L traegt weiter, Befund 1 allein ist es).
Bewusst nicht angefasst: instructions/upgrade-instance.md Schritt 12. Dessen Absatz richtet sich an den Ausfuehrenden einer Migration, der neue Satz in migrate-corpus.md an den Autor eines Migrationsdokuments - zwei Adressaten, keine zweite Kopie im Sinne von Invariante 8.
Verifiziert: lokal 1276 Tests, docs verify und instructions verify gruen; CI auf 0e09cf4 gruen in den Laeufen 296 und 297. Kein Release ausgeloest - release.yml reagiert nur auf ein suffixfreies VERSION, und 6.1.0-beta.2 traegt eines.
**Changelog:** Befund 4 und 6 umgesetzt und abgehakt (`0e09cf4`, `6.1.0-beta.2`). Das Migrationsdokument hat einen eigenen Vorher-Schritt bekommen (alte Schritte 3-6 sind auf 4-7 gerueckt), verweist auf `types/<name>.md.template` statt `types/<name>.md` und nennt die Sprache des Pointer-Absatzes; `instructions/migrate-corpus.md` § "Writing the migration document" traegt das Baseline-Muster jetzt generisch.
**Eine Begruendung korrigiert:** die Sprachbegruendung unter Befund 4b/5 - *"ein Abschnitt, den die Instanz behaelt, ist ihrer, also traegt er die KB-Sprache"* - haelt gegen den Baum nicht. Die Sprachachse ist der **Leser**, nicht der Eigentuemer: `types/type-spec.md` § "Who owns a type-spec", `AGENTS.md` § File naming und der Docstring von `dist_cmd.instance_owned_type_stems` (*"Ownership, not language"*) sagen uebereinstimmend, dass Anleitungsprosa in einem Type-Spec englisch bleibt, auch in einer instanzeigenen Datei. Das Ergebnis von Befund 5 war trotzdem richtig, nur der Weg dorthin - der Bullet war redundant und gehoerte weg, nicht uebersetzt. Das Dokument sagt das jetzt so und verlinkt die Tabelle, statt sie ein drittes Mal zu kopieren.
**Neuer Teilbefund 5b, gefunden beim Nachmessen:** `types/source.md` trug **im Ursprungs-Repo** denselben Rest-Abschnitt wie die Instanz - `types describe source` gab hier 3 `## Authoring guidance`-Koepfe aus, die anderen drei Typen 2. Das Kriterium zu Befund 5 war gegen `nathan` abgehakt, gegen den Stack nie. Da die Datei beim Export zu `types/source.md.template` wird, haette jede neu aufgesetzte Instanz den Defekt adoptiert. Entfernt in `0e09cf4`, geprueft mit genau dem Vorher/Nachher-Diff, den Befund 6 jetzt vorschreibt. Befund 5 ist dadurch in 5a (Instanz) und 5b (Stack) geteilt, mit einem zweiten Kriterium.
Ausserdem: "Stand"-Absatz auf fuenf erledigte Punkte, Schritt 6 des Reproduktionsabschnitts um den Hinweis ergaenzt, dass er gegen einen 6.0.0-Tarball gilt und ab `6.1.0-beta.2` nicht mehr reproduziert. Nicht geaendert: Befund 1, 2 und 3 - die drei offenen sind jetzt ausschliesslich Werkzeugaenderungen. Labels bleiben (`size/L` traegt weiter, Befund 1 allein ist es).
Bewusst nicht angefasst: `instructions/upgrade-instance.md` Schritt 12. Dessen Absatz richtet sich an den Ausfuehrenden einer Migration, der neue Satz in `migrate-corpus.md` an den Autor eines Migrationsdokuments - zwei Adressaten, keine zweite Kopie im Sinne von Invariante 8.
**Verifiziert:** lokal 1276 Tests, `docs verify` und `instructions verify` gruen; CI auf `0e09cf4` gruen in den Laeufen [296](https://gitea.nehmer.net/torben/chemenu/actions/runs/296) und [297](https://gitea.nehmer.net/torben/chemenu/actions/runs/297). Kein Release ausgeloest - `release.yml` reagiert nur auf ein suffixfreies `VERSION`, und `6.1.0-beta.2` traegt eines.
Changelog: Befund 2 und 3 sind entschieden; die beiden § Loesungsvorschlag-Abschnitte sind durch § Entschieden ersetzt und tragen jetzt die Regel statt der Alternativen.
Befund 2: von den beiden Wegen der erste - version notes faellt auf den Feed aus update_url zurueck, statt nur zu sagen, wo die Notes stehen. Ausschlaggebend war, dass instructions/upgrade-instance.md Schritt 2 den eigenen Workaround-Absatz schon als temporaer fuehrt ("This paragraph stops being necessary the day version notes falls back to that feed"). Drei Praezisierungen, die im Vorschlag nicht standen und die die Kollision mit version.pys "the one place in wikitool that talks to a remote host" aufloesen: der Fallback greift nur bei vorhandenem Release-Stamp, ein Dev-Checkout betritt ihn also nie und release.ymls version notes > /tmp/release-notes.md kann keinen Netzaufruf ausloesen; die Notes gehen nach stdout, die Herkunftszeilen nach stderr, damit der Redirect weiter nur den Eintrag traegt; --offline verweigert den Aufruf. Der Feed kennt nur /releases/latest, also wird eine abweichende Version benannt statt eine /releases/tags/<tag>-URL zu raten - der Hauptfall ist ohnehin, dass VERSION in Schritt 2 noch die alte ist.
Befund 3:--take-release nimmt einen Pfad und ist wiederholbar, statt ein pauschales Gegenstueck zu --keep-local zu sein. Die beiden Antworten sind nicht symmetrisch: --keep-local verliert nichts, --take-release verwirft eine lokale Aenderung, und eine Verwerfung benennt ihr Ziel (Schritt 1 dieser Instruktion). Der gemischte Fall wird damit ueberhaupt erst loesbar, beide Flags zusammen komponieren, ein nicht blockierter Pfad ist Exit 1 auch im --dry-run, und die Abbruchmeldung nennt alle drei Antworten samt einsetzbarer Kommandozeile im Muster des Mass-Update-Gates.
Akzeptanzkriterien zu 2 und 3 auf die entschiedene Form nachgezogen: aus zwei sind sechs geworden, jedes mit einer pruefbaren Eigenschaft statt einer Aktivitaet. Reproduktionsabschnitt um den Hinweis ergaenzt, wie Schritt 4 nach dieser Sitzung ausgeht. Befund 1 unberuehrt - laut eigenem Abschnitt eine Betreiberentscheidung, gegen #82 zusammen zu entscheiden und nicht Teil dieser Sitzung. Labels bleiben.
**Changelog:** Befund 2 und 3 sind entschieden; die beiden § Loesungsvorschlag-Abschnitte sind durch § Entschieden ersetzt und tragen jetzt die Regel statt der Alternativen.
**Befund 2:** von den beiden Wegen der erste - `version notes` faellt auf den Feed aus `update_url` zurueck, statt nur zu sagen, wo die Notes stehen. Ausschlaggebend war, dass `instructions/upgrade-instance.md` Schritt 2 den eigenen Workaround-Absatz schon als temporaer fuehrt (*"This paragraph stops being necessary the day `version notes` falls back to that feed"*). Drei Praezisierungen, die im Vorschlag nicht standen und die die Kollision mit `version.py`s *"the one place in `wikitool` that talks to a remote host"* aufloesen: der Fallback greift **nur bei vorhandenem Release-Stamp**, ein Dev-Checkout betritt ihn also nie und `release.yml`s `version notes > /tmp/release-notes.md` kann keinen Netzaufruf ausloesen; die Notes gehen nach stdout, die Herkunftszeilen nach stderr, damit der Redirect weiter nur den Eintrag traegt; `--offline` verweigert den Aufruf. Der Feed kennt nur `/releases/latest`, also wird eine abweichende Version benannt statt eine `/releases/tags/<tag>`-URL zu raten - der Hauptfall ist ohnehin, dass `VERSION` in Schritt 2 noch die alte ist.
**Befund 3:** `--take-release` nimmt einen **Pfad** und ist wiederholbar, statt ein pauschales Gegenstueck zu `--keep-local` zu sein. Die beiden Antworten sind nicht symmetrisch: `--keep-local` verliert nichts, `--take-release` verwirft eine lokale Aenderung, und eine Verwerfung benennt ihr Ziel (Schritt 1 dieser Instruktion). Der gemischte Fall wird damit ueberhaupt erst loesbar, beide Flags zusammen komponieren, ein nicht blockierter Pfad ist Exit 1 auch im `--dry-run`, und die Abbruchmeldung nennt alle drei Antworten samt einsetzbarer Kommandozeile im Muster des Mass-Update-Gates.
Akzeptanzkriterien zu 2 und 3 auf die entschiedene Form nachgezogen: aus zwei sind sechs geworden, jedes mit einer pruefbaren Eigenschaft statt einer Aktivitaet. Reproduktionsabschnitt um den Hinweis ergaenzt, wie Schritt 4 nach dieser Sitzung ausgeht. Befund 1 unberuehrt - laut eigenem Abschnitt eine Betreiberentscheidung, gegen #82 zusammen zu entscheiden und nicht Teil dieser Sitzung. Labels bleiben.
Changelog: Befund 2 und 3 gebaut und abgehakt - 72d01be (6.1.0-beta.3, --take-release) und 0c98080 (6.1.0-beta.4, version notes-Fallback), plus 536093f als Doku-Nachzug ohne Bump. Beide § Entschieden sind zu § Umgesetzt geworden und tragen jetzt, was tatsaechlich geschrieben wurde statt was geschrieben werden sollte; die Praesensformulierungen ueber die beiden Defekte sind auf Vergangenheit umgestellt.
Damit ist Befund 1 der einzige offene. Der "Stand"-Absatz ist durch eine Tabelle ueber alle sieben ersetzt, Befund 1 traegt oben und unten den Vermerk, dass er es ist, sein § Loesungsrichtung heisst jetzt § Zu entscheiden, und der Reproduktionsabschnitt sagt pro Schritt, ab welcher Version er nicht mehr reproduziert - Schritt 5 (Befund 1) ist der einzige, der unveraendert gilt. Akzeptanzkriterien in "offen" und "erledigt" geteilt: 3 offen, 12 abgehakt.
Label nachgezogen:kind/defect -> kind/decision. Was bleibt, ist keine Umsetzung mehr, sondern eine Betreiberentscheidung gegen #82. size/L und prio/planned bleiben - Befund 1 allein traegt beides.
Nicht geschlossen, und ein Vorschlag dazu steht im Koerper: Befund 1 als eigenes Issue auslagern und dieses schliessen, so wie Befund 7 nach #108 gegangen ist. Bewusst nicht einseitig getan, weil die Labels des neuen Issues Antworten auf Fragen sind, die erst die Entscheidung gegen #82 beantwortet.
Verifiziert: 1290 Tests (1276 vorher, +8 fuer --take-release, +6 fuer den Feed-Fallback), auch gegen eine leere Maschine nach testing-conventions.md Schritt 6 identisch gruen; docs verify mit 73 ausgelieferten Dokumenten und 58 Referenzdateien, instructions verify mit 23 Instruktionen und 7 Skills. CI gruen auf allen drei Commits: 298/299, 300/301, 302. Kein Release ausgeloest - release.yml reagiert nur auf ein suffixfreies VERSION.
In der Abschlussphase zusaetzlich gefunden und in 536093f mitgenommen, weil beides Behauptungen ueber die geaenderten Flaechen waren: INSTALL.md fuehrte weiter version check als einzigen Befehl, der ins Netz geht, und tools/CONTRACT.md § "Future considerations (not implemented)" listete noch den MCP-Server-Wrapper und dist upgrade selbst - beide seit ihrer Umsetzung falsch und im selben Dokument weiter oben als existierend beschrieben. docs/ownership-and-templates.md dagegen geprueft und bewusst nicht angefasst: seine Begruendung, eine lokal geaenderte Datei solle "a decision someone takes deliberately" sein, ist genau die Eigenschaft, die --take-release umsetzt.
**Changelog:** Befund 2 und 3 gebaut und abgehakt - `72d01be` (`6.1.0-beta.3`, `--take-release`) und `0c98080` (`6.1.0-beta.4`, `version notes`-Fallback), plus `536093f` als Doku-Nachzug ohne Bump. Beide § Entschieden sind zu § Umgesetzt geworden und tragen jetzt, was tatsaechlich geschrieben wurde statt was geschrieben werden sollte; die Praesensformulierungen ueber die beiden Defekte sind auf Vergangenheit umgestellt.
**Damit ist Befund 1 der einzige offene.** Der "Stand"-Absatz ist durch eine Tabelle ueber alle sieben ersetzt, Befund 1 traegt oben und unten den Vermerk, dass er es ist, sein § Loesungsrichtung heisst jetzt § Zu entscheiden, und der Reproduktionsabschnitt sagt pro Schritt, ab welcher Version er nicht mehr reproduziert - Schritt 5 (Befund 1) ist der einzige, der unveraendert gilt. Akzeptanzkriterien in "offen" und "erledigt" geteilt: 3 offen, 12 abgehakt.
**Label nachgezogen:** `kind/defect` -> `kind/decision`. Was bleibt, ist keine Umsetzung mehr, sondern eine Betreiberentscheidung gegen #82. `size/L` und `prio/planned` bleiben - Befund 1 allein traegt beides.
**Nicht geschlossen, und ein Vorschlag dazu** steht im Koerper: Befund 1 als eigenes Issue auslagern und dieses schliessen, so wie Befund 7 nach #108 gegangen ist. Bewusst nicht einseitig getan, weil die Labels des neuen Issues Antworten auf Fragen sind, die erst die Entscheidung gegen #82 beantwortet.
**Verifiziert:** 1290 Tests (1276 vorher, +8 fuer `--take-release`, +6 fuer den Feed-Fallback), auch gegen eine leere Maschine nach `testing-conventions.md` Schritt 6 identisch gruen; `docs verify` mit 73 ausgelieferten Dokumenten und 58 Referenzdateien, `instructions verify` mit 23 Instruktionen und 7 Skills. CI gruen auf allen drei Commits: [298](https://gitea.nehmer.net/torben/chemenu/actions/runs/298)/[299](https://gitea.nehmer.net/torben/chemenu/actions/runs/299), [300](https://gitea.nehmer.net/torben/chemenu/actions/runs/300)/[301](https://gitea.nehmer.net/torben/chemenu/actions/runs/301), [302](https://gitea.nehmer.net/torben/chemenu/actions/runs/302). Kein Release ausgeloest - `release.yml` reagiert nur auf ein suffixfreies `VERSION`.
**In der Abschlussphase zusaetzlich gefunden** und in `536093f` mitgenommen, weil beides Behauptungen ueber die geaenderten Flaechen waren: INSTALL.md fuehrte weiter `version check` als einzigen Befehl, der ins Netz geht, und `tools/CONTRACT.md` § "Future considerations (not implemented)" listete noch den MCP-Server-Wrapper und `dist upgrade` selbst - beide seit ihrer Umsetzung falsch und im selben Dokument weiter oben als existierend beschrieben. `docs/ownership-and-templates.md` dagegen geprueft und bewusst nicht angefasst: seine Begruendung, eine lokal geaenderte Datei solle *"a decision someone takes deliberately"* sein, ist genau die Eigenschaft, die `--take-release` umsetzt.
Changelog: Befund 1 nach #110 ausgelagert, dieses Issue geschlossen. Damit ist es kein offenes Arbeitspaket mehr, sondern der Laufbericht zum getraceten 5.0.0-auf-6.0.0-Upgrade - sechs Befunde erledigt, einer in #108 und einer in #110 zu eigenen Paketen geworden.
Was sich im Koerper geaendert hat: die Stand-Tabelle sagt jetzt pro Befund "erledigt" oder "ausgelagert nach #NNN"; Befund 1 traegt oben den Auslagerungsvermerk und behaelt seine Evidenz - Trace, Bucket-Messung, Scorer-Ausgabe -, weil die das Messergebnis dieses Laufs ist und #110 sie braucht, statt sie kopiert zu bekommen. Sein § Zu entscheiden ist weg, die Loesungsachsen stehen in #110. Die drei Akzeptanzkriterien zu Befund 1 sind gestrichen mit Ziel, nicht abgehakt, und die Kriterienliste ist auf "Erledigt" (13) plus "ausgelagert" (3) umgestellt. Befund 7 nennt jetzt explizit dasselbe Muster - Evidenz bleibt, Arbeit geht -, weil #110 sich darauf beruft. Reproduktionsschritt 5 ist als der einzige markiert, der unveraendert reproduziert, und als der, den #110 erbt.
Verweis in beide Richtungen:#110 nennt dieses Issue als Herkunft, verweist fuer die 63-Event-Trace auf den ersten Kommentar hier, und haelt fest, dass die Abhaengigkeit zu #82 in diese Richtung laeuft - #82 braucht eine Antwort aus #110, nicht umgekehrt, weil tool.pre/tool.post ohne gemeinsamen Schluessel ebenfalls unter der Harness-UUID landen.
#110 hat dabei ein Kriterium bekommen, das hier fehlte: dass nach einer Aenderung der Schluesselbildung kein bestehender Bucket in budget.json still neu interpretiert wird - kein geerbter Count fuer eine neue Sitzung, kein alter Count gegen eine neue angerechnet. budget.json ist eine maschinengelesene Datei, und "die Form einer maschinengelesenen Datei" steht im Grenzuebertritt-Katalog von instructions/dev/version-parts.md; der Versionsteil ist dort als vor dem ersten Commit zu beantworten vermerkt, nicht zu raten.
Labels von #110: area/process (wie #82, sein Geschwister), kind/decision, prio/planned, size/L - alle vier uebernommen aus dem Urteil, das hier zuletzt fuer Befund 1 allein galt. Kein status/blocked: #110 ist eigenstaendig bearbeitbar, die Abhaengigkeit laeuft in die andere Richtung.
**Changelog:** Befund 1 nach **#110** ausgelagert, dieses Issue geschlossen. Damit ist es kein offenes Arbeitspaket mehr, sondern der Laufbericht zum getraceten 5.0.0-auf-6.0.0-Upgrade - sechs Befunde erledigt, einer in #108 und einer in #110 zu eigenen Paketen geworden.
**Was sich im Koerper geaendert hat:** die Stand-Tabelle sagt jetzt pro Befund "erledigt" oder "ausgelagert nach #NNN"; Befund 1 traegt oben den Auslagerungsvermerk und behaelt seine Evidenz - Trace, Bucket-Messung, Scorer-Ausgabe -, weil die das Messergebnis dieses Laufs ist und #110 sie braucht, statt sie kopiert zu bekommen. Sein § Zu entscheiden ist weg, die Loesungsachsen stehen in #110. Die drei Akzeptanzkriterien zu Befund 1 sind **gestrichen mit Ziel**, nicht abgehakt, und die Kriterienliste ist auf "Erledigt" (13) plus "ausgelagert" (3) umgestellt. Befund 7 nennt jetzt explizit dasselbe Muster - Evidenz bleibt, Arbeit geht -, weil #110 sich darauf beruft. Reproduktionsschritt 5 ist als der einzige markiert, der unveraendert reproduziert, und als der, den #110 erbt.
**Verweis in beide Richtungen:** #110 nennt dieses Issue als Herkunft, verweist fuer die 63-Event-Trace auf den ersten Kommentar hier, und haelt fest, dass die Abhaengigkeit zu #82 in diese Richtung laeuft - #82 braucht eine Antwort aus #110, nicht umgekehrt, weil `tool.pre`/`tool.post` ohne gemeinsamen Schluessel ebenfalls unter der Harness-UUID landen.
**#110 hat dabei ein Kriterium bekommen, das hier fehlte:** dass nach einer Aenderung der Schluesselbildung **kein bestehender Bucket in `budget.json` still neu interpretiert** wird - kein geerbter Count fuer eine neue Sitzung, kein alter Count gegen eine neue angerechnet. `budget.json` ist eine maschinengelesene Datei, und "die Form einer maschinengelesenen Datei" steht im Grenzuebertritt-Katalog von `instructions/dev/version-parts.md`; der Versionsteil ist dort als vor dem ersten Commit zu beantworten vermerkt, nicht zu raten.
Labels von #110: `area/process` (wie #82, sein Geschwister), `kind/decision`, `prio/planned`, `size/L` - alle vier uebernommen aus dem Urteil, das hier zuletzt fuer Befund 1 allein galt. Kein `status/blocked`: #110 ist eigenstaendig bearbeitbar, die Abhaengigkeit laeuft in die andere Richtung.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Zweck
Vollstaendig getracete Durchfuehrung des dokumentierten Upgrade-Pfads (INSTALL.md § "Eine Instanz
aktualisieren", Weg Tarball) auf einer echten ausgelieferten Instanz - nicht im Ursprungs-Repo
und nicht im CI-Replay. Ziel war eine Validierungsgrundlage, gegen die derselbe Lauf auf einer
Dev-Instanz nachgestellt werden kann.
Die Telemetrie des Laufs steht verbatim im ersten Kommentar. Sieben Befunde unten; keiner hat den
Lauf zum Scheitern gebracht, vier davon kosteten jede Instanz Handarbeit, die der dokumentierte
Weg nicht vorsah.
Ausgangslage: Instanz aus
dist export-Tarball,VERSION5.0.0,kb_version5.0.0,63 Seiten,
language: de, Harness Claude Code, sauberer Arbeitsbaum, ein Remote.Herkunft der Befunde. 1 bis 4 stammen aus dem Lauf selbst (2026-09-15). 5 bis 7 und die
Ursachenanalyse unter Befund 1 stammen aus der Review-Sitzung vom 2026-09-16, die das
Sitzungstranskript, die drei Ergebnis-Commits und den Endzustand der Instanz gegengelesen hat.
Abgeschlossen: sechs Befunde erledigt, einer ausgelagert
504149cerledigt; was bleibt, ist eine Betreiberentscheidung und kein Bauauftragversion notesantwortete auf keiner Instanz0c98080,6.1.0-beta.4dist upgradekonnte die Release-Fassung nicht uebernehmen72d01be,6.1.0-beta.30e09cf4,6.1.0-beta.2## Authoring guidanceb5014d9) und im Stack (0e09cf4)0e09cf4504149c,6.1.0-beta.1Dieses Issue ist der Laufbericht und wird als solcher nicht weiter bearbeitet. Alle
Werkzeugaenderungen, die aus ihm Bauauftraege waren, sind gebaut; beide Befunde, die eigene
Arbeitspakete wurden, haben eigene Issues (#108 erledigt, #110 offen). Was hier bleibt und
weiterhin Wert hat, ist die Evidenz: die vollstaendige Trace im ersten Kommentar, die
Lauf-Zusammenfassung mit Zeitstempeln, und der Reproduktionsabschnitt, der pro Schritt sagt, ab
welcher Version er nicht mehr reproduziert.
Lauf-Zusammenfassung
version checkmigrate statusversion notesdist upgrade --dry-rundist upgradedist upgrade --dry-rungit commitder Handreparaturdist upgrade --dry-rundist upgrademigrate statusinstructions syncdoctorsession-id)docs verifytypes/concept.md,types/source.mddocs toc --applydocs verify/instructions verify/lintpublish96e2563ff596publish --confirmdcc17df, gepusht6.0.0-type-guidance-splitmigrate done 6.0.0 --pages 0publishc8c9f9b, 5 Dateien, unter der Gate-Schwelle33
wikitool.call-Events insgesamt, ein einziger unerwarteter Exit-Code (types describe source,Exit 1 um 20:06:09 - das war SIGPIPE durch ein
| headdes Aufrufers, kein Werkzeugfehler; sieheAnmerkung unter Befund 1 und die Ursachenverkettung in Befund 6).
Nicht in der Tabelle, weil nie gelaufen:
migrate verify --from <commit vor dem Tausch>,INSTALL.md Schritt 6, erster Pruefschritt. Siehe Befund 7.
Befund 1: Session-Id-Fallback zersplittert den Lauf - Join-Key tot, Iteration-Budget-Gate faktisch abgeschaltet
Ausgelagert nach #110. Die Evidenz bleibt hier stehen, weil sie das Messergebnis dieses Laufs
ist und den Befund ueberhaupt erst tragbar macht - dasselbe Muster wie bei Befund 7. Die
Entscheidung, die Loesungsachsen und die Akzeptanzkriterien stehen in #110.
Beobachtung
Der Lauf ist eine Sitzung. Die Telemetrie kennt ihn als 21 Sitzungen:
Aufgeschluesselt nach Quelle:
claude-code(Hook)session.start, 5prompt.submittedwikitool(Emitter)session.start, 33wikitool.call, 1gate.refused, 1gate.cleared, 2publish.commitWIKITOOL_SESSION_IDwar nicht gesetzt, der Fallback ist die Parent-PID(
chemenu/session.py). Der Harness startet pro Tool-Call eine neue Shell - also pro Aufrufeine neue PID und damit eine neue "Sitzung".
Folge 1: EVALS.md' Join-Key haelt nicht
EVALS.md§ Architecture: "Everything joins onWIKITOOL_SESSION_ID." In diesem Lauf jointnichts: die Hook-Events tragen die UUID, die wikitool-Events tragen 20 PIDs. Es gibt keinen
gemeinsamen Schluessel.
Das ist im Scorer direkt sichtbar.
eval score --session 3835548(die Sitzung mit derGate-Verweigerung) meldet:
Der Harness hat
prompt.submittedgemeldet - 5 mal, nur unter der UUID. Ausgerechnet dieL2-Regel, die Gate-Befolgung prueft, kann auf dem Hauptharness nie ein Urteil faellen. Das ist
kein fehlender Hook (#82), sondern ein toter Schluessel: #82 wuerde
tool.pre/tool.postergaenzen, die dann ebenfalls unter der UUID landen und weiterhin nicht zu den
wikitool.call-Events joinen. Die beiden Issues muessen zusammen gedacht werden, sonstverdrahtet #82 Hooks, deren Events immer noch niemand zuordnen kann.
Ausserdem scort
eval scorejeweils 1 bis 3 Calls statt 33, waehrend die L1-Strukturpruefung inallen 21 Buckets identisch neu berechnet wird. Ein Lauf ist so nicht bewertbar.
Folge 2: das Iteration-Budget-Gate erreicht seine Schwelle nie
Gravierender, weil es eine der vier in Code gegossenen Sicherungen ist (
AGENTS.md§ Gates:60 Calls pro Sitzung, Loop-Breaker bei 3 identischen in Folge).
tools/.wikitool_session/budget.json, Stand nach dem Lauf:Der Hoechststand ist 9 - und der stammt aus
wiki-setup-nathan, einem Bucket, in demWIKITOOL_SESSION_IDgesetzt war. Jeder PID-Bucket kommt ueber 3 nicht hinaus. Bei 33Calls in einem Lauf hat der Zaehler also nie mehr als 3 von 60 gesehen.
Damit ist das Gate unter diesem Harness nicht "grosszuegig", sondern strukturell
unerreichbar - und der Loop-Breaker gleich mit: drei identische Calls in Folge landen in drei
verschiedenen Buckets. Eine Sicherung, die ausdruecklich deshalb in Code sitzt, weil ein Agent
sich an einer Prompt-Regel vorbeireden kann (
docs/why-gates-are-code.md), ist hier still aus.doctorsagt das Noetige bereits - nur als WARN und ohne die Folge zu nennen:Ursache eine Ebene tiefer:
instructions/session-setup.mdwar auf diesem Harness wirkungslosNachgetragen aus der Review-Sitzung.
doctorverweist aufinstructions/session-setup.md, unddort stand als Schritt, woertlich:
"Run this once per working session" - genau das funktioniert auf Claude Code nicht. Der
Harness fuehrt jeden Bash-Tool-Call in einer frisch initialisierten Shell aus; das
Arbeitsverzeichnis wird uebernommen, Shell-State (Umgebungsvariablen, Funktionen) nicht. Ein
exportin Aufruf N ist in Aufruf N+1 verschwunden. Die 20 verschiedenen Parent-PIDs oben sinddieselbe Tatsache von der anderen Seite gemessen.
Das aendert die Reichweite des Befunds: eine bessere Fallback-Kette repariert den Messwert, aber
die Anleitung, die das Problem eigentlich verhindern soll, war auf dem Hauptharness ein No-op.
Erledigt mit
504149c(6.1.0-beta.1, aus #108):instructions/session-setup.mdnennt jetztdie Inline-Form pro Aufruf, sagt warum ein
exportnur traegt solange die Shell traegt, und gibtden Einzeiler an, mit dem sich beantworten laesst, welcher Fall vorliegt. Der Rest dieses Befunds -
Fallback-Kette, Join-Key, WARN-Haerte - ist davon unberuehrt und in #110 offen.
Nebenbefund zur Trace-Treue
types describe sourcesteht mitexit_code: 1in der Trace (20:06:09). Der Aufruf warerfolgreich; der Exit-Code entstand durch SIGPIPE, weil der Aufrufer die Ausgabe durch
| headgeschickt hat. Der unmittelbar folgende identische Aufruf ohne Pipe steht mit
exit_code: 0da.Wer die Trace als Fehlerquelle auswertet, zaehlt hier einen Werkzeugfehler, den es nicht gab.
Dasselbe
| headist die Ursache von Befund 5 - siehe Befund 6. In #110 als Nebenbefundmitgenommen, weil er dieselbe Trace betrifft.
Befund 2:
version notesscheiterte auf jeder ausgelieferten Instanz - erledigt (0c98080,6.1.0-beta.4)kind/defect- Doku und Realitaet widersprachen sich.INSTALL.md § "Eine Instanz aktualisieren", Schritt 4, verbatim (Stand des Laufs):
Der Aufruf auf der Instanz:
Ursache
version notesliest die lokaleCHANGES.md. Eine ausgelieferte Instanz bekommt dafuer denStub aus
tools/chemenu/dist_templates/CHANGES.md- 9 Zeilen, Vorwort, null Versionseintraege.CHANGES.mdsteht ausserdem inchemenu.ownership.is_upgrade_preserved, wird vondist upgradealso bewusst nie ueberschrieben. Der Stub bleibt der Stub - dauerhaft.
version noteskonnteauf einer Instanz nicht nur damals nicht funktionieren, sondern nie.
Das traf genau den Moment, fuer den der Schritt existiert: den Grenzuebertritt, an dem der
Betreiber wissen muss, was aufhoert zu funktionieren. Der Lauf kam nur weiter, weil die
Release-Notes ueber die Gitea-API gelesen wurden - ein Weg, den INSTALL.md an dieser Stelle nicht
nannte, und den eine Instanz ohne erreichbaren MCP-Server gar nicht hat.
Eine Zwischenstufe hat den Widerspruch dokumentarisch beseitigt, den Defekt aber nicht:
INSTALL.md verweist seit
504149caufinstructions/upgrade-instance.md, deren Schritt 2 dieRelease-Seite aus
release_urllas und ausdruecklich sagte, dassversion notesauf einerInstanz nicht antwortet.
Umgesetzt: Fallback auf den Release-Feed, laut angekuendigt
Von den beiden erwogenen Wegen der erste. Ausschlaggebend war, dass
instructions/upgrade-instance.mdSchritt 2 den eigenen Workaround-Absatz schon als temporaerfuehrte ("This paragraph stops being necessary the day
version notesfalls back to thatfeed"): die billigere Variante haette ihn dauerhaft stehenlassen.
Die Regel, in drei Faellen:
CHANGES.md-Eintrag ist da -> er wird gedruckt. Unveraendert, und der einzigeFall, den das Ursprungs-Repo und CI je erreichen.
unveraendert. Das ist der Wachhund gegen einen Netzaufruf im Ursprungs-Repo: nur eine
ausgelieferte Instanz geht online, und
release.ymlsversion notes > /tmp/release-notes.mdkann den neuen Pfad damit nie betreten. Ein Testverdrahtet einen Fetcher, der beim Aufruf
AssertionErrorwirft, und prueft, dass er nichtaufgerufen wird.
update_urlwird gefragt, derselbe, denversion checkbenutzt (WIKITOOL_UPDATE_URLund--urluebersteuern ihn wie dort).--offlineverweigert den Aufruf und faellt auf dieFehlermeldung zurueck, die dann
release_urlaus dem Stamp nennt.Die Notes gehen nach stdout, die Herkunft nach stderr.
version notesexistiert, damit derRelease-Workflow kein Markdown in der Shell parsen muss (
release.yml:version notes > /tmp/release-notes.md), also darf stdout nichts als den Eintrag tragen.Antwortet der Feed eine andere Version als die gefragte, wird das in der stderr-Kopfzeile
benannt und die Notes werden trotzdem gedruckt. Das ist nicht der Randfall, sondern der
Hauptfall: in Schritt 2 des Upgrades steht
VERSIONnoch auf der alten Version, waehrend diegesuchten Notes die der neuen sind. Der Feed kennt nur
/releases/latest-update_urlist dieeinzige URL, die der Stamp traegt, und eine
/releases/tags/<tag>-URL daraus zusammenzusetzenwaere geraten statt gelesen (Invariante 7).
Nicht erreichbarer Feed: Exit 1, mit dem Feed-Fehler und
release_urlaus dem Stamp. Einleerer
bodyfaellt genauso aus - eine leere Antwort darf nicht als "dieses Release hat nichtszu melden" durchgehen.
Nachgezogen:
instructions/upgrade-instance.mdSchritt 2 (Workaround-Absatz weg, dafuer diebeiden Dinge, die man vor dem Lesen der Ausgabe wissen muss), das Vorwort derselben Datei (es
nennt keine Werkzeugluecke mehr - es waren zwei, jetzt sind es null), INSTALL.md § "Version und
Updates" an zwei Stellen (die Notes-Quelle, und die inzwischen falsche Behauptung,
version checksei der einzige Befehl, der ins Netz geht),tools/CONTRACT.mdin beiden Tabellen sowiedie Modul-Docstrings von
version_cmd.pyundversion.fetch_latest, die dieselbeEin-Netzaufruf-Behauptung trugen.
Befund 3:
dist upgradekannte keinen Weg, die Release-Fassung einer lokal geaenderten Datei zu uebernehmen - erledigt (72d01be,6.1.0-beta.3)kind/defect/ fehlende Faehigkeit.Beobachtung
Der Dry-Run meldete genau eine lokal geaenderte Datei:
Der Unterschied war reine Whitespace-Formatierung einer Markdown-Tabelle (Spaltenauffuellung,
vermutlich ein Format-on-Save), inhaltlich identisch.
kb/CONTRACT.mdist dabei stackeigen:instructions/private-instance.mdfuehrt<stage>/CONTRACT.mdausdruecklich als Maschinerie, ander "an instance never edits it".
Beide damals angebotenen Wege waren hier falsch:
--keep-localbehielt die Drift. Der neue Stamp schreibt trotzdem die Release-Digest - dieDatei divergierte also bei jedem kuenftigen
dist upgradeerneut und wurde jedes Mal wiedergemeldet. Fuer eine Datei, die der Instanz gar nicht gehoert, der dauerhaft falsche Zustand.
entpackten Tarball, dann ein Commit nur zur Herstellung der Clean-Tree-Vorbedingung des
naechsten Kommandos. Drei Schritte, zwei davon ausserhalb des Werkzeugs. Die
Sauberkeits-Vorbedingung und die Handreparatur standen sich dabei gegenseitig im Weg: die
Reparatur macht den Baum unsauber, den das Kommando sauber verlangt.
Der Preis war eine Invariantenverletzung
Das schaerfste Argument fuer eine Werkzeugloesung: der Commit
7fe8353ist ein **rohesgit addgit commit**.AGENTS.mdInvariante 5 sagt "Never call rawgit commit/git push. Publishthrough
tools/wikitool publish" und kennt keine Ausnahme fuer "ist ja nur eine Vorbedingung".Richtig waere
tools/wikitool publish --no-pushgewesen.Bemerkenswert ist weniger der Fehlgriff als die Richtung: eine fehlende Werkzeugfaehigkeit hat den
Lauf an einer in Code gegossenen Regel vorbeigefuehrt, und nichts hat es gemeldet - kein Gate, kein
Check, kein Scorer.
Erwartungshaltung aus dem Transkript
Der Agent kuendigte um 19:46:28, vor dem Lauf ohne Flag, woertlich an: "I'll let the upgrade
take the release's version rather than pinning the local formatting" - und rief
dist upgradedann ohne Flag auf. Das Mentalmodell war also "Default = Release-Fassung nehmen"; der Default ist
Abbruch. Die Fehlermeldung korrigierte das nicht: sie nannte
--keep-localund "reconcile byhand", sagte aber nicht, dass es zu
--keep-localkein Gegenstueck gibt. Es fehlte damit nichtnur ein Flag, sondern auch der Satz, der die falsche Erwartung abfaengt.
Umgesetzt:
--take-release <pfad>, wiederholbar--take-releasenimmt einen Pfad und ist wiederholbar (wiesearch --field), statt ein pauschalesGegenstueck zu
--keep-localzu sein. Der Grund ist die Asymmetrie der beiden Antworten:--keep-locallaesst alles stehen und verliert nichts,--take-releaseverwirft eine lokaleAenderung. Eine Verwerfung benennt ihr Ziel - dasselbe Muster, das
issue-tracking.mdSchritt 1fuer destruktive Schritte verlangt -, und der gemischte Fall (zwei geaenderte Dateien, eine davon
zurueckzusetzen) ist damit ueberhaupt erst loesbar.
dort steht - und zwar auch im
--dry-run: das ist ein Fehler im Argument, nicht einZustand des Baums, und ein still ignorierter Tippfehler haette ein erfolgreiches Upgrade
gemeldet und die Aenderung behalten, die verworfen werden sollte. Es ist die einzige
Verweigerung, die einen Dry-Run nicht-null macht; ein blockierter Pfad tut das weiter nicht.
--take-releaseund--keep-localzusammen sind erlaubt und komponieren. Ohne--keep-localbricht ein blockierter Pfad, zu dem nichts gesagt wurde, weiter ab.--take-releasestimmt die Datei wieder mit der Stamp-Digest ueberein, die Drift ist alsoweg und nicht nur ueberschrieben - genau der Unterschied zu
--keep-local.wuerde.
Die Fehlermeldung des Abbruchs nennt alle drei Antworten samt fertiger Kommandozeile, im Muster
des Mass-Update-Gates, das seine
--confirm-Zeile ebenso zum Einsetzen ausdruckt:Nachgezogen:
instructions/upgrade-instance.mdSchritt 6 traegt statt derDrei-Schritt-Handreparatur die Entscheidung pro Pfad und den Dry-Run, mit dem man sie vorher
sieht; Schritt 7 nimmt die Flags mit.
tools/CONTRACT.mdin beiden Tabellen. Bewusst nichtgeaendert: die Dirty-Tree-Vorbedingung - mit
--take-releaseentfaellt die Handreparatur unddamit der unsaubere Baum, den sie erst erzeugte.
Befund 4:
6.0.0-type-guidance-splitnennt ein Beispiel, das in der Instanz das Gegenteil zeigt - erledigt (0e09cf4)kind/defect, Dokumentation. Betraf das mitgelieferte Migrationsdokument, nicht den Code.4a - das benannte Beispiel war in der Instanz die unmigrierte Datei
instructions/migrations/6.0.0-type-guidance-split.md, Schritt 4, verbatim (Stand des Laufs):Im Ursprungs-Repo stimmt das: dort ist
types/entity.mddie stackeigene, bereits migrierte Datei.In einer ausgelieferten Instanz ist
types/entity.mddie beim Setup adoptierte Kopie - alsogenau die Datei, die noch die alte, zu entfernende Prosa traegt. Wer dem Satz woertlich folgte,
schrieb den Vorher-Zustand ab.
Das gesuchte Beispiel liegt in der Instanz unter
types/entity.md.template- dort steht derNachher-Zustand (Pointer-Absatz,
guidance:-Feld in der Frontmatter), weildist upgradedieTemplates verbatim mitliefert.
Verschaerfung aus der Review-Sitzung, gegen den Baum bestaetigt: im Ursprungs-Repo existiert
ueberhaupt keine
types/*.md.template-Datei -dist exportre-keyttypes/<name>.mderst beimExport zur
.template(dist_cmd._owned_type_stem,_plan_types). Der Satz konnte in einerInstanz also nicht bloss unguenstig sein, er konnte dort strukturell nie stimmen.
Umgesetzt: der Schritt (jetzt Schritt 5, nach dem neu eingezogenen Vorher-Schritt aus Befund 6)
nennt
types/<name>.md.templateals Beispiel und sagt den Grund dazu -types/<name>.mdist dieadoptierte Kopie und damit die Datei, die gerade geaendert wird, die
.templatedaneben traegt denNachher-Zustand. Mit dem Zusatz, die
.templatefuer die Form zu lesen und nicht wholesale zukopieren: ihre
## Frontmatterund ihr## Templatesind die Stack-Defaults, nicht die derInstanz.
4b - die Sprachfrage blieb offen, direkt nachdem 6.0.0 sie verschoben hat
Das Dokument sagte nicht, in welcher Sprache der neue Pointer-Absatz zu schreiben ist. Fuer eine
Instanz mit
language: dewar das nicht ableitbar, denn 6.0.0 hatte diese Grenze gerade erst neugezogen (Bump "Control-Plane-Sprache universell",
docs/language-boundaries.md).Umgesetzt, und dabei gegen
types/type-spec.md§ "Who owns a type-spec" korrigiert. Die imurspruenglichen Befund vorgeschlagene Begruendung "ein Abschnitt, den die Instanz behaelt, ist
ihrer, also traegt er die KB-Sprache" haelt gegen den Baum nicht: die Sprachachse ist der
Leser, nicht der Eigentuemer. Anleitungsprosa in einem Type-Spec ist Control Plane und damit
englisch, unabhaengig davon, wem die Datei gehoert - so steht es in der Tabelle in
types/type-spec.md, so steht es im Docstring vondist_cmd.instance_owned_type_stems("Ownership, not language"), und so steht es in
AGENTS.md§ File naming. Das Ergebnis vonBefund 5 war trotzdem richtig, nur die Begruendung nicht: der uebrige Bullet war redundant und
gehoerte weg, nicht uebersetzt.
Das Dokument sagt jetzt entsprechend:
## Frontmatter-Tabelle## Template-Block + NachsatzDie Tabelle steht im Dokument nicht als Kopie - der Schritt sagt die Antwort in zwei Saetzen
und verlinkt
types/type-spec.md§ "Who owns a type-spec" fuer die vollstaendige Aufteilung(Invariante 8). Der Satz "eine englische Ueberschrift ueber einem Koerper in einer anderen
Sprache ist die halbfertige Fassung dieses Schritts" benennt genau den Zustand, den Befund 5
produziert hat.
Dazu neu, weil es der eigentliche Mechanismus hinter Befund 5 ist: eine behaltene Notiz darf
nicht
## Authoring guidanceheissen.types describesetzt diesen Kopf selbst und inlineddarunter die Guidance-Datei, die ihren eigenen gleichnamigen Abschnitt mitbringt - ein dritter aus
dem Type-Spec-Koerper ist die Doppelung.
4c - kleinere Beobachtung zur Schrittfolge
Schritt 6 (
migrate done 6.0.0 --pages 0) tut genau, was es verspricht, und die Ausgabe istunmissverstaendlich:
Danach bleibt
doctorbeikb-version: 5.0.0 (nothing outstanding up to 6.0.0). Das ist korrektund dokumentiert, sieht aber auf den ersten Blick nach einem haengengebliebenen Upgrade aus. Keine
Aenderung noetig - hier nur vermerkt, weil eine Dev-Instanz-Validierung sonst darueber stolpert.
Befund 5: Rest-Abschnitt
## Authoring guidance- repariert in der Instanz (b5014d9) und im Stack (0e09cf4)kind/defect. Gefunden in der Review-Sitzung, live gegengeprueft und dort auch behoben. Stehthier, weil er die Wirkung von Befund 4b und Befund 6 belegt.
5a - die Instanz (
b5014d9)Bei
types/source.mdwurde der Abschnitt## Autorenanweisungennicht geloescht, sondern zu## Authoring guidanceumbenannt, mit einem verbliebenen deutschen Bullet:Zwei Fehler in drei Zeilen:
Normalzustand und kein Befund:
types describesetzt selbst einen## Authoring guidance-Kopf und inlined darunter die Guidance-Datei, die ihren eigenen gleichnamigenAbschnitt mitbringt - so sieht es bei allen vier Typen aus.
sourcehatte danach einendritten aus dem Type-Spec selbst.
an der das falsch ist, ist der Leser, nicht der Eigentuemer - siehe die Korrektur unter
Befund 4b: Anleitungsprosa bleibt englisch, auch in einer Datei, die der Instanz gehoert.
Richtig war hier trotzdem das Entfernen, weil der Bullet redundant zum
title_prefix: "Source - "in der Frontmatter war, daswikitool new sourceohnehin erzwingt.entity,conceptundcomparisonwaren sauber - betroffen war nursource, der einzige dervier Typen, dessen Prosa umfangreich genug war, dass ein Bullet uebrigblieb, den die
Stack-Guidance nicht abdeckt.
Behoben mit
b5014d9: Abschnitt ersatzlos entfernt, geprueft mit dem Muster aus Befund 6 -types describe sourcevor und nach der Aenderung in je eine Datei, danndiff. Der Diffzeigt genau die vier entfernten Zeilen und sonst nichts; alle vier Typen komponieren jetzt mit
zwei Koepfen.
5b - derselbe Defekt im Stack selbst (
0e09cf4)Nachgetragen 2026-09-16, gefunden beim Nachmessen im Ursprungs-Repo. Das Kriterium unten war
gegen die Instanz
nathanabgehakt, gegen den Stack nie - und dort stand derselbe Rest-Abschnitt:types/source.mdim Ursprungs-Repo trug bis0e09cf4einen## Authoring guidance-Abschnitt mitdemselben einen Bullet (hier englisch, weil
c64479fihn uebersetzt hatte;d49513bhat dann denRest der Prosa ausgelagert und ihn stehenlassen). Das ist kein Instanzschaden, sondern ein
Auslieferungsdefekt: die Datei wird beim Export zu
types/source.md.template, jede neuaufgesetzte Instanz haette ihn mit adoptiert - und ihn dann bei der naechsten
guidance-Migration genau so wiedergefunden wienathan.Entfernt, geprueft mit demselben Vorher/Nachher-Diff: genau vier Zeilen weg, sonst nichts, alle
vier Typen bei zwei Koepfen. Inhaltlich verloren geht nichts -
title_prefixsteht in derFrontmatter, in der Feldtabelle von
types/type-spec.mdund im Nachsatz zum## Template-Blockvon
types/source.md; ausserdem verbietettypes/type-spec.md§ Writing Shape das Wiederholeneiner Schema-Regel im Fliesstext ausdruecklich.
Nebenbeobachtung, kein Befund: dass
types describeeinen## Authoring guidance-Kopf setztund unmittelbar darunter eine Guidance-Datei mit eigenem H1 und eigenem gleichnamigem Abschnitt
inlined, ist eine kosmetische Doppelung im Stack selbst - gleich fuer alle vier Typen, ohne
Auswirkung auf Inhalt oder Werkzeuge.
Befund 6: Schritt 5 des Migrationsdokuments war als Verifikation unfalsifizierbar - erledigt (
0e09cf4)kind/defect, Dokumentation. Direkte Ursache von Befund 5.instructions/migrations/6.0.0-type-guidance-split.md, Schritt 5, verlangte:Kein Schritt davor hielt das Vorher fest. Eine Pruefung gegen einen Zustand, den niemand
aufgeschrieben hat, ist keine Pruefung - sie faellt auf das Gedaechtnis des Ausfuehrenden zurueck,
und bei einer Ausgabe von ueber 150 Zeilen je Typ ist das keins.
Der Lauf hat entsprechend geprueft:
types describedurch| head -250und| tail -80, also inAusschnitten, mit dem Urteil "All four compose correctly, structurally identical to before". Der
Rest-Abschnitt aus Befund 5 liegt in der Mitte der
source-Ausgabe und war in keinem der beidenAusschnitte. Dasselbe
| headerzeugte zugleich den SIGPIPE-Exit-1, den der Nebenbefund unterBefund 1 als Trace-Rauschen fuehrt: ein Verhalten, zwei Symptome.
Umgesetzt an beiden Stellen:
types describe <name>vollstaendig ineine Datei, vor der Aenderung, mit dem Satz, dass ein
head/tailgenau die Mitteverschwinden laesst. Schritt 6 diffed dagegen und nennt zusaetzlich die Ein-Zahl-Probe
grep -c '^## Authoring guidance': zwei ist richtig, drei heisst Rest-Abschnitt im Type-Spec.Die alten Schritte 3-6 sind auf 4-7 gerueckt, die Querverweise mit.
instructions/migrate-corpus.md§ "Writing the migration document" traegt das Mustergenerisch: ein Verifikationsschritt nennt seine eigene Baseline, und zwar in einem frueheren
Schritt.
migrate verifytraegt seine im letzten Commit, ob jemand daran denkt oder nicht -eine Migration an der Maschinerie statt an
kb/hat gar keine, und genau dort entsteht dieBehauptung, die sich nicht widerlegen laesst.
Nicht angefasst:
instructions/upgrade-instance.mdSchritt 12, der seit504149cdasselbe inRichtung des Ausfuehrenden sagt. Zwei Adressaten, zwei Regeln - der Autor eines
Migrationsdokuments und der Betreiber, der eines abarbeitet -, also keine zweite Kopie im Sinne
von Invariante 8.
Befund 7: der Upgrade-Pfad hatte keine agentengerichtete Prozedur - ausgelagert nach #108, dort erledigt
kind/defect, Prozess. Umgesetzt mit504149c(6.1.0-beta.1); hier bleibt die Evidenz aus demLauf stehen, weil sie die Begruendung der Loesung traegt - dasselbe Muster, mit dem Befund 1 jetzt
nach #110 gegangen ist.
Die Schrittfolge stand nur in INSTALL.md - einem Dokument fuer Menschen (
AGENTS.md§ Filenaming: README-foermige Wurzeldateien werden "never by an agent as instruction" geladen).
Ausgefuehrt wird sie von einem Agenten. Der erste Tool-Call des Laufs listete
instructions/mit - der Agent suchte also zuerst eine Instruktion, fand keine, oeffnete
instructions/private-instance.md(der falsche Weg: Clone mit gemeinsamer History stattTarball-Instanz), verwarf sie und griff auf INSTALL.md zurueck.
Was im selben Lauf daraus folgte:
migrate verify --from <commit vor dem Tausch>wurde nie ausgefuehrt, obwohl es inINSTALL.md Schritt 6 der erste Pruefschritt war. Die Lauf-Tabelle oben belegt es lueckenlos.
Der Grund ist praezise benennbar: der Lauf folgte nicht INSTALL.md, sondern dem
Abschlussbericht von
dist upgrade- und in dessen Liste kammigrate verifynicht vor.Ende von Schritt 6, die andere am Ende des Abschlussberichts. Die optionale Migration lief
anschliessend unter dem 5.0.0-Kontrollplan, obwohl
AGENTS.mdim selben Commit +44/-3 bekommenhatte - und genau dort fielen Befund 4b (Sprachgrenze, neu in 6.0.0) und Befund 5 an.
tatsaechlich gelaufene. Dass der Lauf der zweiten folgte, machte die erste zu toter Doku -
genau der Zustand, den Invariante 8 verbietet.
Umgesetzt:
instructions/upgrade-instance.md(manual: true) ist die eine Fassung, dreizehnSchritte, mit dem Sitzungsneustart zwischen Maschinerie-Publish und Migrationskette statt am Ende
und
migrate statusals Wiedereinstiegspunkt. Der Abschlussbericht vondist upgradenennt jetztdie Datei und das Wiedereinstiegskommando statt einer eigenen Liste, INSTALL.md nur noch die
Entscheidung davor. Details und Abgrenzung: #108.
Was der Lauf bestaetigt hat
Ausdruecklich keine Befunde - das hat gehalten:
voraus, dass
docs verifynach dem Update auf adoptierten Page-Type-Specs ohne TOC-Regionfaellt, und nannten
docs toc --applyals Reparatur. Genau das trat ein(
types/concept.md,types/source.md), und genau das reparierte es - ein Werkzeuglauf, keineInhaltsmigration. Nicht identisch mit #106: dort ging es um
kb/CONVENTIONS.md.templateaufeiner frischen Instanz; hier um adoptierte
types/*.mdauf dem Upgrade-Pfad. Der in kb/CONVENTIONS.md.template traegt keine TOC-Region: frische Instanz und CI scheitern an docs verify (#106)umgesetzte Fix (Templates in
toc.target_files()) deckt diesen Fall nicht ab, weil diebetroffenen Dateien bereits im Dateisatz stehen - ihnen fehlte die Region nur, weil sie vor der
Scope-Erweiterung adoptiert wurden.
dist upgradeklassifiziert korrekt - 202 unveraendert / 8 neu / 0 lokal geaendert /0 entfallen, und die 8 neuen Dateien waren exakt die des Bumps (4
*.guidance.md,types/type-guidance.md(.schema.yaml),docs/language-boundaries.md, das Migrationsdokument).migrate statushat richtig nicht blockiert. "Migration: none required" aus denRelease-Notes und "Nothing outstanding" aus dem Werkzeug stimmten ueberein.
Inhalt gebunden, Freigabe mit demselben Token akzeptiert,
gate.refused/gate.clearedbeide inder Trace. Als Datenpunkt fuer #53: 69 Dateien bei Schwelle 10, und der zweite Publish desselben
Laufs lag mit 5 Dateien darunter.
Korrigiert durch Befund 5:types describekomponiert nach der Migration unveraendert.fuer
entity,conceptundcomparisonstimmte es; fuersourcenicht - dort stand bisb5014d9(Instanz) bzw.0e09cf4(Stack) ein dritter## Authoring guidance-Kopf in derkomponierten Ausgabe. Was fuer alle vier haelt: Frontmatter-Felder und
## Template-Block sindbyte-gleich, nur die Herkunft der Anleitungsprosa hat gewechselt.
Reproduktion auf einer Dev-Instanz
Die Reproduktion gilt gegen einen 6.0.0-Tarball - also gegen die Maschinerie, in der die
Befunde aufgetreten sind. Wie ein Lauf gegen das Ergebnis dieses Issues stattdessen ausgeht, steht
unter dem Block.
Was ab dem jeweiligen Stand nicht mehr reproduziert:
6.1.0-beta.26.1.0-beta.3ERWARTET Exit 1- der Abbruch nennt jetzt--take-release <pfad>als dritten Weg, und der erledigt den Fall in einem Kommando statt in drei Handgriffen6.1.0-beta.4ERWARTET Exit 1-version notesantwortet aus dem FeedSchritt 5 reproduziert unveraendert und ist damit der Reproduktionsabschnitt, den #110 erbt.
Akzeptanzkriterien
Erledigt:
instructions/session-setup.mdnennt eine Form, die auf einemHarness mit Shell-pro-Tool-Call wirkt - nicht nur
exporteinmal pro Sitzung. Erledigt in#108 (
504149c): Inline-Form pro Aufruf, mit dem Einzeiler zum Feststellen, welcher Fallvorliegt.
tools/wikitool version notesliefert auf einer frisch ausgelieferten Instanzdie Notes des installierten Release aus dem Feed - stdout traegt nur den Eintrag, die
Herkunftszeilen gehen nach stderr,
--offlineverweigert den Netzaufruf, und einDev-Checkout ohne Release-Stamp betritt den Pfad nie. Erledigt mit
0c98080; dieStamp-Grenze ist mit einem Test abgesichert, der einen werfenden Fetcher verdrahtet und
prueft, dass er nicht aufgerufen wird.
release_urlaus demStamp - nie eine stille Antwort und nie ein Abbruch ohne die Seite, die der Mensch
stattdessen lesen kann. Ein leerer
bodyfaellt genauso aus.instructions/upgrade-instance.mdSchritt 2 traegt den Workaround-Absatz nichtmehr, und INSTALL.md § "Version und Updates" sagt nicht mehr, dass der Befehl auf einer
Instanz nicht antwortet. Das Vorwort der Instruktion nennt gar keine Werkzeugluecke mehr -
es waren zwei, beide sind mit diesem Issue weg.
die Release-Fassung zuruecksetzen (
dist upgrade <source> --take-release <pfad>), ohneHandkopie und ohne Vorbereitungs-Commit;
tools/CONTRACT.mdsdist upgrade-Zeile und dieFehlerkontrakt-Zeile nennen den Weg. Erledigt mit
72d01be.--take-release-Pfad, der nicht blockiert ist, ist Exit 1 - auch im--dry-run, damit ein Tippfehler vor dem Schreiblauf auffaellt. Es bleibt die einzigeVerweigerung, die einen Dry-Run nicht-null macht; ein blockierter Pfad tut das weiter nicht.
Kommandozeile und sagt, dass keine davon der Default ist - so, dass "Default = Release
nehmen" nicht als Erwartung stehenbleibt. Mit einem Test, der genau auf diese vier
Bestandteile prueft.
types/entity.md.templateund sagt, inwelcher Sprache der Pointer-Absatz zu schreiben ist. Erledigt mit
0e09cf4; die Sprachregelist dabei gegen
types/type-spec.md§ "Who owns a type-spec" korrigiert worden (Leser, nichtEigentuemer - siehe Befund 4b) und wird verlinkt statt kopiert.
tools/wikitool types describe sourcegibt in der Instanznathanso viele## Authoring guidance-Koepfe aus wie bei den anderen drei Typen (zwei), und kein Abschnittdort traegt eine englische Ueberschrift ueber deutschem Inhalt. Erledigt mit
b5014d9,geprueft per Vorher/Nachher-Diff.
types/source.mdbeim Export zutypes/source.md.templatewird - sonst adoptiert jede neue Instanz den Defekt erneut.Erledigt mit
0e09cf4, geprueft per Vorher/Nachher-Diff: vier entfernte Zeilen, alle vierTypen bei zwei Koepfen.
und einen Diff;
instructions/migrate-corpus.mdfuehrt dasselbe Muster fuer kuenftigeassisted-Migrationen. Erledigt mit0e09cf4(neuer Schritt 3 im Dokument,§ "Writing the migration document" in
migrate-corpus.md).instructions/upgrade-instance.md(manual: true)existiert,
dist upgrades Abschlussbericht nennt sie, INSTALL.md traegt die Schrittfolgenicht mehr doppelt.
pytest,docs verify,instructions verifyohne neue Befunde fuer alles, was diesesIssue gebaut hat. Stand
6.1.0-beta.4: 1290 Tests (1276 vor der letzten Sitzung, +8 fuer--take-release, +6 fuer denversion notes-Fallback), auch gegen eine leere Maschine nachinstructions/dev/testing-conventions.mdSchritt 6 identisch gruen;docs verifymit 73ausgelieferten Dokumenten und 58 Referenzdateien;
instructions verifymit 23 Instruktionenund 7 Skills. CI gruen auf
72d01be(Laeufe 298, 299),0c98080(300, 301) und536093f(302).
Nicht erledigt, sondern nach #110 ausgelagert - dort in praezisierter Form, mit einer
zusaetzlichen Bedingung zur Bucket-Neuinterpretation, die hier fehlte:
Ein Lauf auf Claude Code landet in genau einem Telemetrie-Bucket-> Session-Id-Fallback zersplittert einen Lauf: Telemetrie-Join-Key tot, Iteration-Budget-Gate strukturell unerreichbar (#110)Das Iteration-Budget-Gate zaehlt ueber einen ganzen Lauf; ein Test mit 61 Aufrufen aus je-> Session-Id-Fallback zersplittert einen Lauf: Telemetrie-Join-Key tot, Iteration-Budget-Gate strukturell unerreichbar (#110)eigener Shell sieht die Verweigerung
Entschieden und dokumentiert, wie sich das zu #82 verhaelt-> Session-Id-Fallback zersplittert einen Lauf: Telemetrie-Join-Key tot, Iteration-Budget-Gate strukturell unerreichbar (#110)Herkunft und Artefakte
Lauf vom 2026-09-15, Instanz
5.0.0 -> 6.0.0, Harness Claude Code, Sitzung671c1b9a.Ergebnis-Commits in der Instanz:
7fe8353(Handreparatur aus Befund 3),dcc17df(Maschinerie),c8c9f9b(optionale Migration).Review-Sitzung vom 2026-09-16: hat Transkript, die drei Commits und den Endzustand gegengelesen
und Befund 5, 6, 7 sowie die Ursachenabschnitte unter Befund 1 und 3 ergaenzt. Befund 5 ist dabei
live gegen die Instanz geprueft, nicht aus dem Transkript abgeleitet, und in derselben Sitzung
repariert (
b5014d9intorben/nathan). Aus derselben Sitzung stammt #108, das Befund 7abgeschlossen hat.
Erste Umsetzungssitzung vom 2026-09-16 (
0e09cf4,6.1.0-beta.2): Befund 4 und 6 umgesetzt,dabei die Sprachbegruendung aus Befund 4b/5 gegen
types/type-spec.mdkorrigiert und dieStack-Haelfte von Befund 5 gefunden und behoben. Modelle: Entwurf, Versionsteil und Grenzurteile
auf Opus 5, mechanische Mitte auf Opus 5, Abschluss auf Opus 5 - der in
stack-devSchritt 3angebotene Wechsel auf Sonnet wurde angeboten und nicht gezogen.
Zweite Umsetzungssitzung vom 2026-09-16 (
72d01be=6.1.0-beta.3,0c98080=6.1.0-beta.4,536093f= Doku-Nachzug ohne Bump): Befund 3 und 2 entschieden und gebaut, inzwei Publishes plus einem Nachzug aus der Abschlussphase. Beide Bumps
--minormit--impact medium- neue Faehigkeit, in beide Richtungen ein Drop-in, kein Grenzuebertritt, unddamit bleibt die Bump-Liste des Kandidaten flach, wie sie es bei vier gleichgewichtigen
Upgrade-Pfad-Aenderungen sein soll. In der Abschlussphase zusaetzlich gefunden und mitgenommen:
INSTALL.md behauptete weiter,
version checksei der einzige Befehl, der ins Netz geht, undtools/CONTRACT.md§ "Future considerations (not implemented)" fuehrte noch denMCP-Server-Wrapper und
dist upgradeselbst - beide seit ihrer Umsetzung falsch und im selbenDokument weiter oben als existierend beschrieben. Modelle: Entwurf, Versionsteil und Grenzurteile
auf Opus 5, mechanische Mitte (Code, Tests, Bumps, Pruefungen) auf Opus 5, Abschluss auf Opus 5 -
der Wechsel auf Sonnet wurde an beiden vorgesehenen Stellen angeboten und nicht gezogen.
Abschluss am 2026-09-16: Befund 1 nach #110 ausgelagert und dieses Issue geschlossen. Die
Evidenz zu Befund 1 bleibt hier - Trace, Bucket-Messung, Scorer-Ausgabe, Reproduktionsschritt 5 -,
weil sie das Messergebnis dieses Laufs ist; #110 traegt die Entscheidung, die Loesungsachsen und
praezisierte Akzeptanzkriterien. Dasselbe Muster wie bei Befund 7 und #108, an derselben Stelle
begruendet.
Nicht stale, und deshalb bewusst nicht angefasst:
docs/ownership-and-templates.md. SeineBegruendung - eine lokal geaenderte Datei sei "a decision someone takes deliberately instead of
one an upgrade takes for them" - ist genau die Eigenschaft, die
--take-releaseumsetzt, indemes den Pfad benennen laesst.
docs/version-model.mdunddocs/why-gates-are-code.mdsind vonbeiden Aenderungen nicht beruehrt.
Erster Kommentar traegt die vollstaendige Telemetrie des Laufs verbatim - 63 Events, aus 21
Bucket-Dateien nach
tszusammengefuehrt. Einzige Aenderung: die drei identischen 69-Pfad-Arraysin
gate.refused/gate.cleared/publish.commitsind elidiert und die Liste steht einmal darunter.Das ist der Teil dieses Issues, den #110 als Evidenz braucht und der hier bewusst bleibt, statt
kopiert zu werden.
Das rohe Sitzungstranskript ist bewusst nicht angehaengt: die Instanz ist privat, das
Transkript traegt Seitentitel, Infrastrukturthemen und Kontaktdaten aus
kb/, und dieses Repo istoeffentlich (
instructions/private-instance.md: "the cost of a mistaken push is disclosure ratherthan inconvenience"). Die Telemetrie unten ist dagegen geprueft frei davon - sie enthaelt nur
Stack-Pfade, Kommandonamen und Exit-Codes. Wer das Transkript fuer die Dev-Instanz-Validierung
braucht, bekommt es auf Anfrage redigiert (ANSI entfernt, Instanzinhalte maskiert).
Nebenbefund aus genau dieser Redaktion, weil er
chemenu.telemetry.scrubbetrifft: eineMaskierung per Regex ueber Terminalausgabe greift nicht, solange die ANSI-Sequenzen drinstehen -
ssh://git@host:PORT/...undVorname Nachname <mail@host>waren durch eingestreuteFarbcodes aufgetrennt und ueberlebten den ersten Durchgang unmaskiert. ANSI muss vor der
Maskierung entfernt werden, nicht danach. Nicht in #110 mitgenommen - eigenstaendiger Befund an
einer anderen Stelle, und bis heute kein eigenes Issue.
Telemetrie des Laufs - verbatim
63 Events, zusammengefuehrt aus den 21 Bucket-Dateien unter
reports/telemetry/<session>/trace.jsonl(Filter:
-newermt "2026-09-15 21:40"lokal = 19:40 UTC), sortiert nachts. Unveraendert bis aufeine Sache: die drei identischen 69-Pfad-Arrays in
gate.refused,gate.clearedund dem erstenpublish.commitsind durch<<69 Pfade - elidiert, Liste einmal unten>>ersetzt; die Liste stehtdarunter. Ausserdem sind bei
prompt.submitteddie Feldertranscript_pathundcwdentfernt(lokale Pfade, kein Informationswert hier) -
prompt,prompt_sha256undprompt_lengthstehenunveraendert da.
Dass dieselbe Datei 21
session_id-Werte traegt, ist Befund 1 und kein Artefakt der Zusammenfuehrung.Die elidierte 69-Pfad-Liste
Identisch in
gate.refused,gate.clearedund dem erstenpublish.commit- das ist derCommit
dcc17df:Abgeleitete Kennzahlen
Event-Histogramm ueber die 63 Zeilen:
Laufzeiten, soweit auffaellig:
docs verifykalt 1644.6 ms, danach 403-415 ms (dreimal gemessen).publish --confirm1181.8 ms inklusive Push, der zweite Publish 970.4 ms. Alles andere unter300 ms.
budget.statetaucht in der ganzen Trace nicht auf, obwohl das Event imcompleteness-Feld jedessession.startals meldbar gefuehrt wird - passt zu Befund 1, Folge 2:ein Zaehler, der nie in die Naehe seiner Schwelle kommt, hat auch nichts zu melden.
Upgrade-Lauf 5.0.0 -> 6.0.0 auf ausgelieferter Instanz: Laufbericht, Telemetrie, vier Befundeto Upgrade-Lauf 5.0.0 -> 6.0.0 auf ausgelieferter Instanz: Laufbericht, Telemetrie, sieben BefundeChangelog: Review-Sitzung vom 2026-09-16 gegen Transkript, die drei Ergebnis-Commits und den Endzustand der Instanz. Neu: Befund 5 (Rest-Abschnitt
## Authoring guidanceintypes/source.md, doppelter Kopf intypes describe source- live gegengeprueft), Befund 6 (Schritt 5 des Migrationsdokuments prueft gegen ein Vorher, das niemand festhaelt - direkte Ursache von Befund 5), Befund 7 (Upgrade-Pfad ohne agentengerichtete Prozedur;migrate verifynie gelaufen, Session nie neu gestartet, drei konkurrierende Reihenfolgen). Befund 1 hat einen Ursachenabschnitt bekommen:instructions/session-setup.mdschreibtexport ...einmal pro Sitzung, was auf einem Harness mit Shell-pro-Tool-Call wirkungslos ist. Befund 3 hat die Erwartungshaltung aus dem Transkript, Befund 4a die Verschaerfung, dass es im Ursprungs-Repo gar keinetypes/*.md.templategibt.Korrigiert: die Zeile unter "Was der Lauf bestaetigt hat",
types describekomponiere unveraendert - das gilt fuer drei der vier Typen, nicht fuersource. Akzeptanzkriterien um vier Punkte erweitert, Reproduktionsabschnitt um Schritt 6 (Diff-Gegentest). Befund 7 ist als Bauauftrag nach #108 ausgelagert. Titel undsize/nachgezogen: vier -> sieben Befunde,size/M->size/L, weil daraus jetzt mehrere Arbeitspakete werden statt eines.Changelog: Befund 5 ist repariert (
b5014d9intorben/nathan) und das Kriterium abgehakt. Dabei eine Zahl in Befund 5 korrigiert: zwei## Authoring guidance-Koepfe sind der Normalzustand -types describesetzt selbst einen und inlined darunter die Guidance-Datei mit ihrem eigenen gleichnamigen Abschnitt, bei allen vier Typen.sourcehatte einen dritten aus dem Type-Spec. Der Reproduktionsabschnitt nennt jetzt 3 statt 2 als Erwartungswert und die Gegenprobe an den anderen drei Typen. Befund 6 traegt die Gegenprobe: die Reparatur lief mit Vorher-Datei und Diff, und genau deshalb war das Ergebnis belastbar. "Stand"-Absatz oben auf drei erledigte Punkte aktualisiert.Changelog: Befund 4 und 6 umgesetzt und abgehakt (
0e09cf4,6.1.0-beta.2). Das Migrationsdokument hat einen eigenen Vorher-Schritt bekommen (alte Schritte 3-6 sind auf 4-7 gerueckt), verweist auftypes/<name>.md.templatestatttypes/<name>.mdund nennt die Sprache des Pointer-Absatzes;instructions/migrate-corpus.md§ "Writing the migration document" traegt das Baseline-Muster jetzt generisch.Eine Begruendung korrigiert: die Sprachbegruendung unter Befund 4b/5 - "ein Abschnitt, den die Instanz behaelt, ist ihrer, also traegt er die KB-Sprache" - haelt gegen den Baum nicht. Die Sprachachse ist der Leser, nicht der Eigentuemer:
types/type-spec.md§ "Who owns a type-spec",AGENTS.md§ File naming und der Docstring vondist_cmd.instance_owned_type_stems("Ownership, not language") sagen uebereinstimmend, dass Anleitungsprosa in einem Type-Spec englisch bleibt, auch in einer instanzeigenen Datei. Das Ergebnis von Befund 5 war trotzdem richtig, nur der Weg dorthin - der Bullet war redundant und gehoerte weg, nicht uebersetzt. Das Dokument sagt das jetzt so und verlinkt die Tabelle, statt sie ein drittes Mal zu kopieren.Neuer Teilbefund 5b, gefunden beim Nachmessen:
types/source.mdtrug im Ursprungs-Repo denselben Rest-Abschnitt wie die Instanz -types describe sourcegab hier 3## Authoring guidance-Koepfe aus, die anderen drei Typen 2. Das Kriterium zu Befund 5 war gegennathanabgehakt, gegen den Stack nie. Da die Datei beim Export zutypes/source.md.templatewird, haette jede neu aufgesetzte Instanz den Defekt adoptiert. Entfernt in0e09cf4, geprueft mit genau dem Vorher/Nachher-Diff, den Befund 6 jetzt vorschreibt. Befund 5 ist dadurch in 5a (Instanz) und 5b (Stack) geteilt, mit einem zweiten Kriterium.Ausserdem: "Stand"-Absatz auf fuenf erledigte Punkte, Schritt 6 des Reproduktionsabschnitts um den Hinweis ergaenzt, dass er gegen einen 6.0.0-Tarball gilt und ab
6.1.0-beta.2nicht mehr reproduziert. Nicht geaendert: Befund 1, 2 und 3 - die drei offenen sind jetzt ausschliesslich Werkzeugaenderungen. Labels bleiben (size/Ltraegt weiter, Befund 1 allein ist es).Bewusst nicht angefasst:
instructions/upgrade-instance.mdSchritt 12. Dessen Absatz richtet sich an den Ausfuehrenden einer Migration, der neue Satz inmigrate-corpus.mdan den Autor eines Migrationsdokuments - zwei Adressaten, keine zweite Kopie im Sinne von Invariante 8.Verifiziert: lokal 1276 Tests,
docs verifyundinstructions verifygruen; CI auf0e09cf4gruen in den Laeufen 296 und 297. Kein Release ausgeloest -release.ymlreagiert nur auf ein suffixfreiesVERSION, und6.1.0-beta.2traegt eines.Changelog: Befund 2 und 3 sind entschieden; die beiden § Loesungsvorschlag-Abschnitte sind durch § Entschieden ersetzt und tragen jetzt die Regel statt der Alternativen.
Befund 2: von den beiden Wegen der erste -
version notesfaellt auf den Feed ausupdate_urlzurueck, statt nur zu sagen, wo die Notes stehen. Ausschlaggebend war, dassinstructions/upgrade-instance.mdSchritt 2 den eigenen Workaround-Absatz schon als temporaer fuehrt ("This paragraph stops being necessary the dayversion notesfalls back to that feed"). Drei Praezisierungen, die im Vorschlag nicht standen und die die Kollision mitversion.pys "the one place inwikitoolthat talks to a remote host" aufloesen: der Fallback greift nur bei vorhandenem Release-Stamp, ein Dev-Checkout betritt ihn also nie undrelease.ymlsversion notes > /tmp/release-notes.mdkann keinen Netzaufruf ausloesen; die Notes gehen nach stdout, die Herkunftszeilen nach stderr, damit der Redirect weiter nur den Eintrag traegt;--offlineverweigert den Aufruf. Der Feed kennt nur/releases/latest, also wird eine abweichende Version benannt statt eine/releases/tags/<tag>-URL zu raten - der Hauptfall ist ohnehin, dassVERSIONin Schritt 2 noch die alte ist.Befund 3:
--take-releasenimmt einen Pfad und ist wiederholbar, statt ein pauschales Gegenstueck zu--keep-localzu sein. Die beiden Antworten sind nicht symmetrisch:--keep-localverliert nichts,--take-releaseverwirft eine lokale Aenderung, und eine Verwerfung benennt ihr Ziel (Schritt 1 dieser Instruktion). Der gemischte Fall wird damit ueberhaupt erst loesbar, beide Flags zusammen komponieren, ein nicht blockierter Pfad ist Exit 1 auch im--dry-run, und die Abbruchmeldung nennt alle drei Antworten samt einsetzbarer Kommandozeile im Muster des Mass-Update-Gates.Akzeptanzkriterien zu 2 und 3 auf die entschiedene Form nachgezogen: aus zwei sind sechs geworden, jedes mit einer pruefbaren Eigenschaft statt einer Aktivitaet. Reproduktionsabschnitt um den Hinweis ergaenzt, wie Schritt 4 nach dieser Sitzung ausgeht. Befund 1 unberuehrt - laut eigenem Abschnitt eine Betreiberentscheidung, gegen #82 zusammen zu entscheiden und nicht Teil dieser Sitzung. Labels bleiben.
Changelog: Befund 2 und 3 gebaut und abgehakt -
72d01be(6.1.0-beta.3,--take-release) und0c98080(6.1.0-beta.4,version notes-Fallback), plus536093fals Doku-Nachzug ohne Bump. Beide § Entschieden sind zu § Umgesetzt geworden und tragen jetzt, was tatsaechlich geschrieben wurde statt was geschrieben werden sollte; die Praesensformulierungen ueber die beiden Defekte sind auf Vergangenheit umgestellt.Damit ist Befund 1 der einzige offene. Der "Stand"-Absatz ist durch eine Tabelle ueber alle sieben ersetzt, Befund 1 traegt oben und unten den Vermerk, dass er es ist, sein § Loesungsrichtung heisst jetzt § Zu entscheiden, und der Reproduktionsabschnitt sagt pro Schritt, ab welcher Version er nicht mehr reproduziert - Schritt 5 (Befund 1) ist der einzige, der unveraendert gilt. Akzeptanzkriterien in "offen" und "erledigt" geteilt: 3 offen, 12 abgehakt.
Label nachgezogen:
kind/defect->kind/decision. Was bleibt, ist keine Umsetzung mehr, sondern eine Betreiberentscheidung gegen #82.size/Lundprio/plannedbleiben - Befund 1 allein traegt beides.Nicht geschlossen, und ein Vorschlag dazu steht im Koerper: Befund 1 als eigenes Issue auslagern und dieses schliessen, so wie Befund 7 nach #108 gegangen ist. Bewusst nicht einseitig getan, weil die Labels des neuen Issues Antworten auf Fragen sind, die erst die Entscheidung gegen #82 beantwortet.
Verifiziert: 1290 Tests (1276 vorher, +8 fuer
--take-release, +6 fuer den Feed-Fallback), auch gegen eine leere Maschine nachtesting-conventions.mdSchritt 6 identisch gruen;docs verifymit 73 ausgelieferten Dokumenten und 58 Referenzdateien,instructions verifymit 23 Instruktionen und 7 Skills. CI gruen auf allen drei Commits: 298/299, 300/301, 302. Kein Release ausgeloest -release.ymlreagiert nur auf ein suffixfreiesVERSION.In der Abschlussphase zusaetzlich gefunden und in
536093fmitgenommen, weil beides Behauptungen ueber die geaenderten Flaechen waren: INSTALL.md fuehrte weiterversion checkals einzigen Befehl, der ins Netz geht, undtools/CONTRACT.md§ "Future considerations (not implemented)" listete noch den MCP-Server-Wrapper unddist upgradeselbst - beide seit ihrer Umsetzung falsch und im selben Dokument weiter oben als existierend beschrieben.docs/ownership-and-templates.mddagegen geprueft und bewusst nicht angefasst: seine Begruendung, eine lokal geaenderte Datei solle "a decision someone takes deliberately" sein, ist genau die Eigenschaft, die--take-releaseumsetzt.Changelog: Befund 1 nach #110 ausgelagert, dieses Issue geschlossen. Damit ist es kein offenes Arbeitspaket mehr, sondern der Laufbericht zum getraceten 5.0.0-auf-6.0.0-Upgrade - sechs Befunde erledigt, einer in #108 und einer in #110 zu eigenen Paketen geworden.
Was sich im Koerper geaendert hat: die Stand-Tabelle sagt jetzt pro Befund "erledigt" oder "ausgelagert nach #NNN"; Befund 1 traegt oben den Auslagerungsvermerk und behaelt seine Evidenz - Trace, Bucket-Messung, Scorer-Ausgabe -, weil die das Messergebnis dieses Laufs ist und #110 sie braucht, statt sie kopiert zu bekommen. Sein § Zu entscheiden ist weg, die Loesungsachsen stehen in #110. Die drei Akzeptanzkriterien zu Befund 1 sind gestrichen mit Ziel, nicht abgehakt, und die Kriterienliste ist auf "Erledigt" (13) plus "ausgelagert" (3) umgestellt. Befund 7 nennt jetzt explizit dasselbe Muster - Evidenz bleibt, Arbeit geht -, weil #110 sich darauf beruft. Reproduktionsschritt 5 ist als der einzige markiert, der unveraendert reproduziert, und als der, den #110 erbt.
Verweis in beide Richtungen: #110 nennt dieses Issue als Herkunft, verweist fuer die 63-Event-Trace auf den ersten Kommentar hier, und haelt fest, dass die Abhaengigkeit zu #82 in diese Richtung laeuft - #82 braucht eine Antwort aus #110, nicht umgekehrt, weil
tool.pre/tool.postohne gemeinsamen Schluessel ebenfalls unter der Harness-UUID landen.#110 hat dabei ein Kriterium bekommen, das hier fehlte: dass nach einer Aenderung der Schluesselbildung kein bestehender Bucket in
budget.jsonstill neu interpretiert wird - kein geerbter Count fuer eine neue Sitzung, kein alter Count gegen eine neue angerechnet.budget.jsonist eine maschinengelesene Datei, und "die Form einer maschinengelesenen Datei" steht im Grenzuebertritt-Katalog voninstructions/dev/version-parts.md; der Versionsteil ist dort als vor dem ersten Commit zu beantworten vermerkt, nicht zu raten.Labels von #110:
area/process(wie #82, sein Geschwister),kind/decision,prio/planned,size/L- alle vier uebernommen aus dem Urteil, das hier zuletzt fuer Befund 1 allein galt. Keinstatus/blocked: #110 ist eigenstaendig bearbeitbar, die Abhaengigkeit laeuft in die andere Richtung.