# Breaking-change guard

The breaking-change guard diffs freshly extracted contracts on every workspace update and reports each incompatible change with the consumer files it hits.

Where [blast radius](https://docs.repowise.dev/multi-repo/blast-radius) answers *what could
be affected*, the breaking-change guard answers a sharper question:
**did a provider change in a way that actually breaks its consumers?**
On every `repowise update --workspace`, the freshly-extracted contracts
are diffed against the previously-indexed set, and each incompatible
provider change is reported with the exact consumer files that call it.

## Detected change kinds

| Kind | Severity | Fires when |
|------|----------|-----------|
| `removed_endpoint` | breaking | A provider route, gRPC method, or topic that existed before is gone |
| `removed_field` | breaking (response) / warning (request) | A request or response field disappeared |
| `field_type_changed` | breaking | A field's type changed (e.g. `string → int64`) |
| `field_number_changed` | breaking | A proto field's wire number changed |
| `field_required` | breaking | A field became required, or a new required field was added |

**Non-breaking changes never flag.** An added *optional* field, a
widened set, or a brand-new endpoint produces no record. Field-level
diffs need a contract *schema*: today gRPC carries one (proto message
fields, recovered by the existing proto parser), and HTTP gains
field-level checks when an OpenAPI spec is present. Route-level removal
is detected for every transport from the contract id alone. Data/DB
contracts don't get field-level diffing, only table-level removal. The
consumer side is a heuristic SQL match, not a typed schema.

Impacted consumers are resolved from the matched contract links, the
same provider↔consumer evidence the
[system graph](https://docs.repowise.dev/multi-repo/system-map)'s edges are built from. Impact
is therefore endpoint-precise (the consumer file that calls the changed
contract) and direct (the first reachability hop, which is exactly what
a contract break endangers; transitive ripple stays the job of blast
radius).

## Three ways to use it

### REST

`GET /api/workspace/breaking-changes` returns the report from the most
recent update, filterable by `repo` or `severity`. Each change carries
its provider, detail, and impacted consumers with both code sides.

### MCP

The [`get_risk`](https://docs.repowise.dev/mcp/get-risk) PR-mode directive carries a
`breaking_changes` block listing the provider contracts that changed
incompatibly in the diff's repo and the consumers they endanger, across
repos.

### System Map

Toggle **Breaking changes** above the
[System Map](https://docs.repowise.dev/multi-repo/system-map): changed providers are
badged with their breaking count, the consumers they endanger are badged
*at risk*, and the seams between them are highlighted (an additive
overlay, so the map stays whole). A side panel lists each change with
both the provider and consumer files.

> **Note**
>
> The guard compares the current index against the previous one, so the
> first workspace index has nothing to diff against. Breaking changes
> appear from the second `repowise update --workspace` onward.
