Files
iptvnator/.codex/skills/release-notes/SKILL.md
T
4grayandClaude Opus 5 4e5132cbb5 ci(release): gate PRs on an authored release note (#1257)
* 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>
2026-07-25 21:51:04 +02:00

2.8 KiB

name, description
name description
release-notes Write the .changes/ release note that every PR with a user-visible change must include. Use when creating or finishing a PR that changes behavior in apps/ or libs/, when the "Release note gate" CI check fails, or when deciding whether the no-release-note label applies.

Release Notes (.changes/)

Every PR with a user-visible change adds one note file under .changes/. At release time the notes become the GitHub release body, the CHANGELOG.md section, and the website blog scaffold. CI enforces this: the Release note gate check fails any PR that touches runtime code under apps/ or libs/ without an added .changes/*.md file or the no-release-note label.

File format

Name: .changes/<area>-<short-slug>.md — area matches the conventional-commit scope of the PR.

---
type: feature
area: playback
issues: [1187]
screenshot: up-next-rail
---

Series now show an "Up Next" rail beside the player on wide windows: the rest
of the current season, watch progress, and click-to-play inline.
Field Required Value
type yes breaking / feature / fix / perf / internal
area yes lowercase slug = conventional-commit scope
issues no [1187] or bare 1187 — issues this PR closes
screenshot no slug from the release screenshot manifest
  • No version field. The release version is chosen at release time.
  • Never write a PR number. The generator resolves it from git.
  • Unknown keys fail validation — this is what catches typos like scopr:.

Writing the body

One to three sentences, present tense, max 400 characters, written for a user, not a reviewer:

  • ❌ "Refactor WebVideoControlsAdapter to hoist volume state"
  • ✅ "The player now remembers volume between episodes"
  • ❌ "Fix off-by-one in resolveEnrichmentSeasonNumber"
  • ✅ "Series with a season marker in the title no longer show the wrong season"

type: internal is for changes worth recording but invisible to users (dependency bumps with behavior risk, packaging moves). They are excluded from the release body and blog, and collapsed in CHANGELOG.md.

When to skip (no-release-note label)

Test-only changes, docs, CI/workflow plumbing, pure refactors with no behavior change. The gate auto-exempts *.spec.ts, *.e2e.ts, __snapshots__/, apps/website/, apps/*-e2e/, apps/*-mock-server/, libs/shared/testing/ and *.md — if only those changed, no label needed.

Verify before finishing

pnpm run release:notes:validate

Full format reference: .changes/README.md. Gate policy: tools/release/check-release-note-gate.mjs.