Context
“We won’t break the API” is a sentence. Breaks happen through small edits nobody flags: a field made optional, a max length lowered, an enum value dropped.Decision
The surface is kept as data incontract/: the OpenAPI paths and every MCP
tool’s input schema at the moment they were frozen. lib/contract.ts compares
the current surface with it by the rules in
Stability: inputs may only accept more, outputs may
only promise more. The test suite checks the branch’s own copy; CI checks the
base branch’s, so re-freezing in the same change can’t hide a break.
Consequences
- Additions have to be frozen too (
pnpm contract:freeze), so what ships in 1.1 is as fixed as what shipped in 1.0. - The comparison is structural. A change of meaning with the same shape is still caught only in review.