# Entwicklung dieses Stacks Dieses Dokument richtet sich an Menschen, die an `tools/wikitool`, dem Type-Schema oder der Instruction-/Skill-Schicht selbst arbeiten - nicht an den Konsumenten einer Instanz. Für die Gegenseite (eine Instanz installieren, aktualisieren, betreiben) siehe [INSTALL.md](INSTALL.md). **Diese Datei wird nicht ausgeliefert.** Sie ist das menschliche Gegenstück zu `instructions/dev/`, das `tools/wikitool dist export` vollständig ausschließt: eine ausgelieferte Instanz hat keinen Release-Workflow, keine CI und kein Issue-Board, also braucht sie auch keine Anleitung dafür. `dist_cmd.ROOT_FILES` listet sie deshalb bewusst nicht - der Grund steht dort als Kommentar, damit eine spätere Sitzung die vermeintliche Lücke nicht "repariert". Und weil sie nicht ausgeliefert wird, darf sie - anders als `README.md`, `INSTALL.md` oder `EVALS.md`, die `instructions verify` auf genau diesen Punkt prüft - nach `instructions/dev/` verlinken. ## Entwicklungsumgebung Am Stack wird in einem Klon dieses Repos gearbeitet. Ein solcher Klon ist **keine Instanz** und wird nie eine. Instanzen entstehen ausschließlich aus Releases, siehe [INSTALL.md](INSTALL.md). ```bash git clone https://gitea.nehmer.net/torben/chemenu.git cd chemenu ``` Danach den Agenten `instructions/bootstrap.md` ausführen lassen: Preflight, dann `tools/wikitool instructions sync`. Die Agenten-Seite dazu - was hier anders ist als in einer Instanz und wie `dist export` als Testwerkzeug läuft - steht in [instructions/dev/dev-setup.md](instructions/dev/dev-setup.md). Was ein Klon mitbringt und eine Instanz nicht: - **Den Demo-Korpus.** Rund 170 Seiten, die den Stack selbst dokumentieren: Gates, Lint, Versionierung, Suche, das Wiki-Muster. Er ist Testbett und begehbares Beispiel, keine produktive Wissensbasis. Was an ihm geändert werden darf, regelt [instructions/dev/corpus-policy.md](instructions/dev/corpus-policy.md). - **Eine Demo-Persona** in `USER.md`/`SOUL.md`. `doctor` meldet beide als ausgefüllt. Sie beschreiben den Demo-Betrieb, nicht dich. - **Telemetrie an.** Ohne `.wikitool-release.json` ist der Klon die Messstation, mit der der Stack sich selbst bewertet ([EVALS.md](EVALS.md) § „Whether it runs at all“). - **`instructions/dev/`, `commonplace/`, `.gitea/` und diese Datei.** `dist export` liefert davon nichts aus. `ENVIRONMENT.md` fehlt nach jedem Klon, weil die Datei gitignored ist: Sie beschreibt einen Checkout, nicht das Repo. Wer sie anlegt (Vorlage `ENVIRONMENT.md.template`, Schritt 5 in `instructions/bootstrap.md`), erspart jeder Stack-Sitzung die Fragen nach Harness, `gitea-mcp` und Remote. ### `dist export` als Build- und Testwerkzeug `tools/wikitool dist export ` schreibt genau den Baum, den ein Release ausliefert: Maschinerie ohne Wiki-Inhalt, ohne Git-Historie, ohne `instructions/dev/`. Damit prüft man vor einem Release, was ausgeliefert würde (`--dry-run` listet es nur). Und man spielt den Installationsweg nach, ohne auf ein Release zu warten: Tarball daraus bauen wie `.gitea/workflows/release.yml`, dann das Preflight-Skript in einem leeren Verzeichnis mit `--archive ` starten. Der CI-Schritt „The distribution works as a fresh instance“ in `.gitea/workflows/ci.yml` macht genau das. Für eine echte Instanz ist ein solcher Export kein Weg. Ihm fehlen die Release-Herkunft im Stamp, und `dist upgrade --latest` vergleicht später gegen einen Stand, den es nie als Release gab. ### Ein Release-Feed in einem nicht öffentlichen Repo Wer den Stack in einem eigenen, nicht öffentlichen Repo betreibt und Instanzen von dort aktualisiert, lässt `WIKITOOL_UPDATE_URL` auf dessen Feed zeigen. Dabei gibt es eine Eigenheit: Gitea antwortet anonymen Aufrufern für ein unsichtbares Repo mit demselben `404` wie für ein gar nicht existierendes. Ein fehlendes Release und ein fehlender Zugriff sehen dann identisch aus – „kein Update gefunden“ wäre in dem Fall schlicht falsch. Dagegen hilft ein Gitea-Token mit Lesezugriff in `WIKITOOL_UPDATE_TOKEN`. Es geht nur an Downloads auf demselben Host wie der Feed. ## Der Release-Ablauf Zwischen zwei Releases führt der Stack **einen** laufenden Versionskandidaten statt einer neuen Nummer pro Bump. Das volle Modell - Zustandsort, Eskalationslogik, warum eine Nummer erst durch ein Release verbraucht wird - steht in [instructions/dev/version-parts.md](instructions/dev/version-parts.md) und [docs/version-model.md](docs/version-model.md). Hier nur der Ablauf, in der Reihenfolge, in der eine Sitzung ihn tatsächlich durchläuft: 1. **Bump eröffnet oder eskaliert den Kandidaten, gewichtet mit `--impact`.** ```bash tools/wikitool version bump --minor --title "Was sich geändert hat" --impact medium ``` Schreibt `VERSION` als `X.Y.Z-beta.N` und öffnet (oder aktualisiert) den passenden `CHANGES.md`-Eintrag. Mehrere Bumps für dieselbe Änderung sind normal - jeder aktualisiert denselben Eintrag, statt einen neuen zu eröffnen. `--impact high|medium|low` (Default `medium`) gruppiert den Eintrag; `tools/wikitool version regrade` korrigiert eine Note später, wenn der Gesamteindruck des Kandidaten den Blick auf einen früheren Bump ändert. 2. **Der Eintrag bekommt seine Prosa - zweigeteilt.** `bump` schreibt nur das Skelett (Heading, Datum, Autor, die maschinenverwaltete, gruppierte Bump-Titel-Liste, ggf. Breaking-/Migration-Zeile). Darunter kommen zwei Autorenanteile: eine kurze Zusammenfassung (ein paar Sätze, worum es in diesem Release geht) direkt unter der Liste, und darunter je Bump ein eigener `### `-Changeset-Absatz. Details dazu in [instructions/dev/version-parts.md](instructions/dev/version-parts.md) § The candidate model. 3. **Verify laufen lassen, bevor irgendetwas gepublished wird:** ```bash cd tools && .venv/bin/python -m pytest -q tools/wikitool docs verify tools/wikitool instructions verify ``` 4. **`version release` fixiert den Kandidaten**, sobald er ausgeliefert werden soll: ```bash tools/wikitool version release --title "Zusammenfassender Titel" ``` Streicht den `-beta.N`-Suffix aus `VERSION` und schließt den Changelog-Eintrag. `--title` ist optional - ohne ihn bleibt der Titel des letzten Bumps stehen; mit ihm bekommt ein Kandidat, der mehrere Bump-Titel gesammelt hat, eine zusammenfassende Überschrift. Verweigert, wenn der Kandidat zwei oder mehr Bumps gesammelt hat und die Zusammenfassung aus Schritt 2 noch fehlt - ein Kandidat mit genau einem Bump ist davon ausgenommen. Committet und pusht nichts (Invariante 5 in [AGENTS.md](AGENTS.md)). 5. **Publish bewegt `VERSION` auf `main`.** ```bash tools/wikitool publish --message "..." ``` Das Mass-Update-Gate und das Publish-Remote-Gate gelten wie bei jedem anderen Publish - siehe [instructions/gates.md](instructions/gates.md). 6. **CI übernimmt den Rest.** `.gitea/workflows/release.yml` reagiert auf jeden Push, der `VERSION` bewegt: Eine suffixbehaftete `VERSION` (ein Kandidat) lässt den Job sauber überspringen, bevor er die Releases-API überhaupt anfragt - Betas werden nie veröffentlicht. Eine suffixfreie `VERSION` baut die Distribution (`dist export`), erzeugt Tag und Release und lädt Tarball und Prüfsumme hoch, dazu die beiden Preflight-Skripte und die Anleitungen `setup-instance.md` und `preflight.md`, auf die der Installationssatz in `INSTALL.md` zeigt. **CI setzt den Tag, nie eine Sitzung** - das hält Invariante 5 intakt. Die drei Verify-Befehle stehen oben in Schritt 3; was jeder von ihnen prüft, steht in [tools/CONTRACT.md](tools/CONTRACT.md) und wird dort von `docs verify` gegen die tatsächliche CLI gehalten. Hier steht es bewusst **nicht** noch einmal: eine zweite Beschreibung derselben Befehle ist genau die Kopie, die driftet (AGENTS.md Invariante 8), und dieses Dokument liegt außerhalb der Dateien, die der Kommando-Datensatz-Check von `docs verify` abdeckt - hier fällt eine Drift also niemandem auf. Was `pytest` an dieser Stelle vom Entwickler erwartet, steht in [instructions/dev/testing-conventions.md](instructions/dev/testing-conventions.md). ## Die CI-Hälfte `.gitea/workflows/ci.yml` läuft auf jeden Push/PR gegen `main` (Content-Pfade ausgenommen) und führt Testsuite, `docs verify`, `instructions verify` sowie einen vollständigen `setup-instance.md`-Replay aus: ein lokal gebauter Release-Tarball, das Preflight-Skript mit `--archive` in einem leeren Verzeichnis, dann die Schritte der Anleitung - derselbe Pfad, den ein neuer Nutzer tatsächlich geht, nur ohne Download. `.gitea/workflows/nightly.yml` ist der Drift-Check gegen die Zeit statt gegen einen Commit. `.gitea/workflows/release.yml` ist Schritt 6 oben. Drei weitere Workflows tragen die Live-Tests der Tracker-Adapter (Super Productivity, CalDAV): `ci.yml` führt in einem eigenen Schritt die CalDAV-Hälfte gegen ein Radicale als Prozess aus, `tracker-live.yml` läuft nachts und deckt beide Anbieter ab, und `sp-live-image.yml` baut täglich (bei neuer Version) und monatlich (immer) das Image `chemenu-sp-live` mit der jeweils aktuellen Super-Productivity-Version. Das Image folgt dem Update-Kanal der Desktop-Clients, nicht einer festen Version. Einmalig nach dem allerersten Push muss das Paket von Hand dem Repo `torben/chemenu` zugeordnet werden. Was die Suite schreibt, wie man sie gegen einen eigenen Tracker laufen lässt und was ein roter Lauf bedeutet, steht in [instructions/dev/tracker-testing.md](instructions/dev/tracker-testing.md). ## Stack-Entwicklung als eigener Sitzungstyp Der `stack-dev`-Skill (`instructions/dev/`, nur in diesem Ursprungs-Repo vorhanden) fasst die Regeln für eine Sitzung, die den Stack selbst statt Wiki-Inhalt bearbeitet: wann Quellenbindung nicht gilt, wo Design endet und die mechanische Phase beginnt (mit dem Modellwechsel-Hinweis), und endet mit dem Publish. Die Schlussphase - Issue-Body als Rewrite statt Kommentar, `docs/`-Veralterung, die Modell-Handover-Zeile über die ganze Sitzung - liegt seit `4.6.0` in einem eigenen Folge-Skill, `stack-close`, den `stack-dev` an dieser Stelle übergibt statt sie als weiteren eigenen Schritt zu führen. Siehe [instructions/dev/issue-tracking.md](instructions/dev/issue-tracking.md) für den Issue-Tracker selbst.