From c60da967dd6b518389b4f03e0a489736c4594bba Mon Sep 17 00:00:00 2001 From: Yihui Liao <44729383+yihuiliao@users.noreply.github.com> Date: Fri, 22 May 2026 16:33:34 -0700 Subject: [PATCH 01/11] add release note skill to claude and update changelog --- .claude/skills/release-notes/SKILL.md | 129 ++++++++++ scripts/changelog.js | 355 ++++++++++++++++++++++---- 2 files changed, 438 insertions(+), 46 deletions(-) create mode 100644 .claude/skills/release-notes/SKILL.md diff --git a/.claude/skills/release-notes/SKILL.md b/.claude/skills/release-notes/SKILL.md new file mode 100644 index 00000000000..bd6a2098bfb --- /dev/null +++ b/.claude/skills/release-notes/SKILL.md @@ -0,0 +1,129 @@ +# Release Notes Skill + +Polish the scaffolded release-notes files generated by `scripts/changelog.js`. + +## Setup + +`GITHUB_TOKEN` with repo read access must be set (used by the changelog script when it looks up PR authors). + +## Background + +`yarn releaseNotes` now: +- Buckets commits by library (React Aria Components, React Spectrum v3, Spectrum 2) using the per-package diffs. +- Detects the next version per library by scanning the corresponding releases directory. +- Writes one scaffold MDX file per non-empty bucket with the standard frontmatter, version heading, intro placeholder, a `## Changelog` populated with raw commit lines, and a `## Released packages` placeholder. + +Target paths: +- React Aria Components → `packages/dev/s2-docs/pages/react-aria/releases/vX-Y-Z.mdx` +- Spectrum 2 → `packages/dev/s2-docs/pages/s2/releases/vX-Y-Z.mdx` + +A commit that touches both `@react-aria/*` and `@react-spectrum/*` is written into both library files — intentional. + +## Steps + +### 1. Run the script and collect the file paths + +```bash +yarn releaseNotes +``` + +Output: +``` +✓ Wrote packages/dev/s2-docs/pages/react-aria/releases/v1-18-0.mdx (42 commits) +✓ Wrote packages/dev/s2-docs/pages/s2/releases/v1-4-0.mdx (18 commits) +✓ Wrote packages/dev/docs/pages/releases/2026-05-22.mdx (8 commits) +``` + +If a target file already exists the script prints a `⚠` line and dumps that bucket's commits to stdout instead. Hand-merge into the existing file. + +If the user has already run the script, work from the files it created — do not re-run it. + +### 2. Identify pre-release packages + +Scan `packages/` for `package.json` files whose version contains `alpha`, `beta`, or `rc`. Note the component names — commits that touch only these packages should be called out as Under Construction inside the relevant file's intro paragraph or a dedicated section if the team prefers. + +### 2.5. Record the expected commit count for each file + +Each scaffold file contains a comment directly above `## Changelog`: + +``` +{/* Commits: N */} +## Changelog +``` + +Before rewriting, read this value and store it as the expected count for that file. You will verify against it after the rewrite. + +### 3. For each scaffolded file, rewrite the `## Changelog` body in place + +Do **not** touch the frontmatter, version heading, or `## Released packages` block (the team fills the latter in by hand from `yarn bumpVersions`). + +For each commit line in the existing `## Changelog`: + +**Rewrite the message.** Original format: `Type (Scope): Summary - [@user](url) - [PR](url)` → target `Summary - [@user](url) - [PR](url)`. + +First, evaluate whether the commit subject is a clear, user-facing description. If it is vague or developer-internal (e.g. "fix typo", "address review comments", "nit", "updates", "wip", "cleanup"), fetch the PR body and changed files for more context: + +```bash +gh pr view --json title,body,files +``` + +Use the PR description and changed file paths to write a more informative summary. If the PR has no useful description either, use the file paths to infer the affected area (e.g. files under `packages/@react-aria/table/` → Table-related fix). + +If `gh pr view` exits non-zero (PR deleted, no access, closed without merge, etc.), do **not** skip the commit. Keep the original commit subject and mark it for manual review: + +``` +- [NEEDS REVIEW] - +``` + +Then apply the standard formatting rules: + +- Strip the type prefix (`fix:`, `feat:`, `docs:`, etc.). +- Keep as a single grammatically correct sentence. +- Verbs first-person present tense, no subject (e.g. "Adds support for..." not "Added" or "I added"). +- Wrap camelCase/code terms in backticks: `onClick`, `isDisabled`, `onPress`. +- Do NOT use backticks for component names. +- Replace: RAC → React Aria, V3 → React Spectrum. +- Capitalize component names: Accordion, Autocomplete, Badge, Breadcrumbs, Buttons, Calendar, Checkbox, CheckboxGroup, Collections, ColorArea, ColorField, ColorPicker, ColorSlider, ColorSwatch, ColorSwatchPicker, ColorWheel, ComboBox, Date and Time, DateField, DatePicker, DateRangePicker, Dialog, Disclosure, DisclosureGroup, Drag and Drop, DropZone, FileTrigger, Form, InlineAlert, Link, Listbox, ListView, Menu, Meter, Modal, NotificationBadge, NumberField, Picker, ProgressBar, ProgressCircle, RadioGroup, RangeCalendar, SearchField, Select, Slider, StatusLight, Switch, Table, Tabs, TagGroup, TextArea, TextField, TimeField, Toast, ToggleButton, ToggleButtonGroup, Tooltip, Tree, Virtualizer. + +**Group by component.** Replace the flat list under `## Changelog` with sub-headings: + +``` +## Changelog + +### General Changes +- ... + +### Autocomplete +- ... + +### ComboBox +- ... +``` + +- Each component gets its own sub-heading — never group multiple components under a combined heading like "Checkbox, RadioGroup, Switch". If a single PR affects multiple components, duplicate the entry under each component's own heading. +- Sub-headings in alphabetical order, with "General Changes" first. +- Use `-` (not `*`) for list items inside sub-headings, matching the existing release-notes style (see `packages/dev/s2-docs/pages/react-aria/releases/v1-17-0.mdx` for reference). +- Do NOT bold sub-heading text. + +### 3.5. Verify commit count + +After rewriting the `## Changelog` body, count the total number of `-` bullet lines across all component sections in the file. Compare to the expected count from step 2.5. + +- **Count matches:** continue to step 4. +- **Count is less:** diff the original flat commit list against the bullets you wrote to find which entries are missing. Either place them in the correct section, or — if a commit was intentionally merged into another bullet from the same PR — leave a note: `{/* dropped: — merged into above */}`. Do not leave commits silently missing. +- **Count is greater:** this is fine if a multi-component PR was duplicated across sections (intentional per the grouping rules). + +### 4. Fill in `description` and intro paragraph + +Read the rewritten changelog and write: +- The `description` export (in S2-docs files) or the `description:` frontmatter line (in v3 files) — 1–2 sentences highlighting the most notable changes. +- The intro paragraph below the `# vX.Y.Z` (or `# Month DD, YYYY Release`) heading — slightly longer, links to relevant component docs where helpful. + +### 5. Leave `## Released packages` alone + +The team pastes `yarn bumpVersions` output here before publishing. + +## Notes + +- Do not duplicate-dedupe across files. A PR that legitimately touched both RAC and S2 should appear in both files. +- Do not add new top-level sections or rename existing ones — match the structure of the most recent release in each directory. diff --git a/scripts/changelog.js b/scripts/changelog.js index 58e177be6c2..d533270633c 100644 --- a/scripts/changelog.js +++ b/scripts/changelog.js @@ -1,12 +1,195 @@ const exec = require('child_process').execSync; const spawn = require('child_process').spawnSync; const fs = require('fs'); +const path = require('path'); const Octokit = require('@octokit/rest'); const octokit = new Octokit({ auth: `token ${process.env.GITHUB_TOKEN}` }); +const LIBRARY_ORDER = ['React Aria Components', 'Spectrum 2']; + +const LIBRARY_CONFIG = { + 'React Aria Components': { + releasesDir: 'packages/dev/s2-docs/pages/react-aria/releases', + template: 's2docs', + tag: 'React Aria', + docsDir: 'packages/dev/s2-docs/pages/react-aria' + }, + 'Spectrum 2': { + releasesDir: 'packages/dev/s2-docs/pages/s2/releases', + template: 's2docs', + tag: 'S2', + docsDir: 'packages/dev/s2-docs/pages/s2' + }, +}; + +function packageToLibrary(name) { + if (name.startsWith('@spectrum-icons/')) { + return null; + } + if ( + name.startsWith('@internationalized/') || + name === 'react-stately' || name.startsWith('@react-stately/') || + name === 'react-aria' || name.startsWith('@react-aria/') || + name === 'react-aria-components' || name === 'tailwindcss-react-aria-components' || + name.startsWith('@react-types/') + ) { + return 'React Aria Components'; + } + if (name === '@react-spectrum/s2') { + return 'Spectrum 2'; + } + if (name === '@adobe/react-spectrum' || name.startsWith('@react-spectrum/')) { + return 'React Spectrum'; + } + return null; +} + +function nextVersionFilename(releasesDir) { + let entries = fs.readdirSync(releasesDir); + let versions = []; + for (let entry of entries) { + let m = entry.match(/^v(\d+)-(\d+)-(\d+)\.mdx$/); + if (m) { + versions.push([Number(m[1]), Number(m[2]), Number(m[3])]); + } + } + if (versions.length === 0) { + return {filename: 'v1-0-0.mdx', version: '1.0.0'}; + } + versions.sort((a, b) => b[0] - a[0] || b[1] - a[1] || b[2] - a[2]); + let [major, minor] = versions[0]; + let nextMinor = minor + 1; + return {filename: `v${major}-${nextMinor}-0.mdx`, version: `${major}.${nextMinor}.0`}; +} + +function todayYMD() { + let d = new Date(); + let y = d.getFullYear(); + let m = String(d.getMonth() + 1).padStart(2, '0'); + let day = String(d.getDate()).padStart(2, '0'); + return `${y}-${m}-${day}`; +} + +function prettyDate(ymd) { + let [y, m, d] = ymd.split('-').map(Number); + let months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; + return `${months[m - 1]} ${d}, ${y}`; +} + +function licenseHeader(year) { + return `{/* Copyright ${year} Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. */}`; +} + +function scaffoldS2Docs({version, dateYMD, tag, body, commitCount}) { + let year = new Date().getFullYear(); + return `${licenseHeader(year)} + +import {InstallCommand} from '../../../src/InstallCommand'; + +import {Layout} from '../../../src/Layout'; +export default Layout; + +import docs from 'docs:@react-spectrum/s2'; + +export const hideNav = true; +export const section = 'Releases'; +export const tags = ['release', '${tag}']; +export const date = '${prettyDate(dateYMD)}'; +export const title = 'v${version}'; +export const description = '[TODO: 1–2 sentence highlights summary]'; +export const isSubpage = true; + +# v${version} + +[TODO: intro paragraph] + +{/* Commits: ${commitCount} */} +## Changelog + +${body} + +## Released packages + +\`\`\` +[TODO: paste output of yarn bumpVersions here] +\`\`\` +`; +} + +function scaffoldV3Blog({dateYMD, body, commitCount}) { + let year = new Date().getFullYear(); + return `${licenseHeader(year)} + +import {BlogPostLayout, Hero, Image} from '@react-spectrum/docs'; +export default BlogPostLayout; + +--- +description: [TODO: 1–2 sentence highlights summary] +date: ${dateYMD} +--- + +# ${prettyDate(dateYMD)} Release + +[TODO: intro paragraph] + +{/* Commits: ${commitCount} */} +## Changelog + +${body} + +## Released packages + +\`\`\` +[TODO: paste output of yarn bumpVersions here] +\`\`\` +`; +} + +function resolveTag(pkgName, pkgVersion) { + let exact = `${pkgName}@${pkgVersion}`; + let check = spawn('git', ['rev-parse', '--verify', exact], {encoding: 'utf8'}); + if (check.status === 0) { + return exact; + } + // Exact tag doesn't exist (e.g. a patch bump without a new tag) — fall back to the + // most recent tag for this package using git's version sort. + let list = spawn('git', ['tag', '-l', '--sort=version:refname', `${pkgName}@*`], {encoding: 'utf8'}); + let tags = list.stdout.trim().split('\n').filter(Boolean); + return tags.length > 0 ? tags[tags.length - 1] : null; +} + +async function renderCommitLine(commit) { + let message = ''; + let user = ''; + let pr; + + let m = commit[3].match(/(.*?) \(#(\d+)\)$/); + + if (m) { + let prId = m[2]; + message = m[1]; + + let res = await octokit.request('GET /repos/adobe/react-spectrum/pulls/{pull}', {pull: prId}); + user = `[@${res.data.user.login}](${res.data.user.html_url})`; + pr = `https://github.com/adobe/react-spectrum/pull/${prId}`; + } else { + message = commit[3]; + user = commit[2]; + } + + return `* ${message} - ${user}` + (pr ? ` - [PR](${pr})` : ''); +} + run(); async function run() { @@ -15,68 +198,148 @@ async function run() { .split(require('os').EOL) .filter(Boolean) .map(x => JSON.parse(x)); - let commits = new Map(); - // Diff each package individually. Some packages might have been skipped during last release, - // so we cannot simply look at the last tag on the whole repo. + let commitsByLibrary = new Map(); + let libraryTags = new Map(); + for (let name in packages) { let filePath = packages[name].location + '/package.json'; let pkg = JSON.parse(fs.readFileSync(filePath, 'utf8')); - if (!pkg.private) { - // Diff this package since the last published version, according to the package.json. - // The release script creates a tag for each package version. - let tag = `${pkg.name}@${pkg.version}`; - - let args = [ - 'log', - `${tag}..HEAD`, - '--pretty="%H%x00%aI%x00%an%x00%s"', - packages[name].location, - - // filter out non-code changes - ':!**/test/**', - ':!**/stories/**', - ':!**/chromatic/**' - ]; - - let res = spawn('git', args, {encoding: 'utf8'}); - if (res.stdout.length === 0) { - continue; + if (pkg.private) { + continue; + } + + let library = packageToLibrary(pkg.name); + if (!library) { + if (!pkg.name.startsWith('@spectrum-icons/')) { + console.warn(`⚠ No library mapping for ${pkg.name} — skipped`); } + continue; + } - for (let line of res.stdout.split('\n')) { - if (line === '') { - continue; - } + let tag = resolveTag(pkg.name, pkg.version); + if (!tag) { + console.warn(`⚠ No tag found for ${pkg.name}@${pkg.version} — skipped`); + continue; + } + + if (!libraryTags.has(library)) { + libraryTags.set(library, tag); + } - let info = line.replace(/^"|"$/g, '').split('\0'); - commits.set(info[0], info); + let args = [ + 'log', + `${tag}..HEAD`, + '--pretty="%H%x00%aI%x00%an%x00%s"', + packages[name].location, + + // filter out non-code changes + ':!**/test/**', + ':!**/stories/**', + ':!**/chromatic/**' + ]; + + let res = spawn('git', args, {encoding: 'utf8'}); + if (res.stdout.length === 0) { + continue; + } + + if (!commitsByLibrary.has(library)) { + commitsByLibrary.set(library, new Map()); + } + let bucket = commitsByLibrary.get(library); + + for (let line of res.stdout.split('\n')) { + if (line === '') { + continue; + } + + let info = line.replace(/^"|"$/g, '').split('\0'); + if (info[3] === 'Publish') { + continue; } + bucket.set(info[0], info); } } - let sortedCommits = [...commits.values()].sort((a, b) => (a[1] < b[1] ? -1 : 1)); + // Scan docs directories for docs-only commits (e.g. guide additions, new component pages). + // These live in private packages skipped by the main loop above. + for (let [library, config] of Object.entries(LIBRARY_CONFIG)) { + if (!config.docsDir) continue; + let tag = libraryTags.get(library); + if (!tag) continue; - for (let commit of sortedCommits) { - let message = ''; - let user = ''; - let pr; + let args = [ + 'log', + `${tag}..HEAD`, + '--pretty="%H%x00%aI%x00%an%x00%s"', + config.docsDir, + ':!**/releases/**' // don't include the release note files themselves + ]; - //look for commits with pr # - let m = commit[3].match(/(.*?) \(#(\d+)\)$/); + let res = spawn('git', args, {encoding: 'utf8'}); + if (!res.stdout || res.stdout.length === 0) continue; - if (m) { - let prId = m[2]; - message = m[1]; + if (!commitsByLibrary.has(library)) { + commitsByLibrary.set(library, new Map()); + } + let bucket = commitsByLibrary.get(library); - let res = await octokit.request('GET /repos/adobe/react-spectrum/pulls/{pull}', {pull: prId}); - user = `[@${res.data.user.login}](${res.data.user.html_url})`; - pr = `https://github.com/adobe/react-spectrum/pull/${prId}`; + for (let line of res.stdout.split('\n')) { + if (line === '') continue; + let info = line.replace(/^"|"$/g, '').split('\0'); + if (info[3] === 'Publish') continue; + bucket.set(info[0], info); // keyed by hash — deduplicates with source commits + } + } + + for (let library of LIBRARY_ORDER) { + let bucket = commitsByLibrary.get(library); + if (!bucket || bucket.size === 0) { + continue; + } + + let sorted = [...bucket.values()].sort((a, b) => (a[1] < b[1] ? -1 : 1)); + let lines = []; + for (let commit of sorted) { + lines.push(await renderCommitLine(commit)); + } + let body = lines.join('\n'); + + let config = LIBRARY_CONFIG[library]; + let dir = config.releasesDir; + let outPath; + let content; + + if (config.template === 'v3blog') { + let ymd = todayYMD(); + outPath = path.join(dir, `${ymd}.mdx`); + content = scaffoldV3Blog({dateYMD: ymd, body, commitCount: lines.length}); } else { - // not a pr so just print what we know from the commit - message = commit[3]; - user = commit[2]; + let {filename, version} = nextVersionFilename(dir); + outPath = path.join(dir, filename); + content = scaffoldS2Docs({version, dateYMD: todayYMD(), tag: config.tag, body, commitCount: lines.length}); + } + + if (fs.existsSync(outPath)) { + console.warn(`⚠ ${outPath} already exists — skipping (${bucket.size} commits not written). Printing to stdout instead:\n`); + console.log(`# ${library}\n`); + console.log(body); + console.log(); + continue; + } + + fs.writeFileSync(outPath, content); + console.log(`✓ Wrote ${outPath} (${bucket.size} commits)`); + } + + let v3Bucket = commitsByLibrary.get('React Spectrum'); + if (v3Bucket && v3Bucket.size > 0) { + console.warn(`\nℹ ${v3Bucket.size} React Spectrum (v3) commit(s) were not written to a file. Review them manually if needed:\n`); + let sorted = [...v3Bucket.values()].sort((a, b) => (a[1] < b[1] ? -1 : 1)); + for (let commit of sorted) { + console.warn(` ${commit[3]}`); } - console.log(`* ${message} - ${user}` + (pr ? ` - [PR](${pr})` : '')); + console.warn(); } } From 8d39d65c98d11dd46b6b28a722f36a799d71230a Mon Sep 17 00:00:00 2001 From: Yihui Liao <44729383+yihuiliao@users.noreply.github.com> Date: Tue, 26 May 2026 11:29:42 -0700 Subject: [PATCH 02/11] update skill --- .claude/skills/release-notes/SKILL.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.claude/skills/release-notes/SKILL.md b/.claude/skills/release-notes/SKILL.md index bd6a2098bfb..958fdf5bc4b 100644 --- a/.claude/skills/release-notes/SKILL.md +++ b/.claude/skills/release-notes/SKILL.md @@ -110,7 +110,10 @@ Then apply the standard formatting rules: After rewriting the `## Changelog` body, count the total number of `-` bullet lines across all component sections in the file. Compare to the expected count from step 2.5. - **Count matches:** continue to step 4. -- **Count is less:** diff the original flat commit list against the bullets you wrote to find which entries are missing. Either place them in the correct section, or — if a commit was intentionally merged into another bullet from the same PR — leave a note: `{/* dropped: — merged into above */}`. Do not leave commits silently missing. +- **Count is less:** diff the original flat commit list against the bullets you wrote to find which entries are missing. Either place them in the correct section, or leave a drop note explaining why. Do not leave commits silently missing. Drop note forms: + - `{/* dropped: ([PR](url)) — merged into above */}` — use **only** when the bullet directly above literally describes what this PR did (i.e. you rewrote the bullet to incorporate this PR's changes). Do not use this if the bullet above doesn't mention the content of this PR. + - `{/* dropped: ([PR](url)) — follow-up refinement of [PR](url) */}` — use for iterative polish PRs (design feedback, followups, small adjustments) whose user-facing change is fully captured by the initial feature PR's bullet. + - `{/* dropped: ([PR](url)) — internal tooling */}` — use for commits with no user-facing impact (lint migration, formatting, yarn upgrade, internal scripts, test-only changes). - **Count is greater:** this is fine if a multi-component PR was duplicated across sections (intentional per the grouping rules). ### 4. Fill in `description` and intro paragraph From f0769fb5dc7265bbb754e1808f0d2b4ca8e5936e Mon Sep 17 00:00:00 2001 From: Yihui Liao <44729383+yihuiliao@users.noreply.github.com> Date: Tue, 26 May 2026 15:17:23 -0700 Subject: [PATCH 03/11] add commits to test claude on --- .../pages/react-aria/releases/v1-18-0.mdx | 77 +++++++++++++++++++ .../dev/s2-docs/pages/s2/releases/v1-4-0.mdx | 73 ++++++++++++++++++ 2 files changed, 150 insertions(+) create mode 100644 packages/dev/s2-docs/pages/react-aria/releases/v1-18-0.mdx create mode 100644 packages/dev/s2-docs/pages/s2/releases/v1-4-0.mdx diff --git a/packages/dev/s2-docs/pages/react-aria/releases/v1-18-0.mdx b/packages/dev/s2-docs/pages/react-aria/releases/v1-18-0.mdx new file mode 100644 index 00000000000..400399f101a --- /dev/null +++ b/packages/dev/s2-docs/pages/react-aria/releases/v1-18-0.mdx @@ -0,0 +1,77 @@ +{/* Copyright 2026 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. */} + +import {InstallCommand} from '../../../src/InstallCommand'; + +import {Layout} from '../../../src/Layout'; +export default Layout; + +import docs from 'docs:@react-spectrum/s2'; + +export const hideNav = true; +export const section = 'Releases'; +export const tags = ['release', 'React Aria']; +export const date = 'May 26, 2026'; +export const title = 'v1.18.0'; +export const description = '[TODO: 1–2 sentence highlights summary]'; +export const isSubpage = true; + +# v1.18.0 + +[TODO: intro paragraph] + +{/* Commits: 40 */} +## Changelog + +* fix: type mismatch in useTextField for textarea elements - [@nami8824](https://github.com/nami8824) - [PR](https://github.com/adobe/react-spectrum/pull/9884) +* fix: support CSS logical properties for Tabs animations - [@SupaSeeka](https://github.com/SupaSeeka) - [PR](https://github.com/adobe/react-spectrum/pull/9951) +* fix: layout shift in s2 modal docs - [@nwidynski](https://github.com/nwidynski) - [PR](https://github.com/adobe/react-spectrum/pull/9956) +* Fix/numberfield format options input reset - [@Wonchang0314](https://github.com/Wonchang0314) - [PR](https://github.com/adobe/react-spectrum/pull/9905) +* chore: update NumberParser's cldr data - [@snowystinger](https://github.com/snowystinger) - [PR](https://github.com/adobe/react-spectrum/pull/9907) +* fix: ensure Virtualized Table loader width is up to date with table width - [@LFDanLu](https://github.com/LFDanLu) - [PR](https://github.com/adobe/react-spectrum/pull/9961) +* feat: Support help text in Checkbox, Radio, and Switch - [@devongovett](https://github.com/devongovett) - [PR](https://github.com/adobe/react-spectrum/pull/9877) +* fix: compatibility for modern icu - [@nwidynski](https://github.com/nwidynski) - [PR](https://github.com/adobe/react-spectrum/pull/9887) +* feat: Add TableFooter component - [@devongovett](https://github.com/devongovett) - [PR](https://github.com/adobe/react-spectrum/pull/9943) +* fix: Improve consistency of Popover --trigger-width - [@devongovett](https://github.com/devongovett) - [PR](https://github.com/adobe/react-spectrum/pull/9913) +* feat: Add Calendar multi-select, week/day views, weeksInMonth, month/year pickers, and unavailable anchor date - [@devongovett](https://github.com/devongovett) - [PR](https://github.com/adobe/react-spectrum/pull/9948) +* fix: prevent chromatic storybook crash by exporting directly from TableLayout - [@LFDanLu](https://github.com/LFDanLu) - [PR](https://github.com/adobe/react-spectrum/pull/9979) +* chore: re-export mergeRefs from react-aria index - [@lixiaoyan](https://github.com/lixiaoyan) - [PR](https://github.com/adobe/react-spectrum/pull/9976) +* fix(Collections): handle falsy keys properly - [@reidbarber](https://github.com/reidbarber) - [PR](https://github.com/adobe/react-spectrum/pull/9993) +* chore: Yarn upgrade - [@snowystinger](https://github.com/snowystinger) - [PR](https://github.com/adobe/react-spectrum/pull/9817) +* fix: omit disabledKeys from MenuSection - [@LFDanLu](https://github.com/LFDanLu) - [PR](https://github.com/adobe/react-spectrum/pull/9997) +* fix: preserve multi-select ComboBox selections for custom input - [@romansndlr](https://github.com/romansndlr) - [PR](https://github.com/adobe/react-spectrum/pull/9945) +* fix: Menu, Select, and ComboBox inside Tabs - [@devongovett](https://github.com/devongovett) - [PR](https://github.com/adobe/react-spectrum/pull/10019) +* fix: MenuItem render function should not receive an href when not a link - [@devongovett](https://github.com/devongovett) - [PR](https://github.com/adobe/react-spectrum/pull/10018) +* fix(Tree): Dont set focusedKey to a section node key - [@chirokas](https://github.com/chirokas) - [PR](https://github.com/adobe/react-spectrum/pull/10020) +* chore: Format with oxfmt - [@devongovett](https://github.com/devongovett) - [PR](https://github.com/adobe/react-spectrum/pull/10030) +* docs: add client side routing guide to RAC Link page - [@RobHannay](https://github.com/RobHannay) - [PR](https://github.com/adobe/react-spectrum/pull/9992) +* feat: Add SliderFill component and improve default SliderOutput formatting - [@devongovett](https://github.com/devongovett) - [PR](https://github.com/adobe/react-spectrum/pull/10021) +* chore: Migrate from eslint to oxlint - [@devongovett](https://github.com/devongovett) - [PR](https://github.com/adobe/react-spectrum/pull/10049) +* fix: fix optimize-locales-plugin not "tree shaking" react-aria package - [@wojtekmaj](https://github.com/wojtekmaj) - [PR](https://github.com/adobe/react-spectrum/pull/10023) +* Add sr-Latn placeholder to datepicker for Serbian Latin locale - [@nklaasse](https://github.com/nklaasse) - [PR](https://github.com/adobe/react-spectrum/pull/10034) +* fix: SpinButton spins forever if the first change results in a disabled button - [@snowystinger](https://github.com/snowystinger) - [PR](https://github.com/adobe/react-spectrum/pull/9813) +* fix: eslint warnings that came back - [@snowystinger](https://github.com/snowystinger) - [PR](https://github.com/adobe/react-spectrum/pull/10038) +* chore: cleanup after lint migration - [@snowystinger](https://github.com/snowystinger) - [PR](https://github.com/adobe/react-spectrum/pull/10054) +* chore: create .mcpb bundles for the MCP servers - [@reidbarber](https://github.com/reidbarber) - [PR](https://github.com/adobe/react-spectrum/pull/9870) +* docs: improve S2 docs markdown / agent skill - [@reidbarber](https://github.com/reidbarber) - [PR](https://github.com/adobe/react-spectrum/pull/9939) +* fix(GridLayout): support RTL direction for drop target positioning - [@miguelcalderon](https://github.com/miguelcalderon) - [PR](https://github.com/adobe/react-spectrum/pull/9848) +* fix: properly scroll body if keyboard focusing a item with no other scroll parents - [@LFDanLu](https://github.com/LFDanLu) - [PR](https://github.com/adobe/react-spectrum/pull/9780) +* feat: Support DnD in S2 collection components - [@LFDanLu](https://github.com/LFDanLu) - [PR](https://github.com/adobe/react-spectrum/pull/9791) +* feat: Allow collection items to be values other than objects - [@devongovett](https://github.com/devongovett) - [PR](https://github.com/adobe/react-spectrum/pull/10047) +* feat: add highlight selection to S2 TableView - [@yihuiliao](https://github.com/yihuiliao) - [PR](https://github.com/adobe/react-spectrum/pull/9883) +* feat: Test utils API audit, additional feature coverage (RTL, grid nav, etc) in prep for RC/1.0 - [@LFDanLu](https://github.com/LFDanLu) - [PR](https://github.com/adobe/react-spectrum/pull/9998) +* docs: add RAC docs to S2 skill, and clean up - [@reidbarber](https://github.com/reidbarber) - [PR](https://github.com/adobe/react-spectrum/pull/10083) +* feat: S2 DnD docs and followups - [@LFDanLu](https://github.com/LFDanLu) - [PR](https://github.com/adobe/react-spectrum/pull/10082) +* chore: Add code mod for test util API updates - [@LFDanLu](https://github.com/LFDanLu) - [PR](https://github.com/adobe/react-spectrum/pull/10099) + +## Released packages + +``` +[TODO: paste output of yarn bumpVersions here] +``` diff --git a/packages/dev/s2-docs/pages/s2/releases/v1-4-0.mdx b/packages/dev/s2-docs/pages/s2/releases/v1-4-0.mdx new file mode 100644 index 00000000000..3b6ac21710c --- /dev/null +++ b/packages/dev/s2-docs/pages/s2/releases/v1-4-0.mdx @@ -0,0 +1,73 @@ +{/* Copyright 2026 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. */} + +import {InstallCommand} from '../../../src/InstallCommand'; + +import {Layout} from '../../../src/Layout'; +export default Layout; + +import docs from 'docs:@react-spectrum/s2'; + +export const hideNav = true; +export const section = 'Releases'; +export const tags = ['release', 'S2']; +export const date = 'May 26, 2026'; +export const title = 'v1.4.0'; +export const description = '[TODO: 1–2 sentence highlights summary]'; +export const isSubpage = true; + +# v1.4.0 + +[TODO: intro paragraph] + +{/* Commits: 36 */} +## Changelog + +* fix: Update docs and codemod for RouterConfig module augmentation to use subpaths - [@devongovett](https://github.com/devongovett) - [PR](https://github.com/adobe/react-spectrum/pull/9941) +* docs: add docs for pressScale - [@reidbarber](https://github.com/reidbarber) - [PR](https://github.com/adobe/react-spectrum/pull/9940) +* fix(S2): adjust Menu/Picker transition to prevent background color transition - [@reidbarber](https://github.com/reidbarber) - [PR](https://github.com/adobe/react-spectrum/pull/9953) +* feat: Support help text in Checkbox, Radio, and Switch - [@devongovett](https://github.com/devongovett) - [PR](https://github.com/adobe/react-spectrum/pull/9877) +* feat: Add TableFooter component - [@devongovett](https://github.com/devongovett) - [PR](https://github.com/adobe/react-spectrum/pull/9943) +* fix: Improve consistency of Popover --trigger-width - [@devongovett](https://github.com/devongovett) - [PR](https://github.com/adobe/react-spectrum/pull/9913) +* feat: Add Calendar multi-select, week/day views, weeksInMonth, month/year pickers, and unavailable anchor date - [@devongovett](https://github.com/devongovett) - [PR](https://github.com/adobe/react-spectrum/pull/9948) +* fix: miscentered empty state and loaders in S2 TableView - [@LFDanLu](https://github.com/LFDanLu) - [PR](https://github.com/adobe/react-spectrum/pull/9980) +* fix(Collections): handle falsy keys properly - [@reidbarber](https://github.com/reidbarber) - [PR](https://github.com/adobe/react-spectrum/pull/9993) +* feat: Textfield prefix - [@snowystinger](https://github.com/snowystinger) - [PR](https://github.com/adobe/react-spectrum/pull/9957) +* chore: Yarn upgrade - [@snowystinger](https://github.com/snowystinger) - [PR](https://github.com/adobe/react-spectrum/pull/9817) +* fix: Backward compatibility with lightningcss color-scheme vars - [@devongovett](https://github.com/devongovett) - [PR](https://github.com/adobe/react-spectrum/pull/10010) +* Fix types for page.css - [@devongovett](https://github.com/devongovett) - [PR](https://github.com/adobe/react-spectrum/pull/10016) +* fix: checkbox in cardview and radio and switch - [@snowystinger](https://github.com/snowystinger) - [PR](https://github.com/adobe/react-spectrum/pull/10012) +* fix: calendar header spacing - [@snowystinger](https://github.com/snowystinger) - [PR](https://github.com/adobe/react-spectrum/pull/10017) +* feat: Add S2 LabeledValue - [@yihuiliao](https://github.com/yihuiliao) - [PR](https://github.com/adobe/react-spectrum/pull/9965) +* chore: Format with oxfmt - [@devongovett](https://github.com/devongovett) - [PR](https://github.com/adobe/react-spectrum/pull/10030) +* feat: Add SliderFill component and improve default SliderOutput formatting - [@devongovett](https://github.com/devongovett) - [PR](https://github.com/adobe/react-spectrum/pull/10021) +* chore: Migrate from eslint to oxlint - [@devongovett](https://github.com/devongovett) - [PR](https://github.com/adobe/react-spectrum/pull/10049) +* feat: Textfield prefix followup - [@snowystinger](https://github.com/snowystinger) - [PR](https://github.com/adobe/react-spectrum/pull/10014) +* chore: cleanup after lint migration - [@snowystinger](https://github.com/snowystinger) - [PR](https://github.com/adobe/react-spectrum/pull/10054) +* fix: textfield prefix design feedback - [@snowystinger](https://github.com/snowystinger) - [PR](https://github.com/adobe/react-spectrum/pull/10060) +* chore: S2 LabeledValue follow-up - [@yihuiliao](https://github.com/yihuiliao) - [PR](https://github.com/adobe/react-spectrum/pull/10061) +* chore: create .mcpb bundles for the MCP servers - [@reidbarber](https://github.com/reidbarber) - [PR](https://github.com/adobe/react-spectrum/pull/9870) +* chore: design feedback examples - [@snowystinger](https://github.com/snowystinger) - [PR](https://github.com/adobe/react-spectrum/pull/10070) +* feat: Add missing hold affordance icon for longPress S2 MenuTriggers - [@LFDanLu](https://github.com/LFDanLu) - [PR](https://github.com/adobe/react-spectrum/pull/10066) +* docs: improve S2 docs markdown / agent skill - [@reidbarber](https://github.com/reidbarber) - [PR](https://github.com/adobe/react-spectrum/pull/9939) +* feat: Support DnD in S2 collection components - [@LFDanLu](https://github.com/LFDanLu) - [PR](https://github.com/adobe/react-spectrum/pull/9791) +* feat: Allow collection items to be values other than objects - [@devongovett](https://github.com/devongovett) - [PR](https://github.com/adobe/react-spectrum/pull/10047) +* feat: add highlight selection to S2 TableView - [@yihuiliao](https://github.com/yihuiliao) - [PR](https://github.com/adobe/react-spectrum/pull/9883) +* feat: Test utils API audit, additional feature coverage (RTL, grid nav, etc) in prep for RC/1.0 - [@LFDanLu](https://github.com/LFDanLu) - [PR](https://github.com/adobe/react-spectrum/pull/9998) +* docs: add RAC docs to S2 skill, and clean up - [@reidbarber](https://github.com/reidbarber) - [PR](https://github.com/adobe/react-spectrum/pull/10083) +* chore: Button color update - [@snowystinger](https://github.com/snowystinger) - [PR](https://github.com/adobe/react-spectrum/pull/10069) +* chore: testing follow up for highlight selection & dnd - [@yihuiliao](https://github.com/yihuiliao) - [PR](https://github.com/adobe/react-spectrum/pull/10084) +* feat: S2 DnD docs and followups - [@LFDanLu](https://github.com/LFDanLu) - [PR](https://github.com/adobe/react-spectrum/pull/10082) +* chore: Add code mod for test util API updates - [@LFDanLu](https://github.com/LFDanLu) - [PR](https://github.com/adobe/react-spectrum/pull/10099) + +## Released packages + +``` +[TODO: paste output of yarn bumpVersions here] +``` From bbcc011d5d2294de28f31165af6090e25463f341 Mon Sep 17 00:00:00 2001 From: Yihui Liao <44729383+yihuiliao@users.noreply.github.com> Date: Tue, 26 May 2026 15:42:19 -0700 Subject: [PATCH 04/11] update skill --- .claude/skills/release-notes/SKILL.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/.claude/skills/release-notes/SKILL.md b/.claude/skills/release-notes/SKILL.md index 958fdf5bc4b..07eaeb64307 100644 --- a/.claude/skills/release-notes/SKILL.md +++ b/.claude/skills/release-notes/SKILL.md @@ -111,9 +111,14 @@ After rewriting the `## Changelog` body, count the total number of `-` bullet li - **Count matches:** continue to step 4. - **Count is less:** diff the original flat commit list against the bullets you wrote to find which entries are missing. Either place them in the correct section, or leave a drop note explaining why. Do not leave commits silently missing. Drop note forms: - - `{/* dropped: ([PR](url)) — merged into above */}` — use **only** when the bullet directly above literally describes what this PR did (i.e. you rewrote the bullet to incorporate this PR's changes). Do not use this if the bullet above doesn't mention the content of this PR. - - `{/* dropped: ([PR](url)) — follow-up refinement of [PR](url) */}` — use for iterative polish PRs (design feedback, followups, small adjustments) whose user-facing change is fully captured by the initial feature PR's bullet. - - `{/* dropped: ([PR](url)) — internal tooling */}` — use for commits with no user-facing impact (lint migration, formatting, yarn upgrade, internal scripts, test-only changes). + All drop notes must include the PR link, author handle, and author profile URL so the entry can be reinstated quickly if needed. Format: + + `{/* dropped: [@author](https://github.com/author) - [PR](url) — reason */}` + + Reason values: + - `merged into above` — use **only** when the bullet directly above literally describes what this PR did (i.e. you rewrote the bullet to incorporate this PR's changes). Do not use this if the bullet above doesn't mention the content of this PR. + - `follow-up refinement of [PR](url)` — use for iterative polish PRs (design feedback, followups, small adjustments) whose user-facing change is fully captured by the initial feature PR's bullet. + - `internal tooling` — use for commits with no user-facing impact (lint migration, formatting, yarn upgrade, internal scripts, test-only changes). - **Count is greater:** this is fine if a multi-component PR was duplicated across sections (intentional per the grouping rules). ### 4. Fill in `description` and intro paragraph From a9bc758793eca63b130edcf8c702afedd137c9dc Mon Sep 17 00:00:00 2001 From: Yihui Liao <44729383+yihuiliao@users.noreply.github.com> Date: Wed, 27 May 2026 15:48:27 -0700 Subject: [PATCH 05/11] more skill updates --- .claude/skills/release-notes/SKILL.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/.claude/skills/release-notes/SKILL.md b/.claude/skills/release-notes/SKILL.md index 07eaeb64307..5e792049278 100644 --- a/.claude/skills/release-notes/SKILL.md +++ b/.claude/skills/release-notes/SKILL.md @@ -83,8 +83,19 @@ Then apply the standard formatting rules: - Wrap camelCase/code terms in backticks: `onClick`, `isDisabled`, `onPress`. - Do NOT use backticks for component names. - Replace: RAC → React Aria, V3 → React Spectrum. +- Strip editorial hedging — remove words like *inadvertently*, *accidentally*, *incorrectly*, *mistakenly* from bug descriptions. Just describe what changed. - Capitalize component names: Accordion, Autocomplete, Badge, Breadcrumbs, Buttons, Calendar, Checkbox, CheckboxGroup, Collections, ColorArea, ColorField, ColorPicker, ColorSlider, ColorSwatch, ColorSwatchPicker, ColorWheel, ComboBox, Date and Time, DateField, DatePicker, DateRangePicker, Dialog, Disclosure, DisclosureGroup, Drag and Drop, DropZone, FileTrigger, Form, InlineAlert, Link, Listbox, ListView, Menu, Meter, Modal, NotificationBadge, NumberField, Picker, ProgressBar, ProgressCircle, RadioGroup, RangeCalendar, SearchField, Select, Slider, StatusLight, Switch, Table, Tabs, TagGroup, TextArea, TextField, TimeField, Toast, ToggleButton, ToggleButtonGroup, Tooltip, Tree, Virtualizer. +**Drop internal/infrastructure commits.** Before writing a bullet, ask: *"Would a user of this library notice this change in their own code or in the browser?"* If no, drop the entry and add a drop-note comment instead. Common cases to drop: +- Build-tool or bundler fixes (e.g. lightningcss compatibility, Parcel internals) +- TypeScript fixes for non-public or internal files (e.g. `page.css` type stubs) +- Documentation copy-only changes with no API or behavior impact +- Internal tooling, lint migrations, script changes + +Use the standard `internal tooling` drop-note format (see §3.5) for any dropped commit. + +**One bullet per new API surface.** When a single PR introduces multiple distinct public APIs — new components, new props, new callback arguments — write one bullet per API rather than one bullet per PR. Fetch the PR body (`gh pr view --json body,files`) and scan the description and changed file paths for each new exported symbol or prop. Each deserves its own line. + **Group by component.** Replace the flat list under `## Changelog` with sub-headings: ``` @@ -100,7 +111,7 @@ Then apply the standard formatting rules: - ... ``` -- Each component gets its own sub-heading — never group multiple components under a combined heading like "Checkbox, RadioGroup, Switch". If a single PR affects multiple components, duplicate the entry under each component's own heading. +- Each component gets its own sub-heading — never group multiple components under a combined heading like "Checkbox, RadioGroup, Switch". If a single PR affects multiple components, write one bullet per component, each naming only that component, and place each under its own sub-heading. Do not name sibling components inside the bullet text — e.g. write "Add drag and drop support" under **ListView**, **Table**, and **TreeView** separately, not "Add drag and drop support to ListView, Table, and TreeView" under a single heading. - Sub-headings in alphabetical order, with "General Changes" first. - Use `-` (not `*`) for list items inside sub-headings, matching the existing release-notes style (see `packages/dev/s2-docs/pages/react-aria/releases/v1-17-0.mdx` for reference). - Do NOT bold sub-heading text. From cbfe26b338588258c322384360eb66544a700a9e Mon Sep 17 00:00:00 2001 From: Yihui Liao <44729383+yihuiliao@users.noreply.github.com> Date: Fri, 29 May 2026 11:17:16 -0700 Subject: [PATCH 06/11] update skill --- .claude/skills/release-notes/SKILL.md | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/.claude/skills/release-notes/SKILL.md b/.claude/skills/release-notes/SKILL.md index 5e792049278..faa30ab96d6 100644 --- a/.claude/skills/release-notes/SKILL.md +++ b/.claude/skills/release-notes/SKILL.md @@ -1,3 +1,7 @@ +--- +description: Polish the scaffolded release notes MDX files generated by `yarn releaseNotes` — rewrites the Changelog section, groups by component, and fills in the intro/description. +--- + # Release Notes Skill Polish the scaffolded release-notes files generated by `scripts/changelog.js`. @@ -61,12 +65,18 @@ For each commit line in the existing `## Changelog`: **Rewrite the message.** Original format: `Type (Scope): Summary - [@user](url) - [PR](url)` → target `Summary - [@user](url) - [PR](url)`. -First, evaluate whether the commit subject is a clear, user-facing description. If it is vague or developer-internal (e.g. "fix typo", "address review comments", "nit", "updates", "wip", "cleanup"), fetch the PR body and changed files for more context: +First, evaluate whether the commit subject is a clear, user-facing description. If it is vague or developer-internal (e.g. "fix typo", "address review comments", "nit", "updates", "wip", "cleanup"), fetch the PR body, changed files, and the rspbot ts-diff comment for more context: ```bash gh pr view --json title,body,files +gh pr view --json comments --jq '.comments[] | select(.author.login == "rspbot") | .body' ``` +The rspbot comment contains a TypeScript diff of the public API surface — new/changed/removed exports and props. If present, treat it as the authoritative source for: +- Prop names and their exact spelling (already in TypeScript syntax — wrap in backticks directly) +- Whether a change is public API (exported) vs. internal-only +- How many distinct API surfaces a single PR introduced (drives the one-bullet-per-API-surface rule below) + Use the PR description and changed file paths to write a more informative summary. If the PR has no useful description either, use the file paths to infer the affected area (e.g. files under `packages/@react-aria/table/` → Table-related fix). If `gh pr view` exits non-zero (PR deleted, no access, closed without merge, etc.), do **not** skip the commit. Keep the original commit subject and mark it for manual review: @@ -84,14 +94,19 @@ Then apply the standard formatting rules: - Do NOT use backticks for component names. - Replace: RAC → React Aria, V3 → React Spectrum. - Strip editorial hedging — remove words like *inadvertently*, *accidentally*, *incorrectly*, *mistakenly* from bug descriptions. Just describe what changed. +- When a bug fix enables something users previously couldn't do reliably, frame it as a user capability rather than an internal fix. Prefer "Allow `formatOptions` to be passed as an inline object without resetting user input" over "Fix resetting the typed value when `formatOptions` is passed as an inline object". - Capitalize component names: Accordion, Autocomplete, Badge, Breadcrumbs, Buttons, Calendar, Checkbox, CheckboxGroup, Collections, ColorArea, ColorField, ColorPicker, ColorSlider, ColorSwatch, ColorSwatchPicker, ColorWheel, ComboBox, Date and Time, DateField, DatePicker, DateRangePicker, Dialog, Disclosure, DisclosureGroup, Drag and Drop, DropZone, FileTrigger, Form, InlineAlert, Link, Listbox, ListView, Menu, Meter, Modal, NotificationBadge, NumberField, Picker, ProgressBar, ProgressCircle, RadioGroup, RangeCalendar, SearchField, Select, Slider, StatusLight, Switch, Table, Tabs, TagGroup, TextArea, TextField, TimeField, Toast, ToggleButton, ToggleButtonGroup, Tooltip, Tree, Virtualizer. **Drop internal/infrastructure commits.** Before writing a bullet, ask: *"Would a user of this library notice this change in their own code or in the browser?"* If no, drop the entry and add a drop-note comment instead. Common cases to drop: - Build-tool or bundler fixes (e.g. lightningcss compatibility, Parcel internals) - TypeScript fixes for non-public or internal files (e.g. `page.css` type stubs) -- Documentation copy-only changes with no API or behavior impact - Internal tooling, lint migrations, script changes +**Do not drop docs commits without checking the files.** A commit prefixed `docs:` or containing "docs" in the message is not automatically internal. Before dropping, fetch `gh pr view --json body,files` and scan: +- **New user-facing guides or how-tos on component pages** (e.g. a "Client side routing" section added to `Link.mdx`, a migration guide, an accessibility pattern explanation): include if a library user would find it directly actionable. Write the bullet as "Add [topic] guide to [Component] documentation". +- **Starter template changes** (`starters/docs/src/`, `starters/tailwind/src/`): these power the docs site examples, not standalone copyable files — treat changes here as docs site fixes. +- **Internal docs** (agent skills, CONTRIBUTING.md, build documentation, internal READMEs): drop as internal tooling. + Use the standard `internal tooling` drop-note format (see §3.5) for any dropped commit. **One bullet per new API surface.** When a single PR introduces multiple distinct public APIs — new components, new props, new callback arguments — write one bullet per API rather than one bullet per PR. Fetch the PR body (`gh pr view --json body,files`) and scan the description and changed file paths for each new exported symbol or prop. Each deserves its own line. @@ -112,6 +127,7 @@ Use the standard `internal tooling` drop-note format (see §3.5) for any dropped ``` - Each component gets its own sub-heading — never group multiple components under a combined heading like "Checkbox, RadioGroup, Switch". If a single PR affects multiple components, write one bullet per component, each naming only that component, and place each under its own sub-heading. Do not name sibling components inside the bullet text — e.g. write "Add drag and drop support" under **ListView**, **Table**, and **TreeView** separately, not "Add drag and drop support to ListView, Table, and TreeView" under a single heading. +- When a PR affects multiple components but with *different* behavior per component, write distinct descriptions for each rather than copying the same bullet. Fetch the PR body and changed files to understand which component got which change — e.g. a `--trigger-width` fix that adds standalone support to Popover and also fixes Group-wrapped inputs in ComboBox needs two different bullets with two different descriptions. - Sub-headings in alphabetical order, with "General Changes" first. - Use `-` (not `*`) for list items inside sub-headings, matching the existing release-notes style (see `packages/dev/s2-docs/pages/react-aria/releases/v1-17-0.mdx` for reference). - Do NOT bold sub-heading text. From 79c87c47635ba9f16ef93c95f5e96afd6ac5936a Mon Sep 17 00:00:00 2001 From: Yihui Liao <44729383+yihuiliao@users.noreply.github.com> Date: Fri, 29 May 2026 11:18:06 -0700 Subject: [PATCH 07/11] Revert "add commits to test claude on" This reverts commit f0769fb5dc7265bbb754e1808f0d2b4ca8e5936e. --- .../pages/react-aria/releases/v1-18-0.mdx | 77 ------------------- .../dev/s2-docs/pages/s2/releases/v1-4-0.mdx | 73 ------------------ 2 files changed, 150 deletions(-) delete mode 100644 packages/dev/s2-docs/pages/react-aria/releases/v1-18-0.mdx delete mode 100644 packages/dev/s2-docs/pages/s2/releases/v1-4-0.mdx diff --git a/packages/dev/s2-docs/pages/react-aria/releases/v1-18-0.mdx b/packages/dev/s2-docs/pages/react-aria/releases/v1-18-0.mdx deleted file mode 100644 index 400399f101a..00000000000 --- a/packages/dev/s2-docs/pages/react-aria/releases/v1-18-0.mdx +++ /dev/null @@ -1,77 +0,0 @@ -{/* Copyright 2026 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. */} - -import {InstallCommand} from '../../../src/InstallCommand'; - -import {Layout} from '../../../src/Layout'; -export default Layout; - -import docs from 'docs:@react-spectrum/s2'; - -export const hideNav = true; -export const section = 'Releases'; -export const tags = ['release', 'React Aria']; -export const date = 'May 26, 2026'; -export const title = 'v1.18.0'; -export const description = '[TODO: 1–2 sentence highlights summary]'; -export const isSubpage = true; - -# v1.18.0 - -[TODO: intro paragraph] - -{/* Commits: 40 */} -## Changelog - -* fix: type mismatch in useTextField for textarea elements - [@nami8824](https://github.com/nami8824) - [PR](https://github.com/adobe/react-spectrum/pull/9884) -* fix: support CSS logical properties for Tabs animations - [@SupaSeeka](https://github.com/SupaSeeka) - [PR](https://github.com/adobe/react-spectrum/pull/9951) -* fix: layout shift in s2 modal docs - [@nwidynski](https://github.com/nwidynski) - [PR](https://github.com/adobe/react-spectrum/pull/9956) -* Fix/numberfield format options input reset - [@Wonchang0314](https://github.com/Wonchang0314) - [PR](https://github.com/adobe/react-spectrum/pull/9905) -* chore: update NumberParser's cldr data - [@snowystinger](https://github.com/snowystinger) - [PR](https://github.com/adobe/react-spectrum/pull/9907) -* fix: ensure Virtualized Table loader width is up to date with table width - [@LFDanLu](https://github.com/LFDanLu) - [PR](https://github.com/adobe/react-spectrum/pull/9961) -* feat: Support help text in Checkbox, Radio, and Switch - [@devongovett](https://github.com/devongovett) - [PR](https://github.com/adobe/react-spectrum/pull/9877) -* fix: compatibility for modern icu - [@nwidynski](https://github.com/nwidynski) - [PR](https://github.com/adobe/react-spectrum/pull/9887) -* feat: Add TableFooter component - [@devongovett](https://github.com/devongovett) - [PR](https://github.com/adobe/react-spectrum/pull/9943) -* fix: Improve consistency of Popover --trigger-width - [@devongovett](https://github.com/devongovett) - [PR](https://github.com/adobe/react-spectrum/pull/9913) -* feat: Add Calendar multi-select, week/day views, weeksInMonth, month/year pickers, and unavailable anchor date - [@devongovett](https://github.com/devongovett) - [PR](https://github.com/adobe/react-spectrum/pull/9948) -* fix: prevent chromatic storybook crash by exporting directly from TableLayout - [@LFDanLu](https://github.com/LFDanLu) - [PR](https://github.com/adobe/react-spectrum/pull/9979) -* chore: re-export mergeRefs from react-aria index - [@lixiaoyan](https://github.com/lixiaoyan) - [PR](https://github.com/adobe/react-spectrum/pull/9976) -* fix(Collections): handle falsy keys properly - [@reidbarber](https://github.com/reidbarber) - [PR](https://github.com/adobe/react-spectrum/pull/9993) -* chore: Yarn upgrade - [@snowystinger](https://github.com/snowystinger) - [PR](https://github.com/adobe/react-spectrum/pull/9817) -* fix: omit disabledKeys from MenuSection - [@LFDanLu](https://github.com/LFDanLu) - [PR](https://github.com/adobe/react-spectrum/pull/9997) -* fix: preserve multi-select ComboBox selections for custom input - [@romansndlr](https://github.com/romansndlr) - [PR](https://github.com/adobe/react-spectrum/pull/9945) -* fix: Menu, Select, and ComboBox inside Tabs - [@devongovett](https://github.com/devongovett) - [PR](https://github.com/adobe/react-spectrum/pull/10019) -* fix: MenuItem render function should not receive an href when not a link - [@devongovett](https://github.com/devongovett) - [PR](https://github.com/adobe/react-spectrum/pull/10018) -* fix(Tree): Dont set focusedKey to a section node key - [@chirokas](https://github.com/chirokas) - [PR](https://github.com/adobe/react-spectrum/pull/10020) -* chore: Format with oxfmt - [@devongovett](https://github.com/devongovett) - [PR](https://github.com/adobe/react-spectrum/pull/10030) -* docs: add client side routing guide to RAC Link page - [@RobHannay](https://github.com/RobHannay) - [PR](https://github.com/adobe/react-spectrum/pull/9992) -* feat: Add SliderFill component and improve default SliderOutput formatting - [@devongovett](https://github.com/devongovett) - [PR](https://github.com/adobe/react-spectrum/pull/10021) -* chore: Migrate from eslint to oxlint - [@devongovett](https://github.com/devongovett) - [PR](https://github.com/adobe/react-spectrum/pull/10049) -* fix: fix optimize-locales-plugin not "tree shaking" react-aria package - [@wojtekmaj](https://github.com/wojtekmaj) - [PR](https://github.com/adobe/react-spectrum/pull/10023) -* Add sr-Latn placeholder to datepicker for Serbian Latin locale - [@nklaasse](https://github.com/nklaasse) - [PR](https://github.com/adobe/react-spectrum/pull/10034) -* fix: SpinButton spins forever if the first change results in a disabled button - [@snowystinger](https://github.com/snowystinger) - [PR](https://github.com/adobe/react-spectrum/pull/9813) -* fix: eslint warnings that came back - [@snowystinger](https://github.com/snowystinger) - [PR](https://github.com/adobe/react-spectrum/pull/10038) -* chore: cleanup after lint migration - [@snowystinger](https://github.com/snowystinger) - [PR](https://github.com/adobe/react-spectrum/pull/10054) -* chore: create .mcpb bundles for the MCP servers - [@reidbarber](https://github.com/reidbarber) - [PR](https://github.com/adobe/react-spectrum/pull/9870) -* docs: improve S2 docs markdown / agent skill - [@reidbarber](https://github.com/reidbarber) - [PR](https://github.com/adobe/react-spectrum/pull/9939) -* fix(GridLayout): support RTL direction for drop target positioning - [@miguelcalderon](https://github.com/miguelcalderon) - [PR](https://github.com/adobe/react-spectrum/pull/9848) -* fix: properly scroll body if keyboard focusing a item with no other scroll parents - [@LFDanLu](https://github.com/LFDanLu) - [PR](https://github.com/adobe/react-spectrum/pull/9780) -* feat: Support DnD in S2 collection components - [@LFDanLu](https://github.com/LFDanLu) - [PR](https://github.com/adobe/react-spectrum/pull/9791) -* feat: Allow collection items to be values other than objects - [@devongovett](https://github.com/devongovett) - [PR](https://github.com/adobe/react-spectrum/pull/10047) -* feat: add highlight selection to S2 TableView - [@yihuiliao](https://github.com/yihuiliao) - [PR](https://github.com/adobe/react-spectrum/pull/9883) -* feat: Test utils API audit, additional feature coverage (RTL, grid nav, etc) in prep for RC/1.0 - [@LFDanLu](https://github.com/LFDanLu) - [PR](https://github.com/adobe/react-spectrum/pull/9998) -* docs: add RAC docs to S2 skill, and clean up - [@reidbarber](https://github.com/reidbarber) - [PR](https://github.com/adobe/react-spectrum/pull/10083) -* feat: S2 DnD docs and followups - [@LFDanLu](https://github.com/LFDanLu) - [PR](https://github.com/adobe/react-spectrum/pull/10082) -* chore: Add code mod for test util API updates - [@LFDanLu](https://github.com/LFDanLu) - [PR](https://github.com/adobe/react-spectrum/pull/10099) - -## Released packages - -``` -[TODO: paste output of yarn bumpVersions here] -``` diff --git a/packages/dev/s2-docs/pages/s2/releases/v1-4-0.mdx b/packages/dev/s2-docs/pages/s2/releases/v1-4-0.mdx deleted file mode 100644 index 3b6ac21710c..00000000000 --- a/packages/dev/s2-docs/pages/s2/releases/v1-4-0.mdx +++ /dev/null @@ -1,73 +0,0 @@ -{/* Copyright 2026 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. */} - -import {InstallCommand} from '../../../src/InstallCommand'; - -import {Layout} from '../../../src/Layout'; -export default Layout; - -import docs from 'docs:@react-spectrum/s2'; - -export const hideNav = true; -export const section = 'Releases'; -export const tags = ['release', 'S2']; -export const date = 'May 26, 2026'; -export const title = 'v1.4.0'; -export const description = '[TODO: 1–2 sentence highlights summary]'; -export const isSubpage = true; - -# v1.4.0 - -[TODO: intro paragraph] - -{/* Commits: 36 */} -## Changelog - -* fix: Update docs and codemod for RouterConfig module augmentation to use subpaths - [@devongovett](https://github.com/devongovett) - [PR](https://github.com/adobe/react-spectrum/pull/9941) -* docs: add docs for pressScale - [@reidbarber](https://github.com/reidbarber) - [PR](https://github.com/adobe/react-spectrum/pull/9940) -* fix(S2): adjust Menu/Picker transition to prevent background color transition - [@reidbarber](https://github.com/reidbarber) - [PR](https://github.com/adobe/react-spectrum/pull/9953) -* feat: Support help text in Checkbox, Radio, and Switch - [@devongovett](https://github.com/devongovett) - [PR](https://github.com/adobe/react-spectrum/pull/9877) -* feat: Add TableFooter component - [@devongovett](https://github.com/devongovett) - [PR](https://github.com/adobe/react-spectrum/pull/9943) -* fix: Improve consistency of Popover --trigger-width - [@devongovett](https://github.com/devongovett) - [PR](https://github.com/adobe/react-spectrum/pull/9913) -* feat: Add Calendar multi-select, week/day views, weeksInMonth, month/year pickers, and unavailable anchor date - [@devongovett](https://github.com/devongovett) - [PR](https://github.com/adobe/react-spectrum/pull/9948) -* fix: miscentered empty state and loaders in S2 TableView - [@LFDanLu](https://github.com/LFDanLu) - [PR](https://github.com/adobe/react-spectrum/pull/9980) -* fix(Collections): handle falsy keys properly - [@reidbarber](https://github.com/reidbarber) - [PR](https://github.com/adobe/react-spectrum/pull/9993) -* feat: Textfield prefix - [@snowystinger](https://github.com/snowystinger) - [PR](https://github.com/adobe/react-spectrum/pull/9957) -* chore: Yarn upgrade - [@snowystinger](https://github.com/snowystinger) - [PR](https://github.com/adobe/react-spectrum/pull/9817) -* fix: Backward compatibility with lightningcss color-scheme vars - [@devongovett](https://github.com/devongovett) - [PR](https://github.com/adobe/react-spectrum/pull/10010) -* Fix types for page.css - [@devongovett](https://github.com/devongovett) - [PR](https://github.com/adobe/react-spectrum/pull/10016) -* fix: checkbox in cardview and radio and switch - [@snowystinger](https://github.com/snowystinger) - [PR](https://github.com/adobe/react-spectrum/pull/10012) -* fix: calendar header spacing - [@snowystinger](https://github.com/snowystinger) - [PR](https://github.com/adobe/react-spectrum/pull/10017) -* feat: Add S2 LabeledValue - [@yihuiliao](https://github.com/yihuiliao) - [PR](https://github.com/adobe/react-spectrum/pull/9965) -* chore: Format with oxfmt - [@devongovett](https://github.com/devongovett) - [PR](https://github.com/adobe/react-spectrum/pull/10030) -* feat: Add SliderFill component and improve default SliderOutput formatting - [@devongovett](https://github.com/devongovett) - [PR](https://github.com/adobe/react-spectrum/pull/10021) -* chore: Migrate from eslint to oxlint - [@devongovett](https://github.com/devongovett) - [PR](https://github.com/adobe/react-spectrum/pull/10049) -* feat: Textfield prefix followup - [@snowystinger](https://github.com/snowystinger) - [PR](https://github.com/adobe/react-spectrum/pull/10014) -* chore: cleanup after lint migration - [@snowystinger](https://github.com/snowystinger) - [PR](https://github.com/adobe/react-spectrum/pull/10054) -* fix: textfield prefix design feedback - [@snowystinger](https://github.com/snowystinger) - [PR](https://github.com/adobe/react-spectrum/pull/10060) -* chore: S2 LabeledValue follow-up - [@yihuiliao](https://github.com/yihuiliao) - [PR](https://github.com/adobe/react-spectrum/pull/10061) -* chore: create .mcpb bundles for the MCP servers - [@reidbarber](https://github.com/reidbarber) - [PR](https://github.com/adobe/react-spectrum/pull/9870) -* chore: design feedback examples - [@snowystinger](https://github.com/snowystinger) - [PR](https://github.com/adobe/react-spectrum/pull/10070) -* feat: Add missing hold affordance icon for longPress S2 MenuTriggers - [@LFDanLu](https://github.com/LFDanLu) - [PR](https://github.com/adobe/react-spectrum/pull/10066) -* docs: improve S2 docs markdown / agent skill - [@reidbarber](https://github.com/reidbarber) - [PR](https://github.com/adobe/react-spectrum/pull/9939) -* feat: Support DnD in S2 collection components - [@LFDanLu](https://github.com/LFDanLu) - [PR](https://github.com/adobe/react-spectrum/pull/9791) -* feat: Allow collection items to be values other than objects - [@devongovett](https://github.com/devongovett) - [PR](https://github.com/adobe/react-spectrum/pull/10047) -* feat: add highlight selection to S2 TableView - [@yihuiliao](https://github.com/yihuiliao) - [PR](https://github.com/adobe/react-spectrum/pull/9883) -* feat: Test utils API audit, additional feature coverage (RTL, grid nav, etc) in prep for RC/1.0 - [@LFDanLu](https://github.com/LFDanLu) - [PR](https://github.com/adobe/react-spectrum/pull/9998) -* docs: add RAC docs to S2 skill, and clean up - [@reidbarber](https://github.com/reidbarber) - [PR](https://github.com/adobe/react-spectrum/pull/10083) -* chore: Button color update - [@snowystinger](https://github.com/snowystinger) - [PR](https://github.com/adobe/react-spectrum/pull/10069) -* chore: testing follow up for highlight selection & dnd - [@yihuiliao](https://github.com/yihuiliao) - [PR](https://github.com/adobe/react-spectrum/pull/10084) -* feat: S2 DnD docs and followups - [@LFDanLu](https://github.com/LFDanLu) - [PR](https://github.com/adobe/react-spectrum/pull/10082) -* chore: Add code mod for test util API updates - [@LFDanLu](https://github.com/LFDanLu) - [PR](https://github.com/adobe/react-spectrum/pull/10099) - -## Released packages - -``` -[TODO: paste output of yarn bumpVersions here] -``` From 80e7ee0ae8557a40609369178e1c75809b6685cb Mon Sep 17 00:00:00 2001 From: Yihui Liao <44729383+yihuiliao@users.noreply.github.com> Date: Fri, 29 May 2026 14:55:06 -0700 Subject: [PATCH 08/11] cleanup script --- scripts/changelog.js | 79 +++++++++++++++----------------------------- 1 file changed, 26 insertions(+), 53 deletions(-) diff --git a/scripts/changelog.js b/scripts/changelog.js index d533270633c..7ac1d4e5611 100644 --- a/scripts/changelog.js +++ b/scripts/changelog.js @@ -13,19 +13,18 @@ const LIBRARY_ORDER = ['React Aria Components', 'Spectrum 2']; const LIBRARY_CONFIG = { 'React Aria Components': { releasesDir: 'packages/dev/s2-docs/pages/react-aria/releases', - template: 's2docs', tag: 'React Aria', docsDir: 'packages/dev/s2-docs/pages/react-aria' }, 'Spectrum 2': { releasesDir: 'packages/dev/s2-docs/pages/s2/releases', - template: 's2docs', tag: 'S2', docsDir: 'packages/dev/s2-docs/pages/s2' }, }; function packageToLibrary(name) { + // Skip this package if (name.startsWith('@spectrum-icons/')) { return null; } @@ -41,6 +40,8 @@ function packageToLibrary(name) { if (name === '@react-spectrum/s2') { return 'Spectrum 2'; } + // V3 has no LIBRARY_CONFIG entry. Its commits are collected but only printed + // as a warning, never written to a file. See the v3Bucket handling at the end of run(). if (name === '@adobe/react-spectrum' || name.startsWith('@react-spectrum/')) { return 'React Spectrum'; } @@ -62,6 +63,7 @@ function nextVersionFilename(releasesDir) { versions.sort((a, b) => b[0] - a[0] || b[1] - a[1] || b[2] - a[2]); let [major, minor] = versions[0]; let nextMinor = minor + 1; + // Always produces a minor bump (x.N+1.0) return {filename: `v${major}-${nextMinor}-0.mdx`, version: `${major}.${nextMinor}.0`}; } @@ -126,35 +128,6 @@ ${body} `; } -function scaffoldV3Blog({dateYMD, body, commitCount}) { - let year = new Date().getFullYear(); - return `${licenseHeader(year)} - -import {BlogPostLayout, Hero, Image} from '@react-spectrum/docs'; -export default BlogPostLayout; - ---- -description: [TODO: 1–2 sentence highlights summary] -date: ${dateYMD} ---- - -# ${prettyDate(dateYMD)} Release - -[TODO: intro paragraph] - -{/* Commits: ${commitCount} */} -## Changelog - -${body} - -## Released packages - -\`\`\` -[TODO: paste output of yarn bumpVersions here] -\`\`\` -`; -} - function resolveTag(pkgName, pkgVersion) { let exact = `${pkgName}@${pkgVersion}`; let check = spawn('git', ['rev-parse', '--verify', exact], {encoding: 'utf8'}); @@ -173,15 +146,21 @@ async function renderCommitLine(commit) { let user = ''; let pr; + // commit fields: [0] hash, [1] ISO date, [2] author name, [3] subject (from --pretty format) let m = commit[3].match(/(.*?) \(#(\d+)\)$/); if (m) { let prId = m[2]; message = m[1]; - let res = await octokit.request('GET /repos/adobe/react-spectrum/pulls/{pull}', {pull: prId}); - user = `[@${res.data.user.login}](${res.data.user.html_url})`; - pr = `https://github.com/adobe/react-spectrum/pull/${prId}`; + try { + let res = await octokit.request('GET /repos/adobe/react-spectrum/pulls/{pull}', {pull: prId}); + user = `[@${res.data.user.login}](${res.data.user.html_url})`; + pr = `https://github.com/adobe/react-spectrum/pull/${prId}`; + } catch (e) { + console.warn(`⚠ Could not fetch PR #${prId} from GitHub (${e.message}) — falling back to git author`); + user = commit[2]; + } } else { message = commit[3]; user = commit[2]; @@ -202,8 +181,8 @@ async function run() { let commitsByLibrary = new Map(); let libraryTags = new Map(); - for (let name in packages) { - let filePath = packages[name].location + '/package.json'; + for (let {location} of packages) { + let filePath = location + '/package.json'; let pkg = JSON.parse(fs.readFileSync(filePath, 'utf8')); if (pkg.private) { continue; @@ -223,6 +202,7 @@ async function run() { continue; } + // Track the first tag seen per library as the anchor for git log ranges. if (!libraryTags.has(library)) { libraryTags.set(library, tag); } @@ -231,7 +211,7 @@ async function run() { 'log', `${tag}..HEAD`, '--pretty="%H%x00%aI%x00%an%x00%s"', - packages[name].location, + location, // filter out non-code changes ':!**/test/**', @@ -255,14 +235,14 @@ async function run() { } let info = line.replace(/^"|"$/g, '').split('\0'); - if (info[3] === 'Publish') { + if (info[3] === 'Publish') { // skip Publish commits continue; } - bucket.set(info[0], info); + bucket.set(info[0], info); // keyed by hash — deduplicates commits touching multiple packages } } - // Scan docs directories for docs-only commits (e.g. guide additions, new component pages). + // Scan docs directories for docs-only commits // These live in private packages skipped by the main loop above. for (let [library, config] of Object.entries(LIBRARY_CONFIG)) { if (!config.docsDir) continue; @@ -293,6 +273,7 @@ async function run() { } } + // Handle commits under RAC and S2 for (let library of LIBRARY_ORDER) { let bucket = commitsByLibrary.get(library); if (!bucket || bucket.size === 0) { @@ -300,10 +281,7 @@ async function run() { } let sorted = [...bucket.values()].sort((a, b) => (a[1] < b[1] ? -1 : 1)); - let lines = []; - for (let commit of sorted) { - lines.push(await renderCommitLine(commit)); - } + let lines = await Promise.all(sorted.map(renderCommitLine)); let body = lines.join('\n'); let config = LIBRARY_CONFIG[library]; @@ -311,15 +289,9 @@ async function run() { let outPath; let content; - if (config.template === 'v3blog') { - let ymd = todayYMD(); - outPath = path.join(dir, `${ymd}.mdx`); - content = scaffoldV3Blog({dateYMD: ymd, body, commitCount: lines.length}); - } else { - let {filename, version} = nextVersionFilename(dir); - outPath = path.join(dir, filename); - content = scaffoldS2Docs({version, dateYMD: todayYMD(), tag: config.tag, body, commitCount: lines.length}); - } + let {filename, version} = nextVersionFilename(dir); + outPath = path.join(dir, filename); + content = scaffoldS2Docs({version, dateYMD: todayYMD(), tag: config.tag, body, commitCount: lines.length}); if (fs.existsSync(outPath)) { console.warn(`⚠ ${outPath} already exists — skipping (${bucket.size} commits not written). Printing to stdout instead:\n`); @@ -333,6 +305,7 @@ async function run() { console.log(`✓ Wrote ${outPath} (${bucket.size} commits)`); } + // Handle commits in V3 let v3Bucket = commitsByLibrary.get('React Spectrum'); if (v3Bucket && v3Bucket.size > 0) { console.warn(`\nℹ ${v3Bucket.size} React Spectrum (v3) commit(s) were not written to a file. Review them manually if needed:\n`); From b77bec7cd5d69bf96105e6661d059c242ab1882f Mon Sep 17 00:00:00 2001 From: Yihui Liao <44729383+yihuiliao@users.noreply.github.com> Date: Fri, 29 May 2026 14:58:06 -0700 Subject: [PATCH 09/11] fix formatting --- scripts/changelog.js | 57 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 44 insertions(+), 13 deletions(-) diff --git a/scripts/changelog.js b/scripts/changelog.js index 7ac1d4e5611..2bce12dfc9b 100644 --- a/scripts/changelog.js +++ b/scripts/changelog.js @@ -20,7 +20,7 @@ const LIBRARY_CONFIG = { releasesDir: 'packages/dev/s2-docs/pages/s2/releases', tag: 'S2', docsDir: 'packages/dev/s2-docs/pages/s2' - }, + } }; function packageToLibrary(name) { @@ -30,9 +30,12 @@ function packageToLibrary(name) { } if ( name.startsWith('@internationalized/') || - name === 'react-stately' || name.startsWith('@react-stately/') || - name === 'react-aria' || name.startsWith('@react-aria/') || - name === 'react-aria-components' || name === 'tailwindcss-react-aria-components' || + name === 'react-stately' || + name.startsWith('@react-stately/') || + name === 'react-aria' || + name.startsWith('@react-aria/') || + name === 'react-aria-components' || + name === 'tailwindcss-react-aria-components' || name.startsWith('@react-types/') ) { return 'React Aria Components'; @@ -77,7 +80,20 @@ function todayYMD() { function prettyDate(ymd) { let [y, m, d] = ymd.split('-').map(Number); - let months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; + let months = [ + 'January', + 'February', + 'March', + 'April', + 'May', + 'June', + 'July', + 'August', + 'September', + 'October', + 'November', + 'December' + ]; return `${months[m - 1]} ${d}, ${y}`; } @@ -136,7 +152,9 @@ function resolveTag(pkgName, pkgVersion) { } // Exact tag doesn't exist (e.g. a patch bump without a new tag) — fall back to the // most recent tag for this package using git's version sort. - let list = spawn('git', ['tag', '-l', '--sort=version:refname', `${pkgName}@*`], {encoding: 'utf8'}); + let list = spawn('git', ['tag', '-l', '--sort=version:refname', `${pkgName}@*`], { + encoding: 'utf8' + }); let tags = list.stdout.trim().split('\n').filter(Boolean); return tags.length > 0 ? tags[tags.length - 1] : null; } @@ -158,7 +176,9 @@ async function renderCommitLine(commit) { user = `[@${res.data.user.login}](${res.data.user.html_url})`; pr = `https://github.com/adobe/react-spectrum/pull/${prId}`; } catch (e) { - console.warn(`⚠ Could not fetch PR #${prId} from GitHub (${e.message}) — falling back to git author`); + console.warn( + `⚠ Could not fetch PR #${prId} from GitHub (${e.message}) — falling back to git author` + ); user = commit[2]; } } else { @@ -235,7 +255,8 @@ async function run() { } let info = line.replace(/^"|"$/g, '').split('\0'); - if (info[3] === 'Publish') { // skip Publish commits + if (info[3] === 'Publish') { + // skip Publish commits continue; } bucket.set(info[0], info); // keyed by hash — deduplicates commits touching multiple packages @@ -254,7 +275,7 @@ async function run() { `${tag}..HEAD`, '--pretty="%H%x00%aI%x00%an%x00%s"', config.docsDir, - ':!**/releases/**' // don't include the release note files themselves + ':!**/releases/**' // don't include the release note files themselves ]; let res = spawn('git', args, {encoding: 'utf8'}); @@ -269,7 +290,7 @@ async function run() { if (line === '') continue; let info = line.replace(/^"|"$/g, '').split('\0'); if (info[3] === 'Publish') continue; - bucket.set(info[0], info); // keyed by hash — deduplicates with source commits + bucket.set(info[0], info); // keyed by hash — deduplicates with source commits } } @@ -291,10 +312,18 @@ async function run() { let {filename, version} = nextVersionFilename(dir); outPath = path.join(dir, filename); - content = scaffoldS2Docs({version, dateYMD: todayYMD(), tag: config.tag, body, commitCount: lines.length}); + content = scaffoldS2Docs({ + version, + dateYMD: todayYMD(), + tag: config.tag, + body, + commitCount: lines.length + }); if (fs.existsSync(outPath)) { - console.warn(`⚠ ${outPath} already exists — skipping (${bucket.size} commits not written). Printing to stdout instead:\n`); + console.warn( + `⚠ ${outPath} already exists — skipping (${bucket.size} commits not written). Printing to stdout instead:\n` + ); console.log(`# ${library}\n`); console.log(body); console.log(); @@ -308,7 +337,9 @@ async function run() { // Handle commits in V3 let v3Bucket = commitsByLibrary.get('React Spectrum'); if (v3Bucket && v3Bucket.size > 0) { - console.warn(`\nℹ ${v3Bucket.size} React Spectrum (v3) commit(s) were not written to a file. Review them manually if needed:\n`); + console.warn( + `\nℹ ${v3Bucket.size} React Spectrum (v3) commit(s) were not written to a file. Review them manually if needed:\n` + ); let sorted = [...v3Bucket.values()].sort((a, b) => (a[1] < b[1] ? -1 : 1)); for (let commit of sorted) { console.warn(` ${commit[3]}`); From ed422aa7312907da67d51ec67450e3f7594cc9bd Mon Sep 17 00:00:00 2001 From: Yihui Liao <44729383+yihuiliao@users.noreply.github.com> Date: Fri, 29 May 2026 15:05:09 -0700 Subject: [PATCH 10/11] update skill --- .claude/skills/release-notes/SKILL.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.claude/skills/release-notes/SKILL.md b/.claude/skills/release-notes/SKILL.md index faa30ab96d6..c4deebe020c 100644 --- a/.claude/skills/release-notes/SKILL.md +++ b/.claude/skills/release-notes/SKILL.md @@ -102,6 +102,19 @@ Then apply the standard formatting rules: - TypeScript fixes for non-public or internal files (e.g. `page.css` type stubs) - Internal tooling, lint migrations, script changes +**Check whether a bug fix is a follow-up for a change introduced in this same release.** When processing a commit that is labeled `fix:` or is otherwise clearly a bug fix, fetch the PR body and title: + +```bash +gh pr view --json title,body +``` + +Scan the title and body for references to other PRs — patterns like `#1234`, `PR #1234`, `follow-up to #1234`, `follow up for #1234`, `introduced in #1234`, `regression from #1234`, `broken by #1234`, `related to #1234`. If you find one or more referenced PR numbers, check whether any of those numbers appear in the current release's commit list (the flat list in `## Changelog` before your rewrite). + +- **Referenced PR is in this release** → the bug was introduced and fixed within the same release cycle, so users on the previous release never experienced it. Drop this fix bullet using `follow-up refinement of [PR](url)` as the reason (see §3.5 drop notes). Do not mention the bug in the original PR's bullet — the net user-visible effect is just the original change working correctly. +- **Referenced PR is not in this release (or no cross-reference found)** → the bug is a real prod regression that users on the current release could hit. Keep it as a standalone bullet, following the standard framing and tense rules. + +This check matters because surfacing a "fix" for something that was never actually released as broken misleads users about what they should expect. When in doubt (ambiguous reference, cross-repo link), keep the bullet and treat it as a real fix. + **Do not drop docs commits without checking the files.** A commit prefixed `docs:` or containing "docs" in the message is not automatically internal. Before dropping, fetch `gh pr view --json body,files` and scan: - **New user-facing guides or how-tos on component pages** (e.g. a "Client side routing" section added to `Link.mdx`, a migration guide, an accessibility pattern explanation): include if a library user would find it directly actionable. Write the bullet as "Add [topic] guide to [Component] documentation". - **Starter template changes** (`starters/docs/src/`, `starters/tailwind/src/`): these power the docs site examples, not standalone copyable files — treat changes here as docs site fixes. From a919f4d42c24f65ec23b7285be8bb2e5acff6b0b Mon Sep 17 00:00:00 2001 From: Yihui Liao <44729383+yihuiliao@users.noreply.github.com> Date: Fri, 29 May 2026 15:19:36 -0700 Subject: [PATCH 11/11] remove outdated rules --- .cursor/rules/add-subheadings.mdc | 22 --------------- .cursor/rules/categorize-commits.mdc | 36 ------------------------ .cursor/rules/rewrite-commit-message.mdc | 27 ------------------ 3 files changed, 85 deletions(-) delete mode 100644 .cursor/rules/add-subheadings.mdc delete mode 100644 .cursor/rules/categorize-commits.mdc delete mode 100644 .cursor/rules/rewrite-commit-message.mdc diff --git a/.cursor/rules/add-subheadings.mdc b/.cursor/rules/add-subheadings.mdc deleted file mode 100644 index d723ca3dfd5..00000000000 --- a/.cursor/rules/add-subheadings.mdc +++ /dev/null @@ -1,22 +0,0 @@ ---- -alwaysApply: false ---- - -# Step 3: Add sub-headings - -Within the Enhancements, Fixes, and Under Construction categories, group commits by UI component under sub-headings. - -### Sub-heading rules: -- Use sub-headings ONLY for: - - Enhancements - - Fixes - - Under Construction -- Do NOT create sub-headings for: - - Documentation - - To Be Categorized - - S2 -- Sub-headings should be in alphabetical order -- Use "Miscellaneous" as a sub-heading for commits that do not belong to a specific component -- Each category can have its own Miscellaneous sub-heading -- Write sub-headings and commits as unordered lists using a hyphen (-) -- Do NOT bold the sub-heading text diff --git a/.cursor/rules/categorize-commits.mdc b/.cursor/rules/categorize-commits.mdc deleted file mode 100644 index 466190cfadf..00000000000 --- a/.cursor/rules/categorize-commits.mdc +++ /dev/null @@ -1,36 +0,0 @@ ---- -alwaysApply: false ---- - -# Step 1: Categorize commits - -Sort ALL commit messages into one of six main categories. The main categories are the following: -- Enhancements -- Fixes -- Documentation -- Under Construction -- To Be Categorized -- S2 - -Before categorizing commits into other groups, check whether each commit should be classified as “Under Construction.” -- Follow the steps below in order: - 1. Identify pre-release packages - - Use a command such as grep to scan the repository for package versions that include prerelease identifiers (e.g., alpha, beta, rc) - 2. Extract component keywords from commit messages - - Parse each commit message to identify possible component names - - Normalize these keywords (e.g., lowercase, remove punctuation) for easier comparison. - 3. Compare extracted keywords with pre-release packages - - If any keyword matches a package in the list, mark the commit as Under Construction. - 4. Check for explicit prerelease keywords in commit text - - If the commit message directly includes alpha, beta, or rc, classify it as Under Construction, regardless of package matches. - -Next, categorize the remaining commits not categorized as "Under Construction". Use the following keywords to determine the category: -| Keyword | Category | -|----------------------------|----------| -| feat | Enhancements| -| fix | Fixes | -| docs | Documentation | -| chore, revert, bump, build | To Be Categorized | -| S2 | S2 | - -Do not duplicate commits. In terms of priority, it should be Under Construction > S2 > To Be Categorized > Enhancements > Fixes > Documentation diff --git a/.cursor/rules/rewrite-commit-message.mdc b/.cursor/rules/rewrite-commit-message.mdc deleted file mode 100644 index a2d68af8858..00000000000 --- a/.cursor/rules/rewrite-commit-message.mdc +++ /dev/null @@ -1,27 +0,0 @@ ---- -alwaysApply: false ---- - -You are a expert technical writer for front-end development. - -# Step 2: Rewrite commit messages - -Original format: Type (Scope): Summary of changes - [@username](link to username) - [PR](link to PR) -New Format: Summary of changes - [@username](link to username) - [PR](link to PR) - -### General Guidelines: -- Keep the summary as a single, grammatically correct sentence -- Verbs should be first person present tense but do NOT include the subject (e.g. I) -- The message should be concise and easy to read -- Wrap any camelCase or code-like terms (e.g. onClick, onAction, isDisabled) in backticks (``) - - Do NOT use backticks for component names -- Replace specific terms: - - RAC -> React Aria - - V3 -> React Spectrum -- ALWAYS capitalize UI component names - - Example: - - toast -> Toast - - inline alert -> InlineAlert - -### Component Names to Capitalize: -Accordion, Autocomplete, Badge, Breadcrumbs, Buttons, Calendar, Checkbox, CheckboxGroup, Collections, ColorArea, ColorField, ColorPicker, ColorSlider, ColorSwatch, ColorSwatchPicker, ColorWheel, ComboBox, Date and Time, DateField, DatePicker, DateRangePicker, Dialog, Disclosure, DisclosureGroup, Drag and Drop, DropZone, FileTrigger, Form, InlineAlert, Link, Listbox, ListView, Menu, Meter, Modal, NotificationBadge, NumberField, Picker, ProgressBar, ProgressCircle, RadioGroup, RangeCalendar, SearchField, Select, Slider, StatusLight, Switch, Table, Tabs, TagGroup, TextArea, TextField, TimeField, Toast, ToggleButton, ToggleButtonGroup, Tooltip, Tree, Virtualizer.