From qa-contract-testing
Diffs two OpenAPI versions for breaking changes (removed endpoints, deleted schemas, narrowed enums, response shape changes) using `oasdiff breaking`, severity-classifies the findings (ERR / WARN / INFO), and gates CI with `--fail-on`. Use when reviewing an OpenAPI spec change in a PR or on a release tag.
How this skill is triggered — by the user, by Claude, or both
Slash command
/qa-contract-testing:openapi-contract-diffThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
`oasdiff` is the canonical CLI for comparing two OpenAPI specifications
oasdiff is the canonical CLI for comparing two OpenAPI specifications
and reporting breaking changes (oasdiff-readme). It runs
over 250 checks against the diff and classifies each finding into one
of three severity tiers (oasdiff-breaking):
| Severity | Meaning |
|---|---|
ERR | Definite breaking change - should be avoided. |
WARN | Potential breaking change - developers should be aware. |
INFO | Non-breaking change. |
This is the schema-driven counterpart to
pact-contract-testing: use
oasdiff when you don't control the consumers (public APIs, third-
party integrations) and need to gate breaking-change shipment based on
the spec alone.
openapi.yaml, openapi.json,
or split-file equivalent).Unlike Pact, oasdiff doesn't add tests to the repo. Instead it consumes two OpenAPI specs and emits a diff. Authoring effort goes into keeping the spec authoritative - i.e. ensuring the OpenAPI file in the repo accurately describes the implementation. Tools that close the spec-vs-implementation gap (Schemathesis, Spectral lint) pair well with this skill but are out of scope.
# Go install (preferred for native CLI)
go install github.com/oasdiff/oasdiff@latest
# Docker (CI-friendly, no Go toolchain required)
docker pull tufin/oasdiff
(Per oasdiff-readme.)
oasdiff breaking openapi-old.yaml openapi-new.yaml
(Per oasdiff-breaking.)
By default oasdiff prints colorized text. For machine consumption, use
--format:
oasdiff breaking --format json openapi-old.yaml openapi-new.yaml > breaking.json
oasdiff breaking --format yaml openapi-old.yaml openapi-new.yaml
oasdiff breaking --format markdown openapi-old.yaml openapi-new.yaml
oasdiff breaking --format html openapi-old.yaml openapi-new.yaml > breaking.html
Available formats: text (default), JSON, YAML, markdown, HTML, plus single-line (oasdiff-breaking).
The CLI accepts URLs directly:
oasdiff breaking \
https://raw.githubusercontent.com/.../openapi-test1.yaml \
https://raw.githubusercontent.com/.../openapi-test3.yaml
(Adapted from oasdiff-readme.)
docker run --rm -t -v "$PWD:/specs" tufin/oasdiff breaking \
/specs/openapi-old.yaml /specs/openapi-new.yaml
--fail-on for CI gatingThe exit code is the gate signal (oasdiff-breaking):
oasdiff breaking --fail-on ERR ... # exit 1 on ERR; default behavior most teams want
oasdiff breaking --fail-on WARN ... # exit 1 on ERR or WARN; stricter
oasdiff breaking --fail-on INFO ... # exit 1 on any reported change
Default for production gates: --fail-on ERR. Use --fail-on WARN for
stricter projects (financial APIs, public SDKs) where even potentially-
breaking changes need explicit acceptance.
oasdiff ships 250+ checks. The headline breaking patterns (oasdiff-breaking):
responses block.components.schemas - even if no longer referenced.enum array.
Mark the enum x-extensible-enum: true to treat removals as
non-breaking (clients should ignore unknown values).string
to integer, removing a required array entry, etc.required - making a previously-
optional request field required.For the full list, run oasdiff checks to dump the active rule set.
Text output (default) example:
1 changes: 1 error, 0 warning, 0 info
error [response-success-status-removed] at openapi-new.yaml
in API GET /pets
the success response status '200' was removed
JSON output (for downstream parsing):
[
{
"id": "response-success-status-removed",
"level": 3,
"operation": "GET",
"operationId": "listPets",
"path": "/pets",
"source": "openapi-new.yaml",
"section": "paths",
"text": "the success response status '200' was removed"
}
]
level: 3 is ERR, 2 is WARN, 1 is INFO per the standard
oasdiff convention. Pipe to jq for triage:
jq -r '.[] | select(.level==3) | "ERR " + .operation + " " + .path + " :: " + .text' breaking.json
GitHub Actions example using the Docker image and the --fail-on ERR
default:
# .github/workflows/openapi-diff.yml
name: openapi-diff
on:
pull_request:
jobs:
oasdiff:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0 # need merge-base SHA for `git show <base>:openapi.yaml`
- name: Extract base spec
run: |
BASE_SHA=$(git merge-base origin/main HEAD)
git show $BASE_SHA:openapi.yaml > /tmp/openapi-base.yaml
- name: Run oasdiff
run: |
docker run --rm -v "$PWD:/specs" -v /tmp:/tmp tufin/oasdiff breaking \
--fail-on ERR \
--format text \
/tmp/openapi-base.yaml \
/specs/openapi.yaml | tee oasdiff-report.txt
- name: Upload report
if: always()
uses: actions/upload-artifact@v4
with:
name: openapi-breaking-report
path: oasdiff-report.txt
retention-days: 14
fetch-depth: 0 is required so the merge-base of the PR resolves; the
shallow clone GitHub uses by default doesn't include the merge-base
commit.
For a PR comment workflow, generate --format markdown and post via
actions/github-script@v7 - the markdown table format includes a
clickable per-finding rationale.
breaking subcommand, severity tiers,
--fail-on semantics, breaking-change categories.pact-contract-testing - the
consumer-driven counterpart for cases where you control consumers.contract-compatibility-gate - the gate skill that aggregates oasdiff output with Pact and
Protobuf verdicts.Provides a checklist for code reviews covering functionality, security, performance, maintainability, tests, and quality. Use for pull requests, audits, team standards, and developer training.
npx claudepluginhub testland/qa --plugin qa-contract-testing