feat: Versionskandidat statt Bump-pro-Release - VERSION traegt -beta.N, version release fixiert (4.4.0, #42)
CI / verify (push) Successful in 47s
Release / release (push) Successful in 35s

Files changed:
- .gitea/workflows/release.yml
- AGENTS.md
- CHANGES.md
- DEVELOPMENT.md
- README.md
- VERSION
- docs/version-model.md
- instructions/dev/version-parts.md
- tools/CONTRACT.md
- tools/chemenu/commands/dist_cmd.py
- tools/chemenu/commands/docs_verify.py
- tools/chemenu/commands/doctor.py
- tools/chemenu/commands/migrate_cmd.py
- tools/chemenu/commands/version_cmd.py
- tools/chemenu/kb_state.py
- tools/chemenu/tests/test_dist_cmd.py
- tools/chemenu/tests/test_docs_verify.py
- tools/chemenu/tests/test_doctor.py
- tools/chemenu/tests/test_migrate_cmd.py
- tools/chemenu/tests/test_version_cmd.py
- tools/chemenu/version.py
This commit is contained in:
2026-09-03 22:19:41 +02:00
parent b1883befc7
commit d29d400dd3
21 changed files with 1063 additions and 119 deletions
+30
View File
@@ -76,6 +76,36 @@ version bump that shipped it was `--major --no-migration`: boundary-crossing and
corpus, at the same time, which is precisely the combination the two-question split exists to
make visible.
## Why a number is only spent by a release
Everything above is about what a version number *promises*. A separate question turned out to
matter just as much in practice: how many numbers get handed out along the way to making one
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.
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.
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
still being decided during a single dev checkout's session. `release.yml`'s only job with respect
to this is refusing to act on a suffixed `VERSION` at all: not because a beta is unsafe, but
because there is nothing yet to promise.
## Where the procedure lives
The drop-in test, the catalogue of changes that cross the boundary with no page touched, and the