--- name: generate-changes description: > Generate `changes.json` for a .NET release milestone by selecting the correct VMR base/head refs and running `release-notes generate changes`. Handles preview-only multi-branch targeting (`main` vs release branches vs tags) and emits the authoritative manifest of what shipped. DO NOT USE FOR: API diffs (use api-diff), API verification (use api-diff-validation), feature scoring (use generate-features), or writing markdown release notes (use release-notes). --- # Generate `changes.json` Produce the authoritative `changes.json` input for a preview, RC, or GA release milestone. This is the **VMR-aware data acquisition stage** of the release notes pipeline: 1. Determine which milestone(s) are active 2. Resolve the correct `--base` and `--head` refs 3. Run `release-notes generate changes` 4. Write `changes.json` into the correct `release-notes/` folder ## When to use - A new preview, RC, or GA milestone needs fresh shipped-change data - The user wants to know whether **multiple preview milestones** are active at once - The release notes branch should be refreshed after the VMR moved forward - `changes.json` is missing, stale, or suspected to have the wrong ref selection ## Preview-only branch targeting This is the subtle part, and it mostly matters for **previews**. Multiple preview milestones can be active simultaneously: ```text Latest shipped in this repo: Preview 3 VMR main: Preview 5 VMR release branch exists: Preview 4 → Generate one `changes.json` for Preview 4 → Generate one `changes.json` for Preview 5 ``` For each target milestone `N`: | Milestone state | Base ref | Head ref | | --------------------------------------- | ----------- | ------------------ | | Tag exists for N | Tag for N-1 | Tag for N | | Release branch exists for N, no tag yet | Tag for N-1 | Release branch tip | | Only on `main` | Tag for N-1 | `main` | **Critical rule:** never use `main` for milestone `N` if `main` has already moved to `N+1`. ## Inputs The user should provide as much of this as they know: - **Target release** — e.g. `.NET 11 Preview 4`, `.NET 10 RC 2`, `.NET 10 GA` - **VMR clone path** — defaults to a local clone of `dotnet/dotnet` - Optionally, the exact refs if they already know them If the user does **not** specify the milestone, infer it from: 1. `release-notes/{version}/releases.json` in this repo 2. `eng/Versions.props` on `main` in the VMR 3. Matching preview tags and release branches in the VMR ## Process ### 1. Determine the floor from `releases.json` Find the latest shipped milestone in this repo. That tells you the lowest in-flight milestone that may need work. ### 2. Inspect the VMR - Read `eng/Versions.props` on `main` to determine the current prerelease iteration - List matching VMR tags for finalized milestones - List matching VMR release branches for stabilizing milestones Use the [VMR structure reference](../release-notes/references/vmr-structure.md) for naming conventions and branch patterns. ### 3. Resolve `--base` and `--head` For each active milestone: - `--base` is the previous shipped milestone tag - `--head` is the milestone tag, release branch tip, or `main`, depending on what exists ### 4. Generate the file ```bash release-notes generate changes \ --base \ --head \ --version "" \ --date "" \ --labels \ --output release-notes///changes.json ``` Examples: ```bash # Preview milestone release-notes generate changes ~/git/dotnet \ --base v11.0.0-preview.3.26210.100 \ --head origin/release/11.0.1xx-preview4 \ --version "11.0.0-preview.4" \ --labels \ --output release-notes/11.0/preview/preview4/changes.json # GA/patch milestone release-notes generate changes ~/git/dotnet \ --base v10.0.7 \ --head v10.0.8 \ --version "10.0.8" \ --output release-notes/10.0/10.0.8/changes.json ``` ## Output contract The output file must follow the shared schema documented in [changes-schema.md](../release-notes/references/changes-schema.md): - top-level `release_version`, `release_date`, `changes`, `commits` - stable `id` values in `repo@shortcommit` format - same authoritative source of truth used by later skills ## Milestone cross-check `changes.json` is derived from a VMR source-manifest diff. That makes it authoritative for *what flowed into the build*, but it is a commit-shaped view, and a feature can be easy to overlook in it. For the repos that maintain preview milestones, sweep the milestone as a **second, independent view** of the same release and reconcile anything that looks like a user-facing feature but never made it into the notes. ```bash gh api -X GET search/issues \ -f q="repo:dotnet/aspnetcore is:pr is:merged milestone:11.0-preview7" \ --jq '.total_count' ``` Milestone discipline varies by repo, so this check only applies for the repos where it is actually maintained: - dotnet/sdk - dotnet/aspnetcore - dotnet/runtime - dotnet/efcore Rules for using it: - **Supplement, never replace.** `changes.json` stays the source of truth. The milestone is a prompt to go back and look, not an alternative manifest. - **The milestone is a subset.** It excludes infrastructure and dependency-flow PRs that legitimately appear in `changes.json`, so the counts will not match and are not meant to. - **Only the missing direction matters.** What is worth acting on is a PR in the milestone that describes a user-facing change and has no corresponding entry in the notes. - **Verify every addition independently before writing it up.** Milestones are applied by automation, so they are usually right — but they can be changed or applied incorrectly by hand afterwards, and a milestoned PR can still be reverted. Treat a milestone as reliable evidence of where to look and strong but not conclusive evidence that the change shipped. Before promoting anything found this way, confirm it appears in `changes.json` and exists in the build (see [`api-verification.md`](../release-notes/references/api-verification.md) and [`validate-code-samples`](../validate-code-samples/SKILL.md)). The expected outcome is that it checks out; the point is to catch the occasional one that does not. - **Confirm the milestone exists before relying on its absence.** Repos without `11.x` milestones will return zero results, which means "not tracked here", not "nothing shipped". Once `changes.json` exists, the next step is usually `generate-features`.