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
**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
<!-- /wikitool:bumps -->
Bisher bekam jeder `version bump` sofort eine fixierte, dauerhafte Nummer, unabhängig davon, ob
je ein Release dazu erschien - fünf Minor-Bumps ohne Release ergaben fünf Nummern, von denen vier
nie ausgeliefert wurden. `VERSION` trägt jetzt zwischen zwei Releases **einen** laufenden
Kandidaten (`X.Y.Z-beta.N`) statt einer neuen Nummer pro Bump: `--major/--minor/--patch`
eskaliert diesen Kandidaten max-wins gegen den letzten Release, statt daneben eine neue Nummer zu
schreiben, und schrittet dabei nie zurück.
Bisher bekam jeder `version bump` sofort eine fixierte Nummer, und weil `release.yml` auf jede
`VERSION`-Bewegung feuert, wurde daraus sofort ein Release: Nummern entstanden in
Commit-Granularität statt in Release-Granularität. Der 2026-09-03 hat so vier Releases in sechs
Stunden erzeugt (`4.3.0` bis `4.3.3`), zwei davon für reine Prosa-Änderungen - alle vier echt,
keines davon eine Einheit, an der ein Konsument sich hätte orientieren können. `VERSION` trägt
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
(`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
Invariante 5 intakt.
## Verify-Befehle im Überblick
| Befehl | Prüft |
|---|---|
| `cd tools && .venv/bin/python -m pytest -q` | Die gesamte Testsuite, hermetisch gegen eine leere Maschine (siehe `instructions/dev/testing-conventions.md`) |
| `tools/wikitool docs verify` | CLI-Kommandotabelle, Contract-Präsenz, Type-Drift, `.gitignore`-Kanarienvögel, `VERSION`/`CHANGES.md`-Übereinstimmung, Grenzübertritts-Dokumentation |
| `tools/wikitool instructions verify` | Jede Instruction und jeder Skill unter `instructions/`, verwaiste Dateien, `instructions/dev/`-Referenzen von außerhalb |
Die volle Kommandoreferenz inklusive Fehlerkontrakt: [tools/CONTRACT.md](tools/CONTRACT.md).
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 Kommandotabellen-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
+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
entirely.
The two things that actually consume a version number are a release and CI's version gate - and
they disagree about granularity. The gate wants `VERSION` to move on every push that touches
stack-shaped paths, which is a *commit*-level question: has this tree changed since the last
push. A release wants to know something else: has *this specific number* been published, ever.
Handing out a fresh number per bump answers the gate's question by accident and the release's
question wrongly - it treats every bump as if it were about to ship, when most of them are steps
toward a release that hasn't happened yet. Four bumps in one session, on the same day, for the
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.
Two mechanisms decide when a number gets minted, and they answer different questions. CI's
version gate asks a *commit*-level one: has this tree changed since the last push, and if so
has `VERSION` moved with it. A release asks something else entirely: is this a state worth
handing to someone, under a number they will pin against. Tying the second to the first - every
`VERSION` move firing the release workflow - answers the gate correctly and the release question
by accident, because it treats every bump as if it were about to ship when most bumps are steps
toward a release that has not happened yet.
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
gets that. It's to stop treating every movement as a new number. Between two releases the stack
now carries one running candidate, escalating through `-beta.N` as bumps accumulate, and only
`version release` spends the number for real by fixing it and closing its changelog entry. A
number is proposed by a bump and consumed by a release; conflating the two was the actual defect,
not the arithmetic of any single bump.
gets that. It is to stop treating every movement as a number worth publishing. Between two
releases the stack now carries one running candidate, escalating through `-beta.N` as bumps
accumulate, and only `version release` spends the number for real by fixing it and closing its
changelog entry. A number is proposed by a bump and spent by a release; conflating the two was
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
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
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
their own messages.
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
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
ever consumed - the release-granularity CI's version gate wants (`VERSION` must move on every
stack-touching push) was being paid at bump granularity instead. 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.
`bump`. Before that, every bump minted a number *and* a release: CI's version gate requires
`VERSION` to move on every stack-touching push, and `release.yml` fires on every `VERSION`
move, so releases were being cut at commit granularity. 2026-09-03 produced four of them in
six hours (`4.3.0` through `4.3.3`) for one arc of work - all four real, none of them a
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
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.
- **`--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
candidate back down. Declaring the part is still your judgment call, made the same way section
below describes - `escalate()` only ever raises it further.
candidate back down. Declaring the part is still your judgment call, made the same way the
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
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.