diff --git a/.github/skills/breaking-changes/SKILL.md b/.github/skills/breaking-changes/SKILL.md new file mode 100644 index 000000000..425a997a5 --- /dev/null +++ b/.github/skills/breaking-changes/SKILL.md @@ -0,0 +1,71 @@ +--- +name: breaking-changes +description: Audit pull requests for breaking changes in the C# MCP SDK. Examines PR descriptions, review comments, and diffs to identify API and behavioral breaking changes, then reconciles labels with user confirmation. Use when asked to audit breaking changes, check for breaking changes, or review a set of PRs for breaking impact. +compatibility: Requires gh CLI with repo access and GitHub API access for PR details, review history, and labels. +--- + +# Breaking Change Audit + +Audit pull requests in the `modelcontextprotocol/csharp-sdk` repository for breaking changes. This skill examines a range of commits, identifies API and behavioral breaking changes, assesses their impact, reconciles `breaking-change` labels, and returns structured results. + +## Input + +The user provides a commit range in any of these forms: +- `tag..HEAD` (e.g. `v0.8.0-preview.1..HEAD`) +- `tag..tag` (e.g. `v0.8.0-preview.1..v0.9.0-preview.1`) +- `sha..sha` +- `tag..sha` or `sha..HEAD` + +If no range is provided, ask the user to specify one. + +Use the GitHub API to get the full list of PRs merged within the specified range. + +## Process + +### Step 1: Examine Every PR + +For each PR in the range, study: +- PR description and linked issues +- Full review and comment history +- Complete code diff + +Look for both categories of breaking changes: +- **API (compile-time)** — changes to public type signatures, parameter types, return types, removed members, sealed types, new obsoletion attributes, etc. +- **Behavioral (runtime)** — new/changed exceptions, altered return values, changed defaults, modified event ordering, serialization changes, etc. + +See [references/classification.md](references/classification.md) for the full classification guide, including SDK-specific versioning policies (pre-1.0 preview flexibility, experimental APIs, obsoletion lifecycle, and spec-driven changes) that influence how breaks are assessed. + +### Step 2: Assess Impact + +For each identified breaking change, assess: +- **Breadth** — how many consumers are likely affected (widely-used type vs. obscure API) +- **Severity** — compile-time break (immediate build failure) vs. behavioral (subtle runtime difference) +- **Migration** — straightforward fix vs. significant code changes required + +This assessment informs how breaking changes are ordered when presented (most impactful first). + +### Step 3: Reconcile Labels + +Compare findings against existing `breaking-change` labels on PRs. + +Present mismatches to the user interactively: + +- **Unlabeled but appears breaking** → explain why the PR appears breaking, ask user to confirm. If confirmed: apply the `breaking-change` label and ask the user whether to comment on the PR explaining the addition. +- **Labeled but does not appear breaking** → explain why, ask user to confirm removal. If confirmed: remove the label and ask the user whether to comment on the PR explaining the removal. + +### Step 4: Present Results + +Present the final list of confirmed breaking changes, sorted from most impactful to least, with: +- PR number and title +- Classification (API or behavioral) +- Impact assessment summary +- 1-2 bullet description of what breaks and migration guidance + +## Output + +The audit produces a structured list of breaking changes that can be consumed by other skills (e.g. the release-notes skill) or presented directly to the user. + +Each entry contains: +- PR number and description +- Impact ranking (most → least impactful) +- Detail bullets describing the break and migration path diff --git a/.github/skills/breaking-changes/references/classification.md b/.github/skills/breaking-changes/references/classification.md new file mode 100644 index 000000000..c638f81c2 --- /dev/null +++ b/.github/skills/breaking-changes/references/classification.md @@ -0,0 +1,120 @@ +# Breaking Change Classification Guide + +This guide defines how to identify and classify breaking changes in the C# MCP SDK. It is derived from the [dotnet/runtime breaking change guidelines](https://github.com/dotnet/runtime/blob/main/docs/coding-guidelines/breaking-changes.md). + +## Two Categories of Breaking Changes + +### API Breaking Changes (Compile-Time) +Changes that alter the public API surface in ways that break existing code at compile time: + +- **Renaming or removing** a public type, member, or parameter +- **Changing the return type** of a method or property +- **Changing parameter types, order, or count** on a public method +- **Sealing** a type that was previously unsealed (when it has accessible constructors) +- **Making a virtual member abstract** +- **Adding `abstract` to a member** when the type has accessible constructors and is not sealed +- **Removing an interface** from a type's implementation +- **Changing the value** of a public constant or enum member +- **Changing the underlying type** of an enum +- **Adding `readonly` to a field** +- **Removing `params` from a parameter** +- **Adding/removing `in`, `out`, or `ref`** parameter modifiers +- **Renaming a parameter** (breaks named arguments and late-binding) +- **Adding the `[Obsolete]` attribute** or changing its diagnostic ID +- **Adding the `[Experimental]` attribute** or changing its diagnostic ID +- **Removing accessibility** (making a public/protected member less visible) + +### Behavioral Breaking Changes (Runtime) +Changes that don't break compilation but alter observable behavior: + +- **Throwing a new/different exception type** in an existing scenario (unless it's a more derived type) +- **No longer throwing an exception** that was previously thrown +- **Changing return values** for existing inputs +- **Decreasing the range of accepted values** for a parameter +- **Changing default values** for properties, fields, or parameters +- **Changing the order of events** being fired +- **Removing the raising of an event** +- **Changing timing/order** of operations +- **Changing parsing behavior** and throwing new errors +- **Changing serialization format** or adding new fields to serialized types + +## Classification Buckets + +### Bucket 1: Clear Public Contract Violation +Obvious breaking changes to the public API shape. **Always flag these.** + +### Bucket 2: Reasonable Grey Area +Behavioral changes that customers would have reasonably depended on. **Flag and discuss with user.** + +### Bucket 3: Unlikely Grey Area +Behavioral changes that customers could have depended on but probably wouldn't (e.g., corner case corrections). **Flag with lower confidence.** + +### Bug Fixes (Exclude) +Changes that correct incorrect behavior, fix spec compliance, or address security issues are **not breaking changes** even if they alter observable behavior. Examples: +- Fixing encoding to match a specification requirement +- Correcting a logger category or metric name that was wrong +- Fixing exception message leaks that were a security concern +- Moving data to the correct location per protocol spec evolution +- Setting a flag that should have been set automatically (e.g., `IsError` for error content) +- Returning a more specific/informative exception for better diagnostics + +If a change is primarily a bug fix or spec compliance correction, exclude it from the breaking changes list even though the observable behavior changes. + +### Bucket 4: Clearly Non-Public +Changes to internal surface or behavior (e.g., internal APIs, private reflection). **Generally not flagged** unless they could affect ecosystem tools. + +## SDK Versioning Policy + +The classification rules above are derived from the dotnet/runtime breaking change guidelines, but the MCP SDK has its own versioning policy (see `docs/versioning.md`) that provides additional context for classification decisions. + +### Pre-1.0 Preview Status + +Prior to a stable 1.0.0 release, the SDK is in preview and breaking changes can be introduced without prior notice. This does **not** change how breaks are classified — they should still be flagged, labeled, and documented — but it affects the **severity assessment**. Preview consumers expect breaks, so migration guidance matters more than avoidance. + +### Experimental APIs + +APIs annotated with `[Experimental]` (using `MCP`-prefixed diagnostic codes) can change at any time, including within PATCH or MINOR updates. Changes to experimental APIs should still be **noted** in the audit, but classified as **Bucket 3 (Unlikely Grey Area)** or lower unless the API has been widely adopted despite its experimental status. + +### Obsoletion Lifecycle + +The SDK follows a three-step obsoletion process: + +1. **MINOR update**: API marked `[Obsolete]` producing _build warnings_ with migration guidance +2. **MAJOR update**: API marked `[Obsolete]` producing _build errors_ (API throws at runtime) +3. **MAJOR update**: API removed entirely (expected to be rare) + +When auditing, classify each step appropriately: +- Step 1 (adding `[Obsolete]` warning) → API breaking change (new build warning) +- Step 2 (escalating to error) → API breaking change (previously working code now fails) +- Step 3 (removal) → API breaking change; migration guidance should note prior deprecation + +In exceptional circumstances — especially during the pre-1.0 preview period — the obsoletion lifecycle may be compressed (e.g., marking obsolete and removing in the same MINOR release). This should still be flagged as a breaking change but the migration guidance should explain the rationale. + +### Spec-Driven Changes + +Breaking changes necessitated by MCP specification evolution should be flagged and documented normally, but the migration guidance should reference the spec change. If a spec change forces an incompatible API change, preference is given to supporting the most recent spec version. + +## Compatibility Switches + +When a breaking change includes an `AppContext` switch or other opt-in/opt-out mechanism, always note it in the migration guidance. Search for `AppContext.TryGetSwitch`, `DOTNET_` environment variables, and similar compat patterns in the diff. Include the switch name and the value that alters the behavior: + +``` +* Compat switch: `ModelContextProtocol.AspNetCore.AllowNewSessionForNonInitializeRequests` = `true` restores previous behavior +``` + +## What to Study for Each PR + +For every PR in the range, examine: + +1. **PR description** — Authors often describe breaking changes here +2. **Linked issues** — May contain discussion about breaking impact +3. **Review comments** — Reviewers may have flagged breaking concerns +4. **Code diff** — Look at changes to: + - Public type/member signatures + - Exception throwing patterns + - Default values and constants + - Return value changes + - Parameter validation changes + - Attribute changes (`[Obsolete]`, `[Experimental]`, etc.) + - `AppContext.TryGetSwitch` or environment variable compat switches +5. **Labels** — Check if `breaking-change` is already applied diff --git a/.github/skills/bump-version/SKILL.md b/.github/skills/bump-version/SKILL.md new file mode 100644 index 000000000..4d5eb9fde --- /dev/null +++ b/.github/skills/bump-version/SKILL.md @@ -0,0 +1,45 @@ +--- +name: bump-version +description: Bump the SDK version after publishing a release. Reads the current version from src/Directory.Build.props, suggests the next minor version, and creates a pull request with the change. Use when asked to bump the version, prepare for the next release, or increment the version number. +compatibility: Requires gh CLI with repo access for creating branches and pull requests. +--- + +# Bump Version + +Bump the SDK version in `src/Directory.Build.props` after publishing a release and create a pull request with the change. + +## Process + +### Step 1: Read Current Version + +Read `src/Directory.Build.props` on the default branch and extract: +- `` — the `MAJOR.MINOR.PATCH` version +- `` — the prerelease suffix (e.g. `preview.1`), if present + +Display the current version to the user: `{VersionPrefix}-{VersionSuffix}` or `{VersionPrefix}` if no suffix. + +### Step 2: Determine Next Version + +If the user provided a target version in their prompt, use it. Otherwise, suggest the next **minor** version with the same suffix pattern: + +- Current `0.9.0` with suffix `preview.1` → suggest `0.10.0-preview.1` +- Current `1.0.0` with no suffix → suggest `1.1.0` +- Current `1.2.3` with suffix `rc.1` → suggest `1.3.0-rc.1` + +Present the suggestion and let the user confirm or provide an alternative. Parse the confirmed version into its `VersionPrefix` and `VersionSuffix` components. + +### Step 3: Create Pull Request + +1. Create a new branch named `bump-version-to-{version}` (e.g. `bump-version-to-0.10.0-preview.1`) from the default branch +2. Update `src/Directory.Build.props`: + - Set `` to the new prefix + - Set `` to the new suffix, or remove the element if there is no suffix +3. Commit with message: `Bump version to {version}` +4. Push the branch and create a pull request: + - **Title**: `Bump version to {version}` + - **Label**: `infrastructure` + - **Base**: default branch + +### Step 4: Confirm + +Display the pull request URL to the user. diff --git a/.github/skills/release-notes/SKILL.md b/.github/skills/release-notes/SKILL.md new file mode 100644 index 000000000..f93cf0de5 --- /dev/null +++ b/.github/skills/release-notes/SKILL.md @@ -0,0 +1,170 @@ +--- +name: release-notes +description: Prepare release notes for the C# MCP SDK. Categorizes PRs, audits breaking changes, reconciles labels, attributes co-authors, identifies acknowledgements, and creates or updates a draft GitHub release. Use when asked to prepare a release, write release notes, update release notes, create a changelog, or summarize a version. +compatibility: Requires gh CLI with repo access and GitHub API access for PR details, timeline events, and commit trailers. +--- + +# Release Notes Preparation + +Prepare polished, categorized release notes for the `modelcontextprotocol/csharp-sdk` repository. This skill determines the version from the repository's build properties, gathers all data from GitHub, and creates or updates a **draft** release. + +> **Safety: This skill only creates and updates draft releases. It must never publish a release.** If the user asks to publish, decline and instruct them to publish manually through the GitHub UI. + +## Process + +Work through each step sequentially. Present findings at each step and get user confirmation before proceeding. Skip any step that has no applicable items. + +### Step 1: Determine Version and Target + +The user may provide: +- **A git ref** (commit SHA, branch, or tag) — use as the target commit +- **An existing draft release** (URL or tag) — use its target commit +- **No context** — show the last 5 commits on `main` (noting HEAD) and offer the option to enter a branch or tag name instead + +Once the target is established: +1. Read `src/Directory.Build.props` **at the target commit**. Extract `` and ``. The tag and title are `v{VersionPrefix}-{VersionSuffix}`, or `v{VersionPrefix}` if no suffix. Pre-release if `VersionSuffix` is present. +2. Check for an existing draft release matching this tag. +3. Determine the previous release tag from `gh release list` (most recent published release). +4. Get the full list of PRs merged between the previous release tag and the target commit. + +### Step 2: Categorize and Attribute + +Sort every PR into one of four categories. See [references/categorization.md](references/categorization.md) for detailed guidance. + +| Category | Content | +|----------|---------| +| **What's Changed** | Features, bug fixes, improvements, breaking changes | +| **Documentation Updates** | PRs whose sole purpose is documentation | +| **Test Improvements** | Adding, fixing, or unskipping tests; flaky test repairs | +| **Repository Infrastructure Updates** | CI/CD, dependency bumps, version bumps, build system | + +**Entry format** — `* Description #PR by @author` with co-authors when present: +``` +* Description #PR by @author +* Description #PR by @author (co-authored by @user1 @Copilot) +``` + +**Attribution rules:** +- Harvest `Co-authored-by` trailers from all commits in each PR's merge commit +- For Copilot-authored PRs, check the `copilot_work_started` timeline event to identify the triggering user. That person becomes the primary author; `@Copilot` becomes a co-author +- Omit the co-author parenthetical when there are none +- Sort entries within each section by merge date (chronological) + +### Step 3: Breaking Change Audit + +Invoke the **breaking-changes** skill with the commit range from the previous release tag to the target commit. The full audit process applies whether creating new release notes or editing existing ones — examine every PR, assess impact, reconcile labels (offering to add/remove labels and comment on PRs), and get user confirmation. + +When **editing an existing release**, also extract any breaking changes already documented in the current release notes (`## Breaking Changes` section). These must be preserved — never remove breaking changes from existing notes. Reconcile the existing documented breaks with the audit results: +- **Previously documented breaks**: keep them, updating formatting if needed +- **Newly discovered breaks**: add them alongside the existing ones +- **Audit finds no issue with a documented break**: still keep it (do not remove without explicit user request) + +Use the results (confirmed breaking changes with impact ordering and detail bullets) in the remaining steps. + +### Step 4: Review Sections + +Present each section for user review: +1. **Breaking Changes** — sorted most → least impactful (from Step 3 results) +2. **What's Changed** — chronological; includes breaking change PRs +3. **Documentation Updates** — chronological +4. **Test Improvements** — chronological +5. **Repository Infrastructure Updates** — chronological + +### Step 5: Acknowledgements + +Identify contributors beyond PR authors: +1. **New contributors** — first contribution to the repository in this release +2. **Issue reporters** — users who submitted issues resolved by PRs in this release (cite the resolving PR) +3. **PR reviewers** — users who reviewed PRs, excluding PR authors and bots + +Exclude anyone already listed as a PR author. Format: +``` +* @user made their first contribution in #PR +* @user submitted issue #1234 (resolved by #5678) +* @user1 @user2 @user3 reviewed pull requests +``` +Reviewers go on a single bullet, sorted by number of PRs reviewed (most first), without citing the count. + +### Step 6: Preamble + +Every release **must** have a preamble — a short paragraph summarizing the release theme that appears before the first `##` heading. The preamble is not optional. The preamble may mention the presence of breaking changes as part of the theme summary, but the versioning documentation link belongs under the Breaking Changes heading (see template), not in the preamble. + +- **New release**: Draft a preamble based on the categorized changes. +- **Editing an existing release**: Extract the current preamble from the release body (text before the first `##` heading) and present it alongside a newly drafted alternative. + +Present the options and let the user choose one, edit one, or enter their own text or markdown. + +### Step 7: Final Assembly + +1. Combine the confirmed preamble with all sections from previous steps. +2. **Notable callouts** — only if something is extraordinarily noteworthy. +3. Present the **complete release notes** for user approval. + +Follow [references/formatting.md](references/formatting.md) when composing and updating the release body. + +### Step 8: Create or Update Draft Release + +Display release metadata for user review: +- **Title / Tag**: e.g. `v0.9.0-preview.1` +- **Target**: commit SHA, its message, and associated PR link +- **Newer commits**: whether `main` has commits beyond the target +- **Pre-release**: yes/no (based on `VersionSuffix`) + +After confirmation: +- **No existing draft** → create with `gh release create --draft` (always `--draft`) +- **Existing draft** → update the release notes body only +- **Never publish.** Never change pre-release state, title, tag, or other metadata on an existing draft. + +When the user requests revisions after the initial update, always rewrite the complete body as a file — never perform in-place string replacements. See [references/formatting.md](references/formatting.md). + +### Step 9: Bump Version + +After the draft release is created or updated, inform the user that the draft release is now associated with a known target commit (state the short SHA and commit message) and recommend proceeding with a version bump. Ask if they want to create a pull request to bump the version for the next release. If yes, invoke the **bump-version** skill and let it handle determining or requesting the target version number. + +## Edge Cases + +- **PR spans categories**: categorize by primary intent +- **Copilot timeline missing**: fall back to `Co-authored-by` trailers; if still unclear, use `@Copilot` as primary author +- **Draft tag changes**: re-fetch the tag before each `gh release edit` +- **No breaking changes**: omit the Breaking Changes section entirely +- **Single breaking change**: use the same numbered format as multiple + +## Release Notes Template + +Omit empty sections. The preamble is **always required** — it is not inside a section heading. + +```markdown +[Preamble — REQUIRED. Summarize the release theme.] + +## Breaking Changes + +Refer to the [C# SDK Versioning](https://modelcontextprotocol.github.io/csharp-sdk/versioning.html) documentation for details on versioning and breaking change policies. + +1. **Description #PR** + * Detail of the break + * Migration guidance + +## What's Changed + +* Description #PR by @author (co-authored by @user1 @Copilot) + +## Documentation Updates + +* Description #PR by @author (co-authored by @user1 @Copilot) + +## Test Improvements + +* Description #PR by @author (co-authored by @user1 @Copilot) + +## Repository Infrastructure Updates + +* Description #PR by @author (co-authored by @user1 @Copilot) + +## Acknowledgements + +* @user made their first contribution in #PR +* @user submitted issue #1234 (resolved by #5678) +* @user1 @user2 @user3 reviewed pull requests + +**Full Changelog**: https://github.com/modelcontextprotocol/csharp-sdk/compare/previous-tag...new-tag +``` diff --git a/.github/skills/release-notes/references/categorization.md b/.github/skills/release-notes/references/categorization.md new file mode 100644 index 000000000..f9a98cc53 --- /dev/null +++ b/.github/skills/release-notes/references/categorization.md @@ -0,0 +1,116 @@ +# Categorization Guide + +## Category Definitions + +### What's Changed +Feature work, bug fixes, API improvements, performance enhancements, and any other user-facing changes. This includes: +- New API surface area (new types, methods, properties) +- Bug fixes that affect runtime behavior +- Performance improvements +- Breaking changes (these appear in both "Breaking Changes" and "What's Changed") +- Changes that span code + docs (categorize based on the primary intent) + +### Documentation Updates +PRs whose **sole purpose** is documentation. Examples: +- Fixing typos in docs +- Adding or improving XML doc comments (when not part of a functional change) +- Updating conceptual documentation (e.g., files in `docs/`) +- README updates +- Adding CONTRIBUTING.md or similar guides + +**Important**: A PR that changes code AND updates docs should go in "What's Changed" — only pure documentation PRs belong here. However, documentation PRs should still be studied during the breaking change audit, as they may document changes that were not properly flagged as breaking. + +### Repository Infrastructure Updates +PRs that maintain the development environment but don't affect the shipped product or test coverage. Examples: +- Version bumps (`Bump version to X.Y.Z`) +- CI/CD workflow changes (GitHub Actions updates) +- Dependency updates from Dependabot +- Build system changes +- Dev container or codespace configuration +- Copilot instructions updates +- NuGet/package configuration changes + +**Important**: PRs that touch the `tests/` folder should never be categorized as Infrastructure — they belong in either "Test Improvements" or "What's Changed" depending on whether product code was also modified. + +### Test Improvements +PRs focused on test quality, coverage, or reliability. Examples: +- Adding new tests (unit, integration, regression, conformance) +- Fixing broken or incorrect tests +- Addressing flaky tests (timing, race conditions) +- Unskipping or skipping tests +- Test infrastructure improvements (new test helpers, test base classes) + +**Important**: PRs that reference "MCP conformance tests" are not automatically test-only. Examine the PR body and file changes to determine whether product code was modified to achieve conformance — if so, the PR belongs in "What's Changed." Conformance PRs should never be placed in "Repository Infrastructure Updates." + +## Entry Format + +Use this simplified format (GitHub auto-links `#PR` and `@user`): +``` +* Description #PR by @author +``` + +For PRs with co-authors (harvested from `Co-authored-by` commit trailers): +``` +* Description #PR by @author (co-authored by @user1 @user2) +``` + +For Dependabot PRs, do not acknowledge @dependabot[bot]: +``` +* Bump actions/checkout from 5.0.0 to 6.0.0 #1234 +``` + +For direct commits without an associated PR (e.g., version bumps merged directly to the branch), use the commit description and `by @author` but omit the `#PR` reference: +``` +* Bump version to v0.1.0-preview.12 by @halter73 +``` + +For Copilot-authored PRs, identify who triggered Copilot using the `copilot_work_started` timeline event on the PR. That person becomes the primary author, and @Copilot becomes a co-author: +``` +* Add trace-level logging for JSON-RPC payloads #1234 by @halter73 (co-authored by @Copilot) +``` + +## Sorting + +Sort entries within each section by **merge date** (chronological order, oldest first). + +## Examples from Past Releases + +### What's Changed (format example) +``` +* Add Trace-level logging for JSON-RPC payloads in transports #1191 by @halter73 (co-authored by @Copilot) +* Use Process.Kill(entireProcessTree: true) on .NET for faster process termination #1187 by @stephentoub +* Include response body in HttpRequestException for transport client errors #1193 by @halter73 (co-authored by @Copilot) +* Fix race condition in SSE GET request initialization #1212 by @stephentoub +* Fix keyset pagination with monotonic UUIDv7-like task IDs #1215 by @halter73 (co-authored by @Copilot) +* Add ILoggerFactory to StreamableHttpServerTransport #1213 by @halter73 (co-authored by @Copilot) +* Add support for message-level filters to McpServer #1207 by @halter73 +* Add `DistributedCacheEventStreamStore` #1136 by @MackinnonBuck +``` + +### Documentation Updates (format example) +``` +* Fix typo in elicitation.md #1186 by @ruyut +* Fix typo in progress.md #1189 by @ruyut +* Clarify McpMetaAttribute documentation scope #1242 by @stephentoub (co-authored by @Copilot) +``` + +### Repository Infrastructure Updates (format example) +``` +* Bump version to 0.8.0-preview.1 #1181 by @stephentoub (co-authored by @Copilot) +* Bump actions/checkout from 6.0.1 to 6.0.2 #1173 +* Bump the opentelemetry-testing group with 6 updates #1174 +``` + +### Test Improvements (format example) +``` +* Remove 10 second wait from docker tests #1188 by @stephentoub +* Fix Session_TracksActivities test #1200 by @stephentoub +* Add serialization roundtrip tests for all Protocol namespace types #1289 by @stephentoub (co-authored by @Copilot) +``` + +### Acknowledgements (format example) +``` +* @ruyut made their first contribution in #1186 +* @user submitted issue #1234 (resolved by #5678) +* @user1 @user2 @user3 reviewed pull requests +``` diff --git a/.github/skills/release-notes/references/formatting.md b/.github/skills/release-notes/references/formatting.md new file mode 100644 index 000000000..467dbb3f2 --- /dev/null +++ b/.github/skills/release-notes/references/formatting.md @@ -0,0 +1,54 @@ +# Release Notes Formatting Guide + +When creating or editing release notes content, follow these rules to ensure the markdown is well-formed and renders correctly on GitHub. + +## GitHub Auto-Linking + +GitHub automatically links `@username`, `#123`, and `@org/repo#123` references in release notes. Never use full URLs for these — use the shorthand forms: + +- `@stephentoub` not `[@stephentoub](https://github.com/stephentoub)` +- `#1234` not `[#1234](https://github.com/modelcontextprotocol/csharp-sdk/pull/1234)` + +This keeps the markdown source readable and avoids brittle links. + +**Exception — Full Changelog link**: The `**Full Changelog**` compare link at the bottom of the release notes does **not** auto-link. It must use the full URL format: +``` +**Full Changelog**: https://github.com/modelcontextprotocol/csharp-sdk/compare/previous-tag...new-tag +``` +A bare `previous-tag...new-tag` will render as plain text, not a clickable link. + +## Writing the Release Body + +Always compose the full release body as a complete markdown file before uploading. Never perform incremental string replacements on the body through shell commands or API calls — this risks collapsing newlines, introducing encoding artifacts, or corrupting the markdown structure. + +### Workflow for Updates + +When the user requests changes to existing release notes: + +1. Fetch the current release body and save it to a local file +2. **Breaking change audit**: Run the full breaking-changes skill audit on the commit range, just as for new release notes — this includes examining PRs, reconciling labels, offering to comment on PRs, and getting user confirmation. Also extract any breaking changes already documented in the existing release body; these must be preserved and reconciled with the audit results. +3. **Preamble check**: Verify the release has a preamble (text before the first `##` heading). If missing, compose one. The versioning documentation link belongs under the `## Breaking Changes` heading, not in the preamble. +4. Write the **entire** corrected body to a separate local file (ensuring proper line breaks between all sections, entries, and paragraphs) +5. Run `git diff --no-index` between the original and updated files and **always** present the raw diff output directly in the response as a fenced code block with `diff` syntax highlighting. Do not summarize or paraphrase the diff — always show the complete diff to the user. Require explicit confirmation before uploading. For published releases (not drafts), also offer to save the original body to a permanent local file, noting that GitHub does not retain prior versions of release notes. +6. Upload the complete file using `gh release edit --notes-file ` +7. Verify the result by fetching the body again and checking that line count and structure are intact + +### Common Pitfalls + +| Problem | Cause | Prevention | +|---------|-------|------------| +| Body collapses to a single line | PowerShell string manipulation strips newlines during round-trip (variable assignment → `.Replace()` → `WriteAllText`) | Always recreate the full body as a file rather than manipulating in-memory strings | +| UTF-8 BOM / garbage characters | `[System.IO.File]::WriteAllText()` with default encoding adds BOM | Use `UTF8Encoding($false)` or write via a tool that produces BOM-less UTF-8 | +| Stray unicode characters | Encoding mismatch between shell output and file write | Avoid piping `gh` output through PowerShell variable assignment for content that will be written back | + +### Verification Checklist + +After every release body update: + +- [ ] Preamble exists before the first `##` heading +- [ ] If `## Breaking Changes` section exists, it begins with the versioning docs link paragraph before the numbered list +- [ ] Line count matches expected structure (~80+ lines for a typical release) +- [ ] Section headings (`## Breaking Changes`, `## What's Changed`, etc.) each appear on their own line +- [ ] Bullet entries are each on their own line +- [ ] No stray characters at the start of the body +- [ ] Preview the release on GitHub to confirm rendering diff --git a/.github/workflows/release.md b/.github/workflows/release.md index 280c35383..4c4ff6855 100644 --- a/.github/workflows/release.md +++ b/.github/workflows/release.md @@ -7,24 +7,13 @@ The following process is used when publishing new releases to NuGet.org: - Once the state of the branch is known to be good, a release can proceed - **The release workflow _does not_ run tests** -2. **Create a new Release in GitHub** - - Use the link on the repo home page to [Create a new release](https://github.com/modelcontextprotocol/csharp-sdk/releases/new) - - Click the 'Choose a tag' dropdown button - - Type the name using the `v{major}.{minor}.{patch}-{suffix}` pattern - - Click 'Create new tag: ... on publish' - - Click the 'Target' dropdown button - - Choose the 'Recent Commits' tab - - Select the commit to use for the release, ensuring it's one from above where CI is known to be green - - The 'Previous tag' dropdown can remain on 'Auto' unless the previous release deviated from this process - - Click the 'Generate release notes button' - - This will add release notes into the Release description - - The generated release notes include what has changed and the list of new contributors - - Verify the Release title - - It will be populated to match the tag name to be created - - This should be retained, using the release title format matching the `v{major}.{minor}.{patch}-{suffix}` format - - Augment the Release description as desired - - This content is presented used on GitHub and is not persisted into any artifacts - - Check the 'Set as a pre-release' button under the release description if appropriate +2. **Prepare release notes using Copilot CLI** + - From a local clone of the repository, use Copilot CLI to invoke the `release-notes` skill + - Provide the target commit (SHA, branch, or tag) when prompted, ensuring it's one from above where CI is known to be green + - The skill will determine the version from `src/Directory.Build.props`, gather PRs, categorize changes, audit breaking changes, identify acknowledgements, and create a **draft** GitHub release + - Review each section as the skill presents it and confirm or adjust as needed + - After the draft release is created, review it on GitHub + - Check the 'Set as a pre-release' checkbox if appropriate - Click 'Publish release' 3. **Monitor the Release workflow** @@ -33,4 +22,5 @@ The following process is used when publishing new releases to NuGet.org: - Verify the package version becomes listed on at [https://nuget.org/packages/ModelContextProtocol](https://www.nuget.org/packages/ModelContextProtocol) 4. **Update the source to increment the version number** - - Immediately after publishing a new release, the [`/src/Directory.Build.Props`](../../src/Directory.Build.props) file needs to be updated to bump the version to the next expected release version + - The `release-notes` skill will offer to invoke the `bump-version` skill to create a pull request bumping the version + - Alternatively, manually update [`/src/Directory.Build.Props`](../../src/Directory.Build.props) to bump the version to the next expected release version