docs: Faktenkorrekturen in der 4.4.0-Prosa; DEVELOPMENT.md ohne zweite Kommandobeschreibung (4.4.1-beta.1, #47)
CI / verify (push) Successful in 49s
Release / release (push) Successful in 34s

Files changed:
- CHANGES.md
- DEVELOPMENT.md
- VERSION
- docs/version-model.md
- instructions/dev/version-parts.md
This commit is contained in:
2026-09-03 22:48:09 +02:00
parent d29d400dd3
commit abe5497cda
5 changed files with 82 additions and 38 deletions
+45 -6
View File
@@ -35,6 +35,43 @@ dev-checkout concern - readable here, never shipped as something to parse.
--- ---
## 4.4.1-beta.1 - 2026-09-03 - Faktenkorrekturen in der 4.4.0-Prosa; DEVELOPMENT.md ohne zweite Kommandobeschreibung
**Author:** Torben Nehmer
<!-- wikitool:bumps -->
- Faktenkorrekturen in der 4.4.0-Prosa; DEVELOPMENT.md ohne zweite Kommandobeschreibung
<!-- /wikitool:bumps -->
Die Prosa zu 4.4.0 - Changelog-Eintrag, `docs/version-model.md`, `instructions/dev/version-parts.md`,
`DEVELOPMENT.md` - entstand ungeprüft: kein maschineller Check liest sie, und die Sitzung, die sie
schrieb, hat den dafür vorgesehenen Modellwechsel aus `stack-dev` Schritt 6 übersprungen. Ein
nachgeholter Durchgang hat drei Fehler gefunden, von denen der erste der teuerste war.
**Der Befund war an einer Stelle schlicht falsch.** Changelog und `docs/version-model.md`
behaupteten, unter dem alten Modell seien Nummern vergeben worden, die „nie ausgeliefert" wurden -
im Changelog sogar mit einer erfundenen Zahl („fünf Minor-Bumps ... vier nie ausgeliefert"). Das
Gegenteil stimmt: weil `release.yml` auf jede `VERSION`-Bewegung feuerte, wurde **jeder** Bump zu
einem echten, getaggten Release. Der 2026-09-03 hat vier davon in sechs Stunden erzeugt (`4.3.0`
bis `4.3.3`), zwei für reine Prosa-Änderungen. Der Schaden war nie ein Phantom-Release, sondern
dass „Release" aufhörte, etwas zu bedeuten - vier Upgrades an einem Nachmittag sind für einen
Konsumenten kein kleineres Versprechen, sondern ein unleserliches. Beide Stellen tragen jetzt den
tatsächlichen Vorgang samt Beleg.
Dazu zwei kleinere Korrekturen: `version-parts.md` nannte den Stack weiterhin `2.x` (er steht bei
`4.x`), und `DEVELOPMENT.md` trug eine Tabelle, die für jeden Verify-Befehl ein zweites Mal
beschrieb, was er prüft - eine Kopie dessen, was `tools/CONTRACT.md` hält und `docs verify` dort
gegen die CLI prüft. Die Tabelle ist raus; dass die Datei selbst außerhalb der von diesem Check
abgedeckten Dokumente liegt, steht jetzt an ihrer Stelle. Der Code-seitige Teil davon - ob
`DEVELOPMENT.md` in `docs_verify.STAGE_READMES` gehört - hängt an #47, zusammen mit der Lücke im
Skill, die den übersprungenen Modellwechsel überhaupt erst unauffällig gemacht hat.
Kein Verhalten geändert, nur Prosa - und bewusst als laufender Kandidat gelassen statt fixiert:
CIs Version-Gate verlangt die `VERSION`-Bewegung, ein Release verlangt sie nicht. Genau dafür gibt
es das Modell aus 4.4.0.
---
## 4.4.0 - 2026-09-03 - Versionskandidat statt Bump-pro-Release: VERSION traegt -beta.N, version release fixiert ## 4.4.0 - 2026-09-03 - Versionskandidat statt Bump-pro-Release: VERSION traegt -beta.N, version release fixiert
**Author:** Torben Nehmer **Author:** Torben Nehmer
@@ -43,12 +80,14 @@ dev-checkout concern - readable here, never shipped as something to parse.
- Versionskandidat statt Bump-pro-Release: VERSION traegt -beta.N, version release fixiert - Versionskandidat statt Bump-pro-Release: VERSION traegt -beta.N, version release fixiert
<!-- /wikitool:bumps --> <!-- /wikitool:bumps -->
Bisher bekam jeder `version bump` sofort eine fixierte, dauerhafte Nummer, unabhängig davon, ob Bisher bekam jeder `version bump` sofort eine fixierte Nummer, und weil `release.yml` auf jede
je ein Release dazu erschien - fünf Minor-Bumps ohne Release ergaben fünf Nummern, von denen vier `VERSION`-Bewegung feuert, wurde daraus sofort ein Release: Nummern entstanden in
nie ausgeliefert wurden. `VERSION` trägt jetzt zwischen zwei Releases **einen** laufenden Commit-Granularität statt in Release-Granularität. Der 2026-09-03 hat so vier Releases in sechs
Kandidaten (`X.Y.Z-beta.N`) statt einer neuen Nummer pro Bump: `--major/--minor/--patch` Stunden erzeugt (`4.3.0` bis `4.3.3`), zwei davon für reine Prosa-Änderungen - alle vier echt,
eskaliert diesen Kandidaten max-wins gegen den letzten Release, statt daneben eine neue Nummer zu keines davon eine Einheit, an der ein Konsument sich hätte orientieren können. `VERSION` trägt
schreiben, und schrittet dabei nie zurück. jetzt zwischen zwei Releases **einen** laufenden Kandidaten (`X.Y.Z-beta.N`):
`--major/--minor/--patch` eskaliert diesen Kandidaten max-wins gegen den letzten Release, statt
eine neue Nummer danebenzustellen, und geht dabei nie zurück.
`Version` versteht den Suffix, mit einer expliziten Ordnung `Version` versteht den Suffix, mit einer expliziten Ordnung
(`4.4.0-beta.1 < 4.4.0-beta.2 < 4.4.0`, numerisch nach `N`, nicht lexikografisch). `CHANGES.md` (`4.4.0-beta.1 < 4.4.0-beta.2 < 4.4.0`, numerisch nach `N`, nicht lexikografisch). `CHANGES.md`
+7 -9
View File
@@ -71,15 +71,13 @@ eine Sitzung ihn tatsächlich durchläuft:
lädt Tarball plus Prüfsumme hoch. **CI setzt den Tag, nie eine Sitzung** - das hält lädt Tarball plus Prüfsumme hoch. **CI setzt den Tag, nie eine Sitzung** - das hält
Invariante 5 intakt. Invariante 5 intakt.
## Verify-Befehle im Überblick 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
| Befehl | Prüft | 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
| `cd tools && .venv/bin/python -m pytest -q` | Die gesamte Testsuite, hermetisch gegen eine leere Maschine (siehe `instructions/dev/testing-conventions.md`) | außerhalb der Dateien, die der Kommandotabellen-Check von `docs verify` abdeckt - hier fällt eine
| `tools/wikitool docs verify` | CLI-Kommandotabelle, Contract-Präsenz, Type-Drift, `.gitignore`-Kanarienvögel, `VERSION`/`CHANGES.md`-Übereinstimmung, Grenzübertritts-Dokumentation | Drift also niemandem auf. Was `pytest` an dieser Stelle vom Entwickler erwartet, steht in
| `tools/wikitool instructions verify` | Jede Instruction und jeder Skill unter `instructions/`, verwaiste Dateien, `instructions/dev/`-Referenzen von außerhalb | [instructions/dev/testing-conventions.md](instructions/dev/testing-conventions.md).
Die volle Kommandoreferenz inklusive Fehlerkontrakt: [tools/CONTRACT.md](tools/CONTRACT.md).
## Die CI-Hälfte ## Die CI-Hälfte
+1 -1
View File
@@ -1 +1 @@
4.4.0 4.4.1-beta.1
+19 -14
View File
@@ -83,22 +83,27 @@ matter just as much in practice: how many numbers get handed out along the way t
release. For a while the answer was "one per bump," and that turned out to be the wrong grain release. For a while the answer was "one per bump," and that turned out to be the wrong grain
entirely. entirely.
The two things that actually consume a version number are a release and CI's version gate - and Two mechanisms decide when a number gets minted, and they answer different questions. CI's
they disagree about granularity. The gate wants `VERSION` to move on every push that touches version gate asks a *commit*-level one: has this tree changed since the last push, and if so
stack-shaped paths, which is a *commit*-level question: has this tree changed since the last has `VERSION` moved with it. A release asks something else entirely: is this a state worth
push. A release wants to know something else: has *this specific number* been published, ever. handing to someone, under a number they will pin against. Tying the second to the first - every
Handing out a fresh number per bump answers the gate's question by accident and the release's `VERSION` move firing the release workflow - answers the gate correctly and the release question
question wrongly - it treats every bump as if it were about to ship, when most of them are steps by accident, because it treats every bump as if it were about to ship when most bumps are steps
toward a release that hasn't happened yet. Four bumps in one session, on the same day, for the toward a release that has not happened yet.
same eventual release, produced four numbers that a version-check feed would have reported as
four different available upgrades, three of which were never real. The failure mode is not phantom numbers; every one of those releases was real, tagged and
downloadable. It is that "real" stopped meaning anything. On 2026-09-03 this repository cut four
releases in six hours - `4.3.0` through `4.3.3` - for one continuous arc of work, two of them for
prose changes alone. Someone tracking the feed saw four upgrades and had no way to tell which, if
any, was a moment worth stopping for. A release is a promise addressed to a consumer, and a
promise made four times an afternoon is not a smaller promise, it is a less legible one.
The fix is not to slow the gate down - it still wants `VERSION` to move every time, and it still The fix is not to slow the gate down - it still wants `VERSION` to move every time, and it still
gets that. It's to stop treating every movement as a new number. Between two releases the stack gets that. It is to stop treating every movement as a number worth publishing. Between two
now carries one running candidate, escalating through `-beta.N` as bumps accumulate, and only releases the stack now carries one running candidate, escalating through `-beta.N` as bumps
`version release` spends the number for real by fixing it and closing its changelog entry. A accumulate, and only `version release` spends the number for real by fixing it and closing its
number is proposed by a bump and consumed by a release; conflating the two was the actual defect, changelog entry. A number is proposed by a bump and spent by a release; conflating the two was
not the arithmetic of any single bump. the actual defect, not the arithmetic of any single bump.
This is also why a candidate never gets to a distributed instance. The promise a released version This is also why a candidate never gets to a distributed instance. The promise a released version
makes - "install this, and it is exactly what its number says" - has no equivalent for something makes - "install this, and it is exactly what its number says" - has no equivalent for something
+10 -8
View File
@@ -9,7 +9,7 @@ Two questions decide a version bump, and they are **not the same question**:
1. **Is the new version a drop-in replacement for the old one?** This is what the version 1. **Is the new version a drop-in replacement for the old one?** This is what the version
number itself says. Compatibility is read off the **leftmost non-zero component** - on this number itself says. Compatibility is read off the **leftmost non-zero component** - on this
stack (`2.x`) that is MAJOR, on a `0.x` stack it is MINOR. A bump that changes it is called stack (`4.x`) that is MAJOR, on a `0.x` stack it is MINOR. A bump that changes it is called
*boundary-crossing* below, because that is the term `version bump` and `docs verify` use in *boundary-crossing* below, because that is the term `version bump` and `docs verify` use in
their own messages. their own messages.
2. **Must existing content be migrated?** This is a *consequence* a boundary crossing may or 2. **Must existing content be migrated?** This is a *consequence* a boundary crossing may or
@@ -23,11 +23,13 @@ already (see the case study at the end), which is why this file exists.
## The candidate model ## The candidate model
Between two releases the stack carries **one running candidate**, not a fresh version per Between two releases the stack carries **one running candidate**, not a fresh version per
`bump`. Five bumps with no release in between used to mean five numbers, four of which nothing `bump`. Before that, every bump minted a number *and* a release: CI's version gate requires
ever consumed - the release-granularity CI's version gate wants (`VERSION` must move on every `VERSION` to move on every stack-touching push, and `release.yml` fires on every `VERSION`
stack-touching push) was being paid at bump granularity instead. A candidate closes that gap move, so releases were being cut at commit granularity. 2026-09-03 produced four of them in
without touching the gate: `VERSION` still moves on every bump, it just escalates the *same* six hours (`4.3.0` through `4.3.3`) for one arc of work - all four real, none of them a
number instead of handing out a new one. meaningful unit to anyone downstream. A candidate closes that gap without touching the gate:
`VERSION` still moves on every bump, it just escalates the *same* number instead of handing out
a new one, and only `version release` turns it into something the release workflow acts on.
- **State lives in `VERSION` itself**, as an optional `-beta.N` suffix (`4.4.0-beta.3`). No - **State lives in `VERSION` itself**, as an optional `-beta.N` suffix (`4.4.0-beta.3`). No
second state file: the last release is read back out of `CHANGES.md` (the newest entry with no second state file: the last release is read back out of `CHANGES.md` (the newest entry with no
@@ -35,8 +37,8 @@ number instead of handing out a new one.
release - derived, not stored. release - derived, not stored.
- **`--major`/`--minor`/`--patch` is max-wins escalation**, not a step you can undo. A `--patch` - **`--major`/`--minor`/`--patch` is max-wins escalation**, not a step you can undo. A `--patch`
bump on a candidate already at MINOR only advances its bump count (`N`); nothing ever steps a bump on a candidate already at MINOR only advances its bump count (`N`); nothing ever steps a
candidate back down. Declaring the part is still your judgment call, made the same way section candidate back down. Declaring the part is still your judgment call, made the same way the
below describes - `escalate()` only ever raises it further. steps below describe - `escalate()` only ever raises it further.
- **A candidate is never released.** Pre-release is a dev-checkout state; `release.yml` only acts - **A candidate is never released.** Pre-release is a dev-checkout state; `release.yml` only acts
on a suffix-free `VERSION`, so a distributed instance never sees a `-beta.` version at all, and on a suffix-free `VERSION`, so a distributed instance never sees a `-beta.` version at all, and
its parser never has to know the suffix exists. its parser never has to know the suffix exists.