The artifacts and the GitHub release were made by hand (`python -m build` plus
`gh release create`). `.github/workflows/release.yml` now does it from the
pushed tag: Python and PWA tests, the sdist/wheel build, a check that the
wheel carries the PWA, an install-and-serve smoke test of that wheel, and
`gh release create` with the generated notes (prepended by
`.github/release-notes.md`) and the three assets (wheel, sdist, udev rule).
A `workflow_dispatch` run does everything except publish, so the pipeline can
be exercised without minting a release; the artifacts land in the run. A tag
that does not match version.py/PWA_VERSION fails before anything is published.