Contract shape
Each editor accepts an object containing name, description, inputSchema and optional annotations. Tool names use one lowercase initial letter followed by lowercase letters, digits or underscores, up to 64 characters. Descriptions contain 8–1,000 characters. These are workbench constraints, not a claim about every name a browser might accept.
The root input schema must declare type: object. Every named case also supplies an object input. JSON syntax errors, duplicate case names and unknown contract fields stop the comparison. Duplicate keys inside a pasted JSON object follow normal JSON parsing, where the last value wins; avoid them because they obscure intent.
Supported rule families
- Types and values
- A single
type: object, array, string, number, integer, boolean or null.enumandconstconstrain values. - Objects
properties,required,additionalProperties,minPropertiesandmaxProperties. Undeclared fields are accepted unless additionalProperties says otherwise.- Arrays
- One schema for all
items, plusminItems,maxItemsanduniqueItems. Tuple-style item arrays are outside this profile. - Strings and numbers
minLength,maxLength,minimum,maximum, numericexclusiveMinimum/exclusiveMaximum, and positivemultipleOf.- Composition
allOf,anyOf,oneOfandnot. Boolean subschemas are allowed. Each composition list contains one to four schemas.- Documentation
titleanddescriptionare accepted annotations. Changes appear in the path diff; they do not change input validity.
What is excluded
The profile rejects $ref, $defs, $schema, format, pattern, patternProperties, conditional keywords, dependent keywords, type arrays and unlisted extensions. Remote references are never fetched. Regular-expression rules are excluded to avoid running arbitrary expensive patterns in the page. If your real contract relies on one of these, do not delete the rule merely to get a passing comparison. Use your project’s full validator.
default is also excluded. ContractKit never inserts missing values, coerces strings to numbers, strips extra properties or mutates the input. A case with "seats": "2" remains different from one with "seats": 2.
Bounded by design
A run contains 1–30 cases. Each text editor is capped at 60,000 characters; a saved pack is capped at 180 KB. Schema traversal allows at most 160 nodes and depth eight, with at most 40 declared properties and 30 enum choices. Additional total JSON-node and nesting limits protect the interactive page. Reserved property names such as __proto__, constructor and prototype are rejected by this workbench.
The validator stops at the first failing rule for each revision and input. Error paths show where to investigate; they are not an exhaustive list of every defect. A required-field error can occur at the containing object rather than at a missing value.
Implementation and interpretation
Local validation uses @cfworker/json-schema 4.1.1 with Draft 7 selected. It interprets schemas without generating executable code, so the site can retain a Content Security Policy without unsafe-eval. The supported subset and limits above are imposed by ContractKit before that validator runs. See the validator’s primary documentation and our test method.
A fixture comparison is a finite observation. Two contracts can agree on all your cases and still differ on an input you never supplied. Add examples at each boundary you change and retain at least one ordinary successful request.
Required, optional and nullable are three different decisions.
Suppose a tool accepts an optional nickname string. A revision makes that property required but permits either a string or null. The change widens one value rule while tightening presence. Calling the whole revision simply “more permissive” misses the old caller that omitted the field.
| Input | Before: optional string | After: required string or null |
|---|---|---|
{} | Accept | Reject: missing property |
{"nickname":null} | Reject: wrong type | Accept |
{"nickname":""} | Accept | Accept |
{"nickname":"Maple"} | Accept | Accept |
{"nickname":4} | Reject | Reject |
An empty string passes here because neither contract specifies minLength. Null passes in the second contract because its value schema explicitly permits null, not because null means “absent.” The JSON Schema object reference separates required property presence from its value rules.
Run the exact cases in this workbench.
Copy the before contract into Before, the after contract into After, and the five named cases into the case editor. Run cases. The omitted-field case should tighten from accept to reject, while explicit null should widen from reject to accept. The other three are controls.
The after schema uses anyOf with separate type: "string" and type: "null" branches, which this bounded profile supports. A type array is outside this tool’s profile even when another validator supports that expression. Do not rewrite a real schema solely to force it through a smaller checker; use this fixture to learn the distinction.
Decide what null means in the product.
Schema acceptance does not establish whether null clears an existing nickname, leaves it unchanged or asks the user to choose later. Write that behavior separately in the tool contract and implementation. If the old omission meant “keep the existing value,” making presence mandatory can break a caller even when a null replacement is technically accepted.
To preserve omission as well as allow null, remove nickname from the after contract’s required list and rerun the same cases. Then the empty object and explicit null both pass, but they remain distinct inputs. No default is inserted and no account is changed by this local comparison.
Bring the question back to a concrete case.
Open ContractKit ↗