* ci(release): gate PRs on an authored release note
Second slice of the release-notes pipeline (#1256 landed the format and
generator): make the .changes/ habit survive contact with reality.
- "Release note gate" job in ci.yml, PR-only: validates every .changes/*.md,
then requires an added note (or the no-release-note label) when the PR
touches runtime code under apps/ or libs/. Tests, e2e projects, the
website, mock servers, shared testing helpers, snapshots and docs are
auto-exempt.
- Policy lives in tools/release/check-release-note-gate.mjs as a pure
function fed PR files+labels as JSON — unit-tested (10 cases) instead of
encoded in workflow bash. The failure message lists the triggering files
and names the exact fix.
- Labels are fetched live rather than from the stale event payload, so
applying the label and re-running the check works without a new push.
- The job is dependency-free Node: no pnpm install, runs in seconds.
- release-notes and release-cut skills added under .claude/skills/ and
mirrored to .codex/skills/; CLAUDE.md/AGENTS.md sections updated to point
at the gate and the skills.
The no-release-note label itself was created in the repository.
Tests: 47 passing in release-tools (10 new gate cases); gate-step shell
verified with shellcheck at the CI severity; ci.yml YAML-parse checked.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* chore(agents): make the release skills discoverable by Claude Code too
`.codex/skills/**` was un-ignored so Codex picks up repository skills in any
clone, but `.claude` was ignored wholesale — and Claude Code only discovers
skills under `.claude/skills/`. The release-notes and release-cut skills
therefore existed only on whichever machine authored them.
Mirror both skills into `.claude/skills/` and opt them in by name rather than
un-ignoring the directory: contributors keep personal skills there
(i18n-fill, website, …) which must stay local and out of `git status`.
CLAUDE.md/AGENTS.md updated so the "skills live under .codex/skills/" claim
does not go stale, including the requirement to keep mirrored copies in sync.
The CI gate and the CLAUDE.md/AGENTS.md section remain the load-bearing
enforcement; skills only carry the detail.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(ci): only a note this PR authored satisfies the release-note gate
Review follow-ups on #1257 (Codex P2 ×2, Greptile P1).
- Drop `renamed` from the accepted statuses. The PR files API compares
base…head, so a note created and then renamed inside the same PR still
reports as `added`; a `renamed` entry means the file already existed on the
base branch. Accepting it let a runtime-code PR pass by moving another
PR's unconsumed note, which documents nothing and gives the generator no
adding commit to resolve a PR link from.
- Require a direct child of `.changes/`. `loadNotes()` reads only the
immediate directory, so `.changes/sub/note.md` satisfied the old prefix
check while never being validated or rendered into any release surface.
Tests: renamed and nested notes now assert a failing gate (12 gate cases).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>