feat: dist upgrade - apply a stack update, not just detect one (#7)
CI / verify (push) Successful in 54s
Release / release (push) Successful in 34s

Files changed:
- CHANGES.md
- INSTALL.md
- VERSION
- tools/CONTRACT.md
- tools/chemenu/commands/dist_cmd.py
- tools/chemenu/kb_state.py
- tools/chemenu/ownership.py
- tools/chemenu/tests/test_dist_upgrade.py
This commit is contained in:
2026-09-04 11:36:21 +02:00
parent 1b5ffea854
commit cd81ba3d4f
8 changed files with 988 additions and 49 deletions
+50 -1
View File
@@ -35,7 +35,7 @@ dev-checkout concern - readable here, never shipped as something to parse.
---
## 4.5.0-beta.3 - 2026-09-04 - upstream merge: keep gitignored local data under a content stage, refuse a merge git never opened, report what actually changed
## 4.5.0-beta.4 - 2026-09-04 - dist upgrade: apply a stack update, not just detect one (#7)
**Author:** Torben Nehmer
@@ -44,6 +44,7 @@ dev-checkout concern - readable here, never shipped as something to parse.
- wikitool upstream merge/verify: code procedure for taking a stack update, ownership.py as the shared stack/instance boundary
- upstream merge: combined-commit regression test (edit+add+delete+contract+template+contract-delete in one commit)
- upstream merge: keep gitignored local data under a content stage, refuse a merge git never opened, report what actually changed
- dist upgrade: apply a stack update, not just detect one (#7)
<!-- /wikitool:bumps -->
Die Prosa zu 4.4.0 - Changelog-Eintrag, `docs/version-model.md`, `instructions/dev/version-parts.md`,
@@ -159,6 +160,54 @@ jetzt `git diff` zwischen Vor- und Nach-Commit, kennzeichnet Löschungen, und st
bekommen, warum die Grenze ein Prädikat und keine Liste ist — die Begründung, die dieses Issue
erarbeitet hat, gehörte in die Hintergrunddoku und nicht nur in einen Changelog-Eintrag.
**Fünfter Bump: `wikitool dist upgrade` (#7), der zweite der beiden Update-Wege.** `upstream
merge` oben bedient eine Instanz mit gemeinsamer Git-History; `dist upgrade` bedient eine
Instanz aus einem Tarball, ohne History, die bislang eine rein manuelle Prozedur in
`INSTALL.md` durchlaufen musste - Schritt 4 verlangte einen sha256-Vergleich von Hand gegen den
`files`-Block der alten `.wikitool-release.json`.
Die tragende Regel: die Schreibmenge ist genau der `files`-Block der *neuen*
`.wikitool-release.json`, minus was ein Export aus einer leeren Vorlage neu sät
(`chemenu.ownership.is_export_stub`, wie bisher schon für `kb/log.md`/`.gitkeep`) oder einmalig
sät und danach der Instanz gehört (`chemenu.ownership.is_upgrade_preserved`, neu für
`.wikitool-kb.json` und `CHANGES.md`), plus der Stamp selbst. Jeder Kandidatpfad wird gegen die
*alte* Instanz-Summe klassifiziert: unverändert wird geräuschlos überschrieben, neu im Release
wird angelegt, lokal verändert oder gelöscht wird **nie** still überschrieben - der Lauf bricht
mit der vollständigen Liste ab, außer `--keep-local` sagt ausdrücklich, dass die Dateien liegen
bleiben sollen. `--prune` entfernt zusätzlich aus dem Release entfallene Dateien, aber nur
solche, die seit der Installation unverändert sind.
Die Migrationskette nach dem Tausch wird aus den `instructions/migrations/` des *neuen* Baums
ermittelt (`kb_state.load_migrations` bekam dafür einen `directory`-Parameter) und nur
gemeldet, nie ausgeführt - es gibt bewusst kein `migrate run`. Eine bereits gegen die
*installierte* Maschinerie offene Kette lässt den Befehl abbrechen, bevor er die Quelle
überhaupt öffnet. `kb_state.divergent_files()` (bisher nur von `migrate status` gelesen) ist
jetzt eine dünne Hülle um das neue, zwei-Baum-fähige `compare_against_stamp()` - gleiches
Verhalten für den bestehenden Aufrufer, wiederverwendbar für `dist upgrade`s eigenen Vergleich.
Quelle ist immer ein bereits vorhandenes Verzeichnis oder `.tar.gz` - kein Download, das bleibt
allein `version check`s Sache. Ein Tarball muss genau ein Top-Level-Verzeichnis enthalten (die
Form, in der `release.yml` es baut) und wird gegen eine `.sha256`-Beidatei geprüft, falls eine
danebenliegt (fehlt sie: WARN, kein Abbruch). Weitere Abbruchgründe vor jedem Schreiben: fehlende
lokale `VERSION`/`.wikitool-kb.json`/Stamp mit `files`-Block, ein schmutziger Arbeitsbaum (kein
Git-Repo ist ein WARN, keine Sperre), eine Vorab-Version (`-beta.N`) ohne `--pre`, sowie ein
Downgrade; Gleichstand ist ein No-op. Ein Grenzübertritt der Kompatibilität wird laut gemeldet,
blockiert aber nicht. Committet und pusht nichts (Invariante 5).
Gegenüber dem ersten Entwurf des Issues zwei Korrekturen, die dort auch nachgetragen sind: der
`files`-Block wurde entgegen der ursprünglichen Annahme bereits vor diesem Bump gelesen
(`divergent_files`/`migrate status`), und die Migrationskette war ursprünglich falsch begründet
- sie kann nur aus dem *neuen* Baum kommen, nicht durch eine andere Abfragereihenfolge aus der
alten Instanz. 24 neue Tests unter `test_dist_upgrade.py` decken die Klassifikation, alle
Abbruchgründe, `--keep-local`, `--prune` und beide Quellformen (Verzeichnis und Tarball,
inklusive der sha256- und Top-Level-Prüfung) ab.
Bewusst nicht angetastet: `instructions/private-instance.md` (der Clone-Weg ändert sich nicht,
`INSTALL.md` benennt jetzt beide Wege nebeneinander) und die Frage, wie `dist upgrade` mit
Collection-Templates umgeht, deren Namen eine fremde Instanz gar nicht hat - es verhält sich wie
`upstream merge` und schreibt sie, was ein eigenes Issue gegen den Export wäre, keins gegen das
Upgrade.
---
## 4.4.0 - 2026-09-03 - Versionskandidat statt Bump-pro-Release: VERSION traegt -beta.N, version release fixiert