Skip to content
4 changes: 2 additions & 2 deletions .github/skills/api-diff-validation/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ the real ref packs from the build's feed, so it answers "does this API exist in
Read `build-metadata.json` for the target. Use its `nuget.source` as `$FEED` and
the ref-pack versions (e.g. `Microsoft.NETCore.App.Ref@<version>`) as `$VER`. For
behavior that requires the SDK (rare here), install it side-by-side and scoped —
see the `validate-code-samples` skill. Do **not** trust a machine-wide SDK for
preview work.
see the [`validate-code-samples`](../validate-code-samples/SKILL.md) skill. Do
**not** trust a machine-wide SDK for preview work.

## 2. Verify APIs exist with dotnet-inspect

Expand Down
40 changes: 40 additions & 0 deletions .github/skills/generate-changes/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,4 +126,44 @@ The output file must follow the shared schema documented in [changes-schema.md](
- 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:
Comment thread
danroth27 marked this conversation as resolved.

- **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`.
10 changes: 6 additions & 4 deletions .github/skills/release-notes/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
name: release-notes
description: Generate and maintain .NET release notes from `features.json`. Uses `generate-changes` for authoritative shipped-change data, `generate-features` for scoring/triage, `update-existing-branch` for incremental reruns on populated branches, `editorial-scoring` for the shared rubric, `api-diff` to generate API diff reports and `api-diff-validation` for API verification, and a multi-model `review-release-notes` pass for final editorial QA.
compatibility: Requires GitHub MCP server or gh CLI for cross-repo queries. Pairs with the generate-changes, generate-features, update-existing-branch, editorial-scoring, api-diff, api-diff-validation, and review-release-notes skills. Claude Opus 4.6 is the default workflow model; the preferred final reviewer pair is Claude Opus 4.6 + GPT-5.4 for broader editorial feedback.
description: Generate and maintain .NET release notes from `features.json`. Uses `generate-changes` for authoritative shipped-change data, `generate-features` for scoring/triage, `update-existing-branch` for incremental reruns on populated branches, `editorial-scoring` for the shared rubric, `api-diff` to generate API diff reports, `api-diff-validation` for API verification, `validate-code-samples` to build and run the documented claims against the milestone build, and a multi-model `review-release-notes` pass for final editorial QA.
compatibility: Requires GitHub MCP server or gh CLI for cross-repo queries. Pairs with the generate-changes, generate-features, update-existing-branch, editorial-scoring, api-diff, api-diff-validation, validate-code-samples, and review-release-notes skills. Claude Opus 4.6 is the default workflow model; the preferred final reviewer pair is Claude Opus 4.6 + GPT-5.4 for broader editorial feedback.
---

# .NET Release Notes
Expand All @@ -17,8 +17,9 @@ This skill is the **editorial writing stage** of the pipeline. It turns a scored
3. `update-existing-branch` handles incremental reruns when a milestone branch already exists, merging deltas instead of restarting from scratch
4. `api-diff-validation` / `dotnet-inspect` verifies public APIs and confirms suspect features still exist in the shipped build
5. `release-notes` writes curated markdown using the higher-value entries from `features.json`
6. `review-release-notes` runs a final multi-model editorial QA pass against the scoring rubric and examples
7. Output is a set of pull requests per release milestone in dotnet/core: a base PR that holds shared metadata (`changes.json`, `features.json`, `README.md`, `build-metadata.json`) and one PR per component file. Each component PR targets the base branch so component teams review and edit their file in isolation. See [`pr-layout.md`](references/pr-layout.md) for the full layout and naming scheme.
6. `validate-code-samples` builds and runs the documented claims against the milestone build, catching what static API verification cannot see
7. `review-release-notes` runs a final multi-model editorial QA pass against the scoring rubric and examples
8. Output is a set of pull requests per release milestone in dotnet/core: a base PR that holds shared metadata (`changes.json`, `features.json`, `README.md`, `build-metadata.json`) and one PR per component file. Each component PR targets the base branch so component teams review and edit their file in isolation. See [`pr-layout.md`](references/pr-layout.md) for the full layout and naming scheme.

## Local testing (no PRs)

Expand All @@ -45,4 +46,5 @@ handling review comments without clobbering human edits.
- [format-template.md](references/format-template.md) — markdown document structure
- [editorial-rules.md](references/editorial-rules.md) — tone, attribution, naming
- [api-verification.md](references/api-verification.md) — using dotnet-inspect to verify APIs
- [../validate-code-samples/SKILL.md](../validate-code-samples/SKILL.md) — building and running the documented claims against the milestone build
- [examples/](references/examples/) — curated examples from previous releases, organized by component. **Read the examples for your component before writing.** The [examples/README.md](references/examples/README.md) lists 12 editorial principles derived from what works and what doesn't in past release notes.
38 changes: 38 additions & 0 deletions .github/skills/release-notes/references/api-verification.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,32 @@ You do NOT need to verify:
- General concepts (e.g., "Zstandard compression" as a concept vs `ZstandardCompressionProvider` as a type)
- CLI flags (e.g., `dotnet test --artifacts-path`)

## Verify the change shipped in *this* milestone

API verification answers "does this exist in the build". It does not answer "did this change in this
release" — and those come apart in a way that is easy to miss.

A behavior observed in the Preview 7 build was traced to a fix that shipped in **Preview 3**. It was
real, it was verifiable, and documenting it in the Preview 7 notes would have been wrong. Anything
still present from an earlier release will verify perfectly against the current build.

The rule: **every documented change must trace to a PR associated with this milestone**, not merely be observably
true in the build.

1. **`changes.json` membership is the primary test.** If a change has no entry, it did not flow into
this milestone. Confirm before writing it up.
2. **Check the PR's merge date and milestone** where the repo maintains them —
`dotnet/aspnetcore`, `dotnet/runtime`, `dotnet/sdk`, and `dotnet/efcore` do;
`dotnet/roslyn` and `dotnet/razor` do not (see
[`generate-changes`](../../generate-changes/SKILL.md)). For repos without milestones,
`changes.json` membership is the only available provenance signal.
Comment thread
danroth27 marked this conversation as resolved.
3. **Be suspicious of anything discovered by testing rather than from `changes.json`.** Finding a
nice behavior while validating samples is a good way to find *previous* releases' features. Trace
it to a PR associated with this milestone before promoting it.

This is the mirror image of the revert check below: a revert means something in `changes.json` is not
in the build, and stale provenance means something in the build is not in `changes.json`.

## What to do when verification fails

If `dotnet-inspect` can't find a type:
Expand All @@ -128,4 +154,16 @@ If `dotnet-inspect` can't find a type:

When in doubt, describe the feature without naming specific types and link to the PR. A correct prose description is always better than a wrong code sample.

## Static verification is not enough

Everything above confirms that a *managed symbol exists*. Three important classes of error survive it:

- **JavaScript and browser-facing APIs** are invisible to `dotnet-inspect` entirely.
- **Defaults and polarity** are not checked by a name lookup. A rename from `EnableX` to `DisableX`
passes an existence check while inverting the meaning of the documented claim.
- **Runtime behavior** — a sample can compile and still fail on request.

After the notes are drafted, run [`validate-code-samples`](../../validate-code-samples/SKILL.md) to
build and execute the documented claims against the milestone build.

For scoring and feature selection, this is also a **quality bar**: if a change only looks interesting because it seems to add a new API, but you cannot identify that API in the public surface, score it down sharply. That usually means it is internal plumbing, a refactor, or an existing niche surface getting maintenance rather than a real release-note feature.
13 changes: 13 additions & 0 deletions .github/skills/release-notes/references/component-mapping.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,19 @@ Each release notes file gets its own per-component branch named `release-notes/{

The agent assigns each component PR to its default assignee(s) when opening the PR (`gh pr create --assignee ...`), so the right team sees it in their review queue.

### Check that the owners are still current

This table is maintained by hand and goes stale as people change roles. Before opening the PRs, confirm each assignee can actually be assigned:

```bash
gh api repos/dotnet/core/assignees/<user> --silent # exit 0 = assignable, 404 = not
```

A 404 means the mapping is stale, not that the call failed — GitHub will accept the PR creation and
silently drop that assignee. Treat it as a prompt to confirm the current owner and update this file,
rather than working around it. See [`pr-layout.md`](pr-layout.md) for the verification step after
the PRs are created.

### Components contributed out-of-band (not in the VMR)

These components ship with .NET but live outside the VMR, so `changes.json` won't contain entries for them. The agent still creates a stub PR for each so the component team can push their own content (or close the PR if there is nothing noteworthy this milestone).
Expand Down
26 changes: 22 additions & 4 deletions .github/skills/release-notes/references/editorial-rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,18 +108,36 @@ Thank you contributors! ❤️

## Bug fixes section

After features but before community contributors, include a grouped bug fix summary when there are noteworthy fixes. When citing the source work, use linked `org/repo #number` references with a space before `#`:
After features but before community contributors, include a bug fix summary when there are noteworthy fixes.

Group the fixes by namespace or area, with a **flat list of one bullet per fix** under each group. Each bullet is a single markdown link whose target is the PR that made the fix and whose display text is a cleaned-up version of the PR or issue title:

```markdown
## Bug fixes

- **System.Net.Http**
- Fixed authenticated proxy credential handling ([dotnet/runtime #123363](https://github.com/dotnet/runtime/issues/123363))
- [Fix authenticated proxy credential handling](https://github.com/dotnet/runtime/pull/123363)
- **System.Collections**
- Fixed integer overflow in ImmutableArray range validation ([dotnet/runtime #124042](https://github.com/dotnet/runtime/pull/124042))
- [Fix integer overflow in ImmutableArray range validation](https://github.com/dotnet/runtime/pull/124042)
```

Group by namespace/area. Don't include test-only, CI, or infra fixes.
Rules:

- **One fix per bullet.** If a single behavior was fixed by several PRs, give each PR its own bullet rather than bundling links into one sentence.
- **Link to the PR**, not the issue, even when the issue has the better title. Take the wording from whichever reads more clearly and point the link at the PR.
- **Group by namespace or area**, exactly one level deep. Use the namespace where the component has one (`System.Text.Json`), otherwise a feature area (`Blazor`, `JIT / code generation`).
- **No trailing prose.** The bullet is the link and nothing else — no explanation after it, and no `org/repo #number` citation, since the link already carries that.

This overrides the general `org/repo #number` citation style used elsewhere in the notes; that style still applies in feature sections and breaking changes.

Cleaning up the title means making it read as a plain description of the fix:

- Drop branch and process prefixes (`[release/11.0]`, `[main]`, backport markers).
- Drop trailing issue references (`(#12345)`, `Fixes #123`).
- Expand cryptic shorthand into words a reader outside the team would recognize.
- Keep it a single line. If the title is unintelligible without context, rewrite it as a short description of the fix rather than pasting the raw title.

Don't include test-only, CI, or infra fixes.

## Preview-to-preview feedback fixes

Expand Down
38 changes: 36 additions & 2 deletions .github/skills/release-notes/references/format-template.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ Standard document structure for .NET release notes markdown files.

## Bug fixes

- **Category** — Fix description
- **Namespace or area**
- [Cleaned up PR or issue title](https://github.com/<owner>/<repo>/pull/NNNNN)

## Community contributors

Expand Down Expand Up @@ -87,9 +88,42 @@ Known component docs links:
2. **One paragraph of context** — what the feature does and why it matters in concrete terms, with PR/issue links; avoid inferred feelings or marketing-style claims
3. **Code sample** — show the feature in use
4. **Feature ordering** — highest customer impact first
5. **Breaking changes near the end** — low-score entries with `breaking_changes: true` usually belong in a short section before Bug fixes, not as full feature sections
5. **Breaking changes near the end** — low-score entries with `breaking_changes: true` usually belong in a short section before Bug fixes, not as full feature sections. In preview notes this section is also the upgrade guidance for readers coming from the previous preview — see below
6. **Preview feature callout** — when a feature is listed in `release-notes/features.json`, start its section with the standard blockquote callout from that file

## Breaking changes

Preview notes are read mostly by people who are already running the *previous* preview. For them the
most valuable content is often not the new feature, but the thing that stops their existing code
from building or working after they move to this build.

Because each preview's Breaking changes section covers what changed *in that milestone*, it is
already the preview-to-preview upgrade story. Write it for the reader doing that upgrade rather than
as an abstract list of incompatibilities with the last GA release.

Cover these when they apply. They tend to surface only when an existing project is actually upgraded
to the new build, which is one of the reasons
[`validate-code-samples`](../../validate-code-samples/SKILL.md) runs against a maintained sample set:

- **Renamed APIs**, especially renames that invert meaning (`EnableX` becoming `DisableX`). Show the
before and after, and state the new default explicitly.
- **New analyzer diagnostics that fire on previously clean code.** Code that built without warnings
on the last preview and now reports diagnostics is an upgrade issue even though nothing in the
user's code changed. Name the diagnostic IDs.
- **Changed defaults**, where existing code keeps compiling but behaves differently.

**Removed or replaced workarounds** are the one upgrade item that is not a breaking change. If a bug
that required a workaround is now fixed, say so with the fix in Bug fixes, so users can delete the
workaround rather than carrying it forward.

Do not turn this into a changelog of everything that moved. Include an item only when a user
upgrading from the previous preview would otherwise hit a build error, a new warning, or a silent
behavior change.

Attribute these to the milestone that actually changed them. A fix that shipped two previews ago is
not upgrade guidance for this one — see
[api-verification.md](api-verification.md) for the provenance rule.

## Issue and PR references

Always use markdown links with the `{org}/{repo} #{number}` format, with a space before `#`:
Expand Down
Loading
Loading