10 min read
Change Kinds — Complete Reference
Every change kind the StateAnchor gate can evaluate, its lane, score, plain-English description, and a minimal spec example. Use this when reading raw gate output, writing custom policy rules, or understanding why a sync was blocked.
Source of truth: lib/sync-gate.js ERR_KINDS / WARN_KINDS / INFO_KINDS (33 registered kinds total). lib/spec-diff.js emits 30 of those 33 kinds directly — all 16 ERR, 8 of 9 WARN, and 6 of 8 INFO. The remaining 3 (evaluator_disagreement_high, optional_status_code_added, metadata_changed) come from the evaluator council or scanner sources.
ERR lane — always blocks (16 kinds)
ERR-lane changes unambiguously break existing consumers. The gate returns action: block whenever any ERR finding is present, regardless of count or threshold. All 16 ERR kinds are emitted by lib/spec-diff.js.
| Kind | Score | What it means | Minimal example |
|---|---|---|---|
endpoint_removed | 40 | An entire endpoint was deleted from the spec. | Previous: GET /users, DELETE /users/{id}. Current: only GET /users. |
auth_changed | 35 | The security block on an endpoint changed — scheme added, removed, switched, or required scopes changed. | security: null → security: [{ bearer: [] }]. |
opaque_token_scheme_changed | 35 | The token scheme type changed (e.g., bearer → API-key). Emitted alongside auth_changed when the scheme type itself flips. | type: bearer → type: api_key. Every consumer must re-authenticate in the new credential format. |
field_removed | 30 | A required request body field was deleted. For parameter removals see param_removed. | Request body model removes role from its required list and properties. |
required_param_added | 30 | A new required parameter (path, query, header, or cookie) was added to an existing operation. | GET /orders gains ?tenant_id= with required: true. Existing callers are rejected without changing their code. |
param_removed | 30 | A parameter (path, query, header, or cookie) was removed from an endpoint. | GET /users loses its ?include_deleted query parameter. Consumers that send it receive an unrecognised field error. |
success_status_removed | 30 | A 2xx or 3xx HTTP status code was removed from an endpoint’s declared response set. | Operation declared 200 + 202; 202 removed. Polling loops waiting for 202 break. |
error_response_shape_changed | 30 | The field structure of a 4xx or 5xx error response changed (different property names, types, or required fields). | { "code": 404, "message": "..." } → { "error_code": "...", "detail": "...", "trace_id": "..." }. |
response_field_removed | 25 | A required property was removed from a response body schema. | UserResponse loses profile_url. Type-checked clients throw at deserialization. |
response_field_type_changed | 25 | A response body field changed its primitive type incompatibly. | id: integer → id: uuid. Database foreign keys and comparisons may corrupt silently. |
type_changed | 25 | An existing request body field or parameter changed its primitive type. For response field type changes see response_field_type_changed. | amount: string → amount: integer. Consumers parsing the value as the original type fail or corrupt data. |
enum_value_removed | 25 | An enum-typed request field lost one or more allowed values. Additions do not fire this kind. | role: enum [admin, user, guest] → [admin, user]. Consumers branching on guest hit an unhandled case. |
variant_removed | 25 | A variant was removed from a oneOf / anyOf discriminated union. | PaymentMethod oneOf: [CreditCard, BankAccount, CryptoWallet] → [CreditCard, BankAccount]. Consumers using CryptoWallet fail immediately. |
optional_param_now_required | 25 | An existing optional parameter was flipped to required. | ?page had required: false; now required: true. Callers relying on server defaults receive validation errors. |
validation_constraints_tightened | 20 | A request-field validation bound was made stricter: minimum raised, maximum lowered, minLength raised, maxLength lowered, or a regex narrowed. | minimum: 1, maximum: 1000000 → minimum: 100, maximum: 50000. Previously valid payloads return 422. |
response_schema_type_changed | 20 | The overall top-level response schema type changed (e.g., object → array). | GET /users response schema was User (object); now User[] (array). Every consumer parser expecting the old shape fails. |
WARN lane — threshold-based (9 kinds)
WARN-lane changes are potentially breaking depending on consumer behavior. Whether they block depends on warn_count_threshold in your project policy (default: 1 — first WARN blocks; set to 0 to make WARN advisory-only).
Emitted by the diff engine (8 kinds)
| Kind | Score | What it means | Minimal example |
|---|---|---|---|
required_added | 20 | A new required field was added to a request body model, or an existing body field was promoted from optional to required. Body fields only — for parameter-level required changes, see required_param_added / optional_param_now_required in the ERR lane. | Request body gains currency: string added to required: [...]. Consumers not sending it get a validation error. |
deprecation_violation | 15 | A field, parameter, or endpoint marked deprecated: true in the prior spec was removed before its sunset window expired. Emitted alongside the underlying removal kind. | GET /search/v1 had deprecated: true but was removed in this sync before the stated sunset date. |
field_renamed | 15 | A property’s $ref target changed — the diff engine treats this conservatively as a rename. | address: { $ref: AddressV1 } → address: { $ref: AddressV2 }. Consumers bound to the old schema shape lose fields. |
response_field_required | 15 | An existing optional response field was promoted to required in the response schema. Previously-absent field is now always returned. | profile_url was optional in the response; now in the required list. Consumers that handled the absent case may behave incorrectly. |
variant_added | 10 | A new variant was added to a oneOf / anyOf discriminated union. Additive, but consumers with exhaustive match/switch blocks hit an unhandled case. | PaymentMethod oneOf: [CreditCard, BankAccount] → adds CryptoWallet. Existing callers work; exhaustive switches fail on the new variant. |
optional_field_removed | 10 | An optional body field was removed from a request or response schema. Less severe than field_removed (ERR) because the field was not required. | Response model removes nickname which was optional. Consumers reading it receive undefined; null-safety checks may throw. |
response_constraints_relaxed | 10 | A validation bound on a response field was relaxed (max raised, enum broadened). On the response side this can break consumers that assume strict server validation. | Response field score had maximum: 5; now maximum: 100. UI display logic and database constraints designed for the old range may fail. |
response_enum_value_added | 5 | A new value was added to an enum on a response field. Consumers with exhaustive match/switch blocks hit an unhandled case on the new value. | status: enum [pending, processing, complete, failed] → adds cancelled, refunded. Exhaustive switch statements fail on the new values. |
Emitted by evaluator-council sources (1 kind)
This kind is mapped to the WARN lane in sync-gate.js but is not produced by the pure diff engine. It is emitted by the ICE closure-phase evaluator council.
| Kind | Source | What it means |
|---|---|---|
evaluator_disagreement_high | evaluator council | The closure-phase scorer detected high spread between Stage-C evaluators on this sync. Advisory only — never independently blocks. See Evaluator confidence scoring. |
INFO lane — always passes (8 kinds)
INFO-lane changes cannot break existing consumers. The gate always proceeds; findings are recorded in the sync run for visibility.
Emitted by the diff engine (6 kinds)
| Kind | Score | What it means | Minimal example |
|---|---|---|---|
endpoint_added | 0 | A new endpoint appeared in the spec. | Previous: GET /users. Current: GET /users, POST /orders. Consumers that don’t call the new endpoint are unaffected. |
field_added_optional | 0 | A new optional field or parameter was added. | Request body gains nickname: string without appearing in required: [...]. Existing consumers ignore unknown fields. |
deprecated_flag_added | 0 | An operation or field was marked deprecated: true — a forward signal to consumers without any contract change. | GET /search/v1 gains deprecated: true. The endpoint still works; consumers are signalled to migrate. |
constraints_relaxed | 0 | A validation bound on a request field was relaxed (max raised, min lowered, maxLength raised, minLength lowered, enum broadened). Previously valid payloads remain valid. | minimum: 10, maximum: 100 → minimum: 1, maximum: 10000. Consumers gain more flexibility; nothing breaks. |
description_changed | 0 | Only the description text on an endpoint or field changed. No contract impact. | description: "Get users" → description: "List active users with cursor-based pagination". |
endpoint_key_collision | 0 | Two endpoints in the spec normalize to the same internal key (e.g., trailing-slash variants). The first occurrence wins; later duplicates are advisory findings only. | Spec lists both GET /users and GET /users/ — trailing slash normalizes to the same key. |
Emitted by scanner / other sources (2 kinds)
These INFO kinds are classified in sync-gate.js but are not produced by the pure diff engine. They arrive from the live-API scanner or observation-plane sources.
| Kind | Source | What it means |
|---|---|---|
optional_status_code_added | scanner | A new success or error status code was added alongside existing ones. Additive — consumers that don’t branch on the new code are unaffected. |
metadata_changed | scanner | Non-behavioral metadata changed: tags, examples, externalDocs, or non-behavioral extensions. No runtime impact. |
How a change becomes a gate decision
- The diff engine (
lib/spec-diff.js) compares the previous IR to the current IR and emits zero or more findings, each tagged with itskind. lib/sync-gate.jsclassifies each finding into ERR, WARN, INFO, or UNCLASSIFIED based on the sets above.- The highest-severity lane present drives the gate action: ERR →
block(always); WARN →blockonly abovewarn_count_threshold; INFO →proceed; no findings →PASS. - Scorer / canary / OOD signals are overlaid as advisory findings without changing the gate action.