Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/aw/actions-lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@
"repo": "github/gh-aw-actions/setup",
"version": "v0.65.6",
"sha": "31130b20a8fd3ef263acbe2091267c0aace07e09"
},
"github/gh-aw-actions/setup@v0.67.0": {
"repo": "github/gh-aw-actions/setup",
"version": "v0.67.0",
"sha": "cde65c546c2b0f6d3f3a9492a04e6687887c4fe8"
}
}
}
1 change: 1 addition & 0 deletions .github/skills/editorial-scoring/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ This is how foundational but initially niche work can still earn a good score.
- Build or infrastructure churn
- Refactoring with no user-visible effect
- VMR sync noise, dependency updates, or repo automation
- Product-boundary mismatch — repo-adjacent IDE/editor/design-time tooling work when the notes are for a different product surface
- Highly specialized implementation details that read like internal jargon
- Features that only matter after several stacked conditions are true (for example: uses single-file publish **and** cares deeply about startup **and** is willing to do extra training/tuning)
- PRs with very thin descriptions where the only concrete signal is an internal runtime/tooling term like cDAC
Expand Down
10 changes: 5 additions & 5 deletions .github/skills/generate-changes/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name: generate-changes
description: >
Generate `changes.json` for a .NET release milestone by selecting the correct
VMR base/head refs and running `release-notes-gen generate changes`. Handles
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
verification/diffs (use api-diff), feature scoring (use generate-features),
Expand All @@ -17,7 +17,7 @@ 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-gen generate changes`
3. Run `release-notes generate changes`
4. Write `changes.json` into the correct `release-notes/` folder

## When to use
Expand Down Expand Up @@ -90,7 +90,7 @@ For each active milestone:
### 4. Generate the file

```bash
release-notes-gen generate changes <vmr-clone-path> \
release-notes generate changes <vmr-clone-path> \
--base <previous-release-tag> \
--head <current-release-ref> \
--version "<release-version>" \
Expand All @@ -103,15 +103,15 @@ Examples:

```bash
# Preview milestone
release-notes-gen generate changes ~/git/dotnet \
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-gen generate changes ~/git/dotnet \
release-notes generate changes ~/git/dotnet \
--base v10.0.7 \
--head v10.0.8 \
--version "10.0.8" \
Expand Down
53 changes: 50 additions & 3 deletions .github/skills/generate-features/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ The only additions are **optional enrichment fields** on change entries, such as
- `score_reason` (`string`) — short explanation of the score
- `score_breakdown` (`object`) — optional per-dimension scoring details
- `breaking_changes` (`bool`) — mark changes that users may need to react to, even when they are not headline items
- `reverted_by` (`array<string>`) — optional PR URLs or refs that later backed out this change
- `reverts` (`array<string>`) — optional change IDs or PR URLs that this entry reverts or partially reverts

This schema is intentionally loose and can grow as the workflow learns what it needs.

Expand Down Expand Up @@ -65,7 +67,36 @@ Apply the **80/20 rule** from that document: prefer features that make sense to

Do **not** invent features from memory, roadmaps, or PR titles found elsewhere. Everything in `features.json` must trace back to `changes.json`.

### 2. Score what shipped
### 2. Resolve revert state before scoring

Do a quick mechanical revert pass before assigning any positive scores. This step
exists because the original PR may appear in the current `changes.json` while the
revert lands in a later preview and therefore does **not** appear in the same file.

Start with the obvious cases:

```bash
jq -r '.changes[] | select(.title | test("(?i)^(partial(ly)?\\s+)?revert\\b|\\bback out\\b")) | [.repo, .title, .url] | @tsv' changes.json
```

Then, for each candidate you might promote into the draft, search the source repo
for later merged PRs that explicitly revert or back out that PR number or URL:

```bash
gh search prs --repo dotnet/<repo> --state merged \
"\"This reverts https://github.com/dotnet/<repo>/pull/<number>\" OR \"revert <number>\" OR \"back out <number>\"" \
--json number,title,mergedAt,url
```

When a revert is found:

- annotate the original entry with `reverted_by`
- annotate the revert entry with `reverts`, when both are present in the file
- set the original score to `0` unless you can verify the shipped build still contains the feature

Do **not** promote an item to section-worthy status until this pass is complete.

### 3. Score what shipped

Look for signals such as:

Expand All @@ -85,14 +116,30 @@ Down-rank or exclude:
- infra and dependency churn
- test-only changes
- internal refactors with no user-facing impact
- items that violate the product-boundary rule in `editorial-rules.md`
- reverts and partial work that did not survive into the build
- items that mostly require insider knowledge to understand why they matter

### 3. Use API evidence to refine the score
### 4. Use API evidence to refine the score

If a change depends on public APIs, use `api-diff` / `dotnet-inspect` to confirm the API exists in the actual build. Missing or reverted APIs should be scored down or excluded.

### 4. Write `features.json`
### 5. Merge incrementally when `features.json` already exists

If the milestone branch already has a `features.json`, do **not** throw that
work away just because `changes.json` was regenerated. For the full rerun
workflow, follow [`update-existing-branch`](../update-existing-branch/SKILL.md).

Use the existing file as the editorial baseline:

- preserve prior scores and annotations for unchanged entries
- score only the newly added or materially changed IDs
- revisit older scores only when new evidence or review feedback justifies it
- keep useful human edits instead of flattening them back to a machine pass

This should feel like a **merge** operation, not a full restart.

### 6. Write `features.json`

The output typically lives next to `changes.json`:

Expand Down
24 changes: 17 additions & 7 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, `editorial-scoring` for the shared rubric, `api-diff`/`dotnet-inspect` 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, editorial-scoring, api-diff, 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`/`dotnet-inspect` 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, 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 @@ -13,16 +13,26 @@ This skill is the **editorial writing stage** of the pipeline. It turns a scored
## How it works

1. `generate-changes` diffs `source-manifest.json` between VMR refs to produce `changes.json`
2. `generate-features` reads `changes.json` and emits `features.json` with optional scores using the shared `editorial-scoring` rubric
3. `api-diff` / `dotnet-inspect` verifies public APIs and catches missed reverts
4. `release-notes` writes curated markdown using the higher-value entries from `features.json`
5. `review-release-notes` runs a final multi-model editorial QA pass against the scoring rubric and examples
6. Output is one PR per release milestone in dotnet/core, maintained incrementally
2. `generate-features` reads `changes.json`, resolves revert/backout relationships, and emits `features.json` with optional scores using the shared `editorial-scoring` rubric
3. `update-existing-branch` handles incremental reruns when a milestone branch already exists, merging deltas instead of restarting from scratch
4. `api-diff` / `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 one PR per release milestone in dotnet/core, maintained incrementally

## Existing-branch reruns

When the milestone branch already exists and contains drafted markdown, invoke
[`update-existing-branch`](../update-existing-branch/SKILL.md). That shared
skill is the canonical playbook for refreshing `changes.json`, merging the delta
into `features.json`, integrating new material into existing sections, and
handling review comments without clobbering human edits.

## Reference documents

- [quality-bar.md](references/quality-bar.md) — what good release notes look like
- [vmr-structure.md](references/vmr-structure.md) — VMR branches, tags, source-manifest.json
- [../update-existing-branch/SKILL.md](../update-existing-branch/SKILL.md) — how to refresh a populated milestone branch incrementally
- [changes-schema.md](references/changes-schema.md) — the shared `changes.json` / `features.json` schema
- [../editorial-scoring/SKILL.md](../editorial-scoring/SKILL.md) — the reusable scoring rubric and cut guidance
- [feature-scoring.md](references/feature-scoring.md) — how to score and cut features
Expand Down
6 changes: 3 additions & 3 deletions .github/skills/release-notes/references/api-verification.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ When writing release notes for Preview N, the locally installed SDK is often Pre

### Step 1: Generate build metadata

`release-notes-gen generate build-metadata` reads the VMR's version info and queries the nightly NuGet feed to find the correct package versions:
`release-notes generate build-metadata` reads the VMR's version info and queries the nightly NuGet feed to find the correct package versions:

```bash
release-notes-gen generate build-metadata ~/git/dotnet \
release-notes generate build-metadata ~/git/dotnet \
--base v11.0.0-preview.2.26159.112 \
--head origin/release/11.0.1xx-preview3 \
--output build-metadata.json
Expand Down Expand Up @@ -122,7 +122,7 @@ If `dotnet-inspect` can't find a type:

1. **Check your package version first** — are you querying a Preview N-1 package while writing Preview N notes? Find the correct version (see above).
2. **Named differently** — search with a broader pattern (`find "*Zstd*"` instead of the exact name).
3. **Reverted** — check `changes.json` for a revert PR. If the API was added and then reverted in the same preview, it didn't ship. Don't document it.
3. **Reverted or backed out** — first scan `changes.json` for revert titles, then search GitHub for a later merged PR that references reverting the original PR number or URL. The revert may live in the next preview and therefore be absent from the current `changes.json`. If the API or behavior is gone in the final build, don't document the original feature.
4. **Internal** — the type may not be public. Don't document internal types in release notes.
5. **Read the PR tests** — the PR's test files are ground truth. Tests compile and run against the actual API surface. Derive code samples from test assertions rather than guessing type names.

Expand Down
15 changes: 13 additions & 2 deletions .github/skills/release-notes/references/changes-schema.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Shared Schema for `changes.json` and `features.json`

Reference for the `changes.json` file produced by `release-notes-gen generate changes`, and for the derived `features.json` file used by downstream editorial skills. One file per release milestone.
Reference for the `changes.json` file produced by `release-notes generate changes`, and for the derived `features.json` file used by downstream editorial skills. One file per release milestone.

## Overview

Expand Down Expand Up @@ -83,6 +83,8 @@ writing stage to keep long-running features consistent across the release.
- `score_reason` (`string`) — optional short explanation for the score
- `score_breakdown` (`object`) — optional structured scoring details
- `breaking_changes` (`bool`) — optional flag for changes users may need to react to, even if they are not headline features
- `reverted_by` (`array<string>`) — optional list of PR URLs or refs that later reverted or backed out this change
- `reverts` (`array<string>`) — optional list of change IDs or PR URLs this entry reverts or partially reverts

The `product` field is derived from the repo-level [component mapping](component-mapping.md). Infra repos like `arcade` and `symreader` have no `product` field. The `repo` field always matches the VMR manifest path.

Expand All @@ -100,7 +102,7 @@ The `commit` field is the VMR codeflow commit in `dotnet/dotnet` that synced thi

## Conventions

- **No nulls** — required fields are always present. Optional fields (`product`, `labels`, `local_repo_commit`, `score`, `score_reason`, `score_breakdown`, `breaking_changes`) may be absent. Use `""` for missing strings, `0` for missing integers.
- **No nulls** — required fields are always present. Optional fields (`product`, `labels`, `local_repo_commit`, `score`, `score_reason`, `score_breakdown`, `breaking_changes`, `reverted_by`, `reverts`) may be absent. Use `""` for missing strings, `0` for missing integers.
- **Naming** — `snake_case_lower` for JSON fields, `kebab-case-lower` for file names and repo slugs.
- **Public URLs only** — every URL must resolve publicly.
- **Commit URLs use `.diff` form** — for machine consumption.
Expand All @@ -119,6 +121,15 @@ That means a change can be:

In practice, a `score` around `0-4` with `breaking_changes: true` usually means **one line in a "Breaking changes" section**, not a full feature writeup.

## Revert annotations

`changes.json` is the raw shipped-change manifest. During editorial triage,
`features.json` may add `reverted_by` and `reverts` when the agent finds that a
candidate feature was later backed out or partially reverted.

These annotations exist so downstream skills can suppress the original headline
without losing the provenance that explains why the item was cut.

## Example

```json
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ The `runtime` manifest entry covers both Libraries and Runtime. When writing mar
- **Razor → ASP.NET Core** — `dotnet/razor` PRs go in `aspnetcore.md`
- **Templating → SDK** — `dotnet/templating` PRs go in `sdk.md`
- **Roslyn** — covers both C# and Visual Basic. Check PR labels/titles to determine language. Produce `csharp.md` (and `visualbasic.md` if VB-specific features exist).
- **Apply the product-boundary rule** — Razor editor code actions, language-server behavior, and other IDE-only experiences are usually tooling stories, not ASP.NET Core product notes. See `editorial-rules.md`.

### Infrastructure components (skip for release notes)

Expand Down
3 changes: 2 additions & 1 deletion .github/skills/release-notes/references/editorial-rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ Tone, attribution, and content guidelines for .NET release notes.
- **Shared audience filter** — apply the 80/20 rule from `editorial-scoring`; don't redefine a competing threshold here. Keep narrower items only when the broader audience can still see why they matter.
- **The two-sentence test** — if you can only write two sentences about a feature, it's probably an engineering fix, not a feature. Cut it. A community contribution or breaking change can lift a borderline entry, but "fixed an internal bug that happened to be visible" is not a feature.
- **Headlines should convey value** — a heading like "GC regions on macOS" doesn't tell the reader whether this is good or bad. Prefer headings that hint at the benefit: "GC regions enabled on macOS" or "Server GC memory model now available on macOS."
- **Product-boundary rule** — exclude higher-level IDE, editor, or design-time tooling features from product notes unless the release is specifically about that tooling surface. For example, a Razor editor code action should not be presented as an ASP.NET Core feature just because it is adjacent to the web stack.
- **TODO for borderline entries** — when a feature might deserve inclusion but you lack data to justify it (benchmark numbers, real-world impact, user demand), keep the entry but add an HTML `<!-- TODO -->` comment asking for the missing information. This is better than silently including a vague claim or silently cutting something that might matter. The TODO should state what's needed and link to the PR where the data might live.
- **Breaking changes are separate from hype** — a breaking change can be important even when it is not exciting. Keep the score honest; use `breaking_changes: true` to preserve a short callout instead of inflating the item into a headline feature.
- **Clusters can be stronger than the parts** — several related low-score items can justify one section when together they tell a clear story. Keep the individual scores honest, then merge them into one writeup instead of emitting several weak mini-features. Good examples include a group of "Unsafe evolution" changes or multiple runtime entries prefixed with `[browser]`.
Expand All @@ -90,7 +91,7 @@ Thank you [@username](https://github.com/username) for this contribution!

### Community contributors section

At the bottom of each component's notes, list ALL external contributors — not just those with documented features. Use the `community-contribution` label to identify them.
At the bottom of each component's notes, list ALL external contributors — not just those with documented features. Any community contributor with **one or more merged PRs** in the milestone should get a mention exactly once in the **Community contributors** section, even if none of their work was promoted into a feature writeup. Use the `community-contribution` label as a strong signal, but do not rely on it exclusively if the merged PR history shows a clear external contribution.

**Vet the list** — the `community-contribution` label is sometimes wrong. Exclude usernames containing `-msft`, `-microsoft`, or other Microsoft suffixes. When in doubt about whether someone is a Microsoft employee, leave them out of the community list.

Expand Down
1 change: 1 addition & 0 deletions .github/skills/release-notes/references/feature-scoring.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ matter during .NET release-note triage.
- Test-only or infra-only work
- Mechanical refactors with no user-facing impact
- Churn in dependencies or tooling internals
- **Product-boundary mismatch** — apply the product-boundary rule from `editorial-rules.md`; score these to cut-level unless the release is explicitly about that tooling surface
- Reverts, partial implementations, or APIs that do not appear in the actual build
- Changes too small or obscure to justify external attention
- **Stacked audience gates** — if the reader has to care about A, then B, then be willing to do C, the addressable audience shrinks at each step and the score should usually drop hard
Expand Down
2 changes: 1 addition & 1 deletion .github/skills/release-notes/references/quality-bar.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ What good .NET release notes look like. This is the north star — when in doubt

### changes.json — comprehensive and mechanical

Every PR that shipped gets an entry. No editorial judgment — if the `release-notes-gen generate changes` tool found it in the source-manifest diff, it goes in. This is the machine-readable record of what shipped.
Every PR that shipped gets an entry. No editorial judgment — if the `release-notes generate changes` tool found it in the source-manifest diff, it goes in. This is the machine-readable record of what shipped.

Quality criteria:

Expand Down
Loading
Loading