---
name: broker-api-changelog-diffing-tool
description: Use when updating broker SDKs or API integrations to diff release-over-release
  OpenAPI schemas, resolving $ref models and detecting breaking endpoint removals, removed
  response codes and content types, requirement changes, direction-aware enum mutations,
  and type changes before production deployment.
domain: algorithmic-trading
subdomain: broker-integration
tags:
- broker-integration
- api-changelog
- schema-diffing
- openapi
- breaking-changes
- ci-cd
brokers_frameworks:
- OpenAPI 3.x
- Swagger 2.0 (reference resolution only)
version: "3.0.0"
author: algo-trading-skills-contributors
license: Apache-2.0
---

## When to Use

Invoke this before upgrading a broker SDK version or pulling a new API specification
(Binance, Coinbase, IBKR Client Portal and similar publish OpenAPI documents). Broker
releases introduce silent breaking changes — a removed nested response field, a parameter
that quietly became mandatory, a new order status the state machine has never seen. This
skill diffs two schema snapshots and classifies what changed, so a CI job can fail the
build before the change reaches an order path.

The tool is a **gate**, and its failure modes are asymmetric: a false positive costs a
developer a few minutes, a false negative ships a broken integration. Everything about its
classification is biased accordingly.

## When NOT to Use

- **As proof a release is safe.** It compares structure only. Rate limits, auth scope
  changes, altered matching-engine behaviour, changed rounding, new error codes returned
  in a 200 body — none are expressible in a schema, and none will appear in the report. A
  clean diff means "nothing structural broke", not "safe to deploy".
- **On specifications it cannot fully resolve.** Only local (`#/...`) references are
  followed. External and remote `$ref`s are reported as `UNRESOLVED_REF` — that region was
  *not compared*, and treating the report as complete when one is present is a mistake.
- **As a Swagger 2.0 differ.** `#/definitions/...` references resolve, but Swagger 2.0's
  body parameters and top-level `consumes`/`produces` are not modeled; the request-body
  logic assumes OpenAPI 3.x `requestBody.content`. Convert 2.0 documents to 3.x first.
- **As a file loader.** It takes parsed Python dictionaries. Reading and parsing JSON or
  YAML is the caller's job.
- **For composition keywords.** `oneOf`, `anyOf`, `allOf` and `discriminator` are not
  evaluated; schemas using them will diff only at the level the tool can see.

## Prerequisites

- Baseline (older) and target (newer) API schemas, parsed into dictionaries.
- Both documents complete, including the `components`/`definitions` sections the
  `$ref`s point at — a spec split across files must be bundled first, or references will
  come back unresolved.

## Workflow

1. **Load both documents and let the differ reject unusable input.** `diff_schemas`
   raises `SchemaDiffError` when a document is not a mapping or has no `paths`. This is
   deliberate: a failed download or a wrong path yields an empty document, and a differ
   that shrugs and reports zero changes turns the gate green at exactly the moment it
   matters.

2. **Diff endpoints.** Removed paths and removed methods are `CRITICAL_BREAKING`. Only
   real HTTP methods are treated as operations — a Path Item Object also legally carries
   `parameters`, `servers`, `summary`, `description` and `$ref`, and path-level
   `parameters` are diffed as shared across every operation.

3. **Resolve `$ref` before comparing anything.** Real broker specs describe payloads
   almost entirely through references, and a `$ref` schema carries no `type`, `properties`
   or `enum` of its own. Resolution follows `#/components/schemas/...` and
   `#/definitions/...` against the document each side came from, with cycle protection for
   self-referential models.

4. **Treat absence as a change.** A removed response status code, a removed request or
   response content type, and a removed `requestBody` are all breaking and all invisible
   to a differ that walks only the keys present on both sides.

5. **Check requirement transitions in both directions.** A request field or parameter
   moving *into* `required` breaks callers that omit it. A response field moving *out of*
   `required` breaks parsers that assume it is present. Both matter; they are not the same
   check.

6. **Classify enums by direction.** A request enum constrains what the client may send, so
   *removing* a value is breaking. A response enum constrains what the client must handle,
   so *adding* a value is breaking — a new order status silently breaks an exhaustive state
   machine. Newly imposing a request constraint, and dropping a response constraint, are
   breaking too.

7. **Gate the build.** `report.exit_code` is 0 when compatible and 1 otherwise;
   `report.format_report()` renders the findings severity-first. `is_compatible` is False
   if any change is `MEDIUM_BREAKING` or higher.

> Full procedure: see `references/workflows.md`.
> Severity matrix and classification rationale: see `references/standards.md`.
> Printable pre-flight checklist: see `assets/checklist.md`.

## Common Pitfalls

- **Comparing `$ref` schemas without resolving them.** Both sides look like empty objects,
  every check is skipped, and a release that deleted an entire response model reports
  clean. This is the single most likely way to get a false green.
- **Ignoring an `UNRESOLVED_REF` finding.** It is informational in severity but it means a
  region of the schema was never compared. Bundle the spec and re-run.
- **Only diffing keys present on both sides.** Removals are the breaking changes; a
  loop written as `if key in new: compare(...)` cannot see any of them.
- **Treating enum changes as direction-agnostic.** Flagging every set difference raises
  false alarms on request widenings while missing the response additions that actually
  break consumers.
- **Assuming a scalar `type`.** OpenAPI 3.1 allows `type: ["object", "null"]` where 3.0
  used `nullable: true`. An equality test against the literal `"object"` silently skips
  property diffing, and comparing the two spellings reports a mutation that never happened.
- **Treating every key under a path as an HTTP method.** `parameters` is a list and
  `summary` is a string; calling `.get()` on them raises on a perfectly valid document.
- **Letting an empty or malformed document produce a clean report.**
- **Unbounded recursion on self-referential models.** `Order.parent → Order` is ordinary,
  and resolving references without a cycle guard hangs the build.
- **Reading a clean report as deployment approval.** Structure is not behaviour.

## Verification

- Run the unit suite and confirm every test passes:
  `python -m unittest discover -s skills/broker-api-changelog-diffing-tool/scripts`
- Build a fixture whose response model sits behind a `$ref`, delete a field from the
  referenced component, and confirm `REMOVED_RESPONSE_FIELD` is reported. A differ that
  passes every inline-schema test can still fail this one, which is the case that matters.
- Confirm two empty documents raise `SchemaDiffError` rather than reporting compatible.
- Confirm a self-referential model terminates.
- Confirm direction-aware enum behaviour: adding a response enum value is breaking; adding
  a request enum value is not.
- Confirm a Path Item Object carrying `parameters` and `summary` does not raise.
- Mutate fixtures by deep-copying a baseline and changing exactly one thing, so a test for
  one change cannot accidentally introduce another.

## Related Skills

- `broker-api-versioning-migration-playbook`
- `broker-api-deprecation-notice-monitoring`
- `sandbox-vs-production-endpoint-drift`
- `broker-agnostic-adapter-interface`
