diff --git a/CHANGELOG.md b/CHANGELOG.md index 79b279e..57cfd8e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,72 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), ## [Unreleased] +## [0.6.0] - 2026-06-08 + +This is a minor release introducing the `dotnet-change-impact` skill for classifying .NET library and NuGet package changes against Microsoft's official compatibility rules. The skill automatically resolves the current branch against the upstream default branch, compares commits and diffs, and recommends the correct SemVer release bump (Major, Minor, or Patch) along with structured compatibility reasoning. + +### Added + +- `dotnet-change-impact` skill that classifies .NET library changes and recommends version bumps according to Microsoft's official .NET library compatibility model, supporting breaking changes, API diffs, public API changes, dependency updates, TFM and platform support changes, interface and enum modifications, overloads, analyzers, and source generators, +- Automatic default-branch resolution in `dotnet-change-impact` when no explicit change details or compare range are provided; the skill inspects the current Git branch and compares against the upstream default branch, with fallback to `main` or `master`, +- Comprehensive compatibility-categories reference documentation in `references/compatibility-categories.md` covering Major (breaking changes, removals, contract modifications), Minor (new capabilities without breaking changes), and Patch (fixes, maintenance, non-breaking refinement) classifications, +- Per-skill evals coverage for `dotnet-change-impact` including current-branch detection, breaking-change scenarios, API compatibility assessments, dependency updates, and SemVer classification validation, +- `dotnet-change-impact` registered in README skill index with install snippet and detailed "Why dotnet-change-impact?" section explaining Microsoft compatibility grounding and key capabilities, +- Hero image asset for `dotnet-change-impact`. + +### Changed + +- Enhanced README with `dotnet-change-impact` installation guidance and capability showcase, including examples of breaking changes, API diffs, and compatibility assessment workflows, +- Improved prose readability in `dotnet-change-impact` SKILL.md by removing artificial line breaks to enhance natural reading flow. + +## [0.5.0] - 2026-06-07 + +This is a minor release introducing the `git-remote-release` skill for generating GitHub release notes by summarizing commits and pull requests between git tags or branches. This complements the existing changelog and release-notes workflow by providing a GitHub-specific release entry point. + +### Added + +- `git-remote-release` skill that reads a commit range or github.com compare URL, summarizes all commits and pull requests between two git references, and generates GitHub release notes with proper markdown formatting, +- `FORMS.md` for `git-remote-release` with structured input collection for branch/tag range specification, +- Hero image asset for `git-remote-release`, +- Evals coverage for `git-remote-release` release notes generation. + +### Changed + +- Expanded README with `git-remote-release` installation snippet and capability showcase documenting release notes generation from commits, PRs, and branch comparisons. + +## [0.4.6] - 2026-06-07 + +This is a patch release focused on strengthening scaffolding templates with safeguards and coverage guidance, plus clarifying the author-neutral scope of `git-visual-squash-summary` to prevent stale same-named tracking branches from hiding work. + +### Added + +- Safeguards and coverage guidance in `dotnet-new-app-slnx` and `dotnet-new-lib-slnx` shared assets (`AGENTS.md` and `.github/copilot-instructions.md`) documenting bot workspace management, eval isolation discipline, and git identity rules, +- Eval coverage for `git-visual-squash-summary` documenting author-neutral scope and base-branch resolution behavior. + +### Changed + +- Enhanced `git-visual-squash-summary` SKILL.md guidance to clarify author-neutral scope and to explicitly document how the skill compares against repository base branches rather than same-named tracking remotes, preventing stale tracking copies from masking real work, +- Updated `git-visual-squash-summary` shared documentation in scaffold templates to reflect author-neutral behavior and base-branch-first resolution. + +## [0.4.5] - 2026-05-31 + +This is a patch release focused on improving `git-repo-digest` documentation link extraction and validation logic, expanding eval coverage, and establishing explicit git operations safeguards policy for agent-driven repositories. The skill now validates documentation URLs with HTTP HEAD requests before accepting them, provides fallback chains across multiple documentation sources, and fails fast when no valid URL is found. + +### Added + +- Enhanced `git-repo-digest` documentation URL resolution with HTTP validation, probing multiple fallback sources (package URLs, `.docfx/docfx.json` hints, `README.md` links) and requiring at least one `200 OK` response before generating digest prose, +- Evals 43–45 covering HTTP-validated documentation URL fallback chains, convenience package documentation linking, and edge cases, +- Git Operations Safeguards section in `AGENTS.md` establishing mandatory approval gates for agent-driven commits and remote operations, explicitly forbidding automatic pushes, pulls, or fetches without user instruction, +- Additional safeguards context in README and AGENTS.md clarifying that automatic commits pollute history and unexpected remote operations risk data loss. + +### Changed + +- Refined `git-repo-digest` SKILL.md guidance to preserve generated `title` from repository-owned `` metadata instead of replacing with invented prose, and to treat generated documentation links as validated HTTP URLs, +- Enhanced `git-repo-digest` `digest.cs` with robust HTTP client setup, 10-second timeout for documentation validation, improved error handling for failed URL validation requests, and clear retry guidance when candidates fail, +- Updated manifest documentation for `frontmatterHints` to clarify that documentation entries are validated HTTP URLs and that overview `title` comes from repository metadata, +- Expanded eval contracts and test case documentation to cover HTTP validation failures, timeout behavior, and fallback chain exhaustion, +- Updated README.md guidance to highlight HTTP-validated documentation URL resolution and documented git operations safeguards. + ## [0.4.4] - 2026-05-18 This is a minor release strengthening `git-repo-digest` with HTTP-validated documentation URL resolution and repository-owned product title discovery, plus clarifying `git-visual-squash-summary` base branch resolution behavior to prevent stale same-named tracking branches from hiding work. The `git-repo-digest` runner now resolves documentation hosts from package URLs and project configuration, validates candidate documentation URLs with HTTP HEAD requests, falls back across multiple sources (root `PackageProjectUrl`, `.docfx/docfx.json`, README `## Documentation` links), and fails fast when no candidate returns `200 OK`. Repository titles for `result/Index.md` are sourced from a root `Directory.Build.props` `` value, with fallback to the highest-referenced top-level packable `.csproj` `` when the root value is absent. The `git-visual-squash-summary` skill now explicitly avoids using a same-named tracking remote such as `origin/` as a squash base; instead it resolves against the repository's actual base branch such as `origin/main`, `origin/master`, `main`, or `master` before declaring there is nothing to summarize. @@ -275,8 +341,12 @@ This is a minor release that introduces two complementary git workflow skills, e - Improved scaffold fidelity with hidden `.bot` asset preservation, explicit UTF-8 and BOM handling, and checks aimed at preventing mojibake or incomplete generated output. -[Unreleased]: https://github.com/codebeltnet/agentic/compare/v0.4.4...HEAD -[0.4.5]: https://github.com/codebeltnet/agentic/compare/v0.4.3...v0.4.4 +[Unreleased]: https://github.com/codebeltnet/agentic/compare/v0.6.0...HEAD +[0.6.0]: https://github.com/codebeltnet/agentic/compare/v0.5.0...v0.6.0 +[0.5.0]: https://github.com/codebeltnet/agentic/compare/v0.4.6...v0.5.0 +[0.4.6]: https://github.com/codebeltnet/agentic/compare/v0.4.5...v0.4.6 +[0.4.5]: https://github.com/codebeltnet/agentic/compare/v0.4.4...v0.4.5 +[0.4.4]: https://github.com/codebeltnet/agentic/compare/v0.4.3...v0.4.4 [0.4.3]: https://github.com/codebeltnet/agentic/compare/v0.4.2...v0.4.3 [0.4.2]: https://github.com/codebeltnet/agentic/compare/v0.4.1...v0.4.2 [0.4.1]: https://github.com/codebeltnet/agentic/compare/v0.4.0...v0.4.1 diff --git a/README.md b/README.md index 55afa62..6c96113 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,8 @@ npx skills add https://github.com/codebeltnet/agentic --skill trunk-first-repo npx skills add https://github.com/codebeltnet/agentic --skill dotnet-strong-name-signing npx skills add https://github.com/codebeltnet/agentic --skill dotnet-new-app-slnx npx skills add https://github.com/codebeltnet/agentic --skill dotnet-new-lib-slnx -npx skills add https://github.com/codebeltnet/agentic --skill git-remote-release +npx skills add https://github.com/codebeltnet/agentic --skill git-remote-release +npx skills add https://github.com/codebeltnet/agentic --skill dotnet-change-impact # npx skills add https://github.com/codebeltnet/agentic --skill another-skill ``` @@ -94,6 +95,7 @@ npx skills add https://github.com/codebeltnet/agentic --skill git-remote-release | [trunk-first-repo](skills/trunk-first-repo/SKILL.md) | Initialize a git repository following [scaled trunk-based development](https://trunkbaseddevelopment.com/#scaled-trunk-based-development). Seeds an empty `main` branch and creates a versioned feature branch (`v0.1.0/init`), enforcing a PR-first workflow where content only reaches main through peer-reviewed pull requests. | | [dotnet-strong-name-signing](skills/dotnet-strong-name-signing/SKILL.md) | Generate a strong name key (`.snk`) file for signing .NET assemblies using pure .NET cryptography — no Visual Studio Developer PowerShell or `sn.exe` required. Works in any terminal. Defaults to 1024-bit RSA (matching `sn.exe`), with 2048 and 4096 available as options. | | [git-remote-release](skills/git-remote-release/SKILL.md) | Generate GitHub release notes by summarizing all commits and pull requests between two Git tags or branches in a remote GitHub repository. Accepts a compare URL or separate owner/repo, previous ref, and current ref values; falls back to comparing the current branch against the upstream default branch when no input is provided. Produces a human-friendly `## What's Changed` summary with optional GitHub alert blocks, a `Sources:` section preserving PR and commit references, and a full changelog compare link. | +| [dotnet-change-impact](skills/dotnet-change-impact/SKILL.md) | Classify .NET library or NuGet package changes and recommend the correct release bump — `Major`, `Minor`, or `Patch` — for both Semantic Versioning (`MAJOR.MINOR.PATCH`) and .NET assembly/file versioning (`Major.Minor.Build.Revision`), grounded in Microsoft's official .NET compatibility rules. Uses the current Git branch by default when no explicit change details or compare range are provided, resolving it against the upstream/default base branch with local read-only git state. Always returns structured behavioral/binary/source/design-time/backwards compatibility reasoning with the recommendation, even when the bump is clear. | ### Copyable Install Commands @@ -171,12 +173,18 @@ npx skills add https://github.com/codebeltnet/agentic --skill trunk-first-repo npx skills add https://github.com/codebeltnet/agentic --skill dotnet-strong-name-signing ``` -`git-remote-release` - -```bash -npx skills add https://github.com/codebeltnet/agentic --skill git-remote-release -``` - +`git-remote-release` + +```bash +npx skills add https://github.com/codebeltnet/agentic --skill git-remote-release +``` + +`dotnet-change-impact` + +```bash +npx skills add https://github.com/codebeltnet/agentic --skill dotnet-change-impact +``` + ### Why git-visual-commits? Commit messages are the most-read documentation in any codebase — yet they're usually an afterthought. "fix stuff", "wip", "address PR feedback" tells you nothing six months later. Writing good commits takes discipline, and when you're in flow, it's the first thing that slips. @@ -494,8 +502,23 @@ Writing release notes is tedious. Raw commit logs are too noisy, PR titles often - **Source preservation** where every release note includes a `Sources:` section with the original PR and commit references, - **Strict format** that always starts with `## What's Changed` and always ends with the full changelog compare link, - **No invented claims** so every statement in the summary is backed by the commits and pull requests collected, -- **Read-only operation** that never mutates repository state. - +- **Read-only operation** that never mutates repository state. + +### Why dotnet-change-impact? + +Picking the wrong version number is one of the easiest ways to break downstream consumers. A "bug fix" that quietly changes exception behavior, an "innocent" new overload that makes existing calls ambiguous, or a dependency bump that drops a target framework — any of these can be a breaking change shipped as a patch. Conversely, teams sometimes panic and burn a major version on a purely internal refactor. **dotnet-change-impact** brings Microsoft's official .NET compatibility model to that decision. + +**dotnet-change-impact** reads the current Git branch by default, comparing it against the upstream/default base branch with local read-only git state, and recommends `Major`, `Minor`, or `Patch` for both SemVer and .NET `Major.Minor.Build.Revision` versioning. Explicit change descriptions, API diffs, PR summaries, and compare ranges still override the default branch resolution. + +- **Reasoned output by default** — always puts the recommendation first, then explains the key changes, compatibility impact, reasoning, and deterministic decision so the version call is reviewable, +- **Current-branch default** — when no explicit input is supplied, resolves the current branch against the upstream/default base branch, collects commits plus the net diff, and classifies that branch instead of asking for change details first, +- **Grounded in Microsoft guidance** — decisions follow the official [library change rules](https://learn.microsoft.com/en-us/dotnet/core/compatibility/library-change-rules) and [compatibility categories](https://learn.microsoft.com/en-us/dotnet/core/compatibility/categories), +- **Five-category compatibility lens** — behavioral, binary, source, design-time, and backwards compatibility are evaluated explicitly, +- **Conservative by default** — a change that might break existing consumers is treated as breaking, so accidental breaking releases don't ship as a patch or minor, +- **But not alarmist** — internal, non-observable refactors are not inflated to major just because something changed, +- **Precedence-aware** — mixed releases take the highest required bump, +- **Special-case savvy** — dependency updates, bug fixes, new overloads, interface and enum changes, analyzers/source generators, TFM/platform support, and performance changes each get the right default and the right escalation triggers. + ## Repository structure ``` diff --git a/skills/dotnet-change-impact/SKILL.md b/skills/dotnet-change-impact/SKILL.md new file mode 100644 index 0000000..47e5b64 --- /dev/null +++ b/skills/dotnet-change-impact/SKILL.md @@ -0,0 +1,366 @@ +--- +name: dotnet-change-impact +description: > + Classifies .NET library or NuGet package changes and recommends the correct release bump: Major, Minor, or Patch. Applies both Semantic Versioning (MAJOR.MINOR.PATCH) and .NET assembly/file versioning (Major.Minor.Build.Revision), grounded in Microsoft’s official .NET library compatibility rules. Use when evaluating the current branch, breaking changes, API diffs, public API changes, dependency updates, TFM/platform support, interface or enum changes, overloads, analyzers, source generators, or binary/source/behavioral/design-time/backwards compatibility. When no explicit change details or compare range are provided, inspects the current Git branch and compares it against the upstream default branch automatically. Always returns structured compatibility reasoning with the recommendation. +--- + +# .NET Change Impact + +![.NET Change Impact](assets/hero.jpg) + +Classify a proposed change to a .NET library or NuGet package and recommend the correct release bump. This skill exists to stop accidental breaking releases from being shipped as a patch or minor, while staying practical enough not to label every internal refactor as breaking. + +It answers one question: + +> Given these changes, should I bump **Major**, **Minor**, or **Patch**? + +The authority for every decision is Microsoft's official .NET compatibility guidance: + +- https://learn.microsoft.com/en-us/dotnet/core/compatibility/library-change-rules +- https://learn.microsoft.com/en-us/dotnet/core/compatibility/categories + +Treat those two sources as normative. When the deep category definitions or the long tail of +special cases matter, read `references/compatibility-categories.md`. + +## Input + +The skill accepts input in three ways, checked in this order: + +**1. Explicit change details:** + +When the user provides release notes, a PR summary, an API diff, a git diff, a changelog entry, +dependency update details, a bug-fix description, or a stated release intent, classify those +provided changes directly. + +**2. Explicit compare range:** + +When the user provides a base ref, current ref, compare URL, branch name, tag range, or commit +range, inspect that range and classify the changes found there. + +**3. Default resolution (no input provided):** + +When the user does not provide explicit change details or a compare range, operate on the +current Git working repository. See **Default Resolution Behavior** below. + +If required information is missing and cannot be inferred or resolved, ask only for the missing +fact. Do not ask the user to restate the whole change set when the current repository or an +explicit compare range can be inspected. + +## Default Resolution Behavior + +If the user does not provide explicit change details, a compare URL, branch name, tag range, or +commit range, the skill must operate on the current Git working repository. + +In that case, the skill must: + +1. Detect the current branch. +2. Detect the upstream remote for the repository. +3. Detect the upstream repository's default branch — usually `main`, but do not assume `main` if + the remote default branch can be resolved. +4. Compare the current branch against the upstream remote default branch. +5. Include all commits on the current branch that are not present in the upstream remote default + branch. +6. Include the net diff for the comparison range, because compatibility impact often depends on + files changed, signatures, project metadata, target frameworks, package references, generated + assets, and build props/targets. +7. Classify the release bump from the collected branch changes using the normal compatibility + rules. + +The default comparison should be conceptually equivalent to: + +```text +upstream/default-branch...current-branch +``` + +For example, if the current branch is `feature/change-impact` and the upstream default branch is +`main`, the comparison should be treated as: + +```text +upstream/main...feature/change-impact +``` + +If the repository uses `origin` as the upstream remote, use: + +```text +origin/main...current-branch +``` + +If the repository has both `origin` and `upstream`, prefer the remote that represents the +canonical source repository. In fork-based workflows, this is usually `upstream`. In +single-repository workflows, this is usually `origin`. + +The skill must not silently assume the wrong base branch. If the default branch cannot be +resolved, fall back in this order: + +1. `origin/HEAD` +2. `upstream/HEAD` +3. `origin/main` +4. `origin/master` +5. `upstream/main` +6. `upstream/master` + +If none of these can be resolved, ask the user to provide the base branch or compare range. + +Useful read-only commands for default resolution: + +```bash +git rev-parse --abbrev-ref HEAD +git remote +git symbolic-ref refs/remotes/origin/HEAD --short +git symbolic-ref refs/remotes/upstream/HEAD --short +git merge-base HEAD {resolvedBase} +git log --oneline {resolvedBase}..HEAD +git log --format="%H%x09%an%x09%ae%x09%s%x09%b" {resolvedBase}..HEAD +git diff --name-status {resolvedBase}...HEAD +git diff --find-renames {resolvedBase}...HEAD +``` + +Use local Git state only for default resolution unless the user explicitly asks to refresh remote +state. Do not fetch, pull, push, or mutate repository state as part of this skill. + +## Critical: include reasoning every time + +Always return a structured answer with the recommendation and reasoning. Do not collapse clear +decisions to a bare one-word `Major`, `Minor`, or `Patch` response. The reasoning is part of the +value of this skill, especially for branch-level analysis where the user needs to trust why the +bump follows from the diff. + +Use the template in **Output format** below for every answer. Keep the answer concise when the +decision is simple, but still explain the compatibility impact and the deciding facts. + +When the answer is ambiguous, incomplete, or depends on a missing fact, still provide the best +current recommendation and make the missing fact explicit in **Deterministic decision**. + +## Internal analysis process + +Run this analysis internally for every request, regardless of which mode you output. Do **not** +print this checklist verbatim — it is reasoning scaffolding, not the answer format. + +1. Identify all public contract changes (types, members, signatures, accessibility, contracts). +2. Identify all observable behavior changes (return values, exceptions, ordering, serialization). +3. Identify all source compatibility risks (would existing consumer code still compile?). +4. Identify all binary compatibility risks (would existing compiled consumers still load/run?). +5. Identify all design-time compatibility risks (analyzers, generators, build, tooling, restore). +6. Determine whether existing consumers remain backwards compatible end to end. +7. Determine the highest required bump across every change present. +8. Produce the structured output with recommendation, compatibility impact, reasoning, and the + deterministic decision. + +## Version bump rules + +### Major — breaking or potentially breaking + +Recommend `Major` when the change can break, or plausibly break, existing consumers. A change +is breaking if it can affect backwards, binary, source, or design-time compatibility, or change +observable behavior that consumers can reasonably depend on. + +Changes that usually require `Major`: + +- Removing a public type, member, constructor, property, method, event, field, enum value, + attribute, or interface member. +- Renaming public APIs, or changing public method signatures. +- Changing parameter order, parameter types, return types, generic constraints, nullability + contracts, accessibility, or inheritance behavior. +- Making a public API less accessible. +- Making previously valid consumer code fail to compile, or previously compiled consumer + binaries fail at runtime. +- Changing observable behavior consumers can reasonably depend on: serialization, wire format, + persisted data format, exception behavior, ordering, equality, hashing, parsing, formatting, + validation, or default values, in a breaking way. +- Removing or reducing platform, TFM, runtime, OS, architecture, or dependency support. +- Introducing stricter validation that rejects previously accepted inputs — unless it is an + explicitly documented bug fix that is extremely unlikely to affect valid consumers. +- Changing public abstractions or interfaces so implementers must change code. +- Changing package identity, assembly identity, strong name, namespace, or binding expectations + in a way that affects existing consumers. + +### Minor — backward-compatible additions + +Recommend `Minor` when the change adds functionality without breaking source, binary, +design-time, or behavioral compatibility. + +Changes that usually require `Minor`: + +- Adding a new public type, method, property, constructor, overload, event, enum type, or + extension method that does not break existing compilation or behavior. +- Adding optional, opt-in capabilities, or new behavior behind opt-in configuration. +- Adding support for a new TFM, platform, runtime, OS, architecture, or dependency version + without removing existing support. +- Adding new package features while preserving existing contracts. +- Improving performance with no observable semantic breakage. +- Adding diagnostics, analyzers, or source generators that do not break builds by default. + +Be conservative with interfaces: adding members to an existing public interface is usually +breaking for implementers and therefore usually `Major`. Default interface implementations +reduce but do not eliminate source, design-time, and behavioral risk — evaluate them, do not +wave them through. + +### Patch — backward-compatible, no new public surface + +Recommend `Patch` when the change preserves the public contract and adds no new public +functionality. + +Changes that usually require `Patch`: + +- Bug fixes that preserve the intended public contract. +- Dependency updates that do not change the public API, supported TFMs, runtime behavior, or + compatibility guarantees. +- Security fixes that preserve compatibility. +- Internal implementation changes and refactoring with no public or behavioral impact. +- Documentation updates; build, packaging, CI, test, or housekeeping changes. +- Non-breaking performance improvements; non-breaking analyzer or warning changes. + +Key nuance: a bug fix can still be breaking. If the fix changes observable behavior, exception +behavior, serialization, validation, ordering, equality, formatting, parsing, threading, +timing, or side effects that consumers may depend on, evaluate it as a potential `Major`. + +## Precedence rules + +When several changes ship together, choose the **highest** required bump: + +```text +Major > Minor > Patch +``` + +- One breaking change plus several bug fixes → `Major`. +- One backward-compatible feature plus several patches → `Minor`. +- Only housekeeping and bug fixes → `Patch`. + +## Conservative decision policy + +When uncertain, prefer the safer classification: + +- If a change might break existing consumers, recommend `Major`. +- If a change only adds backward-compatible functionality, recommend `Minor`. +- If a change only fixes or maintains existing behavior, recommend `Patch`. + +But do not inflate every behavior change to `Major`. First decide whether the behavior is +actually observable, documented, reasonably relied upon, or compatibility-sensitive. An +internal or non-observable change is not a breaking change just because something changed. + +## Compatibility classification + +When you produce an explanation, classify the impact using these five categories. Full, +Microsoft-grounded definitions and examples live in `references/compatibility-categories.md`; +the summary here is enough for most decisions. + +- **Behavioral change** — the API still compiles and loads, but observable behavior differs + (return values, exceptions, validation, ordering, equality/hash, serialization, formatting, + side effects, threading/timing/caching). Can be breaking even when binary and source + compatibility are intact. +- **Binary compatibility** — existing compiled assemblies may fail against the new version + without recompilation (removed members, changed signatures, changed assembly identity, + changed type shape). Usually implies `Major`. +- **Source compatibility** — existing source must change to compile (renames, signature + changes, removals, new constraints, ambiguous overloads, changed accessibility/nullability, + required language/TFM bumps). Usually implies `Major`. +- **Design-time compatibility** — build, tooling, analyzers, generators, project system, IDE, + or restore behavior changes (new default-on analyzer errors, changed generator output, + build/props/targets changes, SDK/tooling requirements). May imply `Major` if it breaks + existing consumers by default. +- **Backwards compatibility** — the end-to-end test: can existing consumers of the old version + use the new version unmodified, with equivalent behavior? If they cannot compile, run, or + build, or they observe a breaking behavior change, it is not backwards compatible and usually + requires `Major`. + +## Special cases + +These recur often enough to call out directly. The full catalog is in +`references/compatibility-categories.md`. + +- **Dependency updates** — default `Patch`. Escalate to `Minor` or `Major` only if the update + changes public API exposure, supported TFMs/platforms, runtime behavior, introduces + binding/runtime incompatibility, requires consumers to update their own dependencies, or + drops compatibility with existing consumers. +- **Bug fixes** — default `Patch`. If the old, incorrect behavior was publicly observable and + consumers may depend on it, explain the trade-off; it may still need `Major`. +- **New overloads** — usually `Minor`, but check for overload-resolution ambiguity. If existing + source could bind differently or fail to compile, consider `Major`. +- **Interface changes** — adding members to a public interface is usually `Major` (implementers + may fail to compile). Adding a brand-new interface is usually `Minor`. Default interface + members still warrant caution and explanation. +- **Enum changes** — adding values is usually `Minor`, but may be behavioral or source-impacting + if consumers exhaustively switch over values or serialization contracts are affected. Removing + or renaming values is usually `Major`. +- **Analyzer / source generator / build changes** — warnings-only diagnostics that do not break + default builds are usually `Patch` or `Minor` by scope. Diagnostics that become errors by + default, or generated-code changes that break consumers, are usually `Major`. +- **TFM / platform support** — adding support is usually `Minor`; removing support is usually + `Major`. Raising the minimum supported runtime, SDK, language version, OS, or CPU architecture + is usually `Major` when existing consumers are affected. +- **Performance changes** — usually `Patch` when behavior is unchanged; may be `Major` when + timing, ordering, threading, concurrency, caching, resource lifetime, or side effects are + observable and compatibility-sensitive. + +## SemVer and .NET version-number mapping + +### Semantic Versioning — `MAJOR.MINOR.PATCH` + +- `Major` → increment Major, reset Minor and Patch to `0`. +- `Minor` → increment Minor, reset Patch to `0`. +- `Patch` → increment Patch. + +### .NET assembly/file versioning — `Major.Minor.Build.Revision` + +- `Major` → increment Major. Reset or align Minor per project policy. +- `Minor` → increment Minor. +- `Patch` → normally maps to Build or Revision per project policy. +- `Build` and `Revision` are CI/build metadata. Never use them to hide a breaking change, and + do not use them as a substitute for a SemVer Patch when NuGet package versioning is involved. + +## Input interpretation + +Input may arrive as release notes, git commits, PR summaries, API diffs, changelog entries, dependency updates, bug-fix descriptions, a stated release intent, or the default current-branch comparison. Distinguish carefully between: public API changes, internal implementation changes, consumer-visible behavior changes, build/design-time changes, documentation-only changes, package/dependency changes, and platform/TFM support changes. The bump follows the most impactful real change, not the loudest commit subject. + +## Output format + +Use exactly this structure: + +```markdown +## Recommendation + + + +## Key changes identified + + + +## Compatibility impact + +- Behavioral change: +- Binary compatibility: +- Source compatibility: +- Design-time compatibility: +- Backwards compatibility: + +## Reasoning + + + +## Deterministic decision + + +``` + +## Good output characteristics + +- Always includes reasoning, even for clear `Major`, `Minor`, or `Patch` recommendations. +- Puts the recommendation first so the answer is still easy to scan. +- Names the key changes found in the branch or input before interpreting them. +- Picks the highest required bump when changes are mixed. +- Grounds every breaking-change call in whether existing consumers can compile, run, and + observe equivalent behavior. +- Stays conservative on interface and behavioral risk without inflating internal-only changes. + +## Bad output characteristics + +- Collapsing a clear change into a bare one-word answer without explaining why. +- Collapsing an ambiguous or mixed change into a recommendation that hides a compatibility risk. +- Labeling a publicly observable behavior change as `Patch` just because it is "a fix". +- Calling an internal, non-observable refactor `Major`. +- Hiding a breaking change behind a Build/Revision bump. +- Inventing compatibility guarantees or migration steps the input does not support. diff --git a/skills/dotnet-change-impact/assets/hero.jpg b/skills/dotnet-change-impact/assets/hero.jpg new file mode 100644 index 0000000..78e7ba6 Binary files /dev/null and b/skills/dotnet-change-impact/assets/hero.jpg differ diff --git a/skills/dotnet-change-impact/evals/evals.json b/skills/dotnet-change-impact/evals/evals.json new file mode 100644 index 0000000..9794de7 --- /dev/null +++ b/skills/dotnet-change-impact/evals/evals.json @@ -0,0 +1,106 @@ +{ + "skill_name": "dotnet-change-impact", + "evals": [ + { + "id": 1, + "prompt": "In my .NET library I removed the public method `HttpRetryHandler.Send(HttpRequestMessage)` and renamed the public property `Client.BaseUri` to `Client.BaseAddress`. Should I bump major, minor, or patch?", + "expected_output": "Structured answer recommending Major with compatibility reasoning.", + "expectations": [ + "Recommends Major in the Recommendation section", + "Includes Key changes identified, Compatibility impact, Reasoning, and Deterministic decision sections", + "Treats removing a public method and renaming a public property as binary and source breaking" + ] + }, + { + "id": 2, + "prompt": "I added a brand-new public class `RetryPolicy` and a new `WithRetry(this HttpClient client, int attempts)` extension method to my NuGet package. No existing public APIs were touched. What's the version bump?", + "expected_output": "Structured answer recommending Minor with compatibility reasoning.", + "expectations": [ + "Recommends Minor in the Recommendation section", + "Includes concise reasoning even though the change is clear", + "Recognizes purely additive, backward-compatible public surface as Minor", + "Does not escalate to Major when nothing existing was changed or removed" + ] + }, + { + "id": 3, + "prompt": "This release only fixes a NullReferenceException inside an internal cache class (no public API or observable behavior change), updates a transitive dependency from 8.0.1 to 8.0.3, and refreshes the README. Major, minor, or patch?", + "expected_output": "Structured answer recommending Patch with compatibility reasoning.", + "expectations": [ + "Recommends Patch in the Recommendation section", + "Includes concise reasoning even though the change is clear", + "Treats internal-only fix, compatible patch dependency bump, and docs as non-breaking", + "Does not inflate an internal, non-observable refactor to Minor or Major" + ] + }, + { + "id": 4, + "prompt": "I changed `DateParser.Parse` to throw a FormatException on empty input instead of returning null. Should this be a patch? It's just a bug fix.", + "expected_output": "Structured answer: ambiguous because it depends on whether Parse is public and whether consumers rely on the null-return behavior. Should not just say Patch.", + "expectations": [ + "Uses structured reasoning instead of emitting a bare single word", + "Uses the structured template with Recommendation, Compatibility impact, Reasoning, and Deterministic decision sections", + "Flags this as a behavioral change that can be breaking even though it compiles", + "Does not accept the user's framing that a bug fix is automatically a Patch", + "Identifies the deciding fact: whether Parse is public and whether the old null-return behavior is relied upon or documented" + ] + }, + { + "id": 5, + "prompt": "Big release: I added two new public extension methods, fixed a couple of internal bugs, AND removed a public `ILogger` overload that nobody should be using. One command answer please: major, minor, or patch?", + "expected_output": "Structured answer recommending Major, because precedence picks the highest required bump and the public removal is breaking.", + "expectations": [ + "Recommends Major in the Recommendation section", + "Includes concise reasoning despite the user's request for a one-command answer", + "Applies precedence: the breaking public removal dominates the additive and internal changes", + "Does not downgrade to Minor or Patch despite the user implying the removed overload is unused" + ] + }, + { + "id": 6, + "prompt": "I added a new member `Task FlushAsync()` to my public interface `IMessageSink`. Existing implementations are out in the wild via my NuGet package. Why would this be more than a patch?", + "expected_output": "Structured answer recommending Major, explaining that adding a member to a public interface breaks existing implementers (source compatibility).", + "expectations": [ + "Recommends Major", + "Explains that adding a member to an existing public interface breaks existing implementers' source compilation", + "Mentions default interface members as a nuance without treating them as automatically safe", + "Uses the structured explanation template" + ] + }, + { + "id": 7, + "prompt": "I added a new enum value `LogLevel.Trace` to an existing public enum. My library serializes these values to JSON and some consumers switch over them. Patch, minor, or major?", + "expected_output": "Structured answer: usually Minor but flag the exhaustive-switch and serialization-contract risk that could make it behavioral/source-impacting.", + "expectations": [ + "Does not emit a bare one-word answer given the explicit switch and serialization caveats", + "Notes that adding an enum value is usually Minor", + "Flags exhaustive consumer switch statements and serialization contracts as escalation risks", + "Uses the structured explanation template with a clear recommendation" + ] + }, + { + "id": 8, + "prompt": "My library dropped support for net6.0 and now targets only net8.0 and net9.0. Existing consumers on net6.0 will be affected. Version bump?", + "expected_output": "Structured answer recommending Major, because removing TFM/platform support is breaking for existing consumers.", + "expectations": [ + "Recommends Major in the Recommendation section", + "Includes concise reasoning even though the change is clear", + "Treats removing TFM support as breaking for existing consumers", + "Does not classify dropped framework support as Minor or Patch" + ] + }, + { + "id": 9, + "prompt": "Use dotnet-change-impact to classify the version bump for this branch.", + "expected_output": "Current-branch default-resolution behavior: inspect the local Git repository, resolve the current branch against the upstream/default base branch, collect commits and net diff, then classify those changes with structured reasoning. Must not ask the user to provide change details first.", + "expectations": [ + "Follows Default Resolution Behavior when no explicit change details or compare range are provided", + "Detects the current branch and resolves a base branch using local Git state, following the documented fallback order", + "Collects both branch commits and net diff before classifying compatibility impact", + "Returns structured reasoning with the recommendation instead of a bare Major, Minor, or Patch", + "Does not respond with a generic request for release notes, diffs, PR summaries, or bug descriptions when the current repository can be inspected", + "Asks only for the missing base branch or compare range if local default resolution cannot identify one" + ] + } + ] +} diff --git a/skills/dotnet-change-impact/references/compatibility-categories.md b/skills/dotnet-change-impact/references/compatibility-categories.md new file mode 100644 index 0000000..5e3be41 --- /dev/null +++ b/skills/dotnet-change-impact/references/compatibility-categories.md @@ -0,0 +1,204 @@ +# .NET Compatibility Categories — Reference + +Detailed, Microsoft-grounded definitions for the five compatibility categories and the full +catalog of special cases. `SKILL.md` carries the decision engine and summaries; consult this +file when a decision hinges on a nuanced category boundary or an uncommon change shape. + +Normative sources: + +- [Library change rules](https://learn.microsoft.com/en-us/dotnet/core/compatibility/library-change-rules) +- [Compatibility categories](https://learn.microsoft.com/en-us/dotnet/core/compatibility/categories) + +## Table of contents + +1. The five categories in depth +2. Why a change can be breaking even when it compiles +3. Special-case catalog +4. Worked classification examples + +## 1. The five categories in depth + +### Behavioral change + +The public API surface is unchanged — it still compiles against and loads against existing +consumers — but the **observable behavior** differs. This is the category developers most often +under-weight, because nothing in the signature changed. + +Observable behavior includes: + +- Different return values for the same inputs. +- Different exceptions thrown, or exceptions thrown where none were before (and vice versa). +- Different input validation (stricter or looser). +- Different ordering of results (collections, enumerations, query output). +- Different equality or hash-code behavior. +- Different serialization, deserialization, or formatting output. +- Different side effects (files written, events raised, state mutated). +- Different threading, timing, caching, retry, or I/O behavior. + +A behavioral change is breaking when consumers can reasonably depend on the old behavior. It can +be breaking even when binary and source compatibility are fully intact, which is why a "bug fix" +is not automatically a `Patch`. + +### Binary compatibility + +Existing **compiled** consumer assemblies must continue to run against the new version without +recompilation. A change is binary-incompatible when a previously compiled consumer could fail to +load or fail at runtime against the new assembly. Examples: + +- Removing or renaming public members. +- Changing method signatures, return types, or parameter types. +- Changing assembly identity (name, version policy, strong name, public key). +- Changing the shape of a public field, property, event, or method. +- Changing base classes or interface contracts that compiled consumers rely on. + +Binary incompatibility almost always implies `Major`. + +### Source compatibility + +Existing consumer **source code** must continue to compile against the new version without edits. +A change is source-incompatible when previously valid consumer code would no longer compile: + +- Renamed or removed APIs. +- Changed signatures or required new arguments. +- New generic constraints. +- Newly ambiguous overloads (overload resolution now fails). +- Reduced accessibility (e.g., `public` → `internal`). +- Changed nullability annotations that break `enable` consumers, or + analyzer behavior that breaks strict builds. +- A required higher C# language version or TFM. + +Source incompatibility usually implies `Major`. Note the asymmetry: a change can be binary +compatible but source incompatible (e.g., adding an ambiguous overload), or source compatible +but binary incompatible. Evaluate both. + +### Design-time compatibility + +The change affects build, tooling, analyzers, source generators, the project system, the IDE +experience, package restore, or compile-time diagnostics — rather than runtime behavior: + +- New analyzer diagnostics that are **errors by default**. +- Source generator output changes that alter or break generated code. +- Changes to MSBuild targets/props shipped in the package. +- Package restore behavior changes. +- New SDK, workload, or tooling requirements. +- Design-time build failures. + +Design-time incompatibility may imply `Major` when existing consumers are broken by default +(for example, a build that previously succeeded now fails). Warnings-only changes that do not +break a default build are usually lower impact. + +### Backwards compatibility + +This is the synthesizing question, not a separate mechanism: **can an existing consumer of the +old version adopt the new version with no modifications and get equivalent behavior?** If the +consumer cannot compile, cannot run, fails at design time, or observes a breaking behavior +change, the release is not backwards compatible and usually requires `Major`. Use this category +to summarize the combined effect of the other four. + +## 2. Why a change can be breaking even when it compiles + +The most common misclassification is treating "the public API didn't change" as "this is safe". +Microsoft's guidance is explicit that behavioral changes are a first-class compatibility concern. +When evaluating any fix, refactor, or performance improvement, ask: + +- Could a reasonable consumer have written code that depends on the **old** observable behavior? +- Is the old behavior documented, or is it an undocumented implementation detail? +- Does the change alter exceptions, ordering, equality, serialization, validation, timing, or + side effects? + +If the answer points to real consumer reliance, escalate from `Patch` toward `Major`, and +prefer explanation mode so the trade-off is visible. + +## 3. Special-case catalog + +### Dependency updates + +Default `Patch`. Escalate when the dependency update: + +- changes the library's own public API exposure (re-exported types, transitively visible APIs), +- changes supported TFMs or platforms, +- changes runtime behavior consumers can observe, +- introduces a binding or runtime incompatibility, +- forces consumers to update their own references to that dependency, +- removes compatibility with existing consumers. + +If any apply, evaluate as `Minor` (additive, compatible) or `Major` (breaking). + +### Bug fixes + +Default `Patch`. But a bug fix that changes publicly observable behavior may be breaking even +though the fix is "correct". If the old behavior was clearly wrong yet observable, explain the +trade-off; depending on consumer reliance it may still require `Major`. The correctness of the +fix does not by itself make it non-breaking. + +### New overloads + +Usually `Minor`. The risk is **source compatibility**: a new overload can make a previously +unambiguous call ambiguous, or change overload resolution so existing source binds to a different +method or fails to compile. When that risk is real, consider `Major`. + +### Interface changes + +- Adding members to an existing public interface → usually `Major` (existing implementers fail to + compile). +- Adding a brand-new interface → usually `Minor`. +- Adding a **default interface member** → lower source-break risk for implementers, but still + carries source, design-time, and behavioral risk depending on language version, multiple + inheritance of members, and runtime support. Do not treat it as automatically safe — evaluate + and explain. + +### Enum changes + +- Adding enum values → usually `Minor`. But it can be behavioral or source-impacting if consumers + exhaustively `switch` over the values (e.g., with no default arm) or if serialization contracts + depend on the closed set. +- Removing or renaming enum values → usually `Major`. +- Changing the underlying numeric value of an existing member → usually `Major` (binary and + serialization impact). + +### Analyzer / source generator / build changes + +- New diagnostics that are warnings only and do not break default builds → usually `Patch` or + `Minor` depending on feature scope. +- Diagnostics promoted to errors by default → usually `Major` (breaks builds that previously + succeeded). +- Source generator output changes that break or materially alter consumer-visible generated code + → usually `Major`. + +### TFM / platform support + +- Adding support for a new TFM, platform, runtime, OS, or architecture → usually `Minor`. +- Removing support → usually `Major`. +- Raising the minimum supported runtime, SDK, C# language version, OS, or CPU architecture → + usually `Major` when existing consumers are affected. + +### Performance changes + +Usually `Patch` when behavior is unchanged. Escalate toward `Major` when timing, ordering, +threading, concurrency, caching, resource lifetime, or other side effects become observable and +compatibility-sensitive (for example, a change from synchronous to lazy evaluation that alters +when exceptions surface). + +## 4. Worked classification examples + +**Example 1 — clear Major** +Input: "Removed the obsolete `LegacyClient.Connect(string)` overload and renamed `IParser.Read` +to `IParser.ReadAll`." +Output: `Major`. Removal plus a rename of a public interface member breaks binary and source +compatibility, and forces implementers to change code. + +**Example 2 — clear Minor** +Input: "Added a new `RetryPolicy` class and a `WithRetry(...)` extension method; existing APIs +unchanged." +Output: `Minor`. Purely additive, backward-compatible public surface. + +**Example 3 — clear Patch** +Input: "Fixed a NullReferenceException in an internal cache; no public API or observable behavior +change. Updated a transitive dependency patch version." +Output: `Patch`. Internal fix plus a compatible dependency bump. + +**Example 4 — ambiguous, needs explanation** +Input: "Tightened `Parse` to reject empty strings instead of returning null." +This depends on whether `Parse` is public, whether returning null was documented, and whether +consumers rely on it. The deciding fact is the API's visibility and documented contract — surface +that in explanation mode rather than guessing.