docs(changelog): one line per entry, and a pass over the whole file #147

Merged
dan-claude-bot merged 2 commits from docs/changelog-one-line into main 2026-07-21 14:43:50 +00:00
3 changed files with 169 additions and 1241 deletions

File diff suppressed because it is too large Load diff

View file

@ -54,6 +54,42 @@ labels tell you where everything is without opening anything.
7. **Checks must be green**: `shellcheck` and `bash test/cli.sh` locally 7. **Checks must be green**: `shellcheck` and `bash test/cli.sh` locally
mirror what CI runs; the multi-user rehearsal runs in CI on a real Incus. mirror what CI runs; the multi-user rehearsal runs in CI on a real Incus.
## Changelog entries
Every PR that changes behaviour adds **one line** to `## Unreleased`. One line
is the whole rule — if it wraps more than twice in your editor, cut it down.
- **Say what changed, and stop.** Why it was wrong, how it was found, what it
cost, what it implies — that belongs in the PR body and the commit message,
which is where anyone chasing the reasoning already goes. This file answers
one question: what is different in this version.
- **Any word that can be removed, is removed.**
- **Lead with the surface, not the mechanism.** "`state:needs-human` is set at
handoff" beats "the labels workflow now also wakes on `labeled`".
- **Cite the issue or PR**`(#141)` — and let the reader follow it for the
rest.
- **Mark a breaking change** with a leading `BREAKING:`.
- Group under `### Added` / `### Changed` / `### Fixed` / `### Removed`.
- No bold run-in headings, no sub-paragraphs, no code blocks, no prose essays.
Good:
```markdown
- `state:needs-human` is set at handoff, not by the cron (#141)
- An unreadable check rollup no longer reads as "nothing is failing" (#136)
- BREAKING: `--class human|server` is now `--root-door closed|open` (#77)
```
Not an entry — that is a PR body:
```markdown
- **`state:needs-human` no longer waits on the cron to become true** (#141) —
the labels workflow now also wakes on `pull_request_target: labeled` and
`unlabeled`, and the author sets it themselves when handing a PR over. A
review landing was never a trigger. There is no `pull_request_review_target`,
and on fork PRs — which is all of them here — ...
```
## Releases ## Releases
A release is a PR, and merging it ships it A release is a PR, and merging it ships it

View file

@ -301,14 +301,24 @@ check "changelog-monotonic: runnable bash" 0 "" bash -n "$MONO"
# grepo <name> <base-changelog-lines...> — a git repo whose `main` carries the # grepo <name> <base-changelog-lines...> — a git repo whose `main` carries the
# given changelog, left checked out on a branch `pr` off it. Prints the dir. # given changelog, left checked out on a branch `pr` off it. Prints the dir.
#
# It carries its own VERSION, like tree() above, so the checks that run
# changelog-armed.sh against these fixtures read a version that belongs to the
# FIXTURE. Reaching for the repo's real VERSION instead makes those checks pass
# or fail on what box happens to be versioned at today: bare (the release
# ceremony's own tree) sends changelog-armed.sh down its bare branch, where it
# demands a section for THAT version in a fixture changelog that has never
# heard of it. `-dev` here because every one of these fixtures tops out at
# '## Unreleased', which is what a development tree is required to carry.
grepo() { grepo() {
local d="$WORK/$1"; shift local d="$WORK/$1"; shift
mkdir -p "$d" mkdir -p "$d"
git -C "$d" init -q -b main git -C "$d" init -q -b main
git -C "$d" config user.email test@example.invalid git -C "$d" config user.email test@example.invalid
git -C "$d" config user.name test git -C "$d" config user.name test
printf '%s\n' '0.8.1-dev' > "$d/VERSION"
{ echo "# Changelog"; echo; printf '%s\n' "$@"; } > "$d/CHANGELOG.md" { echo "# Changelog"; echo; printf '%s\n' "$@"; } > "$d/CHANGELOG.md"
git -C "$d" add CHANGELOG.md git -C "$d" add CHANGELOG.md VERSION
git -C "$d" commit -qm base git -C "$d" commit -qm base
git -C "$d" checkout -q -b pr git -C "$d" checkout -q -b pr
echo "$d" echo "$d"
@ -333,7 +343,7 @@ check "monotonic: ...and why nothing else says so (git merges it cleanly)" 1 "gi
# Pinned here so a future 'just widen changelog-armed.sh' cannot quietly # Pinned here so a future 'just widen changelog-armed.sh' cannot quietly
# delete the reason this script exists. # delete the reason this script exists.
check "monotonic: ...on a tree changelog-armed.sh calls FINE (the #122 gap)" 0 "agrees" \ check "monotonic: ...on a tree changelog-armed.sh calls FINE (the #122 gap)" 0 "agrees" \
bash "$ARMED" "$G/CHANGELOG.md" "$ROOT/VERSION" bash "$ARMED" "$G/CHANGELOG.md" "$G/VERSION"
# --- the #118 incident as it ACTUALLY happened: a DUPLICATED heading ------- # --- the #118 incident as it ACTUALLY happened: a DUPLICATED heading -------
# The deletion case above is the near-miss. What the bad rebase really produced # The deletion case above is the near-miss. What the bad rebase really produced
@ -354,7 +364,7 @@ check "monotonic: ...on a tree where NOTHING was deleted (containment is blind)"
bash -c 'cd "$1" && bash "$2" main 2>&1 | grep -q "DELETES release heading"' _ "$G" "$MONO" bash -c 'cd "$1" && bash "$2" main 2>&1 | grep -q "DELETES release heading"' _ "$G" "$MONO"
# And, as with the deletion case, the other guard calls this tree fine. # And, as with the deletion case, the other guard calls this tree fine.
check "monotonic: ...on a tree changelog-armed.sh calls FINE" 0 "agrees" \ check "monotonic: ...on a tree changelog-armed.sh calls FINE" 0 "agrees" \
bash "$ARMED" "$G/CHANGELOG.md" "$ROOT/VERSION" bash "$ARMED" "$G/CHANGELOG.md" "$G/VERSION"
# --- the release ceremony's stamp: an ADD, never a removal ----------------- # --- the release ceremony's stamp: an ADD, never a removal -----------------
# '## Unreleased' -> '## 0.8.1 — DATE' adds 0.8.1 and removes no X.Y.Z # '## Unreleased' -> '## 0.8.1 — DATE' adds 0.8.1 and removes no X.Y.Z