9843df99d3
Files changed: - kb/concepts/INDEX.md - kb/concepts/KB Stack Versioning.md - kb/entities/INDEX.md - kb/entities/projects/Chemenu.md - kb/entities/tools/wikitool.md - kb/index.md - kb/log.md - kb/sources/INDEX.md - kb/sources/Source - Version Part Nomenclature and Breaking Change Gate Session 2026-09-02.md - raw/notes/Conversation Transcript - Version Part Nomenclature and Breaking Change Gate Session 2026-09-02.md
219 lines
14 KiB
Markdown
219 lines
14 KiB
Markdown
# Conversation Transcript - Version Part Nomenclature and Breaking Change Gate Session
|
|
|
|
> Source: Claude Code session (`claude-sonnet-5`), chemenu workspace
|
|
> Collected: 2026-09-02
|
|
> Participant: Torben Nehmer
|
|
> Fidelity: **faithful summary transcript, not a verbatim log.** The user's instructions and
|
|
> clarifications are quoted verbatim; the agent's own reasoning and file-reading steps are
|
|
> paraphrased; tool output blocks (`docs verify`, `pytest`, `version bump --dry-run`, the
|
|
> Mass-Update Gate refusal) are real, copied from the actual run, not reconstructed.
|
|
> No second-hand material - no subagent was used.
|
|
> No credentials appeared.
|
|
> Single topic, not cut.
|
|
|
|
Closes Gitea issue #26 (already closed in-session) with `2.5.0`. Commit `31662dc` on `main`.
|
|
Covers picking the version part for a stack change, and adding a user-approval gate for
|
|
breaking changes to `stack-dev`.
|
|
|
|
## Turn 1 - `/stack-dev kümmere dich um #26`
|
|
|
|
The user invoked the `stack-dev` skill with the argument `kümmere dich um #26`. The agent read
|
|
issue #26 via `gitea-mcp` (`torben/chemenu#26`, no comments yet).
|
|
|
|
**The issue's own account** (quoted from its body, since it is the source of the whole
|
|
session): the stack's documentation conflated two questions when choosing a version bump part -
|
|
"Muss der Korpus migriert werden?" and "Ist der Wechsel rückwärtskompatibel?" - and every place
|
|
an agent would consult before a bump stated only the first:
|
|
|
|
| Ort | Wortlaut |
|
|
|---|---|
|
|
| `instructions/dev/stack-dev/SKILL.md`, Schritt 3 | `--major` ⇔ "Existing content must be migrated" |
|
|
| `tools/chemenu/version.py`, module docstring | "from `1.0.0` on the same rule reads as the familiar 'MAJOR means migration'" |
|
|
| `INSTALL.md` § Version und Updates | "ab `1.0.0` liest sich dieselbe Regel als das gewohnte 'MAJOR heißt Migration'" |
|
|
|
|
Only `version bump --help` had it right ("A bump that crosses the compatibility boundary
|
|
additionally requires a migration document ... or `--no-migration`") - the one place read only
|
|
after the decision is already made.
|
|
|
|
The issue's worked example was the `2.0.0` rebranding (`torben/llm-wiki-test1` →
|
|
`torben/chemenu`): no page in `kb/` changed, so the first attempt at that bump was `1.9.0`. Three
|
|
things broke anyway - the release-feed URL baked into every existing instance's
|
|
`.wikitool-release.json` (unrepairable by hand, per invariant 1), the release artefact's
|
|
filename, and the Python package's import name. The correct bump was `--major --no-migration`.
|
|
The issue's "Was 'fertig' heißt" checklist asked for: rewording the three prose locations to
|
|
lead with compatibility rather than migration, a decision aid enumerating which kinds of break
|
|
force MAJOR even with `kb/` untouched, and a check that the three locations do not end up
|
|
contradicting each other (invariant 8). It explicitly scoped out teaching `docs verify` to judge
|
|
*whether* a bump chose the right part - "das ist eine Urteilsfrage und kein hartes Orakel."
|
|
|
|
The agent read the three prose locations plus `tools/chemenu/commands/version_cmd.py` (the
|
|
existing `--no-migration` enforcement) and `docs_verify.py` (`check_migration_for_boundary`), to
|
|
scope the fix before writing anything.
|
|
|
|
## Turn 2 - scope questions, then the user's clarification
|
|
|
|
Before writing, the agent asked two `AskUserQuestion` questions:
|
|
|
|
1. Whether the "Breaking Change" marker and approval requirement should be prose-only or also
|
|
enforced in the tool. Options offered: "Prosa + Marker im Code (Empfehlung)" vs. "Nur Prosa".
|
|
**User picked the enforced option** ("Prosa + Marker im Code (Empfehlung)") - a
|
|
`--breaking` flag on `version bump`, checked by `docs verify`, on the reasoning that a
|
|
pure-prose rule is exactly what drifted in the first place.
|
|
2. Whether the pre-bump approval step should read like one of the codebase's actual gates
|
|
(`instructions/gates.md` language: "anhalten ... auf ausdrückliche Freigabe warten") or as a
|
|
softer "Decision points" entry. **User picked the softer framing** ("Als Entscheidungspunkt")
|
|
- explicitly *not* gate language, to avoid diluting the weight of the three real,
|
|
code-enforced gates (Mass-Update, Publish-Remote, Iteration Budget) by association.
|
|
|
|
The user then added the clarification that drove the rest of the session, quoted in full:
|
|
|
|
> "genaue nomenklatur für major version change: die neue version ist kein drop-in replacement.
|
|
> Sobald irgendwie Hand angelegt werden muss, sei es durch den user oder durch ein
|
|
> Migrationsscript, ist es ein major version change. selbiges gilt, wenn ein update nicht
|
|
> rückgängig gemacht werden kann (Szenario: User aktualisiert den Stack von vA nach vB, vB hat
|
|
> beim User ein Problem, kann jetzt vA nicht mehr einfach so als Downgrade installiert werden,
|
|
> ist es ein breaking change)
|
|
> in allen Fällen muss bei einem Major version change ein "Breaking Change" vermerkt werden.
|
|
> breaking changes sind damit teuer. passe stack-dev so an, dass in diesen Fällen zwingend der
|
|
> user informiert, Alternativen aufgezeigt und eine freigabe eingeholt wird."
|
|
|
|
This sharpened the issue's "compatibility" question into a concrete two-sided test - **forward**
|
|
(can the new version be copied over with zero hand-work, by user or script) and **backward** (can
|
|
the old version still be reinstalled afterwards) - and made explicit that either half failing,
|
|
independent of `kb/`, is what MAJOR actually means.
|
|
|
|
## Turn 3 - what was written
|
|
|
|
**`instructions/dev/version-parts.md` (new).** The single source for the rule. Structure:
|
|
the two-sided drop-in test (step 1); a catalogue of breaks that cross the boundary with `kb/`
|
|
untouched - update path, release-artefact name, package import name, a renamed
|
|
command/flag/envvar, the shape of a machine-read file like `.wikitool-kb.json` (step 2); the
|
|
plain compatible-part table for patch/minor (step 3); the mandatory stop-and-inform-the-user
|
|
step before any boundary-crossing bump, with three named alternatives (avoid via a
|
|
compatibility shim, defer and batch with the next crossing, split into a compatible half now
|
|
and a deprecated-then-removed half later) plus a recommendation (step 4); `--breaking` usage
|
|
(step 5); the pre-existing migration-document-or-`--no-migration` step, now explicitly
|
|
independent of compatibility (step 6); writing the entry body (step 7). Closes with the `2.0.0`
|
|
case study as the worked example, matching the issue's own account.
|
|
|
|
Placed under `instructions/dev/` (not linked from any distributed artifact) because
|
|
`tools/wikitool dist export` prunes that directory wholesale - the agent's first draft linked to
|
|
it from `tools/CONTRACT.md` and the `version.py` docstring, which `tools/wikitool instructions
|
|
verify` correctly rejected:
|
|
|
|
```
|
|
ERROR Instruction layer issues:
|
|
- version-parts.md: lives under instructions/dev/ but is referenced from
|
|
outside it and outside a dist:strip block - `dist export` removes
|
|
instructions/dev/ wholesale, so that reference would dangle in a distributed
|
|
instance. Remove the reference, or wrap it in a <!-- dist:strip-start/end -->
|
|
block if it belongs only to this dev instance.
|
|
```
|
|
|
|
**Rejected approach:** wrapping the reference in `<!-- dist:strip-start/end -->` markers so it
|
|
would still resolve in this repo. Not used - the agent instead rewrote the three shipped
|
|
locations (`tools/CONTRACT.md`, `version.py` docstring, `version_cmd.py` docstring) to state the
|
|
short form of the rule standalone, with no pointer to the dev-only file, since a shipped
|
|
instance never has it to point to.
|
|
|
|
**`instructions/dev/stack-dev/SKILL.md`.** Step 3's table changed from "Existing content must
|
|
be migrated → `--major`" to "Not a drop-in replacement ... → `--major`", with a pointer to
|
|
`version-parts.md` for the full test and catalogue. A new "Decision points" entry: if a change
|
|
turns out not to be a drop-in replacement, stop - do not bump across the boundary on the
|
|
session's own initiative; show the user the concrete break, what each instance must do, and the
|
|
three alternatives from `version-parts.md` step 4, then wait for a go-ahead. Written in the
|
|
softer "Decision points" register per the user's second answer above, not gate language.
|
|
|
|
**`tools/chemenu/version.py`.** Module docstring reworded: "MAJOR means migration" → "MAJOR
|
|
breaks", with a new paragraph stating the two questions are independent and naming both markers.
|
|
New constant `BREAKING_CHANGE_MARKER = "**Breaking Change:**"`, alongside the existing
|
|
`MIGRATION_NONE_MARKER`. `insert_changes_entry()` gained a `breaking_reason` parameter, writing
|
|
the `**Breaking Change:**` line *before* the migration line - the break is what an operator acts
|
|
on first.
|
|
|
|
**`tools/chemenu/commands/version_cmd.py`.** `bump` gained `--breaking TEXT`. Refused on a
|
|
non-boundary-crossing bump; required on a boundary-crossing one, with a refusal message that
|
|
also second-guesses the part choice itself: *"If that sentence is hard to write because nothing
|
|
actually breaks - no hand-work on update, and the old version can still be put back - then the
|
|
bump is probably not --major."* Verified live:
|
|
|
|
```
|
|
$ tools/wikitool version bump --major --title "smoke" --dry-run
|
|
ERROR 2.4.1 -> 3.0.0 crosses the compatibility boundary, so it is not a drop-in
|
|
replacement - re-run with --breaking "<what stops working, and what an instance
|
|
must do about it>".
|
|
If that sentence is hard to write because nothing actually breaks - no
|
|
hand-work on update, and the old version can still be put back - then the
|
|
bump is probably not --major.
|
|
```
|
|
|
|
```
|
|
$ tools/wikitool version bump --minor --title "smoke" --breaking "x" --dry-run
|
|
ERROR --breaking only applies to a bump that crosses the compatibility boundary;
|
|
2.4.1 -> 2.5.0 does not.
|
|
```
|
|
|
|
**`tools/chemenu/commands/docs_verify.py`.** New `check_breaking_change_for_boundary()`,
|
|
independent of the existing `check_migration_for_boundary()` on purpose - the two check
|
|
different things (did the corpus move vs. was the operator told the swap isn't drop-in), and a
|
|
crossing with `kb/` untouched can satisfy the first while failing the second. Wired into
|
|
`verify()` alongside the existing check.
|
|
|
|
**Tests.** `test_version_cmd.py`: nine existing calls updated for the new `breaking=` parameter,
|
|
plus three new tests (`test_a_boundary_crossing_bump_without_breaking_is_refused`,
|
|
`test_breaking_records_what_stops_working_in_the_changelog` - including the ordering assertion
|
|
that `BREAKING_CHANGE_MARKER` precedes `MIGRATION_NONE_MARKER` in the rendered entry -
|
|
`test_breaking_is_refused_on_a_compatible_bump`). `test_docs_verify.py`: four new tests mirroring
|
|
the existing migration-boundary tests but for the breaking-change marker. Full suite: 806 passed.
|
|
|
|
**`tools/CONTRACT.md` and `INSTALL.md`.** Both reworded to lead with the drop-in question and
|
|
treat migration as the second, independent one; `INSTALL.md` additionally tells the reader which
|
|
two lines to look for in release notes (`Breaking Change:` and `Migration:`) before applying an
|
|
update.
|
|
|
|
**`CHANGES.md`.** New `2.5.0` entry written after the bump, body filled in by the agent (the
|
|
tool leaves it empty by design) - includes the "what deliberately did not change" note that
|
|
`docs verify` still does not judge *whether* the chosen part was correct, matching the issue's
|
|
explicit scope-out.
|
|
|
|
## Turn 4 - publish, twice
|
|
|
|
`tools/wikitool version bump --minor --title "..."` was run first (11 changed files, drop-in in
|
|
both directions - the new requirement only binds the *next* boundary crossing, not
|
|
retroactively). `tools/wikitool publish --message "..."` then hit the **Mass-Update Gate**
|
|
(11 counted files ≥ threshold 10):
|
|
|
|
```
|
|
NEEDS USER CLEARANCE Mass-Update Gate: this publish would commit and push 11
|
|
counted files (>= threshold 10) to origin/main. ...
|
|
```
|
|
|
|
Per the gate's own instructions, the agent reproduced the full file-by-area breakdown and the
|
|
`--confirm <token>` line in its reply and ran nothing further that turn. The user replied
|
|
"freigegeben" for both the earlier publish authorization ("publish ist freigegeben") and, in a
|
|
separate turn, this specific token. The agent then ran `tools/wikitool publish --confirm
|
|
95ae372d5677 --message '...'`, which pushed commit `31662dc` to `origin/main`, and verified
|
|
`git rev-parse HEAD origin/main` matched afterward.
|
|
|
|
## Turn 5 - issue closeout and this capture
|
|
|
|
The user asked to update issue #26 "wie vorgeschlagen" (per the agent's own end-of-turn
|
|
suggestion), run `instructions/capture-session.md`, and in the same pass correct
|
|
`kb/entities/tools/wikitool.md`. The agent posted a comment on #26 summarizing what shipped
|
|
(including the parts that went beyond the issue's own checklist - the `--breaking` flag and the
|
|
second `docs verify` check, added because plain prose was judged likely to drift again) and
|
|
closed the issue. This transcript and the `kb/` correction are the remaining two steps of that
|
|
request.
|
|
|
|
## Outcome
|
|
|
|
| | |
|
|
|---|---|
|
|
| Version | `2.4.1` → `2.5.0` (`--minor`: new capability, still drop-in both directions) |
|
|
| Commit | `31662dc` on `main`, pushed to `origin` |
|
|
| Files changed | 11 (+432/-33): `instructions/dev/version-parts.md` (new), `instructions/dev/stack-dev/SKILL.md`, `tools/CONTRACT.md`, `tools/chemenu/version.py`, `tools/chemenu/commands/version_cmd.py`, `tools/chemenu/commands/docs_verify.py`, `tools/chemenu/tests/test_version_cmd.py`, `tools/chemenu/tests/test_docs_verify.py`, `CHANGES.md`, `INSTALL.md`, `VERSION` |
|
|
| Tests | 806 passed (`tools/chemenu/tests/`), including 6 new |
|
|
| Verification | `tools/wikitool docs verify` OK, `tools/wikitool instructions verify` OK (17 instructions, 6 skills, 12 published copies match), `tools/wikitool doctor` clean (only the expected `WIKITOOL_SESSION_ID` WARN) |
|
|
| Issues | #26 closed, comment `torben/chemenu#26` (issuecomment-474) |
|
|
| CI | Not yet observed in this session - a `VERSION` move on `main` triggers a tagged release per `.gitea/workflows/release.yml`; not polled |
|