Four P2 findings, each with a regression test:
- The authored-body check could never fail. The tag workflow appends
GitHub's generated notes to the authored text (FULL_BODY in
build-and-make.yaml), so `release.body` is never empty and testing it for
emptiness proved nothing. It now compares the body against the local
CHANGELOG.md public section — the actual authored source — and reports an
internal-only release as such instead of warning about it.
- A release with no highlights failed the documented card step, which
necessarily included every internal-only release since validation forbids
highlights there. Now the hero card is still rendered when public notes
exist, an internal-only release writes nothing, and both exit 0.
- Card output is keyed by the exact version rather than the minor slug
(0.24.0 and 0.24.1 share a blog post but not a card set), and a run first
removes the cards a previous run wrote there, so a renamed or dropped
highlight cannot leave a stale image waiting to be published. Only files
matching what this tool writes are removed.
- Telegram could still truncate breaking changes into the counter when a
release had no highlights, since the fitting loop trims the tail. Dropping
a breaking entry now fails with an actionable error.
Docs: docs/architecture/release-pipeline.md, .changes/README.md, CLAUDE.md
and AGENTS.md updated for the new output path and the no-highlight and
internal-only behavior.
180 tests passing.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
CodeQL: the pacman rule built a regex from the interpolated version and
escaped only dots. It now compares plain strings, so the exported
requiredAssetRules() carries no escaping trap; only the literal compression
suffix stays a regex.
verify-draft-release:
- Never exits 0 once the release is published. The asset report still
prints (auditing after the fact is useful), but a pre-publication gate
must not report success for a boundary already crossed.
- Polls for the tag run before concluding it does not exist: `gh run list`
reports what is indexed right now, and a run pushed seconds ago routinely
is not. Adds injectable progress/sleep io, so runVerification is async.
- The live sleep timer is deliberately not unref'd — a pending promise does
not hold the event loop open, and an empty loop exits 0, which would turn
a mid-poll exit into a silent false success.
- `--repo` is shape-checked so a typo fails with the usage line.
Announcements: an internal-only release now returns null instead of
throwing a misleading "even a single entry exceeds the limit" error. The
CLI explains it on stderr, leaves stdout empty and exits 0 — the shape
extract-changelog-section.mjs --public already uses for the same case.
Highlight cards: card filenames derive from the note filename, never the
screenshot slug. Two highlights may legitimately share one manifest shot,
which previously made the second card overwrite the first.
Tests: the CLI harness moved to spawnSync — execFileSync discards stderr on
success, hiding exactly the messages these paths emit. Adds regressions for
each finding above (165 passing).
Docs: CLAUDE.md and AGENTS.md release sections (mirrored, verified
identical) now document the highlight field, the announcement/card
commands, and the draft verifier; .changes/README.md records the
internal-only announcement behavior.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Extends the release tooling around the .changes notes pipeline:
- New optional `highlight:` frontmatter field marks a release's headline
changes (max 80 chars, rejected on `type: internal`).
- `--format telegram` renders a paste-ready plain-text post guaranteed to
fit the 4096-character limit (highlights lead, the rest collapses into a
counter); `--format reddit` renders markdown with a suggested title line,
a Highlights section, and a collapsed change list. Both are stdout dry
runs and must run before `--consume`.
- The blog scaffold uses the highlight as a ready section heading instead
of a TODO, and highlight notes without screenshots get their own section.
- `release:verify:draft` waits for the v* tag build via `gh run watch`,
then checks draft status, the authored body, and the complete 27-asset
set (verified against a real full matrix build). Read-only; publishing
stays manual.
- `release:cards:generate` renders branded 1200x630 highlight cards (with
a framed mock-server screenshot strip when the note names one, purely
typographic otherwise) plus a release hero card into dist/, matching the
website palette. Copying into the website tree stays a manual act.
Docs: .changes/README.md, release-notes and release-cut skills (both
copies byte-identical). No release note: release tooling only, no runtime
behavior change.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>