Schemas#
Current version: 0.2. The permanent 0.1 URLs remain available unchanged: assessment input, score result, rubric configuration. See the changelog for interpretation changes and compatibility.
Three JSON Schemas, served at the exact URLs their $id values name. A $id is
a promise, and validators fetch it, so these paths are permanent. When a later
version ships it's served alongside this one, never in place of it. The
version segment belongs to the standard, not to the rubric calculations, so a
recalibration can't orphan every validator that already trusts a URL.
| Schema | What it describes |
|---|---|
assessment-input.schema.json |
What goes in: per-face centering measurements and an enumerated list of defects. |
score-result.schema.json |
What comes out: the grade, the points, the binding face and region, and a line item for every penalty applied. |
rubric-config.schema.json |
The shape of a rubric itself: the penalty tables the scorer reads. This is what makes a per-set or experimental rubric possible without forking the scorer. |
A worked example, end to end#
This is a synthetic arithmetic example. It's the same case the README quickstart runs, the worked example in the rubric, and one of the conformance vectors, so if it ever drifts, three other places say so.
An assessment going in. One moderately damaged bottom-right corner on the back, and nothing else:
{
"back": {
"defects": [
{ "region": "corner", "corner": "br", "severity": "moderate" }
]
}
}
The complete result coming out, generated by the pinned reference scorer:
{
"rubric_id": "default",
"rubric_version": "0.2",
"points": 825,
"grade": 8,
"grade_label": "8",
"band": "LP",
"binding_face": "back",
"binding_region": "corners",
"faces": {
"front": {
"regions": {
"centering": 1000,
"corners": 1000,
"edges": 1000,
"surface": 1000,
"print": 1000
},
"side_points": 1000
},
"back": {
"regions": {
"centering": 1000,
"corners": 825,
"edges": 1000,
"surface": 1000,
"print": 1000
},
"side_points": 825
}
},
"line_items": [
{
"face": "back",
"region": "corners",
"detail": "br moderate",
"penalty": 175
}
]
}
Read that bottom-up and you have the whole method. One penalty of 175 came off
the corners region on the back. That region is now the worst on its face, so
the back scores 825. The front is untouched at 1000, and the card takes the
worse of the two. Then the ladder: every 50 points is half a grade, so 825 is
an 8.
And a slice of the rubric the scorer read to get there. The penalty tables are data, not code:
{
"rubric_id": "default",
"rubric_version": "0.2",
"corner_penalties": {
"de_minimis": 15,
"minor": 55,
"moderate": 175,
"heavy": 325
}
}
Each file declares the address it's served from. If you fetch one and its $id
disagrees with where you got it, something is wrong and you shouldn't trust it.
That check runs against these files before every deploy.
A limit worth knowing before you build#
All three schemas set additionalProperties: false at their root. Unknown
fields are rejected rather than ignored, so a document carrying an extension the
standard doesn't define will fail validation.
These schema roots do not accept arbitrary extra fields. A product may keep a separate, versioned schema and map supported findings into Touchstone inputs; that does not make its extra fields part of the standard. The scorer's treatment of annotation fields is not a substitute for full JSON Schema validation.
Validating against them#
The schemas are ordinary JSON Schema (draft 2020-12) and work in any validator
that resolves $ref by URL. They're also published in the repository, so a
build that must not reach the network can vendor them and check them against
these URLs.