Generate `features.json` from `changes.json` by ranking and annotating shipped changes. `features.json` keeps the same schema as `changes.json` and adds optional scoring fields so release notes, docs, and blog posts can apply different cutoffs. Uses the shared `editorial-scoring` rubric. DO NOT USE FOR: regenerating VMR diffs (use generate-changes) or writing final markdown (use release-notes).
features.jsonCreate a scored, reusable feature list from changes.json.
This is the triage stage of the release notes pipeline. It turns the comprehensive manifest of shipped changes into a ranked set of candidates for external communication.
Use editorial-scoring as the canonical rubric. Do not invent a different notion of importance for this step.
changes.json answers what shipped.
features.json answers what is worth talking about, while staying schema-compatible with changes.json so the two files can be joined, diffed, and compared easily.
features.json intentionally mirrors :
changes.jsonrelease_version, release_date, changes, commitsid, repo, product, and commit key structurecommits{} object so cross-file joins still workThe only additions are optional enrichment fields on change entries, such as:
score (number) — higher means more likely to be documentedscore_reason (string) — short explanation of the scorescore_breakdown (object) — optional per-dimension scoring detailsbreaking_changes (bool) — mark changes that users may need to react to, even when they are not headline itemsreverted_by (array<string>) — optional PR URLs or refs that later backed out this changereverts (array<string>) — optional change IDs or PR URLs that this entry reverts or partially revertsThis schema is intentionally loose and can grow as the workflow learns what it needs.
breaking_changes is separate from score. A change might only be a 3 or 4 on reader interest, but still need to be carried forward as a short migration note because it can affect people upgrading.
Use a consistent numeric scale within a file. The recommended starting point is 0-10:
| Score | Reader reaction | Typical outcome |
|---|---|---|
10 | "This is the first feature I'll enable or test." | Lead story |
8+ | "I'm going to use this when I upgrade." | Strong release-note feature |
6+ | "I'm glad I know about this." | Good grouped release-note material |
4+ | "Someone will care; I can look it up later." | Optional mention or grouping candidate |
2+ | "This is a mystery to me." | Usually skip |
0 | Internal gobbledygook | Never document |
See editorial-scoring for the shared rubric and feature-scoring.md for the detailed heuristics.
Apply the 80/20 rule from that document: prefer features that make sense to most upgraders, and only keep niche items when the broader audience can still appreciate why they matter.
changes.jsonDo not invent features from memory, roadmaps, or PR titles found elsewhere. Everything in features.json must trace back to changes.json.
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:
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:
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:
reverted_byreverts, when both are present in the file0 unless you can verify the shipped build still contains the featureDo not promote an item to section-worthy status until this pass is complete.
Look for signals such as:
If a change is important mainly because users need to adjust to it, set breaking_changes: true even if the score stays low. Do not inflate the score just to keep the item visible.
If several individually modest changes cluster around one theme, keep that cluster in mind as you score. Do not inflate each entry, but do preserve the related items so downstream writing can roll them up into one coherent feature writeup. Title prefixes and labels are often useful clues here, such as a set of [browser] runtime changes or multiple "Unsafe evolution" items.
Down-rank or exclude:
editorial-rules.mdIf 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.
features.json already existsIf 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.
Use the existing file as the editorial baseline:
This should feel like a merge operation, not a full restart.
features.jsonThe output typically lives next to changes.json:
release-notes/{major.minor}/preview/{previewN}/features.json
release-notes/{major.minor}/{major.minor.patch}/features.json
Keep the file mechanically friendly:
score optional, not requiredscore_reason brief and evidence-basedrelease-notes uses higher-scored entries to draft markdownrelease-notes can also surface low-scored entries with breaking_changes: true as one-line callouts in a breaking-changes sectionreview-release-notes re-checks the scores against editorial examples and trims over-scored items