wikitool dist upgrade: ein Update anwenden, nicht nur erkennen #7

Closed
opened 2026-08-30 19:21:54 +00:00 by torben · 6 comments
Owner

Ergebnis

Umgesetzt und veröffentlicht: wikitool dist upgrade wendet ein Update jetzt an, statt es nur zu
erkennen. Commit cd81ba3 auf main, Version 4.5.0-beta.4 (fünfter Bump auf dem laufenden
Kandidaten, --minor - neue Fähigkeit, drop-in in beide Richtungen). CI-Läufe
#147 und
#148 grün. Lokal verifiziert:
docs verify, instructions verify, volle Testsuite (963 grün, davon 24 neu in
test_dist_upgrade.py), zusätzlich einmal gegen eine leere Maschine per
testing-conventions.md Schritt 6.

Der Rest dieses Issues ist die Spezifikation, an der entlang gebaut wurde - siehe unten, was
davon eins zu eins umgesetzt wurde und was bewusst nicht.

Worum es geht

Seit 0.1.0 konnte eine Instanz erkennen, dass sie veraltet ist (wikitool version check
gegen den Release-Feed). Das Anwenden eines Updates war Handarbeit nach INSTALL.md §
„Eine Instanz aktualisieren". wikitool dist upgrade <tarball-oder-verzeichnis> schließt die
Lücke.

Abgrenzung: warum dieses Issue nach #30 noch nötig war

Zwei Update-Wege, nicht austauschbar:

Empfänger Weg Kommando
Instanz als Clone mit gemeinsamer Git-History (diese; die private Arbeitsinstanz) Echter Drei-Wege-Merge upstream merge#30, geliefert
Instanz aus einem Tarball, ohne gemeinsame History Dateitausch gegen die sha256-Basis der Installation dist upgrade — dieses Issue

Was beide verbindet: dieselbe Eigentumsgrenze (chemenu.ownership). dist upgrade ist jetzt der
vierte Aufrufer von ownership.is_stack_owned-artiger Logik, ohne eine eigene Liste zu führen
(AGENTS.md Invariante 8) - siehe „Wie umgesetzt" unten.

Die drei Versionsfakten

Datei Wer schreibt Bedeutung
VERSION version bump / version release Welche Maschinerie liegt hier
.wikitool-release.json dist export (generiert, nie von Hand) Woher diese Maschinerie kam — plus eine sha256 je ausgelieferter Datei
.wikitool-kb.json migrate done (mutierbar, Instanzzustand) In welcher Form der Inhalt vorliegt

Der files-Block des zweiten wird seit der Offer-Migrationsmechanik bereits gelesen
(kb_state.divergent_files() / migrate status) - dist upgrade baut auf dieser Vorarbeit
auf, statt sie zu wiederholen (siehe unten).

Wie umgesetzt

Die Schreibmenge ist genau der files-Block der neuen .wikitool-release.json, minus was
ein Export aus einer leeren Vorlage neu sät (ownership.is_export_stub - bereits vorhanden, für
kb/log.md/.gitkeep) oder einmalig sät und danach der Instanz gehört
(ownership.is_upgrade_preserved - neu, für .wikitool-kb.json und CHANGES.md), plus der
Stamp selbst, immer neu geschrieben. dist_cmd._write_candidates/_classify_files.

Die drei Fälle plus „entfallen", für jeden Kandidatpfad gegen die alte Instanz-Summe:
unverändert → geräuschlos überschrieben; neu im Release → angelegt; lokal verändert oder
lokal gelöscht → nie still überschrieben, Lauf bricht mit vollständiger Liste ab, außer
--keep-local sagt ausdrücklich, dass sie liegen bleiben sollen (dann werden sie bei jedem
weiteren Lauf erneut gemeldet); im alten Stamp, nicht mehr im neuen → gemeldet, nur mit --prune
entfernt, und auch dann nur, wenn seit Installation unverändert.

Reihenfolge, korrigiert gegenüber der ursprünglichen (falschen) Begründung im ersten Entwurf
dieses Issues: die Migrationskette für die neuen Versionen kann nur aus dem neuen Tarball
kommen (instructions/migrations/ dort), nicht durch eine andere Abfragereihenfolge aus der
alten Instanz. Vor jedem Öffnen der Quelle wird stattdessen geprüft, dass gegen die installierte
Maschinerie nichts mehr aussteht
(kb_state.chain(kb_version, VERSION.base) leer) - sonst
Abbruch, bevor überhaupt etwas von der Quelle gelesen wird. Nach dem Öffnen wird die Kette für
die neue Maschinerie ermittelt (kb_state.load_migrations() bekam dafür einen optionalen
directory-Parameter, rückwärtskompatibel - alle bestehenden Aufrufer unverändert) und nur
gemeldet, nie ausgeführt.

Entschiedene Designpunkte, alle wie geplant umgesetzt: kein Download (<source> ist
Verzeichnis oder .tar.gz, version check bleibt der einzige Netzpfad); Tarball muss genau ein
Top-Level-Verzeichnis enthalten (die Form von release.yml), eine .sha256-Beidatei wird
geprüft, fehlt sie: WARN statt Abbruch; --keep-local statt --overwrite-local (es gibt kein
Kommando, das eine lokale Änderung verwirft - das bleibt Betreiberarbeit); --prune nur für
unveränderte entfallene Dateien; Downgrade → Abbruch, Gleichstand → No-op; Vorab-Version
(-beta.N) → Abbruch, außer --pre; schmutziger Arbeitsbaum → Abbruch, kein Git-Repo → WARN;
Kompatibilitätsgrenze → laut gemeldet, blockiert nicht; kein Eintrag in
run_budget.SKIP_COMMAND_PATHS (das Kommando schreibt).

Ein Fehler, den die Tests gefangen haben, nicht das Design: die erste Implementierung prüfte
die Sperre gegen lokal geänderte Dateien vor der --dry-run-Abfrage, sodass ein Dry-Run mit
Exit 1 abgebrochen wäre, statt die Klassifikation nur anzuzeigen. --dry-run ist jetzt in jedem
Fall Exit 0 - sein ganzer Zweck ist, genau diese Liste gefahrlos vorab zu zeigen.

Bewusst nicht umgesetzt

  • Stamp-Rekonstruktion aus einem alten Tarball (--from-release). Eine Instanz ohne lokalen
    Stamp hat weiterhin keine Reparatur - sie bricht mit einem Verweis auf upstream merge ab
    (für den Clone-Fall) bzw. ohne Ausweg (für den Tarball-Fall ohne History). Kein realer Fall
    heute; bei Bedarf ein eigenes, kleines Issue.
  • Collection-Templates fremder Namen. dist upgrade schreibt jedes kb/<name>/COLLECTION.md.template
    aus dem neuen Stamp, unabhängig davon, ob diese Instanz eine Collection dieses Namens hat -
    genau wie upstream merge es seit #30 tut. Konsistent, aber ungeprüft gegen eine Instanz mit
    eigenem Collection-Namen; wäre ein Issue gegen den Export, keins gegen das Upgrade.
  • instructions/private-instance.md unverändert gelassen - der Clone-Weg ändert sich nicht;
    INSTALL.md benennt jetzt beide Wege nebeneinander und verweist von dort aus.

Akzeptanzkriterien

  • dist upgrade --dry-run klassifiziert jeden Pfad in die drei Fälle plus „entfallen" -
    test_dry_run_classifies_every_case_and_writes_nothing,
    test_removed_file_is_reported_and_left_alone_without_prune.
  • Eine lokal veränderte Datei wird nie stillschweigend überschrieben -
    test_locally_modified_file_blocks_the_upgrade_by_default,
    test_locally_deleted_file_blocks_the_upgrade_by_default,
    test_keep_local_proceeds_and_leaves_the_changed_file_untouched.
  • Die Schreibmenge ist genau der files-Block des neuen Stamps minus der einmalig gesäten
    Pfade, kein Pfad außerhalb - test_seeded_once_paths_are_never_written_even_if_the_release_stamp_lists_them
    (parametrisiert über .wikitool-kb.json, CHANGES.md, kb/log.md, raw/notes/.gitkeep).
  • Ein Lauf gegen eine Instanz mit offener Migrationskette bricht ab, bevor irgendetwas
    geschrieben ist - test_outstanding_local_migration_blocks_before_touching_the_source (die
    Quelle wird auf einen nicht existierenden Pfad gesetzt, um das zu erzwingen).
  • Die Kette nach dem Tausch wird aus den Migrationsdokumenten des Tarballs ermittelt, steht
    vor dem Schreiben fest, wird gemeldet, nicht ausgeführt -
    test_migration_chain_is_reported_but_never_executed.
  • Fehlender lokaler Stamp, Pre-Release-Quelle, Downgrade, dirty working tree, Tarball ohne
    genau ein Top-Level-Verzeichnis - je ein Test:
    test_missing_local_stamp_blocks, test_missing_kb_version_blocks,
    test_prerelease_source_is_refused_without_pre_flag, test_pre_flag_allows_a_prerelease_source,
    test_downgrade_is_refused, test_equal_version_is_a_noop, test_dirty_working_tree_blocks,
    test_tarball_with_more_than_one_top_level_entry_is_refused,
    test_source_that_does_not_exist_is_refused,
    test_tarball_sha256_sidecar_mismatch_is_refused.
  • Kein neuer Literal-Satz über Eigentum - _write_candidates liest ausschließlich
    ownership.is_export_stub/ownership.is_upgrade_preserved.
  • Zeile in tools/CONTRACT.mds Kommandotabelle und in der Fehlerkontrakt-Tabelle - beide
    ergänzt, docs verify grün.
  • INSTALL.md § „Eine Instanz aktualisieren" auf den neuen Weg umgestellt, manuelle Prozedur
    bleibt als Rückfallebene benannt (nicht mehr ausgeschrieben - der files-Block macht sie
    redundant, ein Verweis auf tools/CONTRACT.mds Zeile genügt).
  • instructions/private-instance.md bewusst unberührt gelassen.
  • Changelog-Eintrag, MINOR (4.5.0-beta.4).

Relevanter Code

  • tools/chemenu/commands/dist_cmd.pyupgrade_command/run_upgrade, FileClassification,
    _write_candidates, _classify_files, _resolved_source, _extract_single_top_level_dir,
    _verify_sha256_sidecar, _git_working_tree_status, _report_plan.
  • tools/chemenu/ownership.pyUPGRADE_PRESERVED_PATHS, is_upgrade_preserved.
  • tools/chemenu/kb_state.pyload_migrations(directory=None), compare_against_stamp,
    UNCHANGED/MODIFIED/DELETED, divergent_files (jetzt eine Hülle darum).
  • tools/chemenu/tests/test_dist_upgrade.py — 24 Tests.
  • tools/CONTRACT.md, INSTALL.md § „Eine Instanz aktualisieren", CHANGES.md.
## Ergebnis Umgesetzt und veröffentlicht: `wikitool dist upgrade` wendet ein Update jetzt an, statt es nur zu erkennen. Commit `cd81ba3` auf `main`, Version `4.5.0-beta.4` (fünfter Bump auf dem laufenden Kandidaten, `--minor` - neue Fähigkeit, drop-in in beide Richtungen). CI-Läufe [#147](https://gitea.nehmer.net/torben/chemenu/actions/runs/147) und [#148](https://gitea.nehmer.net/torben/chemenu/actions/runs/148) grün. Lokal verifiziert: `docs verify`, `instructions verify`, volle Testsuite (963 grün, davon 24 neu in `test_dist_upgrade.py`), zusätzlich einmal gegen eine leere Maschine per `testing-conventions.md` Schritt 6. Der Rest dieses Issues ist die Spezifikation, an der entlang gebaut wurde - siehe unten, was davon eins zu eins umgesetzt wurde und was bewusst nicht. ## Worum es geht Seit `0.1.0` konnte eine Instanz **erkennen**, dass sie veraltet ist (`wikitool version check` gegen den Release-Feed). Das **Anwenden** eines Updates war Handarbeit nach `INSTALL.md` § „Eine Instanz aktualisieren". `wikitool dist upgrade <tarball-oder-verzeichnis>` schließt die Lücke. ## Abgrenzung: warum dieses Issue nach #30 noch nötig war Zwei Update-Wege, nicht austauschbar: | Empfänger | Weg | Kommando | |---|---|---| | Instanz als Clone mit gemeinsamer Git-History (diese; die private Arbeitsinstanz) | Echter Drei-Wege-Merge | `upstream merge` — #30, geliefert | | Instanz aus einem Tarball, ohne gemeinsame History | Dateitausch gegen die sha256-Basis der Installation | `dist upgrade` — dieses Issue | Was beide verbindet: dieselbe Eigentumsgrenze (`chemenu.ownership`). `dist upgrade` ist jetzt der vierte Aufrufer von `ownership.is_stack_owned`-artiger Logik, ohne eine eigene Liste zu führen (AGENTS.md Invariante 8) - siehe „Wie umgesetzt" unten. ## Die drei Versionsfakten | Datei | Wer schreibt | Bedeutung | |---|---|---| | `VERSION` | `version bump` / `version release` | Welche Maschinerie liegt hier | | `.wikitool-release.json` | `dist export` (generiert, **nie** von Hand) | Woher diese Maschinerie kam — plus eine sha256 je ausgelieferter Datei | | `.wikitool-kb.json` | `migrate done` (mutierbar, Instanzzustand) | In welcher Form der *Inhalt* vorliegt | Der `files`-Block des zweiten wird seit der Offer-Migrationsmechanik bereits gelesen (`kb_state.divergent_files()` / `migrate status`) - `dist upgrade` baut auf dieser Vorarbeit auf, statt sie zu wiederholen (siehe unten). ## Wie umgesetzt **Die Schreibmenge** ist genau der `files`-Block der *neuen* `.wikitool-release.json`, minus was ein Export aus einer leeren Vorlage neu sät (`ownership.is_export_stub` - bereits vorhanden, für `kb/log.md`/`.gitkeep`) oder einmalig sät und danach der Instanz gehört (`ownership.is_upgrade_preserved` - neu, für `.wikitool-kb.json` und `CHANGES.md`), plus der Stamp selbst, immer neu geschrieben. `dist_cmd._write_candidates`/`_classify_files`. **Die drei Fälle plus „entfallen"**, für jeden Kandidatpfad gegen die *alte* Instanz-Summe: unverändert → geräuschlos überschrieben; neu im Release → angelegt; lokal verändert **oder** lokal gelöscht → **nie** still überschrieben, Lauf bricht mit vollständiger Liste ab, außer `--keep-local` sagt ausdrücklich, dass sie liegen bleiben sollen (dann werden sie bei jedem weiteren Lauf erneut gemeldet); im alten Stamp, nicht mehr im neuen → gemeldet, nur mit `--prune` entfernt, und auch dann nur, wenn seit Installation unverändert. **Reihenfolge**, korrigiert gegenüber der ursprünglichen (falschen) Begründung im ersten Entwurf dieses Issues: die Migrationskette für die neuen Versionen kann nur aus dem *neuen* Tarball kommen (`instructions/migrations/` dort), nicht durch eine andere Abfragereihenfolge aus der alten Instanz. Vor jedem Öffnen der Quelle wird stattdessen geprüft, dass **gegen die installierte Maschinerie nichts mehr aussteht** (`kb_state.chain(kb_version, VERSION.base)` leer) - sonst Abbruch, bevor überhaupt etwas von der Quelle gelesen wird. Nach dem Öffnen wird die Kette für die *neue* Maschinerie ermittelt (`kb_state.load_migrations()` bekam dafür einen optionalen `directory`-Parameter, rückwärtskompatibel - alle bestehenden Aufrufer unverändert) und nur **gemeldet**, nie ausgeführt. **Entschiedene Designpunkte**, alle wie geplant umgesetzt: kein Download (`<source>` ist Verzeichnis oder `.tar.gz`, `version check` bleibt der einzige Netzpfad); Tarball muss genau ein Top-Level-Verzeichnis enthalten (die Form von `release.yml`), eine `.sha256`-Beidatei wird geprüft, fehlt sie: WARN statt Abbruch; `--keep-local` statt `--overwrite-local` (es gibt kein Kommando, das eine lokale Änderung verwirft - das bleibt Betreiberarbeit); `--prune` nur für unveränderte entfallene Dateien; Downgrade → Abbruch, Gleichstand → No-op; Vorab-Version (`-beta.N`) → Abbruch, außer `--pre`; schmutziger Arbeitsbaum → Abbruch, kein Git-Repo → WARN; Kompatibilitätsgrenze → laut gemeldet, blockiert nicht; kein Eintrag in `run_budget.SKIP_COMMAND_PATHS` (das Kommando schreibt). **Ein Fehler, den die Tests gefangen haben, nicht das Design:** die erste Implementierung prüfte die Sperre gegen lokal geänderte Dateien *vor* der `--dry-run`-Abfrage, sodass ein Dry-Run mit Exit 1 abgebrochen wäre, statt die Klassifikation nur anzuzeigen. `--dry-run` ist jetzt in jedem Fall Exit 0 - sein ganzer Zweck ist, genau diese Liste gefahrlos vorab zu zeigen. ## Bewusst nicht umgesetzt - **Stamp-Rekonstruktion aus einem alten Tarball** (`--from-release`). Eine Instanz ohne lokalen Stamp hat weiterhin keine Reparatur - sie bricht mit einem Verweis auf `upstream merge` ab (für den Clone-Fall) bzw. ohne Ausweg (für den Tarball-Fall ohne History). Kein realer Fall heute; bei Bedarf ein eigenes, kleines Issue. - **Collection-Templates fremder Namen.** `dist upgrade` schreibt jedes `kb/<name>/COLLECTION.md.template` aus dem neuen Stamp, unabhängig davon, ob diese Instanz eine Collection dieses Namens hat - genau wie `upstream merge` es seit #30 tut. Konsistent, aber ungeprüft gegen eine Instanz mit eigenem Collection-Namen; wäre ein Issue gegen den *Export*, keins gegen das Upgrade. - `instructions/private-instance.md` unverändert gelassen - der Clone-Weg ändert sich nicht; `INSTALL.md` benennt jetzt beide Wege nebeneinander und verweist von dort aus. ## Akzeptanzkriterien - [x] `dist upgrade --dry-run` klassifiziert jeden Pfad in die drei Fälle plus „entfallen" - `test_dry_run_classifies_every_case_and_writes_nothing`, `test_removed_file_is_reported_and_left_alone_without_prune`. - [x] Eine lokal veränderte Datei wird nie stillschweigend überschrieben - `test_locally_modified_file_blocks_the_upgrade_by_default`, `test_locally_deleted_file_blocks_the_upgrade_by_default`, `test_keep_local_proceeds_and_leaves_the_changed_file_untouched`. - [x] Die Schreibmenge ist genau der `files`-Block des neuen Stamps minus der einmalig gesäten Pfade, kein Pfad außerhalb - `test_seeded_once_paths_are_never_written_even_if_the_release_stamp_lists_them` (parametrisiert über `.wikitool-kb.json`, `CHANGES.md`, `kb/log.md`, `raw/notes/.gitkeep`). - [x] Ein Lauf gegen eine Instanz mit offener Migrationskette bricht ab, bevor irgendetwas geschrieben ist - `test_outstanding_local_migration_blocks_before_touching_the_source` (die Quelle wird auf einen nicht existierenden Pfad gesetzt, um das zu erzwingen). - [x] Die Kette nach dem Tausch wird aus den Migrationsdokumenten des Tarballs ermittelt, steht vor dem Schreiben fest, wird gemeldet, nicht ausgeführt - `test_migration_chain_is_reported_but_never_executed`. - [x] Fehlender lokaler Stamp, Pre-Release-Quelle, Downgrade, dirty working tree, Tarball ohne genau ein Top-Level-Verzeichnis - je ein Test: `test_missing_local_stamp_blocks`, `test_missing_kb_version_blocks`, `test_prerelease_source_is_refused_without_pre_flag`, `test_pre_flag_allows_a_prerelease_source`, `test_downgrade_is_refused`, `test_equal_version_is_a_noop`, `test_dirty_working_tree_blocks`, `test_tarball_with_more_than_one_top_level_entry_is_refused`, `test_source_that_does_not_exist_is_refused`, `test_tarball_sha256_sidecar_mismatch_is_refused`. - [x] Kein neuer Literal-Satz über Eigentum - `_write_candidates` liest ausschließlich `ownership.is_export_stub`/`ownership.is_upgrade_preserved`. - [x] Zeile in `tools/CONTRACT.md`s Kommandotabelle und in der Fehlerkontrakt-Tabelle - beide ergänzt, `docs verify` grün. - [x] `INSTALL.md` § „Eine Instanz aktualisieren" auf den neuen Weg umgestellt, manuelle Prozedur bleibt als Rückfallebene benannt (nicht mehr ausgeschrieben - der `files`-Block macht sie redundant, ein Verweis auf `tools/CONTRACT.md`s Zeile genügt). - [x] `instructions/private-instance.md` bewusst unberührt gelassen. - [x] Changelog-Eintrag, MINOR (`4.5.0-beta.4`). ## Relevanter Code - `tools/chemenu/commands/dist_cmd.py` — `upgrade_command`/`run_upgrade`, `FileClassification`, `_write_candidates`, `_classify_files`, `_resolved_source`, `_extract_single_top_level_dir`, `_verify_sha256_sidecar`, `_git_working_tree_status`, `_report_plan`. - `tools/chemenu/ownership.py` — `UPGRADE_PRESERVED_PATHS`, `is_upgrade_preserved`. - `tools/chemenu/kb_state.py` — `load_migrations(directory=None)`, `compare_against_stamp`, `UNCHANGED`/`MODIFIED`/`DELETED`, `divergent_files` (jetzt eine Hülle darum). - `tools/chemenu/tests/test_dist_upgrade.py` — 24 Tests. - `tools/CONTRACT.md`, `INSTALL.md` § „Eine Instanz aktualisieren", `CHANGES.md`.
torben added the prio/waitingsize/L labels 2026-08-31 06:56:44 +00:00
Author
Owner

Nachtrag 2026-09-01 — zwei Annahmen im Text gelten nicht mehr

Der Fallstrick „Ursprungs-Repo ist privat" entfällt. Das Repo wird öffentlich geschaltet. Damit verschwindet die Begründung, WIKITOOL_UPDATE_TOKEN sei Pflicht, und der beschriebene Fall „ein fehlendes Release und ein fehlender Zugriff sehen identisch aus" tritt für den Normalnutzer nicht mehr auf. Er bleibt richtig für private Forks, ist dort aber Sonderfall statt Regel. INSTALL.md wird beim Umschalten entsprechend nachgezogen.

Pfade und Repo-Name sind vom Rename überholt (2.0.0, Issue #3): tools/wiki_tools/… heißt jetzt tools/chemenu/…, und torben/llm-wiki-test1 ist torben/chemenu. Betrifft im Text die Codeverweise auf dist_cmd.py, version.py, kb_state.py sowie die Release-URL.

Die Dringlichkeit hat sich verschoben, die Berechtigung nicht. Die private Arbeitsinstanz wird als Clone mit upstream-Remote angelegt, nicht als Tarball-Empfänger — Stack-Updates kommen dort per git merge upstream/main, also mit echtem Drei-Wege-Merge statt cp -r. Damit ist dist upgrade für diese Instanz nicht der kritische Pfad. Für jede fremde Instanz ohne gemeinsame Git-History bleibt es genau das, was dieses Issue beschreibt, und die Drei-Wege-Klassifikation über die files-Summen in .wikitool-release.json ist weiterhin der richtige Entwurf.

## Nachtrag 2026-09-01 — zwei Annahmen im Text gelten nicht mehr **Der Fallstrick „Ursprungs-Repo ist privat" entfällt.** Das Repo wird öffentlich geschaltet. Damit verschwindet die Begründung, `WIKITOOL_UPDATE_TOKEN` sei Pflicht, und der beschriebene Fall „ein fehlendes Release und ein fehlender Zugriff sehen identisch aus" tritt für den Normalnutzer nicht mehr auf. Er bleibt richtig für private Forks, ist dort aber Sonderfall statt Regel. `INSTALL.md` wird beim Umschalten entsprechend nachgezogen. **Pfade und Repo-Name sind vom Rename überholt** (2.0.0, Issue #3): `tools/wiki_tools/…` heißt jetzt `tools/chemenu/…`, und `torben/llm-wiki-test1` ist `torben/chemenu`. Betrifft im Text die Codeverweise auf `dist_cmd.py`, `version.py`, `kb_state.py` sowie die Release-URL. **Die Dringlichkeit hat sich verschoben, die Berechtigung nicht.** Die private Arbeitsinstanz wird als Clone mit `upstream`-Remote angelegt, nicht als Tarball-Empfänger — Stack-Updates kommen dort per `git merge upstream/main`, also mit echtem Drei-Wege-Merge statt `cp -r`. Damit ist `dist upgrade` für *diese* Instanz nicht der kritische Pfad. Für jede fremde Instanz ohne gemeinsame Git-History bleibt es genau das, was dieses Issue beschreibt, und die Drei-Wege-Klassifikation über die `files`-Summen in `.wikitool-release.json` ist weiterhin der richtige Entwurf.
torben added the status/blocked label 2026-09-02 21:00:13 +00:00
torben added the area/distributionkind/build labels 2026-09-02 21:18:30 +00:00
Author
Owner

Update 2026-09-03: #39/#40 sind gelandet. Die Ownership-Ambiguität, was als "Stack-Datei" vs. "Instanz-Datei" gilt, ist damit geklärt (siehe #39s Tabelle, #40s STACK_REQUIRED_TYPES) - die Drei-Wege-Klassifikation dieses Issues kann sich darauf stützen. Verbleibender Blocker ist ausschließlich #42 (Pre-Release-Vergleichslogik). Reihenfolge unverändert: #7 bleibt der letzte Schritt, nach #28, #38, #42, #30.

**Update 2026-09-03:** #39/#40 sind gelandet. Die Ownership-Ambiguität, was als "Stack-Datei" vs. "Instanz-Datei" gilt, ist damit geklärt (siehe #39s Tabelle, #40s `STACK_REQUIRED_TYPES`) - die Drei-Wege-Klassifikation dieses Issues kann sich darauf stützen. Verbleibender Blocker ist ausschließlich #42 (Pre-Release-Vergleichslogik). Reihenfolge unverändert: #7 bleibt der letzte Schritt, nach #28, #38, #42, #30.
torben added prio/plannedsize/M and removed prio/waitingsize/Lstatus/blocked labels 2026-09-04 08:27:34 +00:00
Author
Owner

Changelog: Body vollständig neu geschrieben — Anforderungen geprüft und ergänzt, die beiden Nachträge oben sind darin aufgegangen und nur noch Historie.

Korrigiert: „den files-Block liest bisher niemand" stimmt nicht mehr (kb_state.divergent_files() / migrate status); die Reihenfolgen-Begründung war falsch hergeleitet — die Migrationsdokumente liegen im neuen Tarball, die alte Instanz kann die Kette gar nicht kennen, also ist die Vorbedingung „nichts offen" das, was vorher aus der Instanz kommen muss, nicht die Kette; Pfade/Repo/Version auf tools/chemenu, torben/chemenu, 4.5.0-beta.3 gezogen; Privat-Repo-Fallstrick auf den Fork-Sonderfall reduziert.

Neu: Abgrenzung gegen upstream merge (#30) als Tabelle — zwei Update-Wege, nicht austauschbar. Die tragende Regel „Schreibmenge = files-Block des neuen Stamps plus Stamp selbst, minus einmalig gesäter Pfade", inkl. der bisher nirgends benannten Ausnahmen .wikitool-kb.json und CHANGES.md. 14 entschiedene Designpunkte mit Begründung (kein Download, --keep-local statt --overwrite-local, --prune, --pre, Downgrade-Abbruch, kein Budget-Skip — letzteres, weil SKIP_COMMAND_PATHS (command, subcommand)-Tupel hält und Flags gar nicht unterscheiden kann). Abschnitt „Offene Konstruktionsarbeit" (Signaturänderungen an load_migrations/divergent_files, Satz in ownership.py) und „Bewusst offen gelassen" (Collection-Templates fremder Namen — Verhalten folgt upstream merge).

Akzeptanzkriterien von 7 auf 11, darunter der strukturelle Test „kein Pfad außerhalb der Schreibmenge" und Abbruchtests für die fünf Vorbedingungen. Das alte Kriterium zum Budget-Gate ist in die Designpunkte gewandert (es war eine Entscheidung, kein Kriterium). Beim CONTRACT-Kriterium präzisiert, dass docs verify nur die Existenz einer backtick-Zelle prüft, nicht beide Tabellen.

Labels: status/blocked runter (#42 geschlossen, damit kein Blocker mehr), prio/waitingprio/planned, size/Lsize/M — die offenen Designfragen, die das L trugen, sind mit dieser Fassung entschieden.

**Changelog:** Body vollständig neu geschrieben — Anforderungen geprüft und ergänzt, die beiden Nachträge oben sind darin aufgegangen und nur noch Historie. Korrigiert: „den `files`-Block liest bisher niemand" stimmt nicht mehr (`kb_state.divergent_files()` / `migrate status`); die Reihenfolgen-Begründung war falsch hergeleitet — die Migrationsdokumente liegen im *neuen* Tarball, die alte Instanz kann die Kette gar nicht kennen, also ist die Vorbedingung „nichts offen" das, was vorher aus der Instanz kommen muss, nicht die Kette; Pfade/Repo/Version auf `tools/chemenu`, `torben/chemenu`, `4.5.0-beta.3` gezogen; Privat-Repo-Fallstrick auf den Fork-Sonderfall reduziert. Neu: Abgrenzung gegen `upstream merge` (#30) als Tabelle — zwei Update-Wege, nicht austauschbar. Die tragende Regel „Schreibmenge = `files`-Block des neuen Stamps plus Stamp selbst, minus einmalig gesäter Pfade", inkl. der bisher nirgends benannten Ausnahmen `.wikitool-kb.json` und `CHANGES.md`. 14 entschiedene Designpunkte mit Begründung (kein Download, `--keep-local` statt `--overwrite-local`, `--prune`, `--pre`, Downgrade-Abbruch, kein Budget-Skip — letzteres, weil `SKIP_COMMAND_PATHS` `(command, subcommand)`-Tupel hält und Flags gar nicht unterscheiden kann). Abschnitt „Offene Konstruktionsarbeit" (Signaturänderungen an `load_migrations`/`divergent_files`, Satz in `ownership.py`) und „Bewusst offen gelassen" (Collection-Templates fremder Namen — Verhalten folgt `upstream merge`). Akzeptanzkriterien von 7 auf 11, darunter der strukturelle Test „kein Pfad außerhalb der Schreibmenge" und Abbruchtests für die fünf Vorbedingungen. Das alte Kriterium zum Budget-Gate ist in die Designpunkte gewandert (es war eine Entscheidung, kein Kriterium). Beim CONTRACT-Kriterium präzisiert, dass `docs verify` nur die Existenz *einer* backtick-Zelle prüft, nicht beide Tabellen. Labels: `status/blocked` runter (#42 geschlossen, damit kein Blocker mehr), `prio/waiting` → `prio/planned`, `size/L` → `size/M` — die offenen Designfragen, die das L trugen, sind mit dieser Fassung entschieden.
Author
Owner

Changelog: Zeile „Ohne lokalen Stamp" korrigiert. Der Verweis auf migrate baseline war falsch — das Kommando repariert .wikitool-kb.json (die Form des Inhalts) und sagt über die Herkunft der Maschinerie nichts; die beiden fehlenden Dateien waren in der Zeile zusammengezogen. Verweis jetzt nur noch auf upstream merge, plus die Begründung, warum ein Clone die Basis anderswo hat (Merge-Base statt Hashes).

Neu unter „Bewusst offen gelassen": Stamp-Rekonstruktion aus einem alten Tarball. Der files-Block ist genau die sha256-Summen des Export-Baums, und der Stamp steht nicht in seinem eigenen Block — das Tarball der Version X ist damit byteweise die Basis, die der Stamp der Version X hält. Ein dist upgrade --from-release <altes-tarball> würde den Abbruch für eine Instanz ohne Stamp auflösen. Nicht entschieden, nicht Teil des ersten Schnitts, aber benannt, damit die Abbruchmeldung den Fall nicht als grundsätzlich unlösbar hinstellt.

Ein Fallstrick ergänzt: der Export schreibt den Stamp nur ins Ziel, nie in die Quelle — dieser Checkout hat also keinen, und das ist kein Defekt. Für version check folgenlos (update_url() fällt auf DEFAULT_UPDATE_URL zurück), für upgrade konstituierend. Erkennung geht ohne Stamp, Anwendung nicht.

**Changelog:** Zeile „Ohne lokalen Stamp" korrigiert. Der Verweis auf `migrate baseline` war falsch — das Kommando repariert `.wikitool-kb.json` (die Form des Inhalts) und sagt über die Herkunft der Maschinerie nichts; die beiden fehlenden Dateien waren in der Zeile zusammengezogen. Verweis jetzt nur noch auf `upstream merge`, plus die Begründung, warum ein Clone die Basis anderswo hat (Merge-Base statt Hashes). Neu unter „Bewusst offen gelassen": **Stamp-Rekonstruktion aus einem alten Tarball.** Der `files`-Block ist genau die sha256-Summen des Export-Baums, und der Stamp steht nicht in seinem eigenen Block — das Tarball der Version X ist damit byteweise die Basis, die der Stamp der Version X hält. Ein `dist upgrade --from-release <altes-tarball>` würde den Abbruch für eine Instanz ohne Stamp auflösen. Nicht entschieden, nicht Teil des ersten Schnitts, aber benannt, damit die Abbruchmeldung den Fall nicht als grundsätzlich unlösbar hinstellt. Ein Fallstrick ergänzt: der Export schreibt den Stamp nur ins Ziel, nie in die Quelle — dieser Checkout hat also keinen, und das ist kein Defekt. Für `version check` folgenlos (`update_url()` fällt auf `DEFAULT_UPDATE_URL` zurück), für `upgrade` konstituierend. Erkennung geht ohne Stamp, Anwendung nicht.
Author
Owner

Changelog: Umgesetzt und geschlossen. Body auf Endstand gebracht - Ergebnis oben (Commit, Version, CI-Läufe, Testergebnis), Design-Abschnitte auf „wie umgesetzt" umgestellt, Bewusst nicht umgesetzt benennt die beiden zurückgestellten Punkte aus der Spezifikation mit Begründung, alle elf Akzeptanzkriterien abgehakt mit den Tests, die sie decken.

Commit cd81ba3 (main), 4.5.0-beta.4. Verifiziert: docs verify, instructions verify, volle Testsuite (963 grün, 24 neu), einmal gegen eine leere Maschine; CI-Läufe #147/#148 grün.

**Changelog:** Umgesetzt und geschlossen. Body auf Endstand gebracht - `Ergebnis` oben (Commit, Version, CI-Läufe, Testergebnis), Design-Abschnitte auf „wie umgesetzt" umgestellt, `Bewusst nicht umgesetzt` benennt die beiden zurückgestellten Punkte aus der Spezifikation mit Begründung, alle elf Akzeptanzkriterien abgehakt mit den Tests, die sie decken. Commit `cd81ba3` (main), `4.5.0-beta.4`. Verifiziert: `docs verify`, `instructions verify`, volle Testsuite (963 grün, 24 neu), einmal gegen eine leere Maschine; CI-Läufe #147/#148 grün.
Author
Owner

Nachtrag nach dem Schließen — stack-dev Schritt 6 zu Ende geführt. Der Abschluss oben hat die docs/-Veralterungsprüfung benannt statt sie durchzuführen. Nachgeholt, mit einem Fund; Commit 368438e, 4.5.0-beta.5, CI-Läufe #149/#150 grün.

docs/ownership-and-templates.md § „The consequence in practice" war durch dieses Issue veraltet. Die Seite beschrieb ein Upgrade als Zweiteilung (verbatim überschreiben, .template-gestützte liegen lassen) und begründete den ersten Teil damit, verbatim ausgelieferte Dateien seien „safe to replace wholesale", weil sie „never instance-specific to begin with" waren — genau die Annahme, die dist upgrade bewusst nicht trifft. Dazu fehlte die dritte Klasse ganz: die einmalig gesäten, danach instanzeigenen Pfade. Beides korrigiert.

Dazu eine Präzisierung in tools/CONTRACT.md, die vorher nirgends stand: nach --keep-local wird der neue Stamp trotzdem vollständig geschrieben und trägt die Release-Summe auch für Dateien, die bewusst nicht geschrieben wurden. Er ist Vergleichsbasis, kein Inventar der Platte — und genau das hält eine übersprungene Datei bei jedem weiteren Lauf als abweichend gemeldet.

Modellaufteilung, damit sie nicht stillschweigend bleibt: Spezifikation auf Opus, Umsetzung und Abschluss auf Sonnet, dieser Nachtrag auf Opus. Der übersprungene docs/-Check fiel genau in die Phase, für die Schritt 6 den Rücksprung vorsieht — das ist kein Zufall, sondern der Beleg dafür, dass die Bruchstelle im Skill an der richtigen Stelle sitzt.

**Nachtrag nach dem Schließen — `stack-dev` Schritt 6 zu Ende geführt.** Der Abschluss oben hat die `docs/`-Veralterungsprüfung *benannt* statt sie durchzuführen. Nachgeholt, mit einem Fund; Commit `368438e`, `4.5.0-beta.5`, CI-Läufe #149/#150 grün. `docs/ownership-and-templates.md` § „The consequence in practice" war durch dieses Issue veraltet. Die Seite beschrieb ein Upgrade als Zweiteilung (verbatim überschreiben, `.template`-gestützte liegen lassen) und begründete den ersten Teil damit, verbatim ausgelieferte Dateien seien „safe to replace wholesale", weil sie „never instance-specific to begin with" waren — genau die Annahme, die `dist upgrade` bewusst *nicht* trifft. Dazu fehlte die dritte Klasse ganz: die einmalig gesäten, danach instanzeigenen Pfade. Beides korrigiert. Dazu eine Präzisierung in `tools/CONTRACT.md`, die vorher nirgends stand: nach `--keep-local` wird der neue Stamp trotzdem vollständig geschrieben und trägt die Release-Summe auch für Dateien, die bewusst nicht geschrieben wurden. Er ist Vergleichsbasis, kein Inventar der Platte — und genau das hält eine übersprungene Datei bei jedem weiteren Lauf als abweichend gemeldet. **Modellaufteilung, damit sie nicht stillschweigend bleibt:** Spezifikation auf Opus, Umsetzung und Abschluss auf Sonnet, dieser Nachtrag auf Opus. Der übersprungene `docs/`-Check fiel genau in die Phase, für die Schritt 6 den Rücksprung vorsieht — das ist kein Zufall, sondern der Beleg dafür, dass die Bruchstelle im Skill an der richtigen Stelle sitzt.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: torben/chemenu#7