TOUCHSTONE 0.1

Touchstone 0.1#

Touchstone 0.1 is the open, reproducible standard for turning a card condition assessment — measured centering plus an enumerated list of defects — into a grade. Given the same enumeration, any implementation of this rubric produces the same number: the arithmetic is the standard, not a proprietary black box. The reference implementation lives alongside this document as scoring.mjs, interpreting the data in rubric.json; this file is the human-readable prose version of the same rules. Philosophy: forgive centering drift near mint, escalate as it becomes the card's defining flaw, and punish damage. A card can drift off center and still be a 10; a crushed corner or a paper break cannot hide behind a good average.

How a score is computed#

  1. Each region — centering, corners, edges, surface, and (config-gated) print — starts at the base score, 1000 (the flawless ceiling), separately per face. In plain terms: every face begins as a perfect card, judged four ways under a rubric with no print_attributes table, five under one that defines it (see Print quality below).
  2. Every defect and every non-zero centering deviation subtracts a penalty from its region. A region's sub-score is 1000 − Σ(penalties), floored at 0 — a region can't go negative no matter how many defects stack. In plain terms: each flaw takes points off the category it belongs to, and a category can be emptied but never owes points.
  3. A face's side_points is the minimum of its region sub-scores (four without a print_attributes table, five with one) — the worst region caps the whole side. In plain terms: a face is only as good as its worst category.
  4. The card's points is the worse of the two sides (pure bottleneck — a flawless back cannot rescue a damaged front). In plain terms: the card is only as good as its worse face. Which face that is, and how a tie between them resolves, is specified in Binding face and binding region below.
  5. points is floored to an integer before the ladder (custom rubrics may produce fractional penalties; the ladder only ever sees whole numbers). In plain terms: fractions are dropped before grading.
  6. The ladder converts points to a grade: raw = floor(points / 50) × 0.5, then grade = raw ≥ 9.5 ? 10 : max(raw, 1). The 9.5 slot is deliberately promoted to 10 — the market's 9→10 value cliff, expressed as arithmetic. Grade never reports below 1. In plain terms: every 50 points is half a grade; 950 or better is a 10 because the 9.5 slot is promoted.

Binding face and binding region#

A score result MUST name the face and the region that produced the grade.

The binding face is the face with the lower side_points. Ties MUST resolve to the front.

The binding region is the lowest-scoring region on the binding face. Ties MUST resolve to the earliest region in the fixed order centering → corners → edges → surface → print, where print participates only when the rubric enables the print-attribute ladders. This order is normative: two implementations that disagree on a tie produce different binding_region values for the same card and are not interoperable.

Centering#

Centering is a measurement, not a defect list entry. Per face, you supply lr_pct (the left border's share of left+right border width) and tb_pct (the top border's share of top+bottom); 50.0 is perfectly centered on that axis.

  • Deviation is the larger of the two axis offsets from 50: deviation = max(|lr_pct − 50|, |tb_pct − 50|). Deviation is the max axis, not the sum of both axes — a card off on only one axis isn't punished twice.

  • Inputs are accepted at any precision but quantized to 0.1 point before scoring, for determinism.

  • The penalty is a progressive, piecewise-linear curve per face. Each curve is a list of segments { up_to, slope }, sorted ascending; a segment spans from the previous segment's up_to (0 for the first) to its own, and the final segment is open-ended (up_to: null). The penalty accumulates slope × span across every segment the deviation crosses, then is rounded half-up ONCE at the endnot banker's rounding (JavaScript's default Math.round and Python's round() both round half-to-even in some cases; this rubric always rounds an exact .5 up). Concretely: penalty = floor(Σ slopeᵢ × spanᵢ + 0.5). A scalar slope s is exactly the one-segment curve [{ up_to: null, slope: s }], so the two forms share one semantics.

    Front curve (deviation in percentage points):

    Segment Slope (pts/pt) Penalty at segment end
    0 → 10 10 100
    10 → 15 15 175
    15 → 20 20 275
    20 → 30 12 395
    30 → 35 20 495
    35 → ∞ 40

    Back curve:

    Segment Slope (pts/pt) Penalty at segment end
    0 → 40 2 80
    40 → ∞ 10
  • Why these knees. The curve is shaped to the philosophy above: drift near mint is forgiven, and each knee is a point at which off-centering stops being a quibble and starts being the thing you notice about the card. Read out as border ratios, a card whose only flaw is centering grades:

    Front border ratio Deviation Penalty Points Grade
    55/45 5 50 950 10
    60/40 10 100 900 9
    65/35 15 175 825 8
    70/30 20 275 725 7
    80/20 30 395 605 6
    85/15 35 495 505 5
    90/10 40 695 305 3

    Each knee sits where a round border ratio lands on a whole grade: 60/40 at 9, 65/35 at 8, 70/30 at 7, 80/20 at 6, 85/15 at 5. No round ratio falls between 85/15 and 90/10, which is why the table steps from 5 straight to 3.

    The back is judged far more gently, because the reverse border is not what a collector is looking at:

    Back border ratio Deviation Penalty Points Grade
    75/25 25 50 950 10
    90/10 40 80 920 9

    Past the last rows on either table — front 90/10, back 90/10 — the curves escalate steeply (40 and 10 pts/pt): centering that bad is the card's defining flaw, and the grade should say so.

  • Worked GEM cutoffs: front 45/55 → deviation 5.0 → penalty 50 → 950 points → still 10; one tenth worse (44.9/55.1) → deviation 5.1 → 51 → 949 → drops to 9. Back 75/25 → deviation 25.0 → 50 → 950 → still 10; the gentle back slope (2/pt) plus half-up rounding holds GEM through 75.2/24.8 (deviation 25.2 → 50.4 → rounds to 50 → 950) and breaks it at 75.3/24.7 (deviation 25.3 → 50.6 → 51 → 949 → 9).

  • Knee-crossing example (also the single-round rule): front deviation 10.1 → 10 × 10 + 0.1 × 15 = 101.5 → rounded once, half-up → 102 → 898 points → grade 8.5. Rounding an exact accumulated .5 up: front deviation 10.3 → 100 + 0.3 × 15 = 104.5105 → 895 → 8.5 (half-to-even would have given 104).

Corner & edge defects#

Every corner (tl/tr/bl/br) and edge (top/right/bottom/left) defect is classified into one of four severity classes:

  • de minimis — faint whitening only visible up close or under angled light. The GEM budget is face-dependent, because front penalties carry the ×1.3 multiplier: a GEM card may keep three de-minimis in a back region (the fourth breaks GEM), but only two in a front region (the third breaks GEM). This matches the empirical finding that top-grade cards routinely carry a few flagged-but-forgiven dings, while holding the front to the stricter standard the multiplier implies.
  • minor — visible at arm's length but small: visible whitening or a small nick, a soft corner touch.
  • moderate — obvious at a glance: the corner point is flattened or rounded but the card layers are intact.
  • heavy — structural: layers visibly separated, frayed, or the corner is fully blunted/bent.

The GEM boundary is a deliberate construction: only de-minimis flaws can keep a 10. The mildest minor defect in the rubric — a minor edge on the back, penalty 51 — lands at 949, one point past the cliff. A defect visible at arm's length does not keep a 10.

Each distinct physical flaw is one defect object, and multiple defect objects MAY target the same corner or edge — a corner with both whitening and a nick is two defects. All corner penalties on a face sum into that face's single corners region sub-score (and likewise all edge penalties into edges): the bottleneck is per-REGION, not per-corner. The corner/edge label is locational metadata and never changes the score. Pooling per region is deliberate — a region is scored collectively; true per-corner sub-scores are a future rubric refinement.

Reference penalties (back-face values; front is ×1.3), deducted from that region:

Severity Corner Edge
de minimis 15 14
minor 55 51
moderate 175 150
heavy 325 305

Front multiplier: ×1.3. Every defect penalty on the front face — corner, edge, or surface — is multiplied by 1.3 and rounded half-up after multiplication (centering is exempt; it already has its own front/back slope). Applied to the tables above: front corner penalties are 20 / 72 / 228 / 423; front edge penalties are 18 / 66 / 195 / 397.

Surface defects#

Surface defects require a depth, a size, and normalized x,y coordinates (0–1 from the top-left of the face) — location is required here because, unlike a corner or edge, "surface" alone doesn't say where on the card.

  • Depthsurface: does not break the gloss (a scuff, a print line). scratch: a visible line you can catch a fingernail on. deep: a crease or break in the paper.
  • Sizedot: about 2mm or less. lt_1cm: under 1cm. lt_5cm: under 5cm. full_card: spans most of the card.

Reference penalty matrix (back-face values; front is ×1.3):

Depth \ Size dot lt_1cm lt_5cm full_card
surface 5 25 80 180
scratch 15 55 160 330
deep 35 110 280 510

The same ×1.3 front multiplier (rounded half-up) applies on the front face.

Line endpoints#

Surface, crease, and edge defects — the three linear regions — MAY carry an optional second point, x2,y2, normalized 0–1 exactly like x,y, turning the placed point into a line segment (e.g. a UI line-drag that measures a scratch and derives its size class from the physical length). Line endpoints are a presentational/derivation aid only and are never scored: the reference scorer validates them when present (each finite and 0–1, and only ever as a pair — a lone x2 is rejected with "x2 and y2 must be provided together") and the arithmetic then ignores them entirely, so a defect with endpoints scores identically to the same defect without them (test/vectors.json pins this). Point-only regions (corner, stain, print_defect) don't declare them — an x2 there is ignored as an unknown annotation field, like any other. Endpoints are a purely additive input-schema affordance: they change no penalty, and the severity/size classes remain the scored input.

Print quality — the criteria that decide vintage 10s and 9s (focus/registration, gloss, print imperfections, border whiteness, creases, staining) — takes two shapes, both mid-band fit:

  1. Attribute laddersfocus, gloss, border are face-global judgments (not located anywhere in particular), assessed independently per face as an optional print: { focus?, gloss?, border? } object on that face. Config-gated: a rubric without print_attributes rejects any print block outright ("print requires a rubric with print_attributes"); an absent object, or an absent attribute within it, means perfect — no penalty. Ladders are short.
  2. Placeable defect typescrease, stain, and print_defect sit alongside corner/edge/surface as region values on a defect. Unlike corner/edge (where x,y are optional annotation), all three require x,y — like surface, location matters for a placed flaw. Each carries a severity drawn from that region's own vocabulary; crease, stain, and print_defect each have a disjoint severity vocabulary from one another and from the shared corner/edge vocabulary — a crease defect never accepts a stain or print_defect severity word (or vice versa), even where a word is spelled the same (heavy is a valid word in all four vocabularies — shared corner/edge, crease, stain, print_defect — independently defined at a different penalty in each).

Mid-band fitting#

Every print-quality number below is fit by choosing the penalty that lands the card at the middle of the target grade's 50-point band, not its edge — penalty = 1000 − (100g + 25) for target grade g. A card whose only flaw is that one attribute or defect lands exactly grade g, with margin against the band edge on both sides.

Attribute ladders (per face, region print)#

Attribute Options → grade cap Penalty
focus sharp→10 · slightly_out→7 · noticeably_out→5 · severely_out→2 0 / 275 / 475 / 775
gloss full→10 · most_retained→7 · some_loss→6 · much_lost→3 · absent→2 0 / 275 / 375 / 675 / 775
border clean→10 · slightly_off_white→9 · yellowed→3 · browned→1.5 0 / 75 / 675 / 825

slightly_off_white caps at 9, not 10: a touch of off-whiteness is a real flaw but not a disqualifying one, so border is the only ladder whose first non-baseline rung doesn't already cost a full grade band. Attribute penalties are multiplier-exempt: front_defect_multiplier (×1.3 on corner/edge/surface) does not apply here — each rung is already an absolute grade cap, not a generic front/back ratio.

Placeable types#

Crease (pools into the surface region bucket; binding_region: "surface" no longer implies scuff/scratch/deep-type wear specifically — it may be a crease):

Severity light full_card heavy through_layers
Penalty 575 675 825 875
Grade cap 4 3 1.5 1

Stain (pools into surface; penalty is per-face — a back stain is forgiven far more than a front one, an asymmetry the generic ×1.3 front multiplier cannot express, so stain_penalties carries explicit {front, back} columns per severity instead):

Severity very_slight slight obvious heavy
Back penalty (cap) 75 (9) 275 (7) 475 (5) 775 (2)
Front penalty (cap) 675 (3) 675 (3) 775 (2) 875 (1)

Print defect (a placed line/dot/snow-type printing flaw; pools into the print region — the same bucket the attribute ladders bottom, so a placed print defect and, say, a gloss rung compete for which one caps the face):

Severity slight minor blemish heavy
Penalty 25 75 275 675
Grade cap 10 9 7 3

slight's penalty (25 → 975 points → raw 9.5 → promoted to 10) is deliberately GEM-compatible: one slight printing imperfection does not, on its own, cost a card its top grade.

Gating#

  • print (the attribute-ladder input) requires a rubric with print_attributes; absent, score() fails loudly.
  • crease requires crease_penalties; stain requires stain_penaltiesneither requires print_attributes — a rubric can support creases and stains without shipping the attribute ladders at all.
  • print_defect is double-gated: it requires both print_defect_penalties (to look up the penalty) and print_attributes (because it pools into the print region, which only exists on a rubric that defines that table — without the second gate, a custom rubric with print_defect_penalties but no print_attributes would silently produce a broken region score).
  • A rubric with none of these tables stays strictly four-region: the print region appears in faces.<face>.regions only when print_attributes is present.

Notes#

  • line_items order is presentation order, not normative — only each line item's region and penalty are meaningful for scoring; the order items are pushed in (and therefore the order a UI would render them) is an implementation artifact of iteration order, not part of the spec.
  • A placed print defect's line_items detail reads "print {severity}" (e.g. "print slight"), matching the "{depth} {size}" / "{corner} {severity}" pattern used by the other defect types.
  • Validation-order asymmetry (observable, non-normative): a surface defect checks depth/size before x/y, so a defect missing both size and coordinates reports the size error first; the three placeable types check x/y before severity, so a crease defect missing both coordinates and a valid severity reports the coordinate error first instead. Which error surfaces first when multiple fields are invalid at once is not part of the spec — only that the input is rejected, and the accepted score is unaffected either way.

Worked example#

A moderate corner defect on the back: penalty 175 (from the corner table above). 1000 − 175 = 825. That region is now the side's minimum, so side_points = 825. With nothing else on the card, points = 825, which falls in the 800–849 step: grade = 8, band = LP.

The same defect on the front instead: the penalty is multiplied by 1.3 first — round(175 × 1.3) = round(227.5) = 228 — then subtracted: 1000 − 228 = 772. That falls in the 750–799 step: grade = 7.5, still band = LP. Same physical defect, worse grade, purely because it's on the face judged more harshly.

Grade ladder#

points maps to grade in 50-point steps (raw = floor(points/50) × 0.5, with the 9.5 slot promoted to 10), and grade maps to a condition band by taking the highest min_grade the grade still reaches:

Points Grade Condition band
950–1000 10 NM
900–949 9 NM
850–899 8.5 LP
800–849 8 LP
750–799 7.5 LP
700–749 7 LP
650–699 6.5 MP
600–649 6 MP
550–599 5.5 MP
500–549 5 MP
450–499 4.5 HP
400–449 4 HP
350–399 3.5 HP
300–349 3 HP
250–299 2.5 DMG
200–249 2 DMG
150–199 1.5 DMG
0–149 1 DMG

Bands, by minimum grade: NM ≥9 · LP ≥7 · MP ≥5 · HP ≥3 · DMG ≥1. Band selection is order-insensitive in the rubric data — the scorer picks whichever band has the highest min_grade the grade still clears.

Versioning#

This standard is Touchstone 0.1. Versions are two-part, major.minor; there is no patch digit. One number everywhere — this prose, the rubric data's rubric_version, and the $id path the schemas are served from all carry it. That is the version to cite when you claim conformance.

Every change to any number in this document or in rubric.json — a curve segment, a penalty, a band boundary — is a new version, never a silent edit in place. This prose is normative, so a number cannot move in the data without moving here too. Every score result names the rubric_id and rubric_version that produced it, so a grade is always reproducible against the exact rules in force when it was computed. Per-set rubrics (e.g. a vintage-basketball override) are not special-cased code — they are new instances of rubric-config.schema.json, resolved by the framework's inheritance (default → game → era/class → set). Conformance with Touchstone 0.1 means passing test/vectors.json. This prose is normative. The reference implementation and the golden vectors are conformance evidence — the executable demonstration that an implementation agrees with this document. Where they and this prose disagree, one of them contains a bug: file it, decide which is wrong, and fix that one. Neither the implementation's internal ordering, its floating-point accumulation, nor the English text of its error messages is part of the standard.

One known limitation is stated here rather than buried: the aggregation is a deliberate first-order approximation — a pure minimum, with no gap-credit or count-rule term. Whether it stays that way is a data-gated decision for a future revision.