{"criteria_id":"breaking-change-diff/v1","corpus":{"id":"breaking-change-diff-corpus/v1","sha256":"3e6c99e06566bc1135e4c8edf01663ae98474036fa045117113516a680e07b18"},"corpus_pin_definition":"The corpus pin is the SHA-256 of the corpus's canonical serialization (the corpus object excluding its sha256 field), computed and verifiable by the published harness (harness.mjs).","price":{"usdc_units_per_pair":20000,"label":"$0.02","basis":"per document pair"},"free_tier":{"per_day":100,"min_interval_ms":5000,"on_exhaustion":"402"},"input_contract":{"forms":["openapi","standalone-schema"],"openapi_versions":["3.0.0","3.0.1","3.0.2","3.0.3","3.1.0","3.1.1"],"schema_dialects":["draft-07","2019-09","2020-12"],"encoding":"JSON only (YAML is not accepted in v1)","caps":{"body_bytes":524288,"document_bytes":262144,"depth":64,"paths_per_document":500,"operations_per_document":2000,"schema_properties_per_document":20000,"findings_listed":500,"ref_hops":32,"ref_expansions_per_document":1000},"malformed":"Malformed, mixed-form, undetermined-dialect, duplicate-key, or over-cap submissions fail at the FREE pre-check with a closed-form error naming the defect class and a location pointer only; no submitted content is echoed. No partial or truncated verdict exists."},"parsing_rules":[{"id":"P1","text":"Duplicate keys: JSON is parsed with a duplicate-detecting parser. A repeated key at the same object level, in either document, is a malformed submission — never last-value-wins."},{"id":"P2","text":"Ordering insensitivity: object key order is ignored everywhere. required arrays, enum arrays, parameter lists (keyed by (name, in)), path-item and operation maps, property maps, tags, security requirement lists, and media-type maps are compared as SETS. allOf / anyOf / oneOf member lists and prefixItems are compared as ordered lists; a pure reordering of such a list is reported under R17, never silently ignored and never upgraded to a removal/addition pair."},{"id":"P3","text":"$ref policy: a $ref whose value begins #/ is a LOCAL reference, resolved inside the same submitted document (JSON Pointer) under a guard of 32 hops / 1,000 expansions per document; a local reference that does not resolve, or a reference cycle, makes the affected subtree NOT-ASSESSABLE (N5) — it is not malformed, and the rest of the document still diffs. Any other $ref value is an EXTERNAL reference: it is never fetched, never dereferenced, and no network call of any kind exists in this service. The subtree under an external reference is NOT-ASSESSABLE (N5), the finding record carries token EXTERNAL-REF-NOT-ASSESSED with the pointer location only (never the URI's value beyond its scheme class), and the report carries the PARTIAL marker. Sibling keywords next to a $ref are graded normally in versions where they have meaning (OpenAPI 3.1 / JSON Schema 2019-09+: read; OpenAPI 3.0.x: $ref siblings are ignored by that specification and are ignored here, with the census counting them)."},{"id":"P4","text":"Unknown keywords and extensions: keys beginning x- are extensions, ignored by every R-rule and counted in the census per document. Unknown non-extension keywords in schema positions are ignored per JSON Schema's own rule, counted in the census, and never graded. Ignoring is never silent in aggregate: both counts print."},{"id":"P5","text":"Descriptive content: title, description, summary, example / examples, and externalDocs are never graded and never produce findings, with the single exception of the deprecated boolean, which is a normative flag graded under R17 as REPORTED-ONLY. A description edit is not a change this service reports."}],"rules":[{"id":"R01","token":"PATH-REMOVED","text":"A path key present in A's paths and absent from B's paths. Its operations are not separately reported; one finding per path."},{"id":"R02","token":"PATH-ADDED","text":"A path key present in B and absent from A."},{"id":"R03","token":"OPERATION-REMOVED","text":"For a path present in both, an HTTP method key present in A and absent in B."},{"id":"R04","token":"OPERATION-ADDED","text":"A method key present in B and absent in A, on a shared path."},{"id":"R05","token":"PARAM-REMOVED","text":"On a shared operation, a parameter keyed (name, in) present in A and absent in B."},{"id":"R06","tokens":["PARAM-NEWLY-REQUIRED","PARAM-ADDED-OPTIONAL"],"text":"On a shared operation: a parameter present in B but not A with required: true, OR a shared parameter whose required changes false/absent to true -> PARAM-NEWLY-REQUIRED. A new parameter with required false/absent -> PARAM-ADDED-OPTIONAL."},{"id":"R07","token":"PARAM-NO-LONGER-REQUIRED","text":"A shared parameter whose required changes true to false/absent."},{"id":"R08","tokens":["REQ-PROPERTY-REMOVED","RESP-PROPERTY-REMOVED"],"text":"A property name present in A's schema properties and absent from B's, at any compared schema node (request side / response side)."},{"id":"R09","tokens":["REQ-PROPERTY-ADDED","RESP-PROPERTY-ADDED"],"text":"A property name present in B and absent in A. If the added request-side property is also in B's required set, R10 fires additionally — the two facts are reported as two findings, never merged."},{"id":"R10","tokens":["REQ-PROPERTY-NEWLY-REQUIRED","RESP-PROPERTY-NEWLY-REQUIRED"],"text":"A property name that enters a schema's required set in B (whether the property itself is new or pre-existing). A response schema's required set is graded symmetrically."},{"id":"R11","tokens":["REQ-REQUIRED-REMOVED","RESP-REQUIRED-REMOVED"],"text":"A property name that leaves a schema's required set in B while the property itself remains. (A property that disappears entirely fires R08 only.)"},{"id":"R12","tokens":["TYPE-SET-EXPANDED","TYPE-SET-CONTRACTED","TYPE-SET-CHANGED"],"text":"At any compared schema node or parameter, the normalised type set differs between A and B. The normalised type set is the set of JSON Schema type names the node admits: from type (string or array form), with OpenAPI 3.0's nullable: true normalised to adding \"null\" to the set, with a missing type normalised to the full seven-type universe set, and with enum NOT folded into the type set. Sb a proper superset of Sa -> TYPE-SET-EXPANDED; Sb a proper subset -> TYPE-SET-CONTRACTED; neither contains the other -> TYPE-SET-CHANGED; equal -> no finding. Subtype relations inside one type are not inferred (integer -> number is TYPE-SET-CHANGED carrying {integer}->{number})."},{"id":"R13","token":"FORMAT-CHANGED","text":"At a node where the type set is unchanged, the format value differs (including added/removed), carrying both format values."},{"id":"R14","tokens":["REQ-ENUM-VALUE-REMOVED","RESP-ENUM-VALUE-REMOVED"],"text":"A value present in A's enum set and absent from B's, at any compared node; one finding per value, carrying the value (scalar values print verbatim; composite values print as type + size only). An enum disappearing entirely is reported value-by-value against the empty set."},{"id":"R15","tokens":["REQ-ENUM-VALUE-ADDED","RESP-ENUM-VALUE-ADDED"],"text":"A value present in B's enum and absent from A's; one finding per value. An enum appearing where A had none is reported value-by-value against the empty set."},{"id":"R16","token":"CONSTRAINT-CHANGED","text":"At a node where the type set is unchanged, any of minimum, maximum, exclusiveMinimum, exclusiveMaximum, minLength, maxLength, minItems, maxItems, minProperties, maxProperties, multipleOf, pattern differs (added, removed, or value-changed): one finding per keyword, carrying the keyword and both values. For the bounded numeric/length keywords the finding additionally carries the mechanical direction word RAISED / LOWERED / ADDED / REMOVED for the bound itself; pattern carries no direction word."},{"id":"R17","tokens":["DEPRECATION-FLAG-CHANGED","COMPOSITION-REORDERED","RESPONSE-STATUS-REMOVED","RESPONSE-STATUS-ADDED","REQ-MEDIATYPE-CHANGED","RESP-MEDIATYPE-CHANGED","NULLABILITY-CHANGED"],"text":"Reported-only observations, reported in a separate REPORTED-ONLY section and counted only in the reported-only group: (a) deprecated flag added, removed, or flipped on any path, operation, parameter, or schema node -> DEPRECATION-FLAG-CHANGED, carrying the A and B boolean values; (b) pure reordering of an ordered composition list under P2 -> COMPOSITION-REORDERED; (c) response status-code set changes on a shared operation -> RESPONSE-STATUS-REMOVED / RESPONSE-STATUS-ADDED; (d) media-type set changes in a shared operation's request or response content -> REQ-MEDIATYPE-CHANGED / RESP-MEDIATYPE-CHANGED; (e) nullable (3.0) / type-set null membership (3.1) flips NOT already captured by R12 -> NULLABILITY-CHANGED (in practice this fires only for the 3.0 nullable keyword on a non-schema node, nullability elsewhere being normalised into the type set)."}],"renames":"There is NO rename rule and NO rename token. A renamed path, operation parameter, or property appears as exactly one REMOVED finding (under its A name) and one ADDED finding (under its B name). The service never infers that two names denote the same thing.","groups":{"REMOVAL-FORM":["PATH-REMOVED","OPERATION-REMOVED","PARAM-REMOVED","REQ-PROPERTY-REMOVED","RESP-PROPERTY-REMOVED","REQ-ENUM-VALUE-REMOVED","RESP-ENUM-VALUE-REMOVED"],"REQUIREMENT-FORM":["PARAM-NEWLY-REQUIRED","REQ-PROPERTY-NEWLY-REQUIRED","RESP-PROPERTY-NEWLY-REQUIRED"],"TYPE/CONSTRAINT-FORM":["TYPE-SET-EXPANDED","TYPE-SET-CONTRACTED","TYPE-SET-CHANGED","FORMAT-CHANGED","CONSTRAINT-CHANGED"],"ADDITION-FORM":["PATH-ADDED","OPERATION-ADDED","REQ-PROPERTY-ADDED","RESP-PROPERTY-ADDED","PARAM-ADDED-OPTIONAL","PARAM-NO-LONGER-REQUIRED","REQ-REQUIRED-REMOVED","RESP-REQUIRED-REMOVED","REQ-ENUM-VALUE-ADDED","RESP-ENUM-VALUE-ADDED"],"REPORTED-ONLY":["DEPRECATION-FLAG-CHANGED","COMPOSITION-REORDERED","RESPONSE-STATUS-REMOVED","RESPONSE-STATUS-ADDED","REQ-MEDIATYPE-CHANGED","RESP-MEDIATYPE-CHANGED","NULLABILITY-CHANGED"]},"not_assessable_token":"EXTERNAL-REF-NOT-ASSESSED","constraint_keywords":["minimum","maximum","exclusiveMinimum","exclusiveMaximum","minLength","maxLength","minItems","maxItems","minProperties","maxProperties","multipleOf","pattern"],"bounded_keywords":["minimum","maximum","exclusiveMinimum","exclusiveMaximum","minLength","maxLength","minItems","maxItems","minProperties","maxProperties","multipleOf"],"type_universe":["array","boolean","integer","null","number","object","string"],"http_methods":["get","put","post","delete","options","head","patch","trace"],"descriptive_keys":["title","description","summary","example","examples","externalDocs"],"scope_block":"This report is a deterministic mechanical comparison of the two documents you submitted, under the rules published in breaking-change-diff/v1. It reports the differences those rules find between the document labelled version A and the document labelled version B, exactly as submitted, as counts and located findings in the published tokens. It does not establish that an upgrade from A to B will break, or will not break, your code or any caller's code: the check sees two documents and no usage, and a difference that matters to one caller is irrelevant to another. It does not assess anything that is not in the two documents — no other version, no changelog or release note, no behaviour of a running service, and no register of known changes, because no such register is used. A report of zero findings means the two submitted documents do not differ under the published rules, and nothing more. Submitted documents are processed in memory only and are never stored.","not_assessable":[{"id":"N1","title":"Semantic usage impact","text":"Whether any difference found (or not found) affects your code, your callers, or any particular usage. The check compares two documents; it does not see any caller, call graph, or runtime."},{"id":"N2","title":"Un-submitted versions and artefacts","text":"Any version other than the two documents submitted, in either direction; any endpoint, schema, or behaviour described anywhere else (changelogs, release notes, documentation, the live service itself)."},{"id":"N3","title":"Behavioural and runtime change not visible in the documents","text":"Changed defaults applied server-side, changed error semantics, changed ordering, pagination, rate limits, performance, side effects, and any behaviour the two documents do not themselves state as compared fields. Two identical documents yield a zero-findings diff even if the implementations differ entirely."},{"id":"N4","title":"Register completeness","text":"There is no register behind this check and none is implied: the diff asserts nothing about changes recorded anywhere else, and a zero-findings diff asserts nothing beyond the two submitted documents (see also N2/N3)."},{"id":"N5","title":"Un-assessable subtrees of the submitted pair","text":"Subtrees under an external $ref (never fetched, P3), under an unresolvable local $ref, or inside a reference cycle: counted in the verdict line as not-assessable subtrees, located by pointer, and excluded from every count of compared nodes. The PARTIAL marker is present whenever N5 is non-empty."},{"id":"N6","title":"Correctness of either document","text":"Whether version A or version B is valid against its own specification beyond the parsing and determination steps of the input contract, whether either document describes the API it claims to describe, and whether either was the version actually deployed. (Document-vs-specification validity is the schema-conformance service's question.)"}],"retention":"Submitted documents are processed in memory only and are never stored — not in KV, not in logs, not in the payments ledger, not in metrics, and not in error records. The payments ledger records only the payer, the time, the price, the criteria version, and the per-class counts. No path, operation, field, or type name, no document text, and no hash of any submitted content is recorded. Because nothing is kept, a paid retry re-sends both documents. The report issued to the payer is the only artefact that carries the diff, under the identifier rule printed in the criteria.","payment_includes":"$0.02 per document pair. The fee is per pair: each pair you submit is charged at $0.02. The payment includes: one pair of submitted documents — two versions of one API-surface document, each an OpenAPI 3.0.x or 3.1.x document in JSON form, or each a standalone JSON Schema document (draft-07, 2019-09, or 2020-12) — compared mechanically under the published rules of the criteria version named in the report, returned as one signed report listing every element the rules record as removed, added, or changed between the two versions you submitted, each item stated as it appears in the documents. A free pre-check returns the headline only: whether the pair can be graded, and the count of recorded changes per class. The report establishes only the differences between the two documents you submitted, under the published rules. It does not establish that your code uses any listed element, that a version with no listed changes is safe to upgrade to, or that the comparison covers anything beyond the two submitted documents; it is not a 'safe to upgrade' certification. Nothing is fetched from any register or external source; no release notes are read. Submitted documents are processed in memory only and are never stored; the payments ledger records only the payer, the time, the price, the criteria version, and the per-class counts.","never":"This service never issues a composite score, a percentage, or a single compatibility number; never issues pass/fail, compatible/incompatible, or any pair-level verdict; never states or implies that any finding will or will not affect any caller's code, usage, or production behaviour; never infers a rename, a move, or an intent behind any difference; never grades, fetches, or references any version, register, changelog, or release note that was not submitted in the call; never echoes prose content (description, summary, examples) from either document in any output, error, ledger, or pre-check; never retains submitted material beyond the request lifetime; and never emits a verdict over a shortened, sliced, or partially parsed submission.","kill_line":"Day-0 = the first OUTSIDE paid settle (house proofs never start the clock). Report-only checkpoint at Day-0 + 60 days; keep/park decision at Day-0 + 6 months. Tripwires: (1) stop-serving: refunds >5% of paid calls in any 30-day window; (2) stop-serving: free use >10x paid use; (3) stop-serving: a wrong verdict from undisclosed stale or partial data; (4) stop-and-review: any report, ledger line, or service surface found to carry submitted content beyond the fenced identifier rule; (5) stop-serving under the affected criteria version: a demonstrated diff error — the failing case enters the test corpus first as a regression case, corrected rules publish as a new criteria version with a new corpus version, the whole corpus re-runs at 100%, and serving resumes only under the new version; reports already issued are not amended or re-issued; (6) stop-and-review: a report from this service found circulating as a 'safe to upgrade' certification — a review opens within 5 business days and exactly one of format/terms remedy, public disavowal, or escalation to stop-serving for the pattern is taken, by recorded decision.","service":"apidiff","version":"1.0.0","endpoints":{"criteria":"GET /criteria (free)","precheck":"POST /precheck (free, headline only: census, verdict line, per-token counts, NOT-ASSESSABLE list in full, scope block)","diff":"POST /diff (paid, $0.02 per document pair)","metrics":"GET /metrics"}}