From fa16e56d4100e738e8df1a0f2bc278778fcb3cbf Mon Sep 17 00:00:00 2001 From: JP Hastings-Spital Date: Mon, 24 Aug 2026 10:52:19 +0100 Subject: [PATCH] =?UTF-8?q?fix:=20move=20changeset=20conventions=20out=20o?= =?UTF-8?q?f=20.changeset/=20=E2=80=94=20knope=20eats=20every=20.md=20ther?= =?UTF-8?q?e=20(ATFS-78ea)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Discovered by actually running `knope --validate` locally: knope treats every .md file in .changeset/ as a changeset to consume, with no special-casing of README.md by name the way the JS changesets CLI has, so a frontmatter-less README failed validation outright ('missing front matter'). Moved the convention doc to CHANGESETS.md at the repo root and updated every reference (CLAUDE.md, changeset-check.yml). Also documents a second thing the same dry-run turned up: a changeset body written as hard-wrapped multi-line prose renders with its first line as a heading and the rest as an orphaned paragraph in CHANGELOG.md — keep a body to one unwrapped line. --- ...riven-versioning-changeset-directory-kn.md | 44 ++++++++----- .changeset/README.md | 55 ---------------- .tangled/workflows/changeset-check.yml | 15 +++-- CHANGESETS.md | 66 +++++++++++++++++++ CLAUDE.md | 11 +++- 5 files changed, 113 insertions(+), 78 deletions(-) delete mode 100644 .changeset/README.md create mode 100644 CHANGESETS.md diff --git a/.beans/ATFS-78ea--changeset-driven-versioning-changeset-directory-kn.md b/.beans/ATFS-78ea--changeset-driven-versioning-changeset-directory-kn.md index c9742cd..9935792 100644 --- a/.beans/ATFS-78ea--changeset-driven-versioning-changeset-directory-kn.md +++ b/.beans/ATFS-78ea--changeset-driven-versioning-changeset-directory-kn.md @@ -41,18 +41,32 @@ convention-only. ## Summary of Changes -Added the `.changeset` directory and its README describing the convention -(bump type + description per PR, `atfs` as the sole package name, and the -exempt paths — `.beans/`, `.tangled/`, `web/`, docs). Added `knope.toml` -configuring `atfs` as an unnamed single package with no versioned files -(version comes purely from the last release tag, matching ATFS-72r8), -`ignore_conventional_commits = true` under `[changes]` so only changesets -drive version bumps, and deliberately no `[github]`/`[gitea]` forge block -since Tangled isn't a forge knope knows — `knope release` just tags the -commit locally, feeding straight into the existing ATFS-bq8m tag-push -ritual. Added `.tangled/workflows/changeset-check.yml`, a pull-request check -that unshallows and fetches `main` (the same guarded pattern image.yml uses -for tag history), diffs the PR against it, and fails if any non-exempt file -changed with no new `.changeset/*.md` added. Documented both in CLAUDE.md: -a clause on the existing release-ritual bullet, and a new sharp-edges bullet -on knope's forge limitation and the fetch-main trick. +Added the `.changeset` directory describing the convention (bump type + +description per PR, `default` as the frontmatter key for `knope.toml`'s +single unnamed package, and the exempt paths — `.beans/`, `.tangled/`, +`web/`, docs). Added `knope.toml` configuring an unnamed single package with +no versioned files (version comes purely from the last release tag, matching +ATFS-72r8), `ignore_conventional_commits = true` under `[changes]` so only +changesets drive version bumps, and deliberately no `[github]`/`[gitea]` +forge block since Tangled isn't a forge knope knows — `knope release` just +tags the commit locally, feeding straight into the existing ATFS-bq8m +tag-push ritual. Added `.tangled/workflows/changeset-check.yml`, a +pull-request check that unshallows and fetches `main` (the same guarded +pattern image.yml uses for tag history), diffs the PR against it, and fails +if any non-exempt file changed with no new `.changeset/*.md` added. +Documented both in CLAUDE.md: a clause on the existing release-ritual +bullet, and a new sharp-edges bullet on knope's forge limitation and the +fetch-main trick. + +**Correction, found by actually running the real `knope` binary +(0.22.3) against this setup:** the convention doc can't live at +`.changeset/README.md` — knope treats every `.md` file in that directory as +a changeset to consume, no special-casing of `README.md` by name the way +the JS changesets CLI has, so a frontmatter-less README failed +`knope --validate` outright. Moved it to `CHANGESETS.md` at the repo root +and updated every reference (CLAUDE.md, `changeset-check.yml`). Also found: +a changeset body written as hard-wrapped multi-line prose renders with its +first line as a heading and the rest as an orphaned paragraph in +`CHANGELOG.md` — `CHANGESETS.md` now says to keep a body to one unwrapped +line. Both caught by `knope --validate` / `knope release --dry-run` before +anything shipped. diff --git a/.changeset/README.md b/.changeset/README.md deleted file mode 100644 index d2173f1..0000000 --- a/.changeset/README.md +++ /dev/null @@ -1,55 +0,0 @@ -# Changesets - -This directory tracks what's changed since the last release, one file per -change, so that the next release's version number and changelog entry are -computed rather than picked by hand. It's read by [knope](https://knope.tech) -(`knope.toml` at the repo root configures it) and the format is the same one -the Node.js [changesets](https://github.com/changesets/changesets) tool uses. - -## Adding one - -Every pull request that touches release-affecting code (see the CI check -below for exactly what that means) needs a new file here. Easiest way: - -```sh -knope document-change -``` - -which prompts for a bump type and description and writes the file for you. -By hand, it's a markdown file with a frontmatter block naming the package and -bump type, then the changelog entry: - -```markdown ---- -atfs: minor ---- - -`atfs init` gained interactive prompts and can write the server record. -``` - -`atfs` is the only package here (a single Go module, no per-package version -files — see `knope.toml`), so every changeset names it. Bump type is one of -`major`, `minor`, `patch`. The body is exactly what will appear in -`CHANGELOG.md`. - -## What doesn't need one - -Changes confined to `.beans/`, `.tangled/`, `web/` (the site deploys -continuously on every push to main, independent of release tags), or `*.md` -docs don't affect what a release contains, so they're exempt — see -`.tangled/workflows/changeset-check.yml` for the exact rule the CI check -enforces. Anything else — `cmd/`, `internal/`, `lexicons/`, `Dockerfile`, -`hack/`, `go.mod` — needs one, even a one-line `patch` changeset for a purely -internal fix with no user-visible effect. - -## Cutting a release - -A maintainer runs `knope release` locally when it's time to cut one — it -reads every file in this directory plus any Conventional Commits, computes -the next version, writes the `CHANGELOG.md` entry, deletes the consumed -changeset files, commits, and tags. From there it's the existing release -ritual (bean ATFS-bq8m): push the tag, and `.tangled/workflows/*.yml` take it -from there exactly as they do for a hand-picked tag. Knope never talks to -Tangled itself — it only knows GitHub and Gitea as forges, so `knope.toml` -configures none, and `knope release` just tags the commit locally, same as -`git tag vX.Y.Z && git push --tags` always has. diff --git a/.tangled/workflows/changeset-check.yml b/.tangled/workflows/changeset-check.yml index 42500dc..0b08474 100644 --- a/.tangled/workflows/changeset-check.yml +++ b/.tangled/workflows/changeset-check.yml @@ -1,8 +1,11 @@ # Fails a pull request that changes release-affecting files without adding -# a .changeset/*.md file — see that directory's README for the convention -# this enforces and knope.toml for how a release actually consumes them. -# Pure git/shell: the check itself never needs knope installed, only a -# maintainer cutting a release does (locally, per the README). +# a .changeset/*.md file — see CHANGESETS.md for the convention this +# enforces and knope.toml for how a release actually consumes them. (That +# doc lives at the repo root, not as .changeset/README.md: knope treats +# every .md file in .changeset/ as a changeset to consume, so a README +# sitting in there fails `knope --validate` outright.) Pure git/shell: the +# check itself never needs knope installed, only a maintainer cutting a +# release does (locally, per CHANGESETS.md). when: - event: ["pull_request"] branch: ["main"] @@ -49,10 +52,10 @@ steps: exit 0 fi - added="$(git diff --name-only --diff-filter=A "$base" HEAD -- '.changeset/*.md' | grep -v '/README\.md$' || true)" + added="$(git diff --name-only --diff-filter=A "$base" HEAD -- '.changeset/*.md')" if [ -z "$added" ]; then echo "This PR changes release-affecting files but adds no .changeset/*.md file." - echo "Run 'knope document-change' locally (or add one by hand, per .changeset/README.md) describing the change and its version-bump type." + echo "Run 'knope document-change' locally (or add one by hand, per CHANGESETS.md) describing the change and its version-bump type." exit 1 fi diff --git a/CHANGESETS.md b/CHANGESETS.md new file mode 100644 index 0000000..2c49627 --- /dev/null +++ b/CHANGESETS.md @@ -0,0 +1,66 @@ +# Changesets + +`.changeset/` tracks what's changed since the last release, one file per +change, so that the next release's version number and changelog entry are +computed rather than picked by hand. It's read by [knope](https://knope.tech) +(`knope.toml` at the repo root configures it) and the file format is the same +one the Node.js [changesets](https://github.com/changesets/changesets) tool +uses. + +This doc lives here rather than as `.changeset/README.md`: knope treats +*every* `.md` file in that directory as a changeset to consume, with no +special-casing of `README.md` by name the way the JS changesets CLI has — +one sits there with no frontmatter and `knope --validate` fails outright. + +## Adding one + +Every pull request that touches release-affecting code (see "What doesn't +need one" below) needs a new file in `.changeset/`. Easiest way: + +```sh +knope document-change +``` + +which prompts for a bump type and description and writes the file for you. +By hand, it's a markdown file with a frontmatter block naming the bump type, +then the changelog entry: + +```markdown +--- +default: minor +--- + +`atfs init` gained interactive prompts and can write the server record. +``` + +`default` is how a changeset refers to `knope.toml`'s single unnamed +`[package]` — atfs has no per-package version files, so there's no package +name to write here; `default` is the literal key knope's own docs use for +this case, not a placeholder. Bump type is one of `major`, `minor`, `patch`. +The body is exactly what will appear in `CHANGELOG.md` — **keep it to one +unwrapped paragraph line**. A hard-wrapped multi-line body gets its first +line rendered as a heading and the rest as an orphaned paragraph beneath +it, confirmed by actually running `knope release --dry-run` against one. + +## What doesn't need one + +Changes confined to `.beans/`, `.tangled/`, `web/` (the site deploys +continuously on every push to main, independent of release tags), or `*.md` +docs don't affect what a release contains, so they're exempt — see +`.tangled/workflows/changeset-check.yml` for the exact rule the CI check +enforces. Anything else — `cmd/`, `internal/`, `lexicons/`, `Dockerfile`, +`hack/`, `go.mod` — needs one, even a one-line `patch` changeset for a purely +internal fix with no user-visible effect. + +## Cutting a release + +A maintainer runs `knope release` locally when it's time to cut one — it +reads every file in `.changeset/` (Conventional Commits are deliberately +ignored as a version-bump source, per `knope.toml`'s `[changes]` section), +computes the next version, writes the `CHANGELOG.md` entry, deletes the +consumed changeset files, commits, and tags. From there it's the existing +release ritual (bean ATFS-bq8m): push the tag, and `.tangled/workflows/*.yml` +take it from there exactly as they do for a hand-picked tag. Knope never +talks to Tangled itself — it only knows GitHub and Gitea as forges, so +`knope.toml` configures none, and `knope release` just tags the commit +locally, same as `git tag vX.Y.Z && git push --tags` always has. diff --git a/CLAUDE.md b/CLAUDE.md index 1e70ae2..50e0a75 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -526,9 +526,16 @@ by an ingress, downloads never are. never tries to talk to one, it just tags the commit locally, which is all the existing tag-push release ritual (ATFS-bq8m) needs. Leaving the forge config out also stops knope auto-detecting one from the git remote, which - would otherwise try (and fail) against Tangled. See `.changeset/README.md` + would otherwise try (and fail) against Tangled. See `CHANGESETS.md` and `knope.toml`'s own comments for the rest of the versioning setup (ATFS-78ea). +- **Knope treats every `.md` file in `.changeset/` as a changeset to + consume — no special-casing of `README.md` by name the way the JS + changesets CLI has.** A convention doc sitting there with no frontmatter + fails `knope --validate`/`knope release` outright (`missing front + matter`), discovered by actually running `knope --validate` locally + against one. That's why the convention doc is `CHANGESETS.md` at the + repo root, not `.changeset/README.md`. - **Nix expands a bare `?ref=` to `refs/heads/`**, so a flake ref pinned to a *tag* must spell it out: `?ref=refs/tags/vX.Y.Z`, never `?ref=vX.Y.Z`, which hunts for a branch of that name and dies with `couldn't find remote @@ -598,7 +605,7 @@ by an ingress, downloads never are. the runner constraints it discovered). **What version to tag and what `CHANGELOG.md` says is computed, not picked by hand**: every PR that touches release-affecting code adds a `.changeset/*.md` file (convention - and exemptions in that directory's own README; + and exemptions in `CHANGESETS.md`; `.tangled/workflows/changeset-check.yml` enforces it), and a maintainer runs `knope release` locally when it's time — it reads the pending changesets, writes the changelog entry, commits, and tags, feeding -- 2.51.2