feat: changelog entries stay compact - one paragraph per topic, a size budget in docs verify and version release, version bump names required migration documents (#184)
Files changed: - CHANGES.md - DEVELOPMENT.md - VERSION - instructions/dev/stack-build/SKILL.md - instructions/dev/version-parts.md - instructions/migrate-corpus.md - tools/CONTRACT.md - tools/chemenu/commands/docs_verify.py - tools/chemenu/commands/version_cmd.py - tools/chemenu/tests/test_docs_verify.py - tools/chemenu/tests/test_version_cmd.py - tools/chemenu/version.py Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SnAJ7Z3CpVD3PRbN73QtU2
This commit is contained in:
1 parent
2ddcd6be19
commit
044943ae51
12 files changed
+718
-170
No files matched your search
@@ -70,22 +70,37 @@ a new one, and only `version release` turns it into something the release workfl
|
||||
reason on the marker line, bullets under a bare marker from the second onward - because a
|
||||
long-running candidate can break compatibility more than once and each break is its own
|
||||
thing to act on. The migration line does not: it answers one yes/no about the candidate as
|
||||
a whole, and `--migration-required` is its retraction path. Nothing retracts a breaking
|
||||
reason; a wrong one is rare enough, and the candidate is dev-local until release.
|
||||
a whole, in one of two forms that never stand side by side. Where a required migration
|
||||
document targets the candidate's base, every bump writes
|
||||
`**Migration:** required - instructions/migrations/<file>.md` (several paths sorted and
|
||||
comma-separated) - the pointer from the release notes to what an operator has to run;
|
||||
otherwise `--no-migration` writes `**Migration:** none required - <reason>`, and
|
||||
`--migration-required` is its retraction path. Nothing retracts a breaking reason; a wrong
|
||||
one is rare enough, and the candidate is dev-local until release.
|
||||
2. **The bump list**, grouped `**High/Medium/Low impact**` (empty groups omitted) - rendered by
|
||||
`version bump`'s `--impact` (default `medium`), corrected after the fact by `version regrade`.
|
||||
Flat and ungrouped, exactly as before this layering existed, when every bump is `medium` - the
|
||||
common case, and the shape every pre-existing region still is.
|
||||
common case, and the shape every pre-existing region still is. **This list is the bullet
|
||||
level:** a smaller change gets nothing in the entry but its bump title.
|
||||
3. **The release summary** - a short paragraph, written once, by hand, when the candidate is
|
||||
ready to ship. `version release` refuses to close an entry with two or more bumps and no
|
||||
summary here; a one-bump entry is exempt, since there the bump's own changeset already reads
|
||||
as the summary.
|
||||
4. **The changesets**, one `### <bump title>` heading per bump, in chronological order - the
|
||||
detail a reader follows into from the graded list above. A changeset is a few sentences,
|
||||
not the full rationale; what needs more than that belongs in the issue tracker, not here.
|
||||
summary here; a one-bump entry is exempt, since there its own paragraph already reads as
|
||||
the summary.
|
||||
4. **One `###` section per topic, not per bump** - for the larger changes only. A topic is
|
||||
what a reader would call one change; it often gathers several bumps, and its heading names
|
||||
the topic rather than repeating a bump title. A later bump on the same topic rewrites that
|
||||
section's paragraph instead of adding a second one. Whether a change earns a section or
|
||||
stays a bullet is the build session's judgment, with no tie to `--impact`.
|
||||
|
||||
The list is the index into the changesets, which is why the bump list's title text and a
|
||||
changeset's `###` heading are the same string.
|
||||
**The budget, checked by `docs verify` and again by `version release` before it writes:** the
|
||||
topmost versioned entry is at most 32 000 bytes (UTF-8) as `version notes` prints it, and every
|
||||
`###` section is **exactly one paragraph** - no blank line in its body - of at most 1 000 bytes,
|
||||
its `###` line excluded. A body runs to the next `### ` or `## ` line or the `---` separator;
|
||||
blank lines at either end do not count. The total is there because an entry grows with every
|
||||
bump: the 8.0.0 candidate collected 81 bumps, one changeset each, reached 129 KB and failed as
|
||||
a release body, and no per-section rule alone would have stopped that. Older entries are not
|
||||
held to it - their release notes are already published. What needs more than one paragraph -
|
||||
the reasoning, the alternatives, the measurements - belongs in the issue, not in the entry.
|
||||
- **`version bump` opens or continues a candidate; `version release` fixes one.** Only `release`
|
||||
strips the suffix and turns the entry into a real, closed release - see its own row in
|
||||
`tools/CONTRACT.md`. Nothing else does, and nothing auto-fixes a candidate on its own.
|
||||
@@ -181,7 +196,8 @@ the three-line test below is usually enough.
|
||||
- Content must change → write the migration document under `instructions/migrations/` per
|
||||
[migrate-corpus.md](../migrate-corpus.md). The escalation bump finds it by the document's
|
||||
`migrates_to:` field, matched against the candidate's **base** - a document targets the
|
||||
release the candidate will become, never a `-beta.N` form of it.
|
||||
release the candidate will become, never a `-beta.N` form of it - and names a required one
|
||||
in the entry's migration line.
|
||||
- Content need not change → `--no-migration "<reason>"`, which records that in the entry.
|
||||
|
||||
Both are also needed by `docs verify`, for the same reason: an instance that learns it must
|
||||
@@ -190,14 +206,15 @@ the three-line test below is usually enough.
|
||||
|
||||
**A `--no-migration` answer can turn out wrong later in the same candidate**, and that is not
|
||||
a hand-edit: a bump escalates, a second change lands under the same running number, and now
|
||||
content does have to move after all. Write the migration document first, then retract the line
|
||||
with `version bump --migration-required` - it removes the `**Migration:** none required` line
|
||||
the earlier bump wrote, and refuses unless a document already targets the new base. Nothing
|
||||
else takes that statement back: the line is machine-written (invariant 1), `docs verify` is
|
||||
satisfied by its bare presence, and the escalation checks in this step run only on the bump
|
||||
that *first* crosses the boundary - so a candidate that keeps a stale `none required` line is
|
||||
never reported by anything. The 5.0.0 candidate is the case: it declared `--no-migration` for
|
||||
a TOC-verification change, then absorbed a schema removal that migrates 152 pages.
|
||||
content does have to move after all. Write the migration document first, then run
|
||||
`version bump --migration-required` - it replaces the `**Migration:** none required` line the
|
||||
earlier bump wrote with the `required` line naming the document, and refuses unless a required
|
||||
document already targets the new base. Any later bump would make the same replacement once
|
||||
the document exists, and `docs verify` reports a crossing entry whose migration line does not
|
||||
name it; the flag is the explicit form, which also refuses when there is nothing to replace.
|
||||
`--no-migration` itself is refused while a required document targets the base. The 5.0.0
|
||||
candidate is the case this guards: it declared `--no-migration` for a TOC-verification
|
||||
change, then absorbed a schema removal that migrates 152 pages.
|
||||
|
||||
7. **Fix the candidate only when the user asks for a release.** Whether a candidate ships is the
|
||||
user's call, never a session's: a work package being finished is not a reason, since the
|
||||
@@ -215,16 +232,18 @@ the three-line test below is usually enough.
|
||||
candidate collected several bump titles along the way; without one, the heading simply keeps
|
||||
whichever bump last set it.
|
||||
|
||||
8. **Write the entry's prose - the summary, and each bump's own changeset.** `bump` leaves both
|
||||
empty on purpose. The **summary** is a short paragraph (a few sentences) written once, at
|
||||
release time, right below the graded bump list: what this release is about, and why, for a
|
||||
reader who will not read the changesets underneath. `version release` refuses to close an
|
||||
entry that collected two or more bumps and has no summary - a one-bump entry is exempt, since
|
||||
there the bump's changeset already reads as one. Each **changeset**, under its own
|
||||
`### <bump title>` heading, is a few sentences on what changed and why - it is the one thing a
|
||||
future reader cannot reconstruct from the diff, but it is not the place for the full rationale
|
||||
of a decision; that belongs in the issue tracker or the commit history, and a changeset that
|
||||
is growing past a paragraph or two is a sign it belongs there instead.
|
||||
8. **Write the entry's prose - a section for a larger change, nothing for a smaller one.** `bump`
|
||||
writes no prose on purpose. Decide per bump: a smaller change is done once its title is in
|
||||
the bump list. A larger one gets a `###` section for its topic - one paragraph, in the budget
|
||||
of § The candidate model, on what changed and why, which is the one thing a future reader
|
||||
cannot reconstruct from the diff. If its topic already has a section, rewrite that paragraph
|
||||
to cover both bumps rather than adding another. The full rationale of a decision belongs in
|
||||
the issue tracker or the commit history; a paragraph pressing against its 1 000 bytes is a
|
||||
sign that part of it belongs there instead. The **summary** is a short paragraph (a few
|
||||
sentences) written once, at release time, right below the graded bump list: what this
|
||||
release is about, and why, for a reader who will not read the sections underneath.
|
||||
`version release` refuses to close an entry that collected two or more bumps and has no
|
||||
summary - a one-bump entry is exempt, since there its own paragraph already reads as one.
|
||||
|
||||
## Decision points
|
||||
|
||||
|
||||
Reference in new issue
Block a user