Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Classifies each change in a public-surface diff (function signature, REST request/response schema, or library API) as breaking, additive, or internal by concrete rules — including the input-vs-output asymmetry that flips the verdict — then derives the semver bump for the release, applying the 0.x pre-1.0 rule. Use when someone hands you a diff of a published API and asks whether it breaks callers or what version the next release should be. Do NOT use for generating a changelog from commit messages, for reviewing a third-party dependency version bump, or for reviewing application source code for bugs.
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-06 | ✗→✓ | ▲ Improved | 567% | 0% |
| case-09 | ✗→✓ | ▲ Improved | 594% | 0% |
| case-14 | ✗→✓ | ▲ Improved | 551% | 0% |
| case-01 | ✗→✗ | = Same ✗ | 560% | 0% |
| case-02 | ✗→✗ | = Same ✗ | 604% | 0% |
Given a diff of a public surface — a function/method signature, a REST request or response schema, a GraphQL type, a CLI flag set, or any library API other people call — classify each change and derive the version bump the release needs.
The base model knows semver at a slogan level ("breaking = major") but applies it unevenly. Its two systematic errors:
0.x.y version it will say "breaking change, so bump to 1.0.0" when the correct bump is the next minor (0.NEXT.0).This skill makes the classification mechanical and the semver bump correct.
A public API is a contract with two sides. A caller sends inputs and consumes outputs. Compatibility is about not breaking the caller, so the two sides have mirror-image rules:
So the same verb classifies differently by side: adding a required field to a request = breaking, but adding a field to a response = additive. Ask "is this the input side or the output side?" before deciding.
| Change | Side | Class | |---|---|---| | Remove or rename a public function, endpoint, field, param, enum member, or error code | either | breaking | | Change a type incompatibly (e.g. int → string, object → array) | either | breaking | | Add a new required request param / argument (no default) | input | breaking | | Make an existing optional request param required | input | breaking | | Tighten a request type: narrow accepted values, add a stricter validator, string → enum, raise a minimum | input | breaking | | Reorder positional parameters | input | breaking | | Remove a response field, or make an always-present response field optional/nullable | output | breaking | | Change the exception/error type thrown for the same condition | output | breaking | | Change a URL path, HTTP method, or default value that alters behavior | either | breaking | | Add a new endpoint, method, or function | — | additive | | Add a new optional request param with a default (omitting it preserves old behavior) | input | additive | | Widen a request type: accept more values than before | input | additive | | Add a new field to a response object | output | additive | | Add a new enum member the request will now accept | input | additive | | Deprecate (mark, keep working) a field or param | — | additive | | Change a private / underscore-prefixed / @internal symbol | — | internal | | Refactor internals with identical observable behavior | — | internal | | Docstring, comment, or formatting change | — | internal | | Bug fix that restores documented behavior | — | internal (patch) |
Adding a new member to an enum a response can now return is not purely additive. Callers that exhaustively switch/match on the old set will hit an unhandled case. Conventionally it is treated as additive/minor (most clients tolerate unknown values), but call it out as a compatibility risk — the safe framing is "additive but may break exhaustive consumers." This is the mirror of accepting a new enum value on the input side, which is unambiguously additive.
The release bump is the highest-impact class in the diff: one breaking change makes the whole release breaking, no matter how many additive changes ride along.
| Highest class in diff | Version ≥ 1.0.0 | Pre-1.0 version 0.y.z | |---|---|---| | breaking | MAJOR — (X+1).0.0 | MINOR — 0.(y+1).0 | | additive | MINOR — X.(Y+1).0 | PATCH — 0.y.(z+1) | | internal / bug fix | PATCH — X.Y.(Z+1) | PATCH — 0.y.(z+1) |
The 0.x rule (pre-1.0): while the major is 0, semver treats the API as unstable, and the common convention (npm, Cargo) shifts every level down one notch — the minor slot plays the role of "major/breaking" and the patch slot absorbs features and fixes. So a breaking change on 0.4.2 releases as 0.5.0, not 1.0.0. Do not promote to 1.0.0 for a breaking change unless the maintainer is explicitly declaring the API stable. (Strict reading: on 0.0.z every change may break; flag that edge if you see it.)
For a given diff, produce:
breaking / additive / internal, one line of why, naming the input-vs-output side when it decides the verdict.Diff (library at 2.3.1):
- def fetch(id):
+ def fetch(id, *, region): # no default valuefetch gained a required argument on the input side → breaking. Existing calls fetch(id) now raise TypeError. Release: 2.3.1 → 3.0.0. Migration: give region a default, or update all call sites.
Diff (REST API at 0.6.4):
response User:
+ last_login: string # new fieldA field added to the output → additive. Pre-1.0, additive bumps the patch → 0.6.4 → 0.6.5.
internal package), it is internal regardless of how drastic the change looks.Other measured skills in the registry, with their headline benchmark lift.