csharp-sdk/.github/skills/breaking-changes/references/classification.md
Jeff Handley 604dcaac06
Add release-notes, breaking-changes, and bump-version Copilot skills (#1328)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-02-20 11:30:36 -08:00

120 lines
6.8 KiB
Markdown

# 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