27 typed change classes
The deterministic taxonomy Delimit's merge gate uses to classify every change in AI-written code before issuing a signed, replayable attestation.
Every change in an OpenAPI diff falls into one of 27 typed classes. Each class has a name, a canonical example, a severity disposition (breaking, additive, or hygiene), and a short consumer-impact note. The merge gate emits a structured record per change; the records are what gets signed into the attestation. This page is the canonical reference.
Source of truth: the ChangeType enum in core/diff_engine_v2.py (lines 10 to 40). The is_breaking property (lines 51 to 70) defines the severity partition shown below.
Wire-incompatible. Existing consumers fail without code change.
Pure expansion. Existing consumers keep working unchanged.
Spec-doc churn. No consumer effect; surfaced for review.
The 27 types
Anchor links are stable. Reports under delimit.ai/reports/ link type IDs back to the matching anchor on this page.
endpoint_removedbreakingA path that existed in the old spec is absent from the new spec. Any consumer call to the old path returns 404.
# old paths: /v2/organizations/{orgId}/workflows: { get: { ... } } # new (path absent)Live finding: cal.com F2 (99 endpoints removed)
method_removedbreakingA specific HTTP method on an existing path is gone. The path may still resolve for other methods, but the dropped verb returns 405.
# old /users/{id}: delete: { ... } get: { ... } # new /users/{id}: get: { ... }required_param_addedbreakingA new request parameter is marked required. Consumers who do not send it now fail validation at the gateway.
# new parameters: - name: tenant_id in: query required: true schema: { type: string }param_removedbreakingA parameter consumers may have been sending is gone. Senders get a strict-mode rejection or silent ignore depending on the runtime.
# old parameters: - { name: before, in: query, schema: { type: string } } # new (parameter absent)Live finding: OpenAI F4 (before, cert_id removals)
response_removedbreakingA documented response code (e.g. 200, 404) on an operation is no longer declared. Consumers parsing strictly typed clients may not have a branch for the new behavior.
# old responses: '200': { ... } '404': { ... } # new responses: '200': { ... }required_field_addedbreakingA field becomes required on a request schema. Existing consumer payloads that omit the field now fail validation.
# new UserCreate: required: [email, name, tenant_id] properties: email: { type: string } name: { type: string } tenant_id: { type: string }Live finding: OpenAI F2 (55 new required fields)
field_removedbreakingA schema property is gone. Code that reads the field from a response, or writes it on a request, hits an undefined or rejected field.
# old WebauthnVerify: properties: rpId: { type: string } rpOrigins: { type: array, items: { type: string } } # new WebauthnVerify: properties: {}Live finding: Supabase F1, F2 (rpId, rpOrigins dropped)
type_changedbreakingA schema property changed JSON type (string to object, array to string, etc.). Any deserializer with a fixed shape breaks on parse.
# old redirect_uri: type: string # new redirect_uris: type: array items: { type: string }Live finding: OpenAI F1 (196 type_changed events)
format_changedbreakingThe string format on a property changed (date to date-time, uuid to plain string, etc.). Validators with format-aware parsers reject the new shape.
# old created_at: type: string format: date # new created_at: type: string format: date-time
enum_value_removedbreakingA value disappears from an enum. Consumers sending the dropped value get rejected; consumers branching on the dropped value have dead code paths but no break on read.
# old trigger: enum: [BOOKING_CREATED, BOOKING_RESCHEDULED, ROUTING_FORM_FALLBACK_HIT] # new trigger: enum: [BOOKING_CREATED, BOOKING_RESCHEDULED]
Live finding: cal.com F5 (ROUTING_FORM_FALLBACK_HIT)
param_type_changedbreakingA parameter (path, query, header) changed type. Senders coercing to the old type get rejected at the boundary.
# old - name: limit in: query schema: { type: string } # new - name: limit in: query schema: { type: integer }param_required_changedbreakingA parameter flipped its required disposition. The breaking direction is optional-to-required (forces senders to update); required-to-optional flips also surface here for visibility.
# old - name: org_id in: query required: false # new - name: org_id in: query required: true
response_type_changedbreakingThe schema type of a response changed (e.g. content-type or top-level shape). Strictly typed clients fail to deserialize.
# old responses: '200': content: application/json: schema: { type: object } # new responses: '200': content: application/json: schema: { type: array, items: { type: object } }security_removedbreakingA security scheme (oauth2 flow, apiKey, http bearer) was removed. Calls authenticated via that scheme now fail authorization.
# old components: securitySchemes: bearerAuth: { type: http, scheme: bearer } # new components: securitySchemes: {}security_scope_removedbreakingAn OAuth2 scope was dropped from a flow. Tokens minted against the removed scope no longer authorize calls that required it.
# old flows: authorizationCode: scopes: read:bookings: Read bookings write:bookings: Write bookings # new flows: authorizationCode: scopes: read:bookings: Read bookingsmax_length_decreasedbreakingA string or array maxLength dropped. Existing values longer than the new limit fail validation on write and may flag on read.
# old description: type: string maxLength: 500 # new description: type: string maxLength: 200
min_length_increasedbreakingA string or array minLength rose. Existing values shorter than the new floor fail validation.
# old password: type: string minLength: 8 # new password: type: string minLength: 12
endpoint_addedadditiveA new path landed. No effect on existing consumers; they keep ignoring routes they never called.
# new paths: /v2/calendars/connections: post: { ... }Live finding: cal.com F4 (7 new calendar routes)
method_addedadditiveA new HTTP method on an existing path. Existing callers on other verbs are unaffected.
# old /users/{id}: get: { ... } # new /users/{id}: get: { ... } patch: { ... }optional_param_addedadditiveA new parameter with required: false. Existing senders omitting it keep working.
# new parameters: - name: include_archived in: query required: false schema: { type: boolean }response_addedadditiveA new documented response code. Existing clients that did not branch on it remain correct because the operation can return it for new behavior only.
# old responses: '200': { ... } # new responses: '200': { ... } '202': { ... }optional_field_addedadditiveA new property added with required: false. Existing read-side code ignores unknown fields; write-side code keeps omitting it.
# new Recipient: properties: email: { type: string } delivery_preference: { type: string } # newly added, optionalLive finding: DocuSign F1 (delivery_preference, 11 roles)
enum_value_addedadditiveA new accepted enum value. Existing senders keep working; readers may want to add a branch for the new value.
# old status: enum: [pending, sent, delivered] # new status: enum: [pending, sent, delivered, queued, retrying]
Live finding: Twilio F1 (4 new status values)
security_addedadditiveA new security scheme available. Existing authenticated calls keep working under the prior schemes.
# new components: securitySchemes: apiKeyAuth: type: apiKey in: header name: X-API-Keydeprecated_addedadditiveAn operation or field was marked deprecated. The contract still works at runtime; the marker is signal to consumers that removal is coming.
# new /v1/legacy/users: get: deprecated: true summary: Use /v2/users insteaddefault_changedadditiveA property default value changed. Senders who omit the field now get the new default, which is a behavior change but does not fail validation.
# old page_size: type: integer default: 25 # new page_size: type: integer default: 50
description_changedhygieneDocumentation text was edited without changing schema or surface. No consumer effect; recorded so spec-doc churn is visible to attestation review.
# old summary: Get user by id # new summary: Retrieve a user by their unique identifier
How the gate uses these
On every diff, the engine emits one record per change. Each record carries the typed class above, the severity, and the path or schema affected. The semver classifier walks the record set: any breaking class promotes the diff to MAJOR; only additive classes hold it at MINOR; only hygiene classes hold it at PATCH. There is no fuzzy scoring step.
The full record set is what gets signed into the attestation. A consumer who replays the attestation sees the same 27 typed classes, the same per-record evidence, and the same semver classification. That is the deterministic part of the deterministic taxonomy: same inputs, same record set, same bytes.
For AI-written code, this is the merge gate. For an AI-assisted merge, this is the audit trail. The signed, replayable attestation is the artifact downstream consumers verify against.