@instruments/materialgraph 0.23.0 → 0.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,90 +1,96 @@
1
1
  const json = [
2
2
  "[{\"name\":\"identify\",\"operationId\":\"retrieval.identify\",\"path\":\"/api/v1/identify\",\"method\":\"POST\",\"description\":\"Answers which canonical graph subject an explicitly selected page is about. Lookup is exact and scoreless; later rungs state their own evidence. The selected-page receipt is never persisted.\",\"inputSchema\":{\"$ref\":\"#/$defs/schema0\",\"$defs\":{\"schema0\":{\"type\":\"object\",\"properties\":{\"url\":{\"type\":\"string\",\"maxLength\":2048,\"format\":\"uri\"},\"title\":{\"type\":\"string\",\"maxLength\":500},\"selectedText\":{\"type\":\"string\",\"maxLength\":2000},\"productLabels\":{\"maxItems\":8,\"type\":\"array\",\"items\":{\"type\":\"string\",\"minLength\":1,\"maxLength\":300}},\"brandLabels\":{\"maxItems\":8,\"type\":\"array\",\"items\":{\"type\":\"string\",\"minLength\":1,\"maxLength\":300}},\"manufacturerCode\":{\"description\":\"Manufacturer code visibly supplied by the selected page; never inferred from a search result.\",\"type\":\"string\",\"minLength\":1,\"maxLength\":300},\"imageUrls\":{\"maxItems\":1,\"type\":\"array\",\"items\":{\"type\":\"string\",\"maxLength\":2048,\"format\":\"uri\"}},\"colourHexes\":{\"maxItems\":8,\"type\":\"array\",\"items\":{\"type\":\"string\",\"pattern\":\"^#?[0-9a-f]{6}$\"}}},\"required\":[\"url\"],\"additionalProperties\":false}}},\"outputSchema\":{\"$ref\":\"#/$defs/schema0\",\"$defs\":{\"schema0\":{\"type\":\"object\",\"properties\":{\"data\":{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"receipt\":{\"type\":\"object\",\"properties\":{\"submittedUrl\":{\"type\":\"string\",\"maxLength\":2048,\"format\":\"uri\"},\"normalizedUrl\":{\"type\":\"string\",\"maxLength\":2048,\"format\":\"uri\"},\"normalizationSteps\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}}},\"required\":[\"submittedUrl\",\"normalizedUrl\",\"normalizationSteps\"],\"additionalProperties\":false},\"evidence\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"enum\":[\"manufacturer-url\",\"brand-homepage\",\"visible-identity\",\"visual\",\"colour\"]},\"url\":{\"type\":\"string\",\"maxLength\":2048,\"format\":\"uri\"},\"detail\":{\"type\":\"string\"}},\"required\":[\"kind\",\"detail\"],\"additionalProperties\":false}},\"outcome\":{\"type\":\"string\",\"const\":\"identified\"},\"rung\":{\"type\":\"string\",\"enum\":[\"lookup\",\"disambiguate\"]},\"subject\":{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"const\":\"variant\"},\"id\":{\"type\":\"string\",\"minLength\":1},\"name\":{\"type\":\"string\",\"minLength\":1},\"brand\":{\"type\":\"object\",\"properties\":{\"id\":{\"type\":\"string\",\"minLength\":1},\"name\":{\"type\":\"string\"},\"slug\":{\"type\":\"string\"}},\"required\":[\"id\",\"name\",\"slug\"],\"additionalProperties\":false}},\"required\":[\"kind\",\"id\",\"name\"],\"additionalProperties\":false}},\"required\":[\"receipt\",\"evidence\",\"outcome\",\"rung\",\"subject\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"receipt\":{\"type\":\"object\",\"properties\":{\"submittedUrl\":{\"type\":\"string\",\"maxLength\":2048,\"format\":\"uri\"},\"normalizedUrl\":{\"type\":\"string\",\"maxLength\":2048,\"format\":\"uri\"},\"normalizationSteps\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}}},\"required\":[\"submittedUrl\",\"normalizedUrl\",\"normalizationSteps\"],\"additionalProperties\":false},\"evidence\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"enum\":[\"manufacturer-url\",\"brand-homepage\",\"visible-identity\",\"visual\",\"colour\"]},\"url\":{\"type\":\"string\",\"maxLength\":2048,\"format\":\"uri\"},\"detail\":{\"type\":\"string\"}},\"required\":[\"kind\",\"detail\"],\"additionalProperties\":false}},\"outcome\":{\"type\":\"string\",\"const\":\"placed\"},\"rung\":{\"type\":\"string\",\"const\":\"lookup\"},\"subject\":{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"enum\":[\"brand\",\"product\"]},\"id\":{\"type\":\"string\",\"minLength\":1},\"name\":{\"type\":\"string\",\"minLength\":1},\"brand\":{\"type\":\"object\",\"properties\":{\"id\":{\"type\":\"string\",\"minLength\":1},\"name\":{\"type\":\"string\"},\"slug\":{\"type\":\"string\"}},\"required\":[\"id\",\"name\",\"slug\"],\"additionalProperties\":false}},\"required\":[\"kind\",\"id\",\"name\"],\"additionalProperties\":false}},\"required\":[\"receipt\",\"evidence\",\"outcome\",\"rung\",\"subject\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"receipt\":{\"type\":\"object\",\"properties\":{\"submittedUrl\":{\"type\":\"string\",\"maxLength\":2048,\"format\":\"uri\"},\"normalizedUrl\":{\"type\":\"string\",\"maxLength\":2048,\"format\":\"uri\"},\"normalizationSteps\":{\"type\":\"array\",\"items",
3
3
  "\":{\"type\":\"string\"}}},\"required\":[\"submittedUrl\",\"normalizedUrl\",\"normalizationSteps\"],\"additionalProperties\":false},\"evidence\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"enum\":[\"manufacturer-url\",\"brand-homepage\",\"visible-identity\",\"visual\",\"colour\"]},\"url\":{\"type\":\"string\",\"maxLength\":2048,\"format\":\"uri\"},\"detail\":{\"type\":\"string\"}},\"required\":[\"kind\",\"detail\"],\"additionalProperties\":false}},\"outcome\":{\"type\":\"string\",\"const\":\"ambiguous\"},\"rung\":{\"type\":\"string\",\"enum\":[\"lookup\",\"disambiguate\",\"match\",\"color\"]},\"candidates\":{\"minItems\":1,\"maxItems\":20,\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"enum\":[\"variant\",\"product\",\"brand\"]},\"id\":{\"type\":\"string\",\"minLength\":1},\"name\":{\"type\":\"string\",\"minLength\":1},\"brand\":{\"type\":\"object\",\"properties\":{\"id\":{\"type\":\"string\",\"minLength\":1},\"name\":{\"type\":\"string\"},\"slug\":{\"type\":\"string\"}},\"required\":[\"id\",\"name\",\"slug\"],\"additionalProperties\":false}},\"required\":[\"kind\",\"id\",\"name\"],\"additionalProperties\":false}},\"truncated\":{\"type\":\"boolean\"}},\"required\":[\"receipt\",\"evidence\",\"outcome\",\"rung\",\"candidates\",\"truncated\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"receipt\":{\"type\":\"object\",\"properties\":{\"submittedUrl\":{\"type\":\"string\",\"maxLength\":2048,\"format\":\"uri\"},\"normalizedUrl\":{\"type\":\"string\",\"maxLength\":2048,\"format\":\"uri\"},\"normalizationSteps\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}}},\"required\":[\"submittedUrl\",\"normalizedUrl\",\"normalizationSteps\"],\"additionalProperties\":false},\"evidence\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"enum\":[\"manufacturer-url\",\"brand-homepage\",\"visible-identity\",\"visual\",\"colour\"]},\"url\":{\"type\":\"string\",\"maxLength\":2048,\"format\":\"uri\"},\"detail\":{\"type\":\"string\"}},\"required\":[\"kind\",\"detail\"],\"additionalProperties\":false}},\"outcome\":{\"type\":\"string\",\"const\":\"unresolved\"},\"rung\":{\"type\":\"string\",\"enum\":[\"match\",\"color\"]}},\"required\":[\"receipt\",\"evidence\",\"outcome\"],\"additionalProperties\":false}]},\"meta\":{\"type\":\"object\",\"properties\":{\"operationId\":{\"type\":\"string\",\"const\":\"retrieval.identify\"},\"requestId\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"operationId\",\"requestId\"],\"additionalProperties\":false},\"links\":{\"type\":\"object\",\"properties\":{},\"additionalProperties\":false},\"warnings\":{\"maxItems\":0,\"type\":\"array\",\"items\":{\"not\":{}}}},\"required\":[\"data\",\"meta\",\"links\",\"warnings\"],\"additionalProperties\":false}}},\"annotations\":{\"readOnlyHint\":true,\"untrustedContentHint\":true},\"requiresVisibleConfirmation\":false,\"requiredHeaders\":[]},{\"name\":\"search\",\"operationId\":\"search_materials_post\",\"path\":\"/api/v1/search\",\"method\":\"POST\",\"description\":\"The stable, authenticated search endpoint — POST JSON-body form, accepting the full `searchMaterialsInputSchema` contract: `text` / `textModes`, an explicit `colours[]` palette (`colourLogic` / `colourTolerance` / `colourAppearance`), `mood`, `sort`, `similarTo` (seed by `variantId` or a raw `vector`), `excludeVariantIds` (up to 256 opaque canonical ids removed before ranking is limited), a raw incoming `image` (`url` or `base64` — the adapter embeds it server-side and drives the image arm), `category` (soft `boost` + hard `filter`), `weights`, typed `filters` (term match or `hasValue` presence), `limit` (max 48), `cursor`, `collapse`, `count`, `view`, `verdicts`, and `explain`. `?explain=1` on the query string is honoured as a shorthand for body `explain: true` (mirrors the GET form); an explicit body `explain` (true or false) always wins. Exactly one selected mode owns ordering: colour→colour, lexical/entity-browse→lexical, variant-text→portrait, and image/image-text→image; filter-only requests are unscored. `image`/`similarTo` drive the image mode, and accompanying text does not create a second scoring request. Caller weights may scale only the live primary signal and cannot activate secondary scoring. Explicit `colours` override any colour word detected in `text`; outside colour mode they may remain diagnostic or enforce a hard displayed-image constraint but do not blend into sc",
4
- "ore. A seeded search ALWAYS carries a `visual_match` verdict cell on each hit (baseline `{ kind: \\\"image\\\", source: \\\"variant\\\"|\\\"vector\\\", variantId }`): `matched` at or above an image cosine of 0.55, else `deviated` with a `looser visual match` deviation. Its `value` is the cosine as a percentage and its `confidence` is deliberately null — a similarity is not a trust confidence. `verdicts: true` (opt-in, full view) is meaningful only with `similarTo: { variantId }`: each hit's `verdict` gains `preferred` SEED cells (baseline `{ kind: \\\"seed\\\" }`) diffing its comparable attributes — numeric plus a virtual `colour` axis — against the seed's, `matched`/`deviated` with a directional `deviation` (`above`/`below`, e.g. 2 mm thicker, warmer); a `vector`/raw-image seed carries no seed variant, so the response `note` says seed cells were skipped. POST defaults to product grain: omitted `collapse` groups results to one representative (highest-ranked) variant per product, reporting hidden variants on that result's `siblingCount` plus a pool-bounded `counts` (variants + products, both `basis:\\\"lowerBound\\\"`) on the envelope. Send `collapse: \\\"none\\\"` explicitly for individual variants. `count: true` adds an opt-in `counts.variants` FEED total — EXACT up to a cap of 1,000 (`basis:\\\"exact\\\"`), `basis:\\\"lowerBound\\\"` beyond the cap and always for ANN-ranked pools (image / colour / visual-text); requests with `excludeVariantIds` also report the honest pool-bounded lower bound because the exact count shapes do not include the exclusion set. `view: \\\"card\\\"` returns the lean `searchCardItemSchema` grid projection instead of the full items; the response echoes `view`, which discriminates the `results` union. `hasImages: true` returns only materials with at least one catalog image (default false keeps the full catalog, and the `count` total honours it — the visual arms are inherently image-backed, so it only filters the lexical attribute lens). Each result's `visualMeanings` (existing accepted/measured meanings — descriptive evidence, never a ranking cause) is included only when `explain` is set and at least one meaning exists. Returns the engine result verbatim (`mode`, `results[]`, `rerank`, `maxScore`, `counts`) plus a `query` echo (the raw `image` bytes/vector are never echoed back — only `{ type }`). Search is dynamic, so success responses carry `Cache-Control: no-store`. Requires the `mg:read` bearer scope.\",\"inputSchema\":{\"$ref\":\"#/$defs/schema0\",\"$defs\":{\"schema0\":{\"type\":\"object\",\"properties\":{\"text\":{\"description\":\"Free-text query, matched VISUALLY against material imagery (\\\"striped wallpaper\\\", \\\"warm terrazzo\\\"). A bare colour word (\\\"sage\\\") automatically runs as a colour-only search.\",\"type\":\"string\",\"maxLength\":500},\"intent\":{\"default\":\"search\",\"description\":\"Budget profile for the search primitive. Default \\\"search\\\" is the full engine. \\\"typeahead\\\" is the as-you-type profile: lexical-only with prefix match on the final token, forced `view:\\\"card\\\"`, `limit` capped at 24, and no mood/colour detection tiers, semantic fallback, category boost, secondary scoring, refinement suggestions, counts, or meanings. `textModes` is ignored under typeahead. Recommended wire shape for library typeahead UIs.\",\"type\":\"string\",\"enum\":[\"search\",\"typeahead\"]},\"textModes\":{\"description\":\"How to interpret `text`. Default [\\\"visual\\\"] — a text→image VISUAL search. \\\"lexical\\\" (opt-in) runs a BM25 ATTRIBUTE-lens search over material names + canonical attribute terms (find by what it's called / what it IS); detected colour words are stripped from the lexical terms and retained only as diagnostic or hard-constraint evidence. Requesting BOTH [\\\"visual\\\",\\\"lexical\\\"] runs the visual search and notes it — compose a separate lexical search if both result sets are required. Ignored when `intent` is \\\"typeahead\\\".\",\"type\":\"array\",\"items\":{\"type\":\"string\",\"enum\":[\"visual\",\"lexical\"]}},\"colours\":{\"description\":\"Explicit colour palette to match. Each entry is EITHER a `hex` OR a `name` (a descriptive or brand paint name resolved server-side — never invent",
5
- " a hex for a name). PRECEDENCE: when set, these override any colour word detected in `text`. Unresolvable names fail with a typed error listing them, so you can correct and retry.\",\"maxItems\":8,\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"hex\":{\"description\":\"The colour to match, as 3- or 6-digit hex. Provide EITHER `hex` OR `name`, never both. Only pass `hex` when you already hold an EXACT hex — for a colour NAME use `name` and let the server resolve it.\",\"type\":\"string\",\"pattern\":\"^#?(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$\"},\"name\":{\"description\":\"A colour NAME to match — a descriptive name ('sage green', 'terracotta', 'warm sand') or a brand paint name ('Gauntlet Gray', 'Hale Navy', 'White Dove'). Resolved server-side to its exact library hex (misspellings are fuzzy-matched). NEVER invent a hex for a name — pass the name here. Provide EITHER `name` OR `hex`, never both.\",\"type\":\"string\",\"minLength\":1,\"maxLength\":120},\"weight\":{\"description\":\"Relative proportion of this colour in the desired palette (0–1). Weights are normalised across the palette; omit for equal parts.\",\"type\":\"number\",\"exclusiveMinimum\":0,\"maximum\":1},\"role\":{\"type\":\"string\",\"enum\":[\"any\",\"is\",\"accent\"],\"description\":\"Per-colour swatch eligibility (default 'any'). PRECEDENCE: `colourLogic` governs cross-colour combination (all/any); `role` governs which swatch of a product may satisfy THIS colour, and ANDs on top of the logic. 'is' = the colour must be the product's DOMINANT swatch; 'accent' = the colour must be a minor swatch (proportion 0.05–0.35) and is excluded from the proportion-match term; 'any' = today's nearest-swatch match.\"},\"sourceName\":{\"description\":\"Human name the colour came from (e.g. \\\"sage\\\", \\\"Elephant's Breath\\\") — echoed back for display; never affects matching. (For a name you want RESOLVED to a hex, use `name` instead.)\",\"type\":\"string\",\"maxLength\":120}}}},\"colourLogic\":{\"description\":\"\\\"all\\\" (default): results should contain the whole palette, ranked by average distance + proportion match. \\\"any\\\": results matching ANY palette colour, ranked by their best single-colour distance.\",\"type\":\"string\",\"enum\":[\"all\",\"any\"]},\"colourTolerance\":{\"description\":\"How close a colour must be to count as a match. 'normal' (default) is today's behaviour; 'tight' narrows the OKLab match threshold and prunes far single-colour matches; 'loose' widens the threshold.\",\"type\":\"string\",\"enum\":[\"tight\",\"normal\",\"loose\"]},\"colourAppearance\":{\"type\":\"object\",\"properties\":{\"lightness\":{\"description\":\"OKLab lightness window (L ∈ [0,1]); dark ≈ 0, light ≈ 1.\",\"type\":\"object\",\"properties\":{\"min\":{\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"max\":{\"type\":\"number\",\"minimum\":0,\"maximum\":1}},\"required\":[\"min\",\"max\"]},\"warmth\":{\"description\":\"OKLab warmth window (b axis); cool < 0 < warm, ≈ −0.1..0.1.\",\"type\":\"object\",\"properties\":{\"min\":{\"type\":\"number\",\"minimum\":-0.5,\"maximum\":0.5},\"max\":{\"type\":\"number\",\"minimum\":-0.5,\"maximum\":0.5}},\"required\":[\"min\",\"max\"]}},\"description\":\"HARD appearance gate on the material's DOMINANT colour, independent of the colour palette — works on a text-only query too. Variants without colour extraction are EXCLUDED whenever any axis is set (NULL scalars never match).\"},\"mood\":{\"type\":\"string\",\"enum\":[\"warm\",\"cool\",\"earthy\",\"moody\",\"bold\",\"light-airy\",\"natural\",\"neutral\",\"romantic\",\"coastal\",\"industrial\",\"luxe\",\"vintage\",\"serene\",\"scandi\",\"cosy\",\"heritage\",\"mid-century\",\"pastel\"],\"description\":\"Mood-language colour entry — resolves to a curated checked-in colour palette. Use it for evocative mood queries ('coastal', 'moody', 'earthy') rather than a single colour. A mood ALONE runs as a colour search; with text it remains diagnostic or hard-constraint evidence and does not blend into the text mode's ordering score. One of: warm (Warm — Reds, oranges, yellows - inviting and cozy); cool (Cool — Blues, teals, cool grays - calm and professional); earthy (Earthy — Browns, tans, terracotta - natural and grounded); moody (Moody — Deep, saturated, dramatic - rich and atmospheric); bold (Bold — High chroma, vibrant - energetic and eye-ca",
6
- "tching); light-airy (Light + Airy — Pastels, pale tones - soft and spacious); natural (Natural — Greens, wood tones, stone - organic and balanced); neutral (Neutral — Whites, grays, blacks - versatile and timeless); romantic (Romantic — Soft pinks, mauves, blush - tender and elegant); coastal (Coastal — Sandy beiges, ocean blues, seafoam - relaxed seaside); industrial (Industrial — Cool grays, concrete, metallic - urban and modern); luxe (Luxe — Deep jewel tones, rich emeralds, sapphires - opulent); vintage (Vintage — Muted, dusty tones - nostalgic and timeworn); serene (Serene — Soft blues, sage, muted lavender - calm and peaceful); scandi (Scandi — Warm whites, pale woods, cream - Scandinavian minimalism); cosy (Cosy — Deep plaster, amber, tobacco - enveloping warmth); heritage (Heritage — Rich historical tones with saturated depth - classic and grounded); mid-century (Mid-Century — Olive, mustard, teal, terracotta - retro modern); pastel (Pastel — Sage, duck egg, blush - soft colors with body).\"},\"sort\":{\"type\":\"string\",\"enum\":[\"relevance\",\"light\",\"dark\",\"vivid\",\"muted\",\"colourful\",\"name\",\"name_desc\",\"recent\"],\"description\":\"Re-order the final scored results (default 'relevance' = the selected mode's primary score). Two families. COLOUR RAMPS: 'light'/'dark' by dominant lightness, 'vivid'/'muted' by chroma, 'colourful' by overall vibrancy — variants without colour extraction sink to the end. CATALOGUE ORDERS: 'name' (A→Z), 'name_desc' (Z→A) and 'recent' (newest first), which read the same `name` / `created_at` columns, in the same directions, that /api/v1/search/browse orders on — one word, one order, on both lanes. Every sort REORDERS the pool the search signals already retrieved; it never widens or substitutes for one. Retired materials sort below visible ones under every non-relevance sort. For an unranked walk of the whole corpus in any of these orders, use the browse surface instead.\"},\"orderBy\":{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"minLength\":1,\"description\":\"The attribute to sort by. Only attributes with a DECLARED value ladder can be ordered — ordering is never inferred from a value's spelling — and anything else is refused with the orderable set named. Orderable today: price_tier.\"},\"direction\":{\"default\":\"asc\",\"description\":\"'asc' (default) walks the ladder from its first rung.\",\"type\":\"string\",\"enum\":[\"asc\",\"desc\"]}},\"required\":[\"attributeId\"],\"description\":\"Sort results along ONE attribute's declared ladder — a separate axis from `sort`, which stays the closed relevance/colour/catalogue vocabulary. Sent together, `orderBy` wins and the response `note` says so. A variant whose value is off the ladder, or which carries no value at all, sorts LAST in both directions: it has no rung, and it is still a real row, so it is placed at the end rather than dropped or guessed at.\"},\"returnAttributes\":{\"maxItems\":8,\"type\":\"array\",\"items\":{\"type\":\"string\",\"minLength\":1},\"description\":\"Attribute ids whose RESOLVED VALUE should ride each result, under `attributes` keyed by attribute id. Values resolve with the normal variant-over-product precedence. THREE STATES, and they are different: an attribute you did not ask for is not a key at all; an attribute you asked for that this variant carries no accepted value for is `null`; anything else is the value (a string, or an array for a multi-valued attribute). An unknown attribute id is refused naming it, never silently dropped. Pair it with `filters` to show what you filtered on.\"},\"similarTo\":{\"description\":\"Find materials that LOOK like a seed image. Supply `variantId` to seed from an existing variant's primary image, `materialBankId` to seed from a Material Bank SKU — the ONE EXTERNAL identifier this API accepts, so a caller already holding a SKU needs no resolve round trip — or `vector`, a pre-computed halfvec literal (`[a,b,…]`) from an externally-embedded image, to drive the image arm directly. A `materialBankId` that names no variant is a 400 naming the offending value, never an empty result set and never an unseeded search. PRECEDENCE: wins over",
7
- " `text` (the response carries a note when both were given).\",\"anyOf\":[{\"type\":\"object\",\"properties\":{\"variantId\":{\"type\":\"string\"}},\"required\":[\"variantId\"]},{\"type\":\"object\",\"properties\":{\"materialBankId\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"materialBankId\"]},{\"type\":\"object\",\"properties\":{\"vector\":{\"type\":\"string\"}},\"required\":[\"vector\"]}]},\"excludeVariantIds\":{\"description\":\"Canonical variant ids that must not appear in this result. Applied before the returned page is cut, so the page refills from the deeper ranked pool instead of being shortened by client-side filtering. IDs are opaque; duplicates are harmless and deduplicated by the engine.\",\"maxItems\":256,\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"image\":{\"description\":\"A raw incoming image to search by (URL or base64). The HTTP adapter embeds it and sets `similarTo: { vector }`; wins over `text`.\",\"oneOf\":[{\"type\":\"object\",\"properties\":{\"type\":{\"type\":\"string\",\"const\":\"url\"},\"url\":{\"type\":\"string\",\"format\":\"uri\",\"description\":\"HTTP(S) URL of the query image. MUST be an https URL.\"}},\"required\":[\"type\",\"url\"]},{\"type\":\"object\",\"properties\":{\"type\":{\"type\":\"string\",\"const\":\"base64\"},\"data\":{\"type\":\"string\",\"minLength\":1,\"description\":\"Base64-encoded image bytes (or a data URI) the caller already holds — e.g. a form upload — so there is nothing to host.\"}},\"required\":[\"type\",\"data\"]}],\"type\":\"object\"},\"category\":{\"type\":\"object\",\"properties\":{\"boost\":{\"description\":\"Soft category preference — results of this product type score higher but others still appear.\",\"type\":\"string\"},\"filter\":{\"description\":\"HARD gate: only these public taxonomy categories are returned. Values are category-group or product-type display names (case-insensitive), or canonical taxonomy keys. A group expands to every child product type; an unknown value errors listing every valid name.\",\"type\":\"array\",\"items\":{\"type\":\"string\"}}}},\"weights\":{\"type\":\"object\",\"properties\":{\"image\":{\"type\":\"number\",\"minimum\":0},\"category\":{\"type\":\"number\",\"minimum\":0},\"colour\":{\"type\":\"number\",\"minimum\":0},\"lexical\":{\"type\":\"number\",\"minimum\":0},\"text\":{\"type\":\"number\",\"minimum\":0}},\"description\":\"Optional multiplier for the selected mode's one ordering signal. At most one applied weight is non-zero: colour→colour, lexical/entity-browse→lexical, variant-text→portrait, image-text/image→image, and filter-only→none. Overrides may scale only that live signal and cannot activate any other. Other similarities are diagnostic or hard-gate receipts and do not blend into score.\"},\"filters\":{\"description\":\"Typed filters applied as a hard restriction before ranking. Each entry is a term match (`attributeId` + `term`), a term SET (`attributeId` + `terms` — ANY one of them satisfies it), a presence check (`attributeId` + `hasValue: true`), or a numeric range (`attributeId` + `min`/`max`). AND/OR: entries AND together, and OR is expressed WITHIN one entry via `terms`. BRAND: `attributeId: \\\"brand\\\"` filters on the brand RELATION — a join every one of the catalogue's variants carries — taking a brand slug or display name, exactly the `value` a `brand` facet reports. Do NOT use the `brand_name` ATTRIBUTE for this: it is a sparse spec value present on about 1.5% of variants and absent entirely from every catalog-ingested brand, so filtering on it returns a near-empty page that reads as a corpus gap. Presence filters always route as hard spec gates. Aesthetic/meaning term filters (pattern, texture, directionality, and the other visual-meaning attributes) are applied as a pool-bounded post-filter over the visual candidate pool rather than a whole-catalog restriction. Each entry may carry a `trust` bar the matching value must clear.\",\"type\":\"array\",\"items\":{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, e.g. \\\"primary_material\\\" or \\\"fire_rating\\\" — or the special id \\\"brand\\\", which filters on the brand RELATION (a join, carried by every variant) rather than on any attribute.\"},\"term\":{\"type\":\"string\",\"description\":\"Canonical term the attribute must equal, e.g. \\\"t",
8
- "extile\\\". For \\\"brand\\\", a brand slug (\\\"kvadrat\\\") or display name (\\\"Kvadrat\\\"), case-insensitive — exactly the `value` a `brand` facet reports. An unrecognised brand is a 400 naming it, never an empty page.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"term\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, or the special id \\\"brand\\\" for the brand relation.\"},\"terms\":{\"minItems\":1,\"type\":\"array\",\"items\":{\"type\":\"string\"},\"description\":\"Canonical terms, ANY ONE of which satisfies the filter — this is how OR is expressed. OR lives inside one entry; separate `filters[]` entries AND together. Used for ordinal scales, where a stricter class satisfies a looser one, and for a multi-select rail — `{attributeId:\\\"brand\\\", terms:[\\\"kvadrat\\\",\\\"rubelli\\\"]}` returns materials from either brand.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"terms\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\"},\"excludesTerm\":{\"type\":\"string\",\"minLength\":1},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"excludesTerm\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id that must have an accepted value, e.g. \\\"sample_image_mm_per_px\\\".\"},\"hasValue\":{\"type\":\"boolean\",\"const\":true,\"description\":\"Require any accepted value for this attribute.\"},\"asOf\":{\"description\":\"Reference date for EPD expiration, exclusive: the value must remain valid after this date.\",\"type\":\"string\",\"format\":\"date\",\"pattern\":\"^(?:(?:\\\\d\\\\d[2468][048]|\\\\d\\\\d[13579][26]|\\\\d\\\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\\\d|30)|(?:02)-(?:0[1-9]|1\\\\d|2[0-8])))$\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"addit",
9
- "ionalProperties\":false}},\"required\":[\"attributeId\",\"hasValue\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id holding a NUMBER, e.g. \\\"light_reflectance_value\\\" or \\\"lead_time_days\\\".\"},\"min\":{\"description\":\"Inclusive lower bound. Omit for a ceiling-only range.\",\"type\":\"number\"},\"max\":{\"description\":\"Inclusive upper bound. Omit for a floor-only range.\",\"type\":\"number\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\"],\"additionalProperties\":false}]}},\"preferences\":{\"description\":\"SOFT spec criteria — rank-never-exclude. Same shape as `filters`, but a preference NEVER removes a result: each hit's `verdict` gains a `preferred` cell, and a failed/missing preferred cell only DEMOTES the hit WITHIN its page (the cursor is from the pre-demotion order).\",\"maxItems\":8,\"type\":\"array\",\"items\":{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, e.g. \\\"primary_material\\\" or \\\"fire_rating\\\" — or the special id \\\"brand\\\", which filters on the brand RELATION (a join, carried by every variant) rather than on any attribute.\"},\"term\":{\"type\":\"string\",\"description\":\"Canonical term the attribute must equal, e.g. \\\"textile\\\". For \\\"brand\\\", a brand slug (\\\"kvadrat\\\") or display name (\\\"Kvadrat\\\"), case-insensitive — exactly the `value` a `brand` facet reports. An unrecognised brand is a 400 naming it, never an empty page.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"term\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, or the special id \\\"brand\\\" for the brand relation.\"},\"terms\":{\"minItems\":1,\"type\":\"array\",\"items\":{\"type\":\"string\"},\"description\":\"Canonical terms, ANY ONE of which satisfies the filter — this is how OR is expressed. OR lives inside one entry; separate `filters[]` entries AND together. Used for ordinal scales, where a stricter class satisfies a looser one, and for a multi-select rail — `{attributeId:\\\"brand\\\", terms:[\\\"kvadrat\\\",\\\"rubelli\\\"]}` returns materials from either brand.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"terms\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\"},\"excludesTerm\":{\"type\":\"string\",\"minLength\":1},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\"",
10
- ":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"excludesTerm\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id that must have an accepted value, e.g. \\\"sample_image_mm_per_px\\\".\"},\"hasValue\":{\"type\":\"boolean\",\"const\":true,\"description\":\"Require any accepted value for this attribute.\"},\"asOf\":{\"description\":\"Reference date for EPD expiration, exclusive: the value must remain valid after this date.\",\"type\":\"string\",\"format\":\"date\",\"pattern\":\"^(?:(?:\\\\d\\\\d[2468][048]|\\\\d\\\\d[13579][26]|\\\\d\\\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\\\d|30)|(?:02)-(?:0[1-9]|1\\\\d|2[0-8])))$\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"hasValue\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id holding a NUMBER, e.g. \\\"light_reflectance_value\\\" or \\\"lead_time_days\\\".\"},\"min\":{\"description\":\"Inclusive lower bound. Omit for a ceiling-only range.\",\"type\":\"number\"},\"max\":{\"description\":\"Inclusive upper bound. Omit for a floor-only range.\",\"type\":\"number\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\"],\"additionalProperties\":false}]}},\"verdicts\":{\"description\":\"Opt-in seed-relative verdict cells. Only meaningful with `similarTo: { variantId }`: costs one seed fetch + one page read to diff each hit's accepted COMPARABLE attributes (numeric + a colour axis) against the seed's, emitting `preferred` `verdict` cells with directional deviations (`above`/`below`, e.g. 2 mm thicker, warmer). Ignored on the default path; a `similarTo: { vector }` / raw-image seed carries no seed variant, so the response notes that seed cells were skipped.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Results per page (default 24, max 48).\",\"type\":\"integer\",\"minimum\":1,\"maximum\":48},\"cursor\":{\"description\":\"Opaque pagination cursor from a previous response. NOTE: ANN search is approximate — tail items may reorder slightly between pages.\",\"type\":\"string\"},\"explain\":{\"description\":\"Include per-signal scores on each result (`signals`).\",\"type\":\"boolean\"},\"collapse\":{\"description\":\"Group results by product before pagination. \\\"product\\\" returns one representative (highest-ranked) variant per product, with hidden siblings counted in `siblingCount`. The engine and GET adapter default to \\\"none\\\"; public POST requests",
11
- " default to \\\"product\\\" and may request \\\"none\\\" explicitly for variant-grain results.\",\"type\":\"string\",\"enum\":[\"none\",\"product\"]},\"count\":{\"default\":false,\"description\":\"Opt-in feed total. When true the response carries `counts.variants` (and `counts.products` under `collapse:\\\"product\\\"`). The variant total is EXACT up to a cap of 1000 and reported as `basis:\\\"lowerBound\\\"` beyond it — and ALWAYS `lowerBound` for ANN-ranked pools (image / colour / visual-text / visual-meaning post-filters), where the finite candidate pool is the only honest figure. Counts are never silently approximated: an exact count is labelled `exact`, a capped or pool-bounded one `lowerBound`. Default false (skip the count query entirely).\",\"type\":\"boolean\"},\"facetCounts\":{\"description\":\"Opt-in per-query facet counts (MG-1010). Name up to 10 facet ids — `brand`, `category`, or spec attribute ids — and the response carries a `facets` block counting each facet's values WITHIN this query's match set. Absent means no `facets` in the response and no counting cost. An id the registry does not know is a 400 naming it (with `didYouMean` candidates); a known but uncountable id lands in `facets.absent` with a reason. Counts follow the same honesty discipline as `count`: ranked (ANN) queries report pool-derived floors labelled `lowerBound` with `poolSize` stated; only a filter-driven query earns `exact`.\",\"maxItems\":10,\"type\":\"array\",\"items\":{\"type\":\"string\",\"minLength\":1}},\"refinementToken\":{\"description\":\"Opaque five-minute receipt from a prior pool-derived faceted search. Resubmitting it narrows that exact finalized pool instead of running a new candidate source.\",\"type\":\"string\",\"minLength\":1,\"maxLength\":131072},\"hasImages\":{\"default\":false,\"description\":\"When true, only return results that have at least one catalog image (a servable card thumbnail). Off by default so search stays honest about the FULL catalog, including image-less records. It filters the LEXICAL attribute lens — the one source that can surface an image-less record; the visual arms (image / colour / text→image) are inherently image-backed, so the flag is a no-op there. The opt-in `counts.variants` feed total honours it too (image-less records are excluded from the count). Default false.\",\"type\":\"boolean\"},\"sampleableOnly\":{\"description\":\"Require an accepted sampleable=true variant fact before pagination. False or absent leaves the corpus unchanged.\",\"type\":\"boolean\"},\"view\":{\"default\":\"full\",\"description\":\"Result projection. \\\"full\\\" (default) returns the complete `searchResultItemSchema` (palette, colourMeta, hasImage, siblings, explain signals/meanings). \\\"card\\\" returns the lean `searchCardItemSchema` (identity + name + thumbnail + score) for grid rendering, skipping the per-page palette / appearance / meaning reads. The response echoes the chosen `view`, which discriminates the `results` union.\",\"type\":\"string\",\"enum\":[\"full\",\"card\"]},\"corpusScope\":{\"description\":\"Which corpus to answer from. samplizeCatalogue admits published US Samplize paint colours regardless of stock. ABSENT means the WHOLE GRAPH — every material MaterialGraph knows, orderable or not; absence is never a narrower default. Three transaction-oriented scopes gate on governed CHANNEL membership: 'materialBankNaOrderable' (Material Bank North America, about 277,000 materials), 'materialBankEuOrderable' (Material Bank Europe, about 65,000) and 'designShopOrderable' (DesignShop, about 34,000, excluding what DesignShop hides from browse). 'materialBankNaAndCatalogue' is a governed discovery corpus over Material Bank NA or Catalogue editorial membership; it is not a buyability claim, and references alone do not confer membership. 'materialBankOrderable' instead restricts retrieval to materials carrying a Material Bank identity, which is a provenance signal rather than a channel one, and admits about 98% of the graph. Scope is distribution/visibility membership, never suitability; its receipt states whether that means transaction or editorial inclusion. Every scope is applied INSIDE the query: `limit` fills with eligible r",
12
- "ows, `counts` describe the SCOPED corpus, and the response echoes the scope under `corpusScope`. This is what post-filtering a returned page cannot give you — a client-side filter shortens the page exactly when the query drifts toward the corpus MaterialGraph carries beyond the Material Bank feed, which is unpredictable from outside. An unknown value is a 400 naming it.\",\"type\":\"string\",\"enum\":[\"all\",\"samplizeCatalogue\",\"materialBankOrderable\",\"materialBankNaOrderable\",\"materialBankNaAndCatalogue\",\"materialBankEuOrderable\",\"designShopOrderable\"]},\"includeSamplingArtifacts\":{\"description\":\"OPS/AUDIT escape hatch — DO NOT use for normal search. Default behaviour (absent or false) EXCLUDES sampling-artifact products (multi-swatch sample cards — orderable samples, never specifiable materials) from every result, count, and candidate pool. Set true only to surface them for ops inspection or an audit.\",\"type\":\"boolean\"}},\"additionalProperties\":false}}},\"outputSchema\":{\"$ref\":\"#/$defs/schema0\",\"$defs\":{\"schema0\":{\"type\":\"object\",\"properties\":{\"data\":{\"oneOf\":[{\"type\":\"object\",\"properties\":{\"view\":{\"default\":\"full\",\"description\":\"Projection echo — always 'full' on this envelope variant.\",\"type\":\"string\",\"const\":\"full\"},\"results\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"resultId\":{\"type\":\"string\",\"description\":\"This exact hit's attribution key — `<responseId>:<rank>`, where rank is 1-based within THIS response. Unique across pages, because each page is its own response with its own id. Store it verbatim next to the outcome; it is opaque, and `attribution.responseId` is the field to group by.\"},\"variantId\":{\"type\":\"string\"},\"brand\":{\"type\":\"string\"},\"product\":{\"type\":\"string\"},\"brandSlug\":{\"type\":[\"string\",\"null\"]},\"productId\":{\"type\":[\"string\",\"null\"]},\"materialBankId\":{\"description\":\"The variant's Material Bank SKU — the ONE EXTERNAL identifier on a result, in contrast to the OPAQUE `variantId`: it is the id you are expected to match against Material Bank's own catalogue (carts, ordering and analytics all key on it), so a result list can be joined without a second lookup. Populated on EVERY lane — all four search arms and both Match lanes. ABSENT means no MB identity is known for the variant, never that the SKU cannot be ordered; presence is the orderability tell, absence is silence.\",\"type\":\"string\"},\"manufacturerCode\":{\"description\":\"The governed manufacturer/source-native item code from `variants.code` (for example \\\"GADLC02\\\"). Populated when the full-result retrieval source carries that canonical field; Match always hydrates it from the variant row. ABSENT means unknown or an older external serving projection, never an invented code. Independent of materialBankId.\",\"type\":\"string\"},\"projectSaveCapability\":{\"description\":\"Explicit source-governed permission to save this exact source-coded variant to a Project. ABSENT means unknown/not allowed; it is never inferred from the brand or name.\",\"type\":\"string\",\"const\":\"allowed\"},\"sampleable\":{\"description\":\"Whether a physical sample is explicitly known to be orderable. Absent means unknown, never unavailable.\",\"type\":\"boolean\"},\"sourceStatus\":{\"description\":\"The governed data status supporting Project capture. ABSENT means unknown. Provisional data remains source-backed but is not a verified catalogue claim.\",\"type\":\"string\",\"const\":\"provisional\"},\"productType\":{\"type\":\"string\"},\"color\":{\"type\":[\"string\",\"null\"]},\"variantName\":{\"type\":[\"string\",\"null\"]},\"imageUrl\":{\"type\":[\"string\",\"null\"]},\"hasImage\":{\"type\":\"boolean\"},\"palette\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"hex\":{\"type\":\"string\",\"description\":\"`#rrggbb` of the swatch, as extracted from the product image.\"},\"proportion\":{\"type\":\"number\",\"description\":\"The swatch's share of the analysed image, in [0,1].\"},\"name\":{\"description\":\"Stored human colour name for the swatch, when one was extracted.\",\"type\":[\"string\",\"null\"]}},\"required\":[\"hex\",\"proportion\"],\"additionalProperties\":false},\"description\":\"The material's main-image palette — the FULL stored palette, uncapped and in stored order. Colours",
13
- " are EXTRACTED FROM PRODUCT IMAGERY (visual likeness, never a declared colour specification). A one-swatch palette means the extractor found one colour — solid-colour materials are genuinely monochrome — never that the list was trimmed. This array is always present: EMPTY means nothing is on record (unknown), not 'this material has no colours'.\"},\"colourMeta\":{\"type\":\"object\",\"properties\":{\"mood\":{\"type\":\"string\"},\"dominantFamily\":{\"type\":\"string\"},\"dominantHex\":{\"type\":\"string\"}},\"additionalProperties\":false},\"colourChip\":{\"type\":\"object\",\"properties\":{\"hex\":{\"type\":\"string\",\"description\":\"`#rrggbb` to fill the chip with. EXTRACTED FROM the product image (a visual likeness), never a manufacturer-declared colour specification — surfaces must not present it as one.\"}},\"required\":[\"hex\"],\"additionalProperties\":false,\"description\":\"Present when this material's product imagery carries nothing beyond one colour, so a surface should render that colour rather than the photograph. Set for COATINGS (paints, primers, stains, floor coatings) whose stored palette quantises to a single swatch — a paint's photo is a flat fill, and the colour we hold structured beats a JPEG of it. ABSENT means the question was not established (unknown product type, unextracted or multi-swatch palette) and the image should be rendered; it NEVER asserts 'this is not a coating'.\"},\"attributes\":{\"description\":\"Resolved attribute values for the ids sent in `returnAttributes`, keyed by attribute id. ABSENT entirely when `returnAttributes` was not sent. A key present with `null` means this variant carries no accepted value for that attribute — absent is not empty and neither is null: the three states are distinct on purpose. Values resolve with the normal variant-over-product precedence.\",\"type\":\"object\",\"propertyNames\":{\"type\":\"string\"},\"additionalProperties\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"array\",\"items\":{\"type\":\"string\"}},{\"type\":\"null\"}]}},\"score\":{\"type\":\"number\"},\"confidence\":{\"description\":\"Match only (MG-1031). How close this material is to the query, as an ABSOLUTE image-space similarity in [0,1] adjusted for colour — the same unit as `qualityFloor` and `comparison.bestRejected`, so all three can be drawn on one axis. Never normalised against the page, so the number means the same thing across runs. It is the TRUE value and may reach 1; rendering it so that no card claims '100%' is the consumer's job (round toward zero, or show '99%+'). Capping it here would put it on a different ceiling from `bestRejected`, which publishes the same unit uncapped. Distinct from `score`, which is the selected mode's weighted primary ranking value on the `[0, maxScore]` scale. Absent outside Match.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"confidenceBand\":{\"description\":\"`confidence` as a word, banded on the engine's measured readings (strong >= 0.85, good >= 0.72, fair >= 0.55 = the default quality floor, weak below it and reachable only under `includeBelowFloor`). Present exactly when `confidence` is.\",\"type\":\"string\",\"enum\":[\"strong\",\"good\",\"fair\",\"weak\"]},\"colourDistance\":{\"description\":\"Match only. Proportion-weighted OKLab distance from the query crop's palette to this material's. `null` means the material carries no stored palette — UNKNOWN colour, never 'no colour' and never 'distant'. Absent when the colour stage did not run.\",\"type\":[\"number\",\"null\"]},\"colourBand\":{\"description\":\"`colourDistance` as a word (identical / very close / close / related / different), or null when unmeasurable. Absent when the colour stage did not run.\",\"type\":[\"string\",\"null\"]},\"signals\":{\"type\":\"object\",\"properties\":{\"portrait\":{\"type\":[\"number\",\"null\"]},\"image\":{\"type\":[\"number\",\"null\"]},\"category\":{\"type\":[\"number\",\"null\"]},\"colour\":{\"type\":[\"number\",\"null\"]},\"lexical\":{\"type\":[\"number\",\"null\"]},\"text\":{\"type\":[\"number\",\"null\"]}},\"required\":[\"image\",\"category\",\"colour\",\"lexical\",\"text\"],\"additionalProperties\":false},\"retired\":{\"description\":\"MB lists the SKU as not visible/retired; demoted in ranking. Omitted when false.\",\"type\":\"boolean\"},\"belowFloor\":{\"description\":\"Pres",
14
- "ent (true) only on quality-floor rejects returned under includeBelowFloor — the item failed the match image-quality floor and would be dropped by default.\",\"type\":\"boolean\"},\"admittedOnColour\":{\"description\":\"Present (true) only on Match rows the colour probe admitted: this material is as close in colour as the catalogue gets, and its image similarity is BELOW the match quality floor by design. It is on the page because no image-retrieved row is nearer in colour, not because it cleared the floor — so a consumer can present it as a colour answer or suppress it knowingly, and a quality check can tell a designed admission from a genuine floor leak. Absent (never false) on rows that came through image retrieval. Distinct from `belowFloor`, which marks rejects returned only under `includeBelowFloor`.\",\"type\":\"boolean\"},\"siblingCount\":{\"description\":\"Additional variants of the same product hidden by `collapse: \\\"product\\\"`. Present only when grouping hid at least one sibling. It is the COMPLETE tally, and stays so when `variants` is capped.\",\"type\":\"integer\",\"minimum\":1,\"maximum\":9007199254740991},\"variants\":{\"description\":\"The product's matching variants, MOST RELEVANT FIRST — present exactly when `collapse: \\\"product\\\"` was requested, and then on every result. This is what makes the product-shaped view usable without a second call per card: the twelve colourways of one tile arrive with the product instead of being reduced to a number. Under the default variant grain (`collapse` absent or `\\\"none\\\"`) the key is ABSENT — every matching variant is already its own result, so there is no group to carry. A one-entry array means the product matched with exactly one variant, which is an answer rather than a gap. ORDER is the ranking pipeline's own and is never re-sorted: `variants[0]` is the group's RANKING representative — the highest-scoring member, whose score is the group's cursor key. If the display face differs from that representative, `explain` names those two identities apart in `anatomy`. CAPPED at 12 entries, with `siblingCount` left as the complete tally — so truncation is decidable from the response alone: `(siblingCount ?? 0) + 1 > variants.length`. A full-length array is never on its own evidence that the product holds no further variants.\",\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"variantId\":{\"type\":\"string\",\"description\":\"The variant's OPAQUE id — round-trip it verbatim, never parse it.\"},\"variantName\":{\"description\":\"The variant's own label — for paint, the colourway name. Null when unnamed.\",\"type\":[\"string\",\"null\"]},\"color\":{\"description\":\"The variant's coarse colour family.\",\"type\":[\"string\",\"null\"]},\"hex\":{\"description\":\"`#rrggbb` dominant colour, extracted from the variant's image (visual likeness, never a declared specification). ABSENT means no palette is on record — unknown, never 'this variant has no colour'.\",\"type\":\"string\"},\"imageUrl\":{\"description\":\"The variant's main image.\",\"type\":[\"string\",\"null\"]},\"materialBankId\":{\"description\":\"The variant's Material Bank SKU, on the same contract as the result item's: ABSENT means no MB identity is on record, never that it cannot be ordered.\",\"type\":\"string\"},\"score\":{\"type\":\"number\",\"description\":\"The variant's own mode-primary, retired-demoted score — the value this array is ordered by, comparable with the result item's `score` and with the other entries here. It is what makes 'most relevant first' checkable.\"}},\"required\":[\"variantId\",\"variantName\",\"color\",\"imageUrl\",\"score\"],\"additionalProperties\":false}},\"visualMeanings\":{\"description\":\"Existing accepted/measured visual meanings — descriptive evidence, not ranking causes. Present only when `explain` was set and at least one meaning exists.\",\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"id\":{\"type\":\"string\",\"description\":\"Registry attribute id, or a measured-meaning id like `colour_mood`.\"},\"label\":{\"type\":\"string\",\"description\":\"Human label — registry presentation label for canonical meanings.\"},\"values\":{\"minItems\":1,\"type\":\"array\",\"items\":{\"type\":\"string\"},\"description\":\"Human-readab",
15
- "le value labels.\"},\"basis\":{\"type\":\"string\",\"enum\":[\"canonical_attribute\",\"image_colour_analysis\"],\"description\":\"Where the meaning comes from: an accepted canonical attribute value, or measured image colour analysis. NEVER derived from vector similarity or scores.\"},\"confidence\":{\"type\":\"number\"},\"sourceBasis\":{\"type\":\"string\"},\"version\":{\"description\":\"Extraction version for measured meanings.\",\"type\":\"string\"},\"appliedAt\":{\"description\":\"ISO timestamp the accepted value was applied.\",\"type\":\"string\"}},\"required\":[\"id\",\"label\",\"values\",\"basis\"],\"additionalProperties\":false}},\"verdict\":{\"description\":\"The shared report card for this hit against the request's `filters` (required cells) and `preferences` (preferred cells): one cell per criterion with its state (matched / unverified / failed / missing), value, per-cell trust, a grade, and the weakest confidence. With `verdicts: true` and a `similarTo: { variantId }` seed it ALSO carries `preferred` SEED cells (baseline `{ kind: \\\"seed\\\" }`) — one per comparable attribute the seed holds, plus a `colour` axis — `matched`/`deviated` with a directional deviation. Present (full view only) whenever a spec filter, preference, or seed verdict produced a cell.\",\"type\":\"object\",\"properties\":{\"cells\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"minLength\":1},\"state\":{\"type\":\"string\",\"enum\":[\"matched\",\"deviated\",\"failed\",\"unverified\",\"missing\",\"not_evaluated\"]},\"strength\":{\"type\":\"string\",\"enum\":[\"required\",\"preferred\"]},\"baseline\":{\"oneOf\":[{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"const\":\"term\"},\"term\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"kind\",\"term\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"const\":\"presence\"}},\"required\":[\"kind\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"const\":\"seed\"},\"variantId\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"kind\",\"variantId\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"const\":\"image\"},\"source\":{\"type\":\"string\",\"enum\":[\"variant\",\"vector\"]},\"variantId\":{\"anyOf\":[{\"type\":\"string\",\"minLength\":1},{\"type\":\"null\"}]}},\"required\":[\"kind\",\"source\",\"variantId\"],\"additionalProperties\":false}],\"type\":\"object\"},\"value\":{\"type\":[\"string\",\"null\"]},\"deviation\":{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"direction\":{\"type\":\"string\",\"enum\":[\"above\",\"below\",\"other\"]},\"delta\":{\"type\":[\"number\",\"null\"]},\"unit\":{\"type\":[\"string\",\"null\"]},\"label\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"direction\",\"delta\",\"unit\",\"label\"],\"additionalProperties\":false},{\"type\":\"null\"}]},\"confidence\":{\"anyOf\":[{\"type\":\"number\",\"minimum\":0,\"maximum\":1},{\"type\":\"null\"}]},\"sourceBasis\":{\"type\":[\"string\",\"null\"]},\"trustGap\":{\"anyOf\":[{\"type\":\"string\",\"enum\":[\"below_confidence\",\"basis_unmet\",\"proposed\"]},{\"type\":\"null\"}]},\"citation\":{\"type\":\"object\",\"properties\":{\"citationId\":{\"type\":\"string\",\"minLength\":1},\"sourceType\":{\"type\":\"string\",\"minLength\":1},\"count\":{\"type\":\"integer\",\"exclusiveMinimum\":0,\"maximum\":9007199254740991}},\"required\":[\"citationId\",\"sourceType\",\"count\"],\"additionalProperties\":false}},\"required\":[\"attributeId\",\"state\",\"strength\",\"baseline\",\"value\",\"deviation\",\"confidence\",\"sourceBasis\",\"trustGap\"],\"additionalProperties\":false}},\"grade\":{\"type\":\"string\",\"enum\":[\"meets_all\",\"close\",\"hard_no\"]},\"minConfidence\":{\"anyOf\":[{\"type\":\"number\",\"minimum\":0,\"maximum\":1},{\"type\":\"null\"}]}},\"required\":[\"cells\",\"grade\",\"minConfidence\"],\"additionalProperties\":false},\"anatomy\":{\"description\":\"How this hit was assembled — the ranking representative behind the displayed variant, and the siblings a product collapse hid. Present only when `explain` was set AND the hit actually holds one of those (a distinct representative, or at least one hidden sibling); a plain uncollapsed variant hit has no anatomy to report.\",\"type\":\"object\",\"properties\":{\"representative\":{\"description\":\"The RANKING identity behind this hit, when it differs from the displayed variant. Its score is the group's cursor key and the it",
16
- "em's `signals` describe IT, not the card's photo. Omitted when the face IS the representative (no collapse, or the collapse fronted the top-ranked member).\",\"type\":\"object\",\"properties\":{\"variantId\":{\"type\":\"string\"},\"variantName\":{\"type\":[\"string\",\"null\"]},\"color\":{\"type\":[\"string\",\"null\"]},\"score\":{\"type\":\"number\"},\"signals\":{\"type\":\"object\",\"properties\":{\"portrait\":{\"type\":[\"number\",\"null\"]},\"image\":{\"type\":[\"number\",\"null\"]},\"category\":{\"type\":[\"number\",\"null\"]},\"colour\":{\"type\":[\"number\",\"null\"]},\"lexical\":{\"type\":[\"number\",\"null\"]},\"text\":{\"type\":[\"number\",\"null\"]}},\"required\":[\"image\",\"category\",\"colour\",\"lexical\",\"text\"],\"additionalProperties\":false}},\"required\":[\"variantId\",\"variantName\",\"color\",\"score\",\"signals\"],\"additionalProperties\":false},\"siblings\":{\"description\":\"Variants of the same product that also matched but were HIDDEN by `collapse: \\\"product\\\"` — the named form of `siblingCount`. Each carries its own per-arm signals, so an operator can see what each sibling matched on. Capped at 12 entries; `siblingCount` is always the complete tally.\",\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"variantId\":{\"type\":\"string\"},\"variantName\":{\"type\":[\"string\",\"null\"]},\"color\":{\"type\":[\"string\",\"null\"]},\"score\":{\"type\":\"number\"},\"signals\":{\"type\":\"object\",\"properties\":{\"portrait\":{\"type\":[\"number\",\"null\"]},\"image\":{\"type\":[\"number\",\"null\"]},\"category\":{\"type\":[\"number\",\"null\"]},\"colour\":{\"type\":[\"number\",\"null\"]},\"lexical\":{\"type\":[\"number\",\"null\"]},\"text\":{\"type\":[\"number\",\"null\"]}},\"required\":[\"image\",\"category\",\"colour\",\"lexical\",\"text\"],\"additionalProperties\":false}},\"required\":[\"variantId\",\"variantName\",\"color\",\"score\",\"signals\"],\"additionalProperties\":false}},\"siblingsTruncated\":{\"description\":\"Present (true) only when more than 12 siblings were hidden and `siblings` lists the highest-ranked slice.\",\"type\":\"boolean\"}},\"additionalProperties\":false}},\"required\":[\"variantId\",\"brand\",\"product\",\"brandSlug\",\"productId\",\"color\",\"variantName\",\"imageUrl\",\"hasImage\",\"palette\",\"score\"],\"additionalProperties\":false}},\"attribution\":{\"type\":\"object\",\"properties\":{\"responseId\":{\"type\":\"string\",\"description\":\"This response's stable id — the same value as the `X-Request-Id` header. Store it next to the outcome (an order, a save, a click) to join that outcome back to the ranking that produced it. Opaque: round-trip it verbatim, never parse it.\"},\"basis\":{\"type\":\"string\",\"enum\":[\"image\",\"colour\",\"text\",\"lexical\",\"entity-browse\",\"match\"],\"description\":\"The ranking basis, groupable across both ranked routes. On /api/v1/search this is the envelope's `mode` verbatim; /api/v1/search/match reports 'match'. Group your stored outcomes by this field.\"},\"issuedAt\":{\"type\":\"string\",\"description\":\"ISO-8601 timestamp of when this response was assembled, so a stored outcome carries its own age without a second lookup.\"}},\"required\":[\"responseId\",\"basis\",\"issuedAt\"],\"additionalProperties\":false,\"description\":\"Result attribution receipt. Present on every successful ranked response. Nothing on our side retains it — what you store IS the record.\"},\"refinement\":{\"description\":\"Inspectable receipt for narrowing this exact finalized candidate pool. The token is opaque and never exposes variant ids.\",\"type\":\"object\",\"properties\":{\"token\":{\"type\":\"string\"},\"expiresAt\":{\"type\":\"string\",\"format\":\"date-time\",\"pattern\":\"^(?:(?:\\\\d\\\\d[2468][048]|\\\\d\\\\d[13579][26]|\\\\d\\\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\\\d|30)|(?:02)-(?:0[1-9]|1\\\\d|2[0-8])))T(?:(?:[01]\\\\d|2[0-3]):[0-5]\\\\d:[0-5]\\\\d(?:\\\\.\\\\d+)?(?:Z))$\"},\"poolSize\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":800},\"schemaVersion\":{\"type\":\"string\"},\"projectionVersion\":{\"type\":\"integer\",\"minimum\":-9007199254740991,\"maximum\":9007199254740991}},\"required\":[\"token\",\"expiresAt\",\"poolSize\",\"schemaVersion\",\"projectionVersion\"],\"additionalProperties\":false},\"understanding\":{\"description\":\"How the ordinary search path interpreted the raw material-domain query: recognized senses, applied/refused/ambiguous outcomes, evidence, and ",
17
- "the semantic text left after exact entity consumption. New standard-search responses include it; optional in the schema for rolling-deploy compatibility.\",\"type\":\"object\",\"properties\":{\"version\":{\"type\":\"string\",\"const\":\"1\"},\"rawQuery\":{\"type\":\"string\"},\"normalisedQuery\":{\"type\":\"string\"},\"residualText\":{\"type\":\"string\"},\"status\":{\"type\":\"string\",\"enum\":[\"resolved\",\"ambiguous\",\"partial\",\"unresolved\"]},\"mode\":{\"type\":\"string\",\"enum\":[\"image\",\"colour\",\"text\",\"lexical\",\"entity-browse\"]},\"senses\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"id\":{\"type\":\"string\",\"minLength\":1},\"kind\":{\"type\":\"string\",\"enum\":[\"brand\",\"product\",\"paint_identity\",\"category\",\"material\",\"colour\",\"attribute\",\"attribute_value\",\"certification\"]},\"label\":{\"type\":\"string\",\"minLength\":1},\"outcome\":{\"type\":\"string\",\"enum\":[\"applied\",\"ambiguous\",\"recognized\",\"refused\"]},\"effect\":{\"type\":\"string\",\"enum\":[\"restrict\",\"prioritize\",\"rerank\",\"offer\",\"none\"]},\"confidence\":{\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"evidence\":{\"type\":\"object\",\"properties\":{\"source\":{\"type\":\"string\",\"minLength\":1},\"matchedText\":{\"type\":\"string\",\"minLength\":1},\"span\":{\"type\":\"object\",\"properties\":{\"start\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"end\":{\"type\":\"integer\",\"exclusiveMinimum\":0,\"maximum\":9007199254740991}},\"required\":[\"start\",\"end\"],\"additionalProperties\":false}},\"required\":[\"source\",\"matchedText\"],\"additionalProperties\":false},\"entity\":{\"type\":\"object\",\"properties\":{\"brandSlug\":{\"type\":\"string\"},\"productId\":{\"type\":\"string\"},\"variantId\":{\"type\":\"string\"},\"materialBankId\":{\"type\":\"string\"},\"productTypeId\":{\"type\":\"string\"},\"paintCode\":{\"type\":\"string\"},\"hex\":{\"type\":\"string\"}},\"additionalProperties\":false},\"reason\":{\"type\":\"string\"}},\"required\":[\"id\",\"kind\",\"label\",\"outcome\",\"effect\",\"confidence\",\"evidence\"],\"additionalProperties\":false}},\"canonicalLexical\":{\"type\":\"object\",\"properties\":{\"original\":{\"type\":\"string\"},\"outcome\":{\"type\":\"string\",\"enum\":[\"resolved\",\"ambiguous\",\"declined\",\"partial\",\"unresolved\"]},\"matches\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"original\":{\"type\":\"string\"},\"canonical\":{\"type\":\"string\"},\"rule\":{\"type\":\"string\",\"enum\":[\"exact\",\"plural-to-singular\",\"typo-to-canonical\"]},\"span\":{\"type\":\"object\",\"properties\":{\"start\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"end\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991}},\"required\":[\"start\",\"end\"],\"additionalProperties\":false},\"coverage\":{\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"readings\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"authority\":{\"type\":\"string\",\"enum\":[\"taxonomy\",\"colorscope\"]},\"identity\":{\"type\":\"string\",\"minLength\":1},\"outcome\":{\"type\":\"string\",\"enum\":[\"equivalent\",\"ambiguous\",\"declined\"]},\"executionStatus\":{\"type\":\"string\",\"enum\":[\"applied\",\"suppressed\",\"executable\",\"recognized-but-unexecutable\",\"declined\"],\"description\":\"MaterialGraph's execution verdict for this provider-owned reading. Taxonomy adapters report 'applied' only when their category or criterion contributed to this query; an adapter-known ambiguous or conflicting reading is 'suppressed'. 'recognized-but-unexecutable' means no current mapping can apply it.\"},\"role\":{\"anyOf\":[{\"type\":\"string\",\"enum\":[\"primary\",\"secondary\"]},{\"type\":\"null\"}]},\"kind\":{\"type\":\"string\",\"enum\":[\"domain\",\"core-colour\",\"paint\"]},\"label\":{\"type\":\"string\"},\"libraryId\":{\"type\":\"string\"},\"code\":{\"type\":\"string\"},\"aliasCount\":{\"type\":\"integer\",\"exclusiveMinimum\":0,\"maximum\":9007199254740991},\"matchForm\":{\"type\":\"string\",\"enum\":[\"name\",\"code\",\"authoritative-term\"]},\"collisionCount\":{\"type\":\"integer\",\"exclusiveMinimum\":0,\"maximum\":9007199254740991},\"familyId\":{\"type\":\"string\"},\"hex\":{\"type\":\"string\",\"pattern\":\"^#[0-9a-f]{6}$\"},\"level\":{\"type\":\"string\",\"enum\":[\"family\",\"tone-band\",\"branch\",\"concept\"]},\"lifecycle\":{\"type\":\"object\",\"properties\":{\"introducedAt\":{\"type\":\"string\",\"minLength\":1},\"reviewedAt\":{\"type\":\"string\",\"minLength\":1},\"status\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"introducedAt\",\"status\"],\"additionalProperties\":false},\"provenance\":{\"type\":\"object\",\"proper",
18
- "ties\":{\"basis\":{\"type\":\"string\",\"minLength\":1},\"snapshot\":{\"type\":\"string\",\"minLength\":1},\"source\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"basis\",\"source\"],\"additionalProperties\":false}},\"required\":[\"authority\",\"identity\",\"outcome\",\"executionStatus\",\"role\"],\"additionalProperties\":false}},\"receipt\":{\"type\":\"object\",\"properties\":{\"original\":{\"type\":\"string\"},\"canonical\":{\"type\":\"string\"},\"rule\":{\"type\":\"string\",\"enum\":[\"exact\",\"plural-to-singular\",\"typo-to-canonical\"]},\"authorities\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"authority\":{\"type\":\"string\",\"enum\":[\"taxonomy\",\"colorscope\"]},\"version\":{\"type\":\"string\",\"minLength\":1},\"revision\":{\"type\":\"string\",\"minLength\":1},\"digest\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"authority\",\"version\",\"revision\"],\"additionalProperties\":false}}},\"required\":[\"original\",\"canonical\",\"rule\",\"authorities\"],\"additionalProperties\":false}},\"required\":[\"original\",\"canonical\",\"rule\",\"span\",\"coverage\",\"readings\",\"receipt\"],\"additionalProperties\":false}},\"unresolved\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"original\":{\"type\":\"string\"},\"canonical\":{\"type\":\"string\"},\"rule\":{\"type\":\"string\",\"enum\":[\"exact\",\"plural-to-singular\",\"typo-to-canonical\"]},\"span\":{\"type\":\"object\",\"properties\":{\"start\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"end\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991}},\"required\":[\"start\",\"end\"],\"additionalProperties\":false},\"coverage\":{\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"readings\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"authority\":{\"type\":\"string\",\"enum\":[\"taxonomy\",\"colorscope\"]},\"identity\":{\"type\":\"string\",\"minLength\":1},\"outcome\":{\"type\":\"string\",\"enum\":[\"equivalent\",\"ambiguous\",\"declined\"]},\"executionStatus\":{\"type\":\"string\",\"enum\":[\"applied\",\"suppressed\",\"executable\",\"recognized-but-unexecutable\",\"declined\"],\"description\":\"MaterialGraph's execution verdict for this provider-owned reading. Taxonomy adapters report 'applied' only when their category or criterion contributed to this query; an adapter-known ambiguous or conflicting reading is 'suppressed'. 'recognized-but-unexecutable' means no current mapping can apply it.\"},\"role\":{\"anyOf\":[{\"type\":\"string\",\"enum\":[\"primary\",\"secondary\"]},{\"type\":\"null\"}]},\"kind\":{\"type\":\"string\",\"enum\":[\"domain\",\"core-colour\",\"paint\"]},\"label\":{\"type\":\"string\"},\"libraryId\":{\"type\":\"string\"},\"code\":{\"type\":\"string\"},\"aliasCount\":{\"type\":\"integer\",\"exclusiveMinimum\":0,\"maximum\":9007199254740991},\"matchForm\":{\"type\":\"string\",\"enum\":[\"name\",\"code\",\"authoritative-term\"]},\"collisionCount\":{\"type\":\"integer\",\"exclusiveMinimum\":0,\"maximum\":9007199254740991},\"familyId\":{\"type\":\"string\"},\"hex\":{\"type\":\"string\",\"pattern\":\"^#[0-9a-f]{6}$\"},\"level\":{\"type\":\"string\",\"enum\":[\"family\",\"tone-band\",\"branch\",\"concept\"]},\"lifecycle\":{\"type\":\"object\",\"properties\":{\"introducedAt\":{\"type\":\"string\",\"minLength\":1},\"reviewedAt\":{\"type\":\"string\",\"minLength\":1},\"status\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"introducedAt\",\"status\"],\"additionalProperties\":false},\"provenance\":{\"type\":\"object\",\"properties\":{\"basis\":{\"type\":\"string\",\"minLength\":1},\"snapshot\":{\"type\":\"string\",\"minLength\":1},\"source\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"basis\",\"source\"],\"additionalProperties\":false}},\"required\":[\"authority\",\"identity\",\"outcome\",\"executionStatus\",\"role\"],\"additionalProperties\":false}},\"receipt\":{\"type\":\"object\",\"properties\":{\"original\":{\"type\":\"string\"},\"canonical\":{\"type\":\"string\"},\"rule\":{\"type\":\"string\",\"enum\":[\"exact\",\"plural-to-singular\",\"typo-to-canonical\"]},\"authorities\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"authority\":{\"type\":\"string\",\"enum\":[\"taxonomy\",\"colorscope\"]},\"version\":{\"type\":\"string\",\"minLength\":1},\"revision\":{\"type\":\"string\",\"minLength\":1},\"digest\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"authority\",\"version\",\"revision\"],\"additionalProperties\":false}}},\"required\":[\"original\",\"canonical\",\"rule\",\"authorities\"],\"additionalProperties\":false}},\"required\":[\"original\",\"canonical\",\"rule\",\"span\",\"coverage\",\"readings\",\"receipt\"],\"additionalPrope",
19
- "rties\":false}}},\"required\":[\"original\",\"outcome\",\"matches\",\"unresolved\"],\"additionalProperties\":false},\"criteria\":{\"type\":\"object\",\"properties\":{\"applied\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"criterion\":{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, e.g. \\\"primary_material\\\" or \\\"fire_rating\\\" — or the special id \\\"brand\\\", which filters on the brand RELATION (a join, carried by every variant) rather than on any attribute.\"},\"term\":{\"type\":\"string\",\"description\":\"Canonical term the attribute must equal, e.g. \\\"textile\\\". For \\\"brand\\\", a brand slug (\\\"kvadrat\\\") or display name (\\\"Kvadrat\\\"), case-insensitive — exactly the `value` a `brand` facet reports. An unrecognised brand is a 400 naming it, never an empty page.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"term\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, or the special id \\\"brand\\\" for the brand relation.\"},\"terms\":{\"minItems\":1,\"type\":\"array\",\"items\":{\"type\":\"string\"},\"description\":\"Canonical terms, ANY ONE of which satisfies the filter — this is how OR is expressed. OR lives inside one entry; separate `filters[]` entries AND together. Used for ordinal scales, where a stricter class satisfies a looser one, and for a multi-select rail — `{attributeId:\\\"brand\\\", terms:[\\\"kvadrat\\\",\\\"rubelli\\\"]}` returns materials from either brand.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"terms\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\"},\"excludesTerm\":{\"type\":\"string\",\"minLength\":1},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"excludesTerm\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id that must have an accepted value, e.g. \\\"sample_image_mm_per_px\\\".\"},\"hasValue\":{\"type\":\"boolean\",\"const\":true,\"description\":\"Require any accepted value for this attribute.\"},\"asOf\":{\"description\":\"Reference date for EPD expiration, exclusive: the value must remain valid after this date.\",\"type\":\"string\",\"format\":\"date\",\"pattern\":\"^(?:(?:\\\\d\\\\d[2468][048]|\\\\d\\\\d[13579][26]|\\\\d\\\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\\\d|30)|",
20
- "(?:02)-(?:0[1-9]|1\\\\d|2[0-8])))$\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"hasValue\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id holding a NUMBER, e.g. \\\"light_reflectance_value\\\" or \\\"lead_time_days\\\".\"},\"min\":{\"description\":\"Inclusive lower bound. Omit for a ceiling-only range.\",\"type\":\"number\"},\"max\":{\"description\":\"Inclusive upper bound. Omit for a floor-only range.\",\"type\":\"number\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\"],\"additionalProperties\":false}]},\"lane\":{\"type\":\"string\",\"enum\":[\"filter\",\"preference\"]},\"lexicalSignal\":{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"minLength\":1},\"canonical\":{\"type\":\"string\",\"minLength\":1},\"identity\":{\"type\":\"string\",\"minLength\":1},\"original\":{\"type\":\"string\",\"minLength\":1},\"relation\":{\"type\":\"string\",\"enum\":[\"equivalent\",\"broader-storage-term\"]},\"span\":{\"type\":\"object\",\"properties\":{\"start\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"end\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991}},\"required\":[\"start\",\"end\"],\"additionalProperties\":false},\"storedTerm\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"attributeId\",\"canonical\",\"identity\",\"original\",\"relation\",\"span\",\"storedTerm\"],\"additionalProperties\":false},\"source\":{\"type\":\"string\",\"enum\":[\"compiled\",\"taxonomy-adapter\"]},\"span\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"criterion\",\"lane\",\"source\",\"span\"],\"additionalProperties\":false}},\"conflicts\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"minLength\":1},\"caller\":{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, e.g. \\\"primary_material\\\" or \\\"fire_rating\\\" — or the special id \\\"brand\\\", which filters on the brand RELATION (a join, carried by every variant) rather than on any attribute.\"},\"term\":{\"type\":\"string\",\"description\":\"Canonical term the attribute must equal, e.g. \\\"textile\\\". For \\\"brand\\\", a brand slug (\\\"kvadrat\\\") or display name (\\\"Kvadrat\\\"), case-insensitive — exactly the `value` a `brand` facet reports. An unrecognised brand is a 400 naming it, never an empty page.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"term\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribu",
21
- "te id, or the special id \\\"brand\\\" for the brand relation.\"},\"terms\":{\"minItems\":1,\"type\":\"array\",\"items\":{\"type\":\"string\"},\"description\":\"Canonical terms, ANY ONE of which satisfies the filter — this is how OR is expressed. OR lives inside one entry; separate `filters[]` entries AND together. Used for ordinal scales, where a stricter class satisfies a looser one, and for a multi-select rail — `{attributeId:\\\"brand\\\", terms:[\\\"kvadrat\\\",\\\"rubelli\\\"]}` returns materials from either brand.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"terms\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\"},\"excludesTerm\":{\"type\":\"string\",\"minLength\":1},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"excludesTerm\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id that must have an accepted value, e.g. \\\"sample_image_mm_per_px\\\".\"},\"hasValue\":{\"type\":\"boolean\",\"const\":true,\"description\":\"Require any accepted value for this attribute.\"},\"asOf\":{\"description\":\"Reference date for EPD expiration, exclusive: the value must remain valid after this date.\",\"type\":\"string\",\"format\":\"date\",\"pattern\":\"^(?:(?:\\\\d\\\\d[2468][048]|\\\\d\\\\d[13579][26]|\\\\d\\\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\\\d|30)|(?:02)-(?:0[1-9]|1\\\\d|2[0-8])))$\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"hasValue\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id holding a NUMBER, e.g. \\\"light_reflectance_value\\\" or \\\"lead_time_days\\\".\"},\"min\":{\"description\":\"Inclusive lower bound. Omit for a ceiling-only range.\",\"type\":\"number\"},\"max\":{\"description\":\"Inclusive upper bound. Omit for a floor-only range.\",\"type\":\"number\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requireme",
22
- "nt is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\"],\"additionalProperties\":false}]},\"compiled\":{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, e.g. \\\"primary_material\\\" or \\\"fire_rating\\\" — or the special id \\\"brand\\\", which filters on the brand RELATION (a join, carried by every variant) rather than on any attribute.\"},\"term\":{\"type\":\"string\",\"description\":\"Canonical term the attribute must equal, e.g. \\\"textile\\\". For \\\"brand\\\", a brand slug (\\\"kvadrat\\\") or display name (\\\"Kvadrat\\\"), case-insensitive — exactly the `value` a `brand` facet reports. An unrecognised brand is a 400 naming it, never an empty page.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"term\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, or the special id \\\"brand\\\" for the brand relation.\"},\"terms\":{\"minItems\":1,\"type\":\"array\",\"items\":{\"type\":\"string\"},\"description\":\"Canonical terms, ANY ONE of which satisfies the filter — this is how OR is expressed. OR lives inside one entry; separate `filters[]` entries AND together. Used for ordinal scales, where a stricter class satisfies a looser one, and for a multi-select rail — `{attributeId:\\\"brand\\\", terms:[\\\"kvadrat\\\",\\\"rubelli\\\"]}` returns materials from either brand.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"terms\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\"},\"excludesTerm\":{\"type\":\"string\",\"minLength\":1},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"excludesTerm\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id that must have an accepted value, e.g. \\\"sample_image_mm_per_px\\\".\"},\"hasValue\":{\"type\":\"boolean\",\"const\":true,\"description\":\"Require any accepted value for this attribute.\"},\"asOf\":{\"description\":\"Reference date for EPD expiration, exclusive: the value must remain valid after this date.\",\"type\":\"string\",\"format\":\"date\",\"pattern\":\"^(?:(?:\\\\d\\\\d[2468][048]|\\\\d\\\\d[13579][26]|\\\\d\\\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\\\d|30)|(?:02)-(?:0[1-9]|1\\\\d|2[0-8])))$\"},\"trust\":{\"type\":\"object\",\"proper",
23
- "ties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"hasValue\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id holding a NUMBER, e.g. \\\"light_reflectance_value\\\" or \\\"lead_time_days\\\".\"},\"min\":{\"description\":\"Inclusive lower bound. Omit for a ceiling-only range.\",\"type\":\"number\"},\"max\":{\"description\":\"Inclusive upper bound. Omit for a floor-only range.\",\"type\":\"number\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\"],\"additionalProperties\":false}]},\"reason\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"attributeId\",\"caller\",\"compiled\",\"reason\"],\"additionalProperties\":false}},\"exclusions\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"criterion\":{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, e.g. \\\"primary_material\\\" or \\\"fire_rating\\\" — or the special id \\\"brand\\\", which filters on the brand RELATION (a join, carried by every variant) rather than on any attribute.\"},\"term\":{\"type\":\"string\",\"description\":\"Canonical term the attribute must equal, e.g. \\\"textile\\\". For \\\"brand\\\", a brand slug (\\\"kvadrat\\\") or display name (\\\"Kvadrat\\\"), case-insensitive — exactly the `value` a `brand` facet reports. An unrecognised brand is a 400 naming it, never an empty page.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"term\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, or the special id \\\"brand\\\" for the brand relation.\"},\"terms\":{\"minItems\":1,\"type\":\"array\",\"items\":{\"type\":\"string\"},\"description\":\"Canonical terms, ANY ONE of which satisfies the filter — this is how OR is expressed. OR lives inside one entry; separate `filters[]` entries AND together. Used for ordinal scales, where a stricter class satisfies a looser one, and for a multi-select rail — `{attributeId:\\\"brand\\\", terms:[\\\"kvadrat\\\",\\\"rubelli\\\"]}` returns materials from either brand.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's",
24
- " REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"terms\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\"},\"excludesTerm\":{\"type\":\"string\",\"minLength\":1},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"excludesTerm\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id that must have an accepted value, e.g. \\\"sample_image_mm_per_px\\\".\"},\"hasValue\":{\"type\":\"boolean\",\"const\":true,\"description\":\"Require any accepted value for this attribute.\"},\"asOf\":{\"description\":\"Reference date for EPD expiration, exclusive: the value must remain valid after this date.\",\"type\":\"string\",\"format\":\"date\",\"pattern\":\"^(?:(?:\\\\d\\\\d[2468][048]|\\\\d\\\\d[13579][26]|\\\\d\\\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\\\d|30)|(?:02)-(?:0[1-9]|1\\\\d|2[0-8])))$\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"hasValue\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id holding a NUMBER, e.g. \\\"light_reflectance_value\\\" or \\\"lead_time_days\\\".\"},\"min\":{\"description\":\"Inclusive lower bound. Omit for a ceiling-only range.\",\"type\":\"number\"},\"max\":{\"description\":\"Inclusive upper bound. Omit for a floor-only range.\",\"type\":\"number\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\"],\"additionalProperties\":false}]},\"execution\":{\"type\":\"string\",\"const\":\"blocked-positive-only-contract\"},\"lane\":{\"type\":\"string\",\"enum\":[\"hard\",\"soft\"]},\"missingValuePolicy\":{\"type\":\"string\",\"const\":\"retain-unknown\"},\"span\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"criterion\",\"execution\",\"lane\",\"missingValuePolicy\",\"span\"],\"additionalProperties\":false}},\"preferences\":{\"type\":\"array\",\"items\":{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, e.g. \\\"primary_material\\\" or \\\"fire_rating\\\" — or the special id \\\"brand\\\", which filters on the brand RELATION (a join, carried by every variant) rather than on any attribute.\"},\"term\":{\"type\":\"string\",\"description\":\"Canonical term the attribute must equal, e.g. \\\"textile\\\". For \\\"brand\\\", a",
25
- " brand slug (\\\"kvadrat\\\") or display name (\\\"Kvadrat\\\"), case-insensitive — exactly the `value` a `brand` facet reports. An unrecognised brand is a 400 naming it, never an empty page.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"term\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, or the special id \\\"brand\\\" for the brand relation.\"},\"terms\":{\"minItems\":1,\"type\":\"array\",\"items\":{\"type\":\"string\"},\"description\":\"Canonical terms, ANY ONE of which satisfies the filter — this is how OR is expressed. OR lives inside one entry; separate `filters[]` entries AND together. Used for ordinal scales, where a stricter class satisfies a looser one, and for a multi-select rail — `{attributeId:\\\"brand\\\", terms:[\\\"kvadrat\\\",\\\"rubelli\\\"]}` returns materials from either brand.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"terms\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\"},\"excludesTerm\":{\"type\":\"string\",\"minLength\":1},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"excludesTerm\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id that must have an accepted value, e.g. \\\"sample_image_mm_per_px\\\".\"},\"hasValue\":{\"type\":\"boolean\",\"const\":true,\"description\":\"Require any accepted value for this attribute.\"},\"asOf\":{\"description\":\"Reference date for EPD expiration, exclusive: the value must remain valid after this date.\",\"type\":\"string\",\"format\":\"date\",\"pattern\":\"^(?:(?:\\\\d\\\\d[2468][048]|\\\\d\\\\d[13579][26]|\\\\d\\\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\\\d|30)|(?:02)-(?:0[1-9]|1\\\\d|2[0-8])))$\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"",
26
- "required\":[\"attributeId\",\"hasValue\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id holding a NUMBER, e.g. \\\"light_reflectance_value\\\" or \\\"lead_time_days\\\".\"},\"min\":{\"description\":\"Inclusive lower bound. Omit for a ceiling-only range.\",\"type\":\"number\"},\"max\":{\"description\":\"Inclusive upper bound. Omit for a floor-only range.\",\"type\":\"number\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\"],\"additionalProperties\":false}]}},\"residualText\":{\"type\":[\"string\",\"null\"]},\"unresolved\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"code\":{\"type\":\"string\",\"enum\":[\"missing-searchable-attribute\",\"missing-dimension-pair-contract\",\"missing-explicit-comparator\",\"negative-criterion-contract-blocked\",\"registry-contract-drift\",\"unsupported-numeric-format\",\"unsupported-unit\",\"unsupported-strict-comparator\"]},\"reason\":{\"type\":\"string\",\"minLength\":1},\"span\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"code\",\"reason\",\"span\"],\"additionalProperties\":false}}},\"required\":[\"applied\",\"conflicts\",\"exclusions\",\"preferences\",\"residualText\",\"unresolved\"],\"additionalProperties\":false},\"productType\":{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"enum\":[\"leaf\",\"group\",\"vector\",\"ambiguous\",\"none\"]},\"gate\":{\"type\":\"string\",\"enum\":[\"hard\",\"constrain\",\"none\"]},\"pass\":{\"type\":\"string\",\"enum\":[\"taxonomy\",\"synonym\",\"display-name\",\"group\",\"head-noun\",\"vector\",\"none\"]},\"reason\":{\"type\":\"string\",\"minLength\":1},\"displayName\":{\"type\":[\"string\",\"null\"]},\"productTypeId\":{\"type\":[\"string\",\"null\"]},\"groupKey\":{\"type\":[\"string\",\"null\"]},\"productTypeIds\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"similarity\":{\"type\":[\"number\",\"null\"]},\"margin\":{\"type\":[\"number\",\"null\"]},\"residualText\":{\"type\":\"string\"},\"vectorPassRan\":{\"type\":\"boolean\"},\"registryVersion\":{\"type\":\"string\",\"minLength\":1},\"durationMs\":{\"type\":\"number\",\"minimum\":0},\"candidates\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"productTypeId\":{\"type\":\"string\",\"minLength\":1},\"displayName\":{\"type\":\"string\",\"minLength\":1},\"similarity\":{\"type\":\"number\"}},\"required\":[\"productTypeId\",\"displayName\",\"similarity\"],\"additionalProperties\":false}}},\"required\":[\"kind\",\"gate\",\"pass\",\"reason\",\"displayName\",\"productTypeId\",\"groupKey\",\"productTypeIds\",\"similarity\",\"margin\",\"residualText\",\"vectorPassRan\",\"registryVersion\",\"durationMs\",\"candidates\"],\"additionalProperties\":false},\"arm\":{\"type\":\"object\",\"properties\":{\"arm\":{\"anyOf\":[{\"type\":\"string\",\"enum\":[\"dense\",\"lexical\",\"colour\"]},{\"type\":\"null\"}]},\"reason\":{\"type\":\"string\",\"minLength\":1},\"reasonDetail\":{\"type\":\"string\",\"enum\":[\"derived-colour-veto\",\"typo-recovery\",\"literal-compound-without-type\"]},\"outcome\":{\"type\":\"string\",\"enum\":[\"dense\",\"lexical\",\"colour\",\"browse\",\"criteria-only\",\"ambiguous\"]},\"decidedBy\":{\"type\":\"string\",\"enum\":[\"deterministic\",\"model\"]},\"productTypeGate\":{\"type\":\"string\",\"enum\":[\"hard\",\"constrain\",\"none\"]},\"productTypeIds\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"residual\":{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"enum\":[\"compound\",\"descriptive\",\"ambiguous\",\"empty\"]},\"tokens\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"resolvedTokens\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"unresolvedTokens\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"resolvedRatio\":{\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"comparisonFrame\":{\"type\":\"boolean\"}},\"required\":[\"kind\",\"tokens\",\"resolvedTokens\",\"unresolvedTokens\",\"resolvedRatio\",\"comparisonFrame\"],\"additionalProperties\":false},\"registryVersi",
27
- "on\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"arm\",\"reason\",\"outcome\",\"decidedBy\",\"productTypeGate\",\"productTypeIds\",\"residual\",\"registryVersion\"],\"additionalProperties\":false},\"ambiguity\":{\"type\":\"object\",\"properties\":{\"outcome\":{\"type\":\"string\",\"enum\":[\"applied\",\"abstained\",\"refused\",\"timeout\"]},\"modelId\":{\"type\":[\"string\",\"null\"]},\"providerRan\":{\"type\":\"boolean\"},\"durationMs\":{\"type\":\"number\",\"minimum\":0},\"timeoutMs\":{\"type\":\"number\",\"exclusiveMinimum\":0},\"validation\":{\"type\":\"string\",\"enum\":[\"valid\",\"invalid\",\"not-run\"]},\"refusalCode\":{\"type\":[\"string\",\"null\"]},\"confidence\":{\"type\":[\"number\",\"null\"]},\"routing\":{\"anyOf\":[{\"type\":\"string\",\"enum\":[\"compound\",\"descriptive\"]},{\"type\":\"null\"}]},\"productTypeIds\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"changedArm\":{\"type\":\"boolean\"}},\"required\":[\"outcome\",\"modelId\",\"providerRan\",\"durationMs\",\"timeoutMs\",\"validation\",\"refusalCode\",\"confidence\",\"routing\",\"productTypeIds\",\"changedArm\"],\"additionalProperties\":false},\"categoryScope\":{\"type\":\"object\",\"properties\":{\"source\":{\"type\":\"string\",\"enum\":[\"caller\",\"anchor\"]},\"mechanism\":{\"type\":\"string\",\"enum\":[\"sql-predicate\",\"allowlist\",\"prune\"]},\"variantCount\":{\"anyOf\":[{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},{\"type\":\"null\"}]},\"ceiling\":{\"type\":\"integer\",\"exclusiveMinimum\":0,\"maximum\":9007199254740991},\"specScanBounded\":{\"type\":\"boolean\"}},\"required\":[\"source\",\"mechanism\",\"variantCount\",\"ceiling\"],\"additionalProperties\":{}},\"specAllowlist\":{\"type\":\"object\",\"properties\":{\"limit\":{\"type\":\"integer\",\"exclusiveMinimum\":0,\"maximum\":9007199254740991},\"returned\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"retrieved\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"truncated\":{\"type\":\"boolean\"},\"emptyMeansScarcity\":{\"type\":\"boolean\"},\"outcome\":{\"type\":\"string\",\"enum\":[\"corpus-scarcity\",\"retrieval-miss\",\"unknown\"]}},\"required\":[\"limit\",\"returned\",\"truncated\",\"emptyMeansScarcity\"],\"additionalProperties\":false},\"unsatisfiableNarrowing\":{\"type\":\"object\",\"properties\":{\"narrowing\":{\"type\":\"string\",\"enum\":[\"product-type\"]},\"gate\":{\"type\":\"string\",\"enum\":[\"hard\"]},\"productTypeId\":{\"type\":\"string\",\"minLength\":1},\"displayName\":{\"type\":\"string\",\"minLength\":1},\"productTypeIds\":{\"type\":\"array\",\"items\":{\"type\":\"string\",\"minLength\":1}},\"pass\":{\"type\":\"string\",\"minLength\":1},\"evidence\":{\"type\":\"string\",\"enum\":[\"zero-accepted-variants\"]},\"relaxedTo\":{\"type\":\"string\",\"enum\":[\"none\"]}},\"required\":[\"narrowing\",\"gate\",\"productTypeId\",\"displayName\",\"productTypeIds\",\"pass\",\"evidence\",\"relaxedTo\"],\"additionalProperties\":false}},\"required\":[\"version\",\"rawQuery\",\"normalisedQuery\",\"residualText\",\"status\",\"mode\",\"senses\"],\"additionalProperties\":false},\"suggestions\":{\"default\":[],\"description\":\"Bounded structured refinements. Each request is a complete executable replacement for the current search request.\",\"maxItems\":6,\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"label\":{\"type\":\"string\",\"minLength\":1,\"description\":\"Human-readable description of the refinement.\"},\"request\":{\"type\":\"object\",\"properties\":{\"text\":{\"description\":\"Free-text query, matched VISUALLY against material imagery (\\\"striped wallpaper\\\", \\\"warm terrazzo\\\"). A bare colour word (\\\"sage\\\") automatically runs as a colour-only search.\",\"type\":\"string\",\"maxLength\":500},\"intent\":{\"default\":\"search\",\"description\":\"Budget profile for the search primitive. Default \\\"search\\\" is the full engine. \\\"typeahead\\\" is the as-you-type profile: lexical-only with prefix match on the final token, forced `view:\\\"card\\\"`, `limit` capped at 24, and no mood/colour detection tiers, semantic fallback, category boost, secondary scoring, refinement suggestions, counts, or meanings. `textModes` is ignored under typeahead. Recommended wire shape for library typeahead UIs.\",\"type\":\"string\",\"enum\":[\"search\",\"typeahead\"]},\"textModes\":{\"description\":\"How to interpret `text`. Default [\\\"visual\\\"] — a text→image VISUAL search. \\\"lexical\\\" (opt-in) runs a BM25 ATTRIBUTE-lens search over material names + canonical attribute terms (find by what it's called / what it IS); detected co",
28
- "lour words are stripped from the lexical terms and retained only as diagnostic or hard-constraint evidence. Requesting BOTH [\\\"visual\\\",\\\"lexical\\\"] runs the visual search and notes it — compose a separate lexical search if both result sets are required. Ignored when `intent` is \\\"typeahead\\\".\",\"type\":\"array\",\"items\":{\"type\":\"string\",\"enum\":[\"visual\",\"lexical\"]}},\"colours\":{\"description\":\"Explicit colour palette to match. Each entry is EITHER a `hex` OR a `name` (a descriptive or brand paint name resolved server-side — never invent a hex for a name). PRECEDENCE: when set, these override any colour word detected in `text`. Unresolvable names fail with a typed error listing them, so you can correct and retry.\",\"maxItems\":8,\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"hex\":{\"description\":\"The colour to match, as 3- or 6-digit hex. Provide EITHER `hex` OR `name`, never both. Only pass `hex` when you already hold an EXACT hex — for a colour NAME use `name` and let the server resolve it.\",\"type\":\"string\",\"pattern\":\"^#?(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$\"},\"name\":{\"description\":\"A colour NAME to match — a descriptive name ('sage green', 'terracotta', 'warm sand') or a brand paint name ('Gauntlet Gray', 'Hale Navy', 'White Dove'). Resolved server-side to its exact library hex (misspellings are fuzzy-matched). NEVER invent a hex for a name — pass the name here. Provide EITHER `name` OR `hex`, never both.\",\"type\":\"string\",\"minLength\":1,\"maxLength\":120},\"weight\":{\"description\":\"Relative proportion of this colour in the desired palette (0–1). Weights are normalised across the palette; omit for equal parts.\",\"type\":\"number\",\"exclusiveMinimum\":0,\"maximum\":1},\"role\":{\"type\":\"string\",\"enum\":[\"any\",\"is\",\"accent\"],\"description\":\"Per-colour swatch eligibility (default 'any'). PRECEDENCE: `colourLogic` governs cross-colour combination (all/any); `role` governs which swatch of a product may satisfy THIS colour, and ANDs on top of the logic. 'is' = the colour must be the product's DOMINANT swatch; 'accent' = the colour must be a minor swatch (proportion 0.05–0.35) and is excluded from the proportion-match term; 'any' = today's nearest-swatch match.\"},\"sourceName\":{\"description\":\"Human name the colour came from (e.g. \\\"sage\\\", \\\"Elephant's Breath\\\") — echoed back for display; never affects matching. (For a name you want RESOLVED to a hex, use `name` instead.)\",\"type\":\"string\",\"maxLength\":120}},\"additionalProperties\":false}},\"colourLogic\":{\"description\":\"\\\"all\\\" (default): results should contain the whole palette, ranked by average distance + proportion match. \\\"any\\\": results matching ANY palette colour, ranked by their best single-colour distance.\",\"type\":\"string\",\"enum\":[\"all\",\"any\"]},\"colourTolerance\":{\"description\":\"How close a colour must be to count as a match. 'normal' (default) is today's behaviour; 'tight' narrows the OKLab match threshold and prunes far single-colour matches; 'loose' widens the threshold.\",\"type\":\"string\",\"enum\":[\"tight\",\"normal\",\"loose\"]},\"colourAppearance\":{\"type\":\"object\",\"properties\":{\"lightness\":{\"description\":\"OKLab lightness window (L ∈ [0,1]); dark ≈ 0, light ≈ 1.\",\"type\":\"object\",\"properties\":{\"min\":{\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"max\":{\"type\":\"number\",\"minimum\":0,\"maximum\":1}},\"required\":[\"min\",\"max\"],\"additionalProperties\":false},\"warmth\":{\"description\":\"OKLab warmth window (b axis); cool < 0 < warm, ≈ −0.1..0.1.\",\"type\":\"object\",\"properties\":{\"min\":{\"type\":\"number\",\"minimum\":-0.5,\"maximum\":0.5},\"max\":{\"type\":\"number\",\"minimum\":-0.5,\"maximum\":0.5}},\"required\":[\"min\",\"max\"],\"additionalProperties\":false}},\"additionalProperties\":false,\"description\":\"HARD appearance gate on the material's DOMINANT colour, independent of the colour palette — works on a text-only query too. Variants without colour extraction are EXCLUDED whenever any axis is set (NULL scalars never match).\"},\"mood\":{\"type\":\"string\",\"enum\":[\"warm\",\"cool\",\"earthy\",\"moody\",\"bold\",\"light-airy\",\"natural\",\"neutral\",\"romantic\",\"coastal\",\"industrial\",\"luxe\",\"vintage\",\"serene\",\"scandi\",\"cosy\",\"heritage\",\"mid-century\",\"pastel\"],\"descri",
29
- "ption\":\"Mood-language colour entry — resolves to a curated checked-in colour palette. Use it for evocative mood queries ('coastal', 'moody', 'earthy') rather than a single colour. A mood ALONE runs as a colour search; with text it remains diagnostic or hard-constraint evidence and does not blend into the text mode's ordering score. One of: warm (Warm — Reds, oranges, yellows - inviting and cozy); cool (Cool — Blues, teals, cool grays - calm and professional); earthy (Earthy — Browns, tans, terracotta - natural and grounded); moody (Moody — Deep, saturated, dramatic - rich and atmospheric); bold (Bold — High chroma, vibrant - energetic and eye-catching); light-airy (Light + Airy — Pastels, pale tones - soft and spacious); natural (Natural — Greens, wood tones, stone - organic and balanced); neutral (Neutral — Whites, grays, blacks - versatile and timeless); romantic (Romantic — Soft pinks, mauves, blush - tender and elegant); coastal (Coastal — Sandy beiges, ocean blues, seafoam - relaxed seaside); industrial (Industrial — Cool grays, concrete, metallic - urban and modern); luxe (Luxe — Deep jewel tones, rich emeralds, sapphires - opulent); vintage (Vintage — Muted, dusty tones - nostalgic and timeworn); serene (Serene — Soft blues, sage, muted lavender - calm and peaceful); scandi (Scandi — Warm whites, pale woods, cream - Scandinavian minimalism); cosy (Cosy — Deep plaster, amber, tobacco - enveloping warmth); heritage (Heritage — Rich historical tones with saturated depth - classic and grounded); mid-century (Mid-Century — Olive, mustard, teal, terracotta - retro modern); pastel (Pastel — Sage, duck egg, blush - soft colors with body).\"},\"sort\":{\"type\":\"string\",\"enum\":[\"relevance\",\"light\",\"dark\",\"vivid\",\"muted\",\"colourful\",\"name\",\"name_desc\",\"recent\"],\"description\":\"Re-order the final scored results (default 'relevance' = the selected mode's primary score). Two families. COLOUR RAMPS: 'light'/'dark' by dominant lightness, 'vivid'/'muted' by chroma, 'colourful' by overall vibrancy — variants without colour extraction sink to the end. CATALOGUE ORDERS: 'name' (A→Z), 'name_desc' (Z→A) and 'recent' (newest first), which read the same `name` / `created_at` columns, in the same directions, that /api/v1/search/browse orders on — one word, one order, on both lanes. Every sort REORDERS the pool the search signals already retrieved; it never widens or substitutes for one. Retired materials sort below visible ones under every non-relevance sort. For an unranked walk of the whole corpus in any of these orders, use the browse surface instead.\"},\"orderBy\":{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"minLength\":1,\"description\":\"The attribute to sort by. Only attributes with a DECLARED value ladder can be ordered — ordering is never inferred from a value's spelling — and anything else is refused with the orderable set named. Orderable today: price_tier.\"},\"direction\":{\"default\":\"asc\",\"description\":\"'asc' (default) walks the ladder from its first rung.\",\"type\":\"string\",\"enum\":[\"asc\",\"desc\"]}},\"required\":[\"attributeId\",\"direction\"],\"additionalProperties\":false,\"description\":\"Sort results along ONE attribute's declared ladder — a separate axis from `sort`, which stays the closed relevance/colour/catalogue vocabulary. Sent together, `orderBy` wins and the response `note` says so. A variant whose value is off the ladder, or which carries no value at all, sorts LAST in both directions: it has no rung, and it is still a real row, so it is placed at the end rather than dropped or guessed at.\"},\"returnAttributes\":{\"maxItems\":8,\"type\":\"array\",\"items\":{\"type\":\"string\",\"minLength\":1},\"description\":\"Attribute ids whose RESOLVED VALUE should ride each result, under `attributes` keyed by attribute id. Values resolve with the normal variant-over-product precedence. THREE STATES, and they are different: an attribute you did not ask for is not a key at all; an attribute you asked for that this variant carries no accepted value for is `null`; anything else is the value (a string, or an array for a multi-valued attribute). An unknown at",
30
- "tribute id is refused naming it, never silently dropped. Pair it with `filters` to show what you filtered on.\"},\"similarTo\":{\"description\":\"Find materials that LOOK like a seed image. Supply `variantId` to seed from an existing variant's primary image, `materialBankId` to seed from a Material Bank SKU — the ONE EXTERNAL identifier this API accepts, so a caller already holding a SKU needs no resolve round trip — or `vector`, a pre-computed halfvec literal (`[a,b,…]`) from an externally-embedded image, to drive the image arm directly. A `materialBankId` that names no variant is a 400 naming the offending value, never an empty result set and never an unseeded search. PRECEDENCE: wins over `text` (the response carries a note when both were given).\",\"anyOf\":[{\"type\":\"object\",\"properties\":{\"variantId\":{\"type\":\"string\"}},\"required\":[\"variantId\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"materialBankId\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"materialBankId\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"vector\":{\"type\":\"string\"}},\"required\":[\"vector\"],\"additionalProperties\":false}]},\"excludeVariantIds\":{\"description\":\"Canonical variant ids that must not appear in this result. Applied before the returned page is cut, so the page refills from the deeper ranked pool instead of being shortened by client-side filtering. IDs are opaque; duplicates are harmless and deduplicated by the engine.\",\"maxItems\":256,\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"image\":{\"description\":\"A raw incoming image to search by (URL or base64). The HTTP adapter embeds it and sets `similarTo: { vector }`; wins over `text`.\",\"oneOf\":[{\"type\":\"object\",\"properties\":{\"type\":{\"type\":\"string\",\"const\":\"url\"},\"url\":{\"type\":\"string\",\"format\":\"uri\",\"description\":\"HTTP(S) URL of the query image. MUST be an https URL.\"}},\"required\":[\"type\",\"url\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"type\":{\"type\":\"string\",\"const\":\"base64\"},\"data\":{\"type\":\"string\",\"minLength\":1,\"description\":\"Base64-encoded image bytes (or a data URI) the caller already holds — e.g. a form upload — so there is nothing to host.\"}},\"required\":[\"type\",\"data\"],\"additionalProperties\":false}],\"type\":\"object\"},\"category\":{\"type\":\"object\",\"properties\":{\"boost\":{\"description\":\"Soft category preference — results of this product type score higher but others still appear.\",\"type\":\"string\"},\"filter\":{\"description\":\"HARD gate: only these public taxonomy categories are returned. Values are category-group or product-type display names (case-insensitive), or canonical taxonomy keys. A group expands to every child product type; an unknown value errors listing every valid name.\",\"type\":\"array\",\"items\":{\"type\":\"string\"}}},\"additionalProperties\":false},\"weights\":{\"type\":\"object\",\"properties\":{\"image\":{\"type\":\"number\",\"minimum\":0},\"category\":{\"type\":\"number\",\"minimum\":0},\"colour\":{\"type\":\"number\",\"minimum\":0},\"lexical\":{\"type\":\"number\",\"minimum\":0},\"text\":{\"type\":\"number\",\"minimum\":0}},\"additionalProperties\":false,\"description\":\"Optional multiplier for the selected mode's one ordering signal. At most one applied weight is non-zero: colour→colour, lexical/entity-browse→lexical, variant-text→portrait, image-text/image→image, and filter-only→none. Overrides may scale only that live signal and cannot activate any other. Other similarities are diagnostic or hard-gate receipts and do not blend into score.\"},\"filters\":{\"description\":\"Typed filters applied as a hard restriction before ranking. Each entry is a term match (`attributeId` + `term`), a term SET (`attributeId` + `terms` — ANY one of them satisfies it), a presence check (`attributeId` + `hasValue: true`), or a numeric range (`attributeId` + `min`/`max`). AND/OR: entries AND together, and OR is expressed WITHIN one entry via `terms`. BRAND: `attributeId: \\\"brand\\\"` filters on the brand RELATION — a join every one of the catalogue's variants carries — taking a brand slug or display name, exactly the `value` a `brand` facet reports. Do NOT use the `brand_name` ATTRIBUTE for this: it is a sparse spec value pres",
31
- "ent on about 1.5% of variants and absent entirely from every catalog-ingested brand, so filtering on it returns a near-empty page that reads as a corpus gap. Presence filters always route as hard spec gates. Aesthetic/meaning term filters (pattern, texture, directionality, and the other visual-meaning attributes) are applied as a pool-bounded post-filter over the visual candidate pool rather than a whole-catalog restriction. Each entry may carry a `trust` bar the matching value must clear.\",\"type\":\"array\",\"items\":{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, e.g. \\\"primary_material\\\" or \\\"fire_rating\\\" — or the special id \\\"brand\\\", which filters on the brand RELATION (a join, carried by every variant) rather than on any attribute.\"},\"term\":{\"type\":\"string\",\"description\":\"Canonical term the attribute must equal, e.g. \\\"textile\\\". For \\\"brand\\\", a brand slug (\\\"kvadrat\\\") or display name (\\\"Kvadrat\\\"), case-insensitive — exactly the `value` a `brand` facet reports. An unrecognised brand is a 400 naming it, never an empty page.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"term\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, or the special id \\\"brand\\\" for the brand relation.\"},\"terms\":{\"minItems\":1,\"type\":\"array\",\"items\":{\"type\":\"string\"},\"description\":\"Canonical terms, ANY ONE of which satisfies the filter — this is how OR is expressed. OR lives inside one entry; separate `filters[]` entries AND together. Used for ordinal scales, where a stricter class satisfies a looser one, and for a multi-select rail — `{attributeId:\\\"brand\\\", terms:[\\\"kvadrat\\\",\\\"rubelli\\\"]}` returns materials from either brand.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"terms\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\"},\"excludesTerm\":{\"type\":\"string\",\"minLength\":1},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"excludesTerm\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id that must have an accepted value, e.g. \\\"sample_image_mm_per_px\\\".\"},\"hasValue\":{\"type\":\"boolean\",\"const\":true,\"description\":\"Require any accepted value for this attribute.\"},\"asOf\":{\"description\":\"Reference date for ",
32
- "EPD expiration, exclusive: the value must remain valid after this date.\",\"type\":\"string\",\"format\":\"date\",\"pattern\":\"^(?:(?:\\\\d\\\\d[2468][048]|\\\\d\\\\d[13579][26]|\\\\d\\\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\\\d|30)|(?:02)-(?:0[1-9]|1\\\\d|2[0-8])))$\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"hasValue\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id holding a NUMBER, e.g. \\\"light_reflectance_value\\\" or \\\"lead_time_days\\\".\"},\"min\":{\"description\":\"Inclusive lower bound. Omit for a ceiling-only range.\",\"type\":\"number\"},\"max\":{\"description\":\"Inclusive upper bound. Omit for a floor-only range.\",\"type\":\"number\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\"],\"additionalProperties\":false}]}},\"preferences\":{\"description\":\"SOFT spec criteria — rank-never-exclude. Same shape as `filters`, but a preference NEVER removes a result: each hit's `verdict` gains a `preferred` cell, and a failed/missing preferred cell only DEMOTES the hit WITHIN its page (the cursor is from the pre-demotion order).\",\"maxItems\":8,\"type\":\"array\",\"items\":{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, e.g. \\\"primary_material\\\" or \\\"fire_rating\\\" — or the special id \\\"brand\\\", which filters on the brand RELATION (a join, carried by every variant) rather than on any attribute.\"},\"term\":{\"type\":\"string\",\"description\":\"Canonical term the attribute must equal, e.g. \\\"textile\\\". For \\\"brand\\\", a brand slug (\\\"kvadrat\\\") or display name (\\\"Kvadrat\\\"), case-insensitive — exactly the `value` a `brand` facet reports. An unrecognised brand is a 400 naming it, never an empty page.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"term\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, or the special id \\\"brand\\\" for the brand relation.\"},\"terms\":{\"minItems\":1,\"type\":\"array\",\"items\":{\"type\":\"string\"},\"description\":\"Canonical terms, ANY ONE of which satisfies the filter — this is how OR is expressed. OR lives inside one entry; separate `filters[]` entries AND together. Used for ordinal scales, where a stricter class satisfies a looser one, and for a multi-select rail — `{attributeId:\\\"bran",
33
- "d\\\", terms:[\\\"kvadrat\\\",\\\"rubelli\\\"]}` returns materials from either brand.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"terms\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\"},\"excludesTerm\":{\"type\":\"string\",\"minLength\":1},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"excludesTerm\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id that must have an accepted value, e.g. \\\"sample_image_mm_per_px\\\".\"},\"hasValue\":{\"type\":\"boolean\",\"const\":true,\"description\":\"Require any accepted value for this attribute.\"},\"asOf\":{\"description\":\"Reference date for EPD expiration, exclusive: the value must remain valid after this date.\",\"type\":\"string\",\"format\":\"date\",\"pattern\":\"^(?:(?:\\\\d\\\\d[2468][048]|\\\\d\\\\d[13579][26]|\\\\d\\\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\\\d|30)|(?:02)-(?:0[1-9]|1\\\\d|2[0-8])))$\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"hasValue\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id holding a NUMBER, e.g. \\\"light_reflectance_value\\\" or \\\"lead_time_days\\\".\"},\"min\":{\"description\":\"Inclusive lower bound. Omit for a ceiling-only range.\",\"type\":\"number\"},\"max\":{\"description\":\"Inclusive upper bound. Omit for a floor-only range.\",\"type\":\"number\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\"],\"additionalProperties\":false}]}},\"verdicts\":{\"description\":\"Opt-in seed-relative verdict cells. Only meaningful with `similarTo: { variantId }`: costs one seed fetch + one page read to diff each hit's accepted COMPARABLE attributes (numeric + a colour axis) against the seed's, emitting `preferred` `verdi",
34
- "ct` cells with directional deviations (`above`/`below`, e.g. 2 mm thicker, warmer). Ignored on the default path; a `similarTo: { vector }` / raw-image seed carries no seed variant, so the response notes that seed cells were skipped.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Results per page (default 24, max 48).\",\"type\":\"integer\",\"minimum\":1,\"maximum\":48},\"cursor\":{\"description\":\"Opaque pagination cursor from a previous response. NOTE: ANN search is approximate — tail items may reorder slightly between pages.\",\"type\":\"string\"},\"explain\":{\"description\":\"Include per-signal scores on each result (`signals`).\",\"type\":\"boolean\"},\"collapse\":{\"description\":\"Group results by product before pagination. \\\"product\\\" returns one representative (highest-ranked) variant per product, with hidden siblings counted in `siblingCount`. The engine and GET adapter default to \\\"none\\\"; public POST requests default to \\\"product\\\" and may request \\\"none\\\" explicitly for variant-grain results.\",\"type\":\"string\",\"enum\":[\"none\",\"product\"]},\"count\":{\"default\":false,\"description\":\"Opt-in feed total. When true the response carries `counts.variants` (and `counts.products` under `collapse:\\\"product\\\"`). The variant total is EXACT up to a cap of 1000 and reported as `basis:\\\"lowerBound\\\"` beyond it — and ALWAYS `lowerBound` for ANN-ranked pools (image / colour / visual-text / visual-meaning post-filters), where the finite candidate pool is the only honest figure. Counts are never silently approximated: an exact count is labelled `exact`, a capped or pool-bounded one `lowerBound`. Default false (skip the count query entirely).\",\"type\":\"boolean\"},\"facetCounts\":{\"description\":\"Opt-in per-query facet counts (MG-1010). Name up to 10 facet ids — `brand`, `category`, or spec attribute ids — and the response carries a `facets` block counting each facet's values WITHIN this query's match set. Absent means no `facets` in the response and no counting cost. An id the registry does not know is a 400 naming it (with `didYouMean` candidates); a known but uncountable id lands in `facets.absent` with a reason. Counts follow the same honesty discipline as `count`: ranked (ANN) queries report pool-derived floors labelled `lowerBound` with `poolSize` stated; only a filter-driven query earns `exact`.\",\"maxItems\":10,\"type\":\"array\",\"items\":{\"type\":\"string\",\"minLength\":1}},\"refinementToken\":{\"description\":\"Opaque five-minute receipt from a prior pool-derived faceted search. Resubmitting it narrows that exact finalized pool instead of running a new candidate source.\",\"type\":\"string\",\"minLength\":1,\"maxLength\":131072},\"hasImages\":{\"default\":false,\"description\":\"When true, only return results that have at least one catalog image (a servable card thumbnail). Off by default so search stays honest about the FULL catalog, including image-less records. It filters the LEXICAL attribute lens — the one source that can surface an image-less record; the visual arms (image / colour / text→image) are inherently image-backed, so the flag is a no-op there. The opt-in `counts.variants` feed total honours it too (image-less records are excluded from the count). Default false.\",\"type\":\"boolean\"},\"sampleableOnly\":{\"description\":\"Require an accepted sampleable=true variant fact before pagination. False or absent leaves the corpus unchanged.\",\"type\":\"boolean\"},\"view\":{\"default\":\"full\",\"description\":\"Result projection. \\\"full\\\" (default) returns the complete `searchResultItemSchema` (palette, colourMeta, hasImage, siblings, explain signals/meanings). \\\"card\\\" returns the lean `searchCardItemSchema` (identity + name + thumbnail + score) for grid rendering, skipping the per-page palette / appearance / meaning reads. The response echoes the chosen `view`, which discriminates the `results` union.\",\"type\":\"string\",\"enum\":[\"full\",\"card\"]},\"corpusScope\":{\"description\":\"Which corpus to answer from. samplizeCatalogue admits published US Samplize paint colours regardless of stock. ABSENT means the WHOLE GRAPH — every material MaterialGraph knows, orderable or not; absence is never a narrower default. Three trans",
35
- "action-oriented scopes gate on governed CHANNEL membership: 'materialBankNaOrderable' (Material Bank North America, about 277,000 materials), 'materialBankEuOrderable' (Material Bank Europe, about 65,000) and 'designShopOrderable' (DesignShop, about 34,000, excluding what DesignShop hides from browse). 'materialBankNaAndCatalogue' is a governed discovery corpus over Material Bank NA or Catalogue editorial membership; it is not a buyability claim, and references alone do not confer membership. 'materialBankOrderable' instead restricts retrieval to materials carrying a Material Bank identity, which is a provenance signal rather than a channel one, and admits about 98% of the graph. Scope is distribution/visibility membership, never suitability; its receipt states whether that means transaction or editorial inclusion. Every scope is applied INSIDE the query: `limit` fills with eligible rows, `counts` describe the SCOPED corpus, and the response echoes the scope under `corpusScope`. This is what post-filtering a returned page cannot give you — a client-side filter shortens the page exactly when the query drifts toward the corpus MaterialGraph carries beyond the Material Bank feed, which is unpredictable from outside. An unknown value is a 400 naming it.\",\"type\":\"string\",\"enum\":[\"all\",\"samplizeCatalogue\",\"materialBankOrderable\",\"materialBankNaOrderable\",\"materialBankNaAndCatalogue\",\"materialBankEuOrderable\",\"designShopOrderable\"]},\"includeSamplingArtifacts\":{\"description\":\"OPS/AUDIT escape hatch — DO NOT use for normal search. Default behaviour (absent or false) EXCLUDES sampling-artifact products (multi-swatch sample cards — orderable samples, never specifiable materials) from every result, count, and candidate pool. Set true only to surface them for ops inspection or an audit.\",\"type\":\"boolean\"}},\"required\":[\"intent\",\"count\",\"hasImages\",\"view\"],\"additionalProperties\":false,\"description\":\"Complete replacement search request. It can be parsed and executed directly; it is not a delta.\"}},\"required\":[\"label\",\"request\"],\"additionalProperties\":false}},\"disambiguation\":{\"description\":\"Present when the query has multiple valid readings — a colour⇄material word ('walnut'), a certification phrase wearing colour words ('red list free'), or a pinned paint chip that is also a colour to look for across materials, or an exact paint name shared by several makers. The reading that ran carries `isDefault`; for a refused paint collision every reading is false. Every reading is a complete executable request. ABSENT means the query was unambiguous, never that the alternative was checked and found empty.\",\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"enum\":[\"compliance-phrase\",\"colour-material\",\"paint-colour\",\"paint-identity\"],\"description\":\"Governed ambiguity class.\"},\"term\":{\"type\":\"string\",\"minLength\":1,\"description\":\"The ambiguous term or phrase.\"},\"interpretations\":{\"minItems\":2,\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"id\":{\"type\":\"string\",\"minLength\":1,\"description\":\"Stable id of this reading within the clarification; exact paints include maker, identity, and swatch.\"},\"label\":{\"type\":\"string\",\"minLength\":1,\"description\":\"Human-facing name for the reading, e.g. 'the colour walnut'.\"},\"kind\":{\"type\":\"string\",\"minLength\":1,\"description\":\"Short noun for what this reading searches.\"},\"isDefault\":{\"type\":\"boolean\",\"description\":\"True for the reading THIS response ran. Every reading is false when the engine intentionally refused to choose, such as an exact cross-library paint collision.\"},\"hex\":{\"description\":\"Representative swatch, present when the reading is a colour.\",\"type\":\"string\"},\"request\":{\"type\":\"object\",\"properties\":{\"text\":{\"description\":\"Free-text query, matched VISUALLY against material imagery (\\\"striped wallpaper\\\", \\\"warm terrazzo\\\"). A bare colour word (\\\"sage\\\") automatically runs as a colour-only search.\",\"type\":\"string\",\"maxLength\":500},\"intent\":{\"default\":\"search\",\"description\":\"Budget profile for the search primitive. Default \\\"search\\\" is the full engine. \\\"typeahead\\\" is the as-you-type profile: le",
36
- "xical-only with prefix match on the final token, forced `view:\\\"card\\\"`, `limit` capped at 24, and no mood/colour detection tiers, semantic fallback, category boost, secondary scoring, refinement suggestions, counts, or meanings. `textModes` is ignored under typeahead. Recommended wire shape for library typeahead UIs.\",\"type\":\"string\",\"enum\":[\"search\",\"typeahead\"]},\"textModes\":{\"description\":\"How to interpret `text`. Default [\\\"visual\\\"] — a text→image VISUAL search. \\\"lexical\\\" (opt-in) runs a BM25 ATTRIBUTE-lens search over material names + canonical attribute terms (find by what it's called / what it IS); detected colour words are stripped from the lexical terms and retained only as diagnostic or hard-constraint evidence. Requesting BOTH [\\\"visual\\\",\\\"lexical\\\"] runs the visual search and notes it — compose a separate lexical search if both result sets are required. Ignored when `intent` is \\\"typeahead\\\".\",\"type\":\"array\",\"items\":{\"type\":\"string\",\"enum\":[\"visual\",\"lexical\"]}},\"colours\":{\"description\":\"Explicit colour palette to match. Each entry is EITHER a `hex` OR a `name` (a descriptive or brand paint name resolved server-side — never invent a hex for a name). PRECEDENCE: when set, these override any colour word detected in `text`. Unresolvable names fail with a typed error listing them, so you can correct and retry.\",\"maxItems\":8,\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"hex\":{\"description\":\"The colour to match, as 3- or 6-digit hex. Provide EITHER `hex` OR `name`, never both. Only pass `hex` when you already hold an EXACT hex — for a colour NAME use `name` and let the server resolve it.\",\"type\":\"string\",\"pattern\":\"^#?(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$\"},\"name\":{\"description\":\"A colour NAME to match — a descriptive name ('sage green', 'terracotta', 'warm sand') or a brand paint name ('Gauntlet Gray', 'Hale Navy', 'White Dove'). Resolved server-side to its exact library hex (misspellings are fuzzy-matched). NEVER invent a hex for a name — pass the name here. Provide EITHER `name` OR `hex`, never both.\",\"type\":\"string\",\"minLength\":1,\"maxLength\":120},\"weight\":{\"description\":\"Relative proportion of this colour in the desired palette (0–1). Weights are normalised across the palette; omit for equal parts.\",\"type\":\"number\",\"exclusiveMinimum\":0,\"maximum\":1},\"role\":{\"type\":\"string\",\"enum\":[\"any\",\"is\",\"accent\"],\"description\":\"Per-colour swatch eligibility (default 'any'). PRECEDENCE: `colourLogic` governs cross-colour combination (all/any); `role` governs which swatch of a product may satisfy THIS colour, and ANDs on top of the logic. 'is' = the colour must be the product's DOMINANT swatch; 'accent' = the colour must be a minor swatch (proportion 0.05–0.35) and is excluded from the proportion-match term; 'any' = today's nearest-swatch match.\"},\"sourceName\":{\"description\":\"Human name the colour came from (e.g. \\\"sage\\\", \\\"Elephant's Breath\\\") — echoed back for display; never affects matching. (For a name you want RESOLVED to a hex, use `name` instead.)\",\"type\":\"string\",\"maxLength\":120}},\"additionalProperties\":false}},\"colourLogic\":{\"description\":\"\\\"all\\\" (default): results should contain the whole palette, ranked by average distance + proportion match. \\\"any\\\": results matching ANY palette colour, ranked by their best single-colour distance.\",\"type\":\"string\",\"enum\":[\"all\",\"any\"]},\"colourTolerance\":{\"description\":\"How close a colour must be to count as a match. 'normal' (default) is today's behaviour; 'tight' narrows the OKLab match threshold and prunes far single-colour matches; 'loose' widens the threshold.\",\"type\":\"string\",\"enum\":[\"tight\",\"normal\",\"loose\"]},\"colourAppearance\":{\"type\":\"object\",\"properties\":{\"lightness\":{\"description\":\"OKLab lightness window (L ∈ [0,1]); dark ≈ 0, light ≈ 1.\",\"type\":\"object\",\"properties\":{\"min\":{\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"max\":{\"type\":\"number\",\"minimum\":0,\"maximum\":1}},\"required\":[\"min\",\"max\"],\"additionalProperties\":false},\"warmth\":{\"description\":\"OKLab warmth window (b axis); cool < 0 < warm, ≈ −0.1..0.1.\",\"type\":\"object\",\"properties\":{\"min\":{\"type\":\"number\",\"mini",
37
- "mum\":-0.5,\"maximum\":0.5},\"max\":{\"type\":\"number\",\"minimum\":-0.5,\"maximum\":0.5}},\"required\":[\"min\",\"max\"],\"additionalProperties\":false}},\"additionalProperties\":false,\"description\":\"HARD appearance gate on the material's DOMINANT colour, independent of the colour palette — works on a text-only query too. Variants without colour extraction are EXCLUDED whenever any axis is set (NULL scalars never match).\"},\"mood\":{\"type\":\"string\",\"enum\":[\"warm\",\"cool\",\"earthy\",\"moody\",\"bold\",\"light-airy\",\"natural\",\"neutral\",\"romantic\",\"coastal\",\"industrial\",\"luxe\",\"vintage\",\"serene\",\"scandi\",\"cosy\",\"heritage\",\"mid-century\",\"pastel\"],\"description\":\"Mood-language colour entry — resolves to a curated checked-in colour palette. Use it for evocative mood queries ('coastal', 'moody', 'earthy') rather than a single colour. A mood ALONE runs as a colour search; with text it remains diagnostic or hard-constraint evidence and does not blend into the text mode's ordering score. One of: warm (Warm — Reds, oranges, yellows - inviting and cozy); cool (Cool — Blues, teals, cool grays - calm and professional); earthy (Earthy — Browns, tans, terracotta - natural and grounded); moody (Moody — Deep, saturated, dramatic - rich and atmospheric); bold (Bold — High chroma, vibrant - energetic and eye-catching); light-airy (Light + Airy — Pastels, pale tones - soft and spacious); natural (Natural — Greens, wood tones, stone - organic and balanced); neutral (Neutral — Whites, grays, blacks - versatile and timeless); romantic (Romantic — Soft pinks, mauves, blush - tender and elegant); coastal (Coastal — Sandy beiges, ocean blues, seafoam - relaxed seaside); industrial (Industrial — Cool grays, concrete, metallic - urban and modern); luxe (Luxe — Deep jewel tones, rich emeralds, sapphires - opulent); vintage (Vintage — Muted, dusty tones - nostalgic and timeworn); serene (Serene — Soft blues, sage, muted lavender - calm and peaceful); scandi (Scandi — Warm whites, pale woods, cream - Scandinavian minimalism); cosy (Cosy — Deep plaster, amber, tobacco - enveloping warmth); heritage (Heritage — Rich historical tones with saturated depth - classic and grounded); mid-century (Mid-Century — Olive, mustard, teal, terracotta - retro modern); pastel (Pastel — Sage, duck egg, blush - soft colors with body).\"},\"sort\":{\"type\":\"string\",\"enum\":[\"relevance\",\"light\",\"dark\",\"vivid\",\"muted\",\"colourful\",\"name\",\"name_desc\",\"recent\"],\"description\":\"Re-order the final scored results (default 'relevance' = the selected mode's primary score). Two families. COLOUR RAMPS: 'light'/'dark' by dominant lightness, 'vivid'/'muted' by chroma, 'colourful' by overall vibrancy — variants without colour extraction sink to the end. CATALOGUE ORDERS: 'name' (A→Z), 'name_desc' (Z→A) and 'recent' (newest first), which read the same `name` / `created_at` columns, in the same directions, that /api/v1/search/browse orders on — one word, one order, on both lanes. Every sort REORDERS the pool the search signals already retrieved; it never widens or substitutes for one. Retired materials sort below visible ones under every non-relevance sort. For an unranked walk of the whole corpus in any of these orders, use the browse surface instead.\"},\"orderBy\":{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"minLength\":1,\"description\":\"The attribute to sort by. Only attributes with a DECLARED value ladder can be ordered — ordering is never inferred from a value's spelling — and anything else is refused with the orderable set named. Orderable today: price_tier.\"},\"direction\":{\"default\":\"asc\",\"description\":\"'asc' (default) walks the ladder from its first rung.\",\"type\":\"string\",\"enum\":[\"asc\",\"desc\"]}},\"required\":[\"attributeId\",\"direction\"],\"additionalProperties\":false,\"description\":\"Sort results along ONE attribute's declared ladder — a separate axis from `sort`, which stays the closed relevance/colour/catalogue vocabulary. Sent together, `orderBy` wins and the response `note` says so. A variant whose value is off the ladder, or which carries no value at all, sorts LAST in both directions: it has no rung, and it",
38
- " is still a real row, so it is placed at the end rather than dropped or guessed at.\"},\"returnAttributes\":{\"maxItems\":8,\"type\":\"array\",\"items\":{\"type\":\"string\",\"minLength\":1},\"description\":\"Attribute ids whose RESOLVED VALUE should ride each result, under `attributes` keyed by attribute id. Values resolve with the normal variant-over-product precedence. THREE STATES, and they are different: an attribute you did not ask for is not a key at all; an attribute you asked for that this variant carries no accepted value for is `null`; anything else is the value (a string, or an array for a multi-valued attribute). An unknown attribute id is refused naming it, never silently dropped. Pair it with `filters` to show what you filtered on.\"},\"similarTo\":{\"description\":\"Find materials that LOOK like a seed image. Supply `variantId` to seed from an existing variant's primary image, `materialBankId` to seed from a Material Bank SKU — the ONE EXTERNAL identifier this API accepts, so a caller already holding a SKU needs no resolve round trip — or `vector`, a pre-computed halfvec literal (`[a,b,…]`) from an externally-embedded image, to drive the image arm directly. A `materialBankId` that names no variant is a 400 naming the offending value, never an empty result set and never an unseeded search. PRECEDENCE: wins over `text` (the response carries a note when both were given).\",\"anyOf\":[{\"type\":\"object\",\"properties\":{\"variantId\":{\"type\":\"string\"}},\"required\":[\"variantId\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"materialBankId\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"materialBankId\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"vector\":{\"type\":\"string\"}},\"required\":[\"vector\"],\"additionalProperties\":false}]},\"excludeVariantIds\":{\"description\":\"Canonical variant ids that must not appear in this result. Applied before the returned page is cut, so the page refills from the deeper ranked pool instead of being shortened by client-side filtering. IDs are opaque; duplicates are harmless and deduplicated by the engine.\",\"maxItems\":256,\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"image\":{\"description\":\"A raw incoming image to search by (URL or base64). The HTTP adapter embeds it and sets `similarTo: { vector }`; wins over `text`.\",\"oneOf\":[{\"type\":\"object\",\"properties\":{\"type\":{\"type\":\"string\",\"const\":\"url\"},\"url\":{\"type\":\"string\",\"format\":\"uri\",\"description\":\"HTTP(S) URL of the query image. MUST be an https URL.\"}},\"required\":[\"type\",\"url\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"type\":{\"type\":\"string\",\"const\":\"base64\"},\"data\":{\"type\":\"string\",\"minLength\":1,\"description\":\"Base64-encoded image bytes (or a data URI) the caller already holds — e.g. a form upload — so there is nothing to host.\"}},\"required\":[\"type\",\"data\"],\"additionalProperties\":false}],\"type\":\"object\"},\"category\":{\"type\":\"object\",\"properties\":{\"boost\":{\"description\":\"Soft category preference — results of this product type score higher but others still appear.\",\"type\":\"string\"},\"filter\":{\"description\":\"HARD gate: only these public taxonomy categories are returned. Values are category-group or product-type display names (case-insensitive), or canonical taxonomy keys. A group expands to every child product type; an unknown value errors listing every valid name.\",\"type\":\"array\",\"items\":{\"type\":\"string\"}}},\"additionalProperties\":false},\"weights\":{\"type\":\"object\",\"properties\":{\"image\":{\"type\":\"number\",\"minimum\":0},\"category\":{\"type\":\"number\",\"minimum\":0},\"colour\":{\"type\":\"number\",\"minimum\":0},\"lexical\":{\"type\":\"number\",\"minimum\":0},\"text\":{\"type\":\"number\",\"minimum\":0}},\"additionalProperties\":false,\"description\":\"Optional multiplier for the selected mode's one ordering signal. At most one applied weight is non-zero: colour→colour, lexical/entity-browse→lexical, variant-text→portrait, image-text/image→image, and filter-only→none. Overrides may scale only that live signal and cannot activate any other. Other similarities are diagnostic or hard-gate receipts and do not blend into score.\"},\"filters\":{\"description\":\"Typed filters app",
39
- "lied as a hard restriction before ranking. Each entry is a term match (`attributeId` + `term`), a term SET (`attributeId` + `terms` — ANY one of them satisfies it), a presence check (`attributeId` + `hasValue: true`), or a numeric range (`attributeId` + `min`/`max`). AND/OR: entries AND together, and OR is expressed WITHIN one entry via `terms`. BRAND: `attributeId: \\\"brand\\\"` filters on the brand RELATION — a join every one of the catalogue's variants carries — taking a brand slug or display name, exactly the `value` a `brand` facet reports. Do NOT use the `brand_name` ATTRIBUTE for this: it is a sparse spec value present on about 1.5% of variants and absent entirely from every catalog-ingested brand, so filtering on it returns a near-empty page that reads as a corpus gap. Presence filters always route as hard spec gates. Aesthetic/meaning term filters (pattern, texture, directionality, and the other visual-meaning attributes) are applied as a pool-bounded post-filter over the visual candidate pool rather than a whole-catalog restriction. Each entry may carry a `trust` bar the matching value must clear.\",\"type\":\"array\",\"items\":{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, e.g. \\\"primary_material\\\" or \\\"fire_rating\\\" — or the special id \\\"brand\\\", which filters on the brand RELATION (a join, carried by every variant) rather than on any attribute.\"},\"term\":{\"type\":\"string\",\"description\":\"Canonical term the attribute must equal, e.g. \\\"textile\\\". For \\\"brand\\\", a brand slug (\\\"kvadrat\\\") or display name (\\\"Kvadrat\\\"), case-insensitive — exactly the `value` a `brand` facet reports. An unrecognised brand is a 400 naming it, never an empty page.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"term\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, or the special id \\\"brand\\\" for the brand relation.\"},\"terms\":{\"minItems\":1,\"type\":\"array\",\"items\":{\"type\":\"string\"},\"description\":\"Canonical terms, ANY ONE of which satisfies the filter — this is how OR is expressed. OR lives inside one entry; separate `filters[]` entries AND together. Used for ordinal scales, where a stricter class satisfies a looser one, and for a multi-select rail — `{attributeId:\\\"brand\\\", terms:[\\\"kvadrat\\\",\\\"rubelli\\\"]}` returns materials from either brand.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"terms\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\"},\"excludesTerm\":{\"type\":\"string\",\"minLength\":1},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequir",
40
- "ement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"excludesTerm\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id that must have an accepted value, e.g. \\\"sample_image_mm_per_px\\\".\"},\"hasValue\":{\"type\":\"boolean\",\"const\":true,\"description\":\"Require any accepted value for this attribute.\"},\"asOf\":{\"description\":\"Reference date for EPD expiration, exclusive: the value must remain valid after this date.\",\"type\":\"string\",\"format\":\"date\",\"pattern\":\"^(?:(?:\\\\d\\\\d[2468][048]|\\\\d\\\\d[13579][26]|\\\\d\\\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\\\d|30)|(?:02)-(?:0[1-9]|1\\\\d|2[0-8])))$\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"hasValue\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id holding a NUMBER, e.g. \\\"light_reflectance_value\\\" or \\\"lead_time_days\\\".\"},\"min\":{\"description\":\"Inclusive lower bound. Omit for a ceiling-only range.\",\"type\":\"number\"},\"max\":{\"description\":\"Inclusive upper bound. Omit for a floor-only range.\",\"type\":\"number\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\"],\"additionalProperties\":false}]}},\"preferences\":{\"description\":\"SOFT spec criteria — rank-never-exclude. Same shape as `filters`, but a preference NEVER removes a result: each hit's `verdict` gains a `preferred` cell, and a failed/missing preferred cell only DEMOTES the hit WITHIN its page (the cursor is from the pre-demotion order).\",\"maxItems\":8,\"type\":\"array\",\"items\":{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, e.g. \\\"primary_material\\\" or \\\"fire_rating\\\" — or the special id \\\"brand\\\", which filters on the brand RELATION (a join, carried by every variant) rather than on any attribute.\"},\"term\":{\"type\":\"string\",\"description\":\"Canonical term the attribute must equal, e.g. \\\"textile\\\". For \\\"brand\\\", a brand slug (\\\"kvadrat\\\") or display name (\\\"Kvadrat\\\"), case-insensitive — exactly the `value` a `brand` facet reports. An unrecognised brand is a 400 naming it, never an empty page.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boo",
41
- "lean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"term\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, or the special id \\\"brand\\\" for the brand relation.\"},\"terms\":{\"minItems\":1,\"type\":\"array\",\"items\":{\"type\":\"string\"},\"description\":\"Canonical terms, ANY ONE of which satisfies the filter — this is how OR is expressed. OR lives inside one entry; separate `filters[]` entries AND together. Used for ordinal scales, where a stricter class satisfies a looser one, and for a multi-select rail — `{attributeId:\\\"brand\\\", terms:[\\\"kvadrat\\\",\\\"rubelli\\\"]}` returns materials from either brand.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"terms\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\"},\"excludesTerm\":{\"type\":\"string\",\"minLength\":1},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"excludesTerm\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id that must have an accepted value, e.g. \\\"sample_image_mm_per_px\\\".\"},\"hasValue\":{\"type\":\"boolean\",\"const\":true,\"description\":\"Require any accepted value for this attribute.\"},\"asOf\":{\"description\":\"Reference date for EPD expiration, exclusive: the value must remain valid after this date.\",\"type\":\"string\",\"format\":\"date\",\"pattern\":\"^(?:(?:\\\\d\\\\d[2468][048]|\\\\d\\\\d[13579][26]|\\\\d\\\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\\\d|30)|(?:02)-(?:0[1-9]|1\\\\d|2[0-8])))$\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"hasValue\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id holding a NUMBER, e.g. \\\"light_reflectance_value\\\" or \\\"lead_time_days\\\".\"},\"min\":{\"description\":\"Inclusive lower bound. Omit for a ceiling-only range.\",\"type\":\"number\"},\"max\":{\"description\":\"Inclusive upper bound. Omit for a floor-only range.\",\"type\":\"number\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"",
42
- "enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\"],\"additionalProperties\":false}]}},\"verdicts\":{\"description\":\"Opt-in seed-relative verdict cells. Only meaningful with `similarTo: { variantId }`: costs one seed fetch + one page read to diff each hit's accepted COMPARABLE attributes (numeric + a colour axis) against the seed's, emitting `preferred` `verdict` cells with directional deviations (`above`/`below`, e.g. 2 mm thicker, warmer). Ignored on the default path; a `similarTo: { vector }` / raw-image seed carries no seed variant, so the response notes that seed cells were skipped.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Results per page (default 24, max 48).\",\"type\":\"integer\",\"minimum\":1,\"maximum\":48},\"cursor\":{\"description\":\"Opaque pagination cursor from a previous response. NOTE: ANN search is approximate — tail items may reorder slightly between pages.\",\"type\":\"string\"},\"explain\":{\"description\":\"Include per-signal scores on each result (`signals`).\",\"type\":\"boolean\"},\"collapse\":{\"description\":\"Group results by product before pagination. \\\"product\\\" returns one representative (highest-ranked) variant per product, with hidden siblings counted in `siblingCount`. The engine and GET adapter default to \\\"none\\\"; public POST requests default to \\\"product\\\" and may request \\\"none\\\" explicitly for variant-grain results.\",\"type\":\"string\",\"enum\":[\"none\",\"product\"]},\"count\":{\"default\":false,\"description\":\"Opt-in feed total. When true the response carries `counts.variants` (and `counts.products` under `collapse:\\\"product\\\"`). The variant total is EXACT up to a cap of 1000 and reported as `basis:\\\"lowerBound\\\"` beyond it — and ALWAYS `lowerBound` for ANN-ranked pools (image / colour / visual-text / visual-meaning post-filters), where the finite candidate pool is the only honest figure. Counts are never silently approximated: an exact count is labelled `exact`, a capped or pool-bounded one `lowerBound`. Default false (skip the count query entirely).\",\"type\":\"boolean\"},\"facetCounts\":{\"description\":\"Opt-in per-query facet counts (MG-1010). Name up to 10 facet ids — `brand`, `category`, or spec attribute ids — and the response carries a `facets` block counting each facet's values WITHIN this query's match set. Absent means no `facets` in the response and no counting cost. An id the registry does not know is a 400 naming it (with `didYouMean` candidates); a known but uncountable id lands in `facets.absent` with a reason. Counts follow the same honesty discipline as `count`: ranked (ANN) queries report pool-derived floors labelled `lowerBound` with `poolSize` stated; only a filter-driven query earns `exact`.\",\"maxItems\":10,\"type\":\"array\",\"items\":{\"type\":\"string\",\"minLength\":1}},\"refinementToken\":{\"description\":\"Opaque five-minute receipt from a prior pool-derived faceted search. Resubmitting it narrows that exact finalized pool instead of running a new candidate source.\",\"type\":\"string\",\"minLength\":1,\"maxLength\":131072},\"hasImages\":{\"default\":false,\"description\":\"When true, only return results that have at least one catalog image (a servable card thumbnail). Off by default so search stays honest about the FULL catalog, including image-less records. It filters the LEXICAL attribute lens — the one source that can surface an image-less record; the visual arms (image / colour / text→image) are inherently image-backed, so the flag is a no-op there. The opt-in `counts.variants` feed total honours it too (image-less records are excluded from the count). Default false.\",\"type\":\"boolean\"},\"sampleableOnly\":{\"description\":\"Require an accepted sampleable=true variant fact before pagination. False or absent leaves the corpus unchanged.\",\"type\":\"boolean\"},\"view\":{\"default\":\"full\",\"description\":\"Result projection. \\\"full\\\" (default) returns the complete `searchResultItemSchema` (pal",
43
- "ette, colourMeta, hasImage, siblings, explain signals/meanings). \\\"card\\\" returns the lean `searchCardItemSchema` (identity + name + thumbnail + score) for grid rendering, skipping the per-page palette / appearance / meaning reads. The response echoes the chosen `view`, which discriminates the `results` union.\",\"type\":\"string\",\"enum\":[\"full\",\"card\"]},\"corpusScope\":{\"description\":\"Which corpus to answer from. samplizeCatalogue admits published US Samplize paint colours regardless of stock. ABSENT means the WHOLE GRAPH — every material MaterialGraph knows, orderable or not; absence is never a narrower default. Three transaction-oriented scopes gate on governed CHANNEL membership: 'materialBankNaOrderable' (Material Bank North America, about 277,000 materials), 'materialBankEuOrderable' (Material Bank Europe, about 65,000) and 'designShopOrderable' (DesignShop, about 34,000, excluding what DesignShop hides from browse). 'materialBankNaAndCatalogue' is a governed discovery corpus over Material Bank NA or Catalogue editorial membership; it is not a buyability claim, and references alone do not confer membership. 'materialBankOrderable' instead restricts retrieval to materials carrying a Material Bank identity, which is a provenance signal rather than a channel one, and admits about 98% of the graph. Scope is distribution/visibility membership, never suitability; its receipt states whether that means transaction or editorial inclusion. Every scope is applied INSIDE the query: `limit` fills with eligible rows, `counts` describe the SCOPED corpus, and the response echoes the scope under `corpusScope`. This is what post-filtering a returned page cannot give you — a client-side filter shortens the page exactly when the query drifts toward the corpus MaterialGraph carries beyond the Material Bank feed, which is unpredictable from outside. An unknown value is a 400 naming it.\",\"type\":\"string\",\"enum\":[\"all\",\"samplizeCatalogue\",\"materialBankOrderable\",\"materialBankNaOrderable\",\"materialBankNaAndCatalogue\",\"materialBankEuOrderable\",\"designShopOrderable\"]},\"includeSamplingArtifacts\":{\"description\":\"OPS/AUDIT escape hatch — DO NOT use for normal search. Default behaviour (absent or false) EXCLUDES sampling-artifact products (multi-swatch sample cards — orderable samples, never specifiable materials) from every result, count, and candidate pool. Set true only to surface them for ops inspection or an audit.\",\"type\":\"boolean\"}},\"required\":[\"intent\",\"count\",\"hasImages\",\"view\"],\"additionalProperties\":false,\"description\":\"Complete replacement search request that runs this reading. Executable directly; it is not a delta.\"}},\"required\":[\"id\",\"label\",\"kind\",\"isDefault\",\"request\"],\"additionalProperties\":false},\"description\":\"Every valid reading. The one that ran is flagged `isDefault`; all are false when the engine intentionally chose none. Each is a complete executable request.\"}},\"required\":[\"kind\",\"term\",\"interpretations\"],\"additionalProperties\":false},\"supersededMoodId\":{\"description\":\"A mood term detected in the query but overridden by a stronger explicit colour reading. Absent means no mood was superseded, not that mood detection ran and found none.\",\"type\":\"string\",\"enum\":[\"warm\",\"cool\",\"earthy\",\"moody\",\"bold\",\"light-airy\",\"natural\",\"neutral\",\"romantic\",\"coastal\",\"industrial\",\"luxe\",\"vintage\",\"serene\",\"scandi\",\"cosy\",\"heritage\",\"mid-century\",\"pastel\"]},\"moodInterpretation\":{\"type\":\"object\",\"properties\":{\"query\":{\"description\":\"The original query text, or null without text.\",\"type\":[\"string\",\"null\"]},\"moodId\":{\"anyOf\":[{\"type\":\"string\",\"enum\":[\"warm\",\"cool\",\"earthy\",\"moody\",\"bold\",\"light-airy\",\"natural\",\"neutral\",\"romantic\",\"coastal\",\"industrial\",\"luxe\",\"vintage\",\"serene\",\"scandi\",\"cosy\",\"heritage\",\"mid-century\",\"pastel\"],\"description\":\"Mood-language colour entry — resolves to a curated checked-in colour palette. Use it for evocative mood queries ('coastal', 'moody', 'earthy') rather than a single colour. A mood ALONE runs as a colour search; with text it remains diagnostic or hard-constraint evidence and does not blend into the text mode's ",
44
- "ordering score. One of: warm (Warm — Reds, oranges, yellows - inviting and cozy); cool (Cool — Blues, teals, cool grays - calm and professional); earthy (Earthy — Browns, tans, terracotta - natural and grounded); moody (Moody — Deep, saturated, dramatic - rich and atmospheric); bold (Bold — High chroma, vibrant - energetic and eye-catching); light-airy (Light + Airy — Pastels, pale tones - soft and spacious); natural (Natural — Greens, wood tones, stone - organic and balanced); neutral (Neutral — Whites, grays, blacks - versatile and timeless); romantic (Romantic — Soft pinks, mauves, blush - tender and elegant); coastal (Coastal — Sandy beiges, ocean blues, seafoam - relaxed seaside); industrial (Industrial — Cool grays, concrete, metallic - urban and modern); luxe (Luxe — Deep jewel tones, rich emeralds, sapphires - opulent); vintage (Vintage — Muted, dusty tones - nostalgic and timeworn); serene (Serene — Soft blues, sage, muted lavender - calm and peaceful); scandi (Scandi — Warm whites, pale woods, cream - Scandinavian minimalism); cosy (Cosy — Deep plaster, amber, tobacco - enveloping warmth); heritage (Heritage — Rich historical tones with saturated depth - classic and grounded); mid-century (Mid-Century — Olive, mustard, teal, terracotta - retro modern); pastel (Pastel — Sage, duck egg, blush - soft colors with body).\"},{\"type\":\"null\"}],\"description\":\"The recognized mood id, or null when none was selected.\"},\"supersededMoodId\":{\"anyOf\":[{\"type\":\"string\",\"enum\":[\"warm\",\"cool\",\"earthy\",\"moody\",\"bold\",\"light-airy\",\"natural\",\"neutral\",\"romantic\",\"coastal\",\"industrial\",\"luxe\",\"vintage\",\"serene\",\"scandi\",\"cosy\",\"heritage\",\"mid-century\",\"pastel\"],\"description\":\"Mood-language colour entry — resolves to a curated checked-in colour palette. Use it for evocative mood queries ('coastal', 'moody', 'earthy') rather than a single colour. A mood ALONE runs as a colour search; with text it remains diagnostic or hard-constraint evidence and does not blend into the text mode's ordering score. One of: warm (Warm — Reds, oranges, yellows - inviting and cozy); cool (Cool — Blues, teals, cool grays - calm and professional); earthy (Earthy — Browns, tans, terracotta - natural and grounded); moody (Moody — Deep, saturated, dramatic - rich and atmospheric); bold (Bold — High chroma, vibrant - energetic and eye-catching); light-airy (Light + Airy — Pastels, pale tones - soft and spacious); natural (Natural — Greens, wood tones, stone - organic and balanced); neutral (Neutral — Whites, grays, blacks - versatile and timeless); romantic (Romantic — Soft pinks, mauves, blush - tender and elegant); coastal (Coastal — Sandy beiges, ocean blues, seafoam - relaxed seaside); industrial (Industrial — Cool grays, concrete, metallic - urban and modern); luxe (Luxe — Deep jewel tones, rich emeralds, sapphires - opulent); vintage (Vintage — Muted, dusty tones - nostalgic and timeworn); serene (Serene — Soft blues, sage, muted lavender - calm and peaceful); scandi (Scandi — Warm whites, pale woods, cream - Scandinavian minimalism); cosy (Cosy — Deep plaster, amber, tobacco - enveloping warmth); heritage (Heritage — Rich historical tones with saturated depth - classic and grounded); mid-century (Mid-Century — Olive, mustard, teal, terracotta - retro modern); pastel (Pastel — Sage, duck egg, blush - soft colors with body).\"},{\"type\":\"null\"}],\"description\":\"A recognized mood displaced by a stronger colour reading.\"}},\"required\":[\"query\",\"moodId\",\"supersededMoodId\"],\"additionalProperties\":false,\"description\":\"Mood interpretation receipt. Present when the standard search evaluated mood; null ids explicitly mean no mood was selected. Absent means mood was not evaluated or the response came from an older server.\"},\"mode\":{\"type\":\"string\",\"enum\":[\"image\",\"colour\",\"text\",\"lexical\",\"entity-browse\"],\"description\":\"The primary ranking source the query resolved to: 'image' (look-alike), 'colour' (the colour arm — pure), 'text' (visual text→image, the default), 'lexical' (BM25 over the attribute/name projection), or 'entity-browse' (the query names a brand and nothing",
45
- " else, so the brand's own catalogue in browse order is returned instead of a similarity ranking). A fully governed hard form + colour query filters the primary source's one candidate pool; that source still owns order and remains the reported mode.\"},\"textLane\":{\"type\":\"string\",\"enum\":[\"image-text\",\"variant-text\"],\"description\":\"Which dense text lane answered: 'image-text' (the query carries evidence it describes appearance, so it is ranked against image vectors) or 'variant-text' (ranked against each variant's written portrait). Absent when the answer did not come from the dense text arm — absence is unknown, not a default.\"},\"rerank\":{\"type\":\"object\",\"properties\":{\"colour\":{\"type\":\"string\",\"enum\":[\"ok\",\"failed\",\"skipped\"]},\"colourWeight\":{\"type\":\"number\"},\"colourLabel\":{\"type\":\"string\"}},\"additionalProperties\":false},\"colourConstraint\":{\"type\":\"object\",\"properties\":{\"basis\":{\"type\":\"string\",\"enum\":[\"explicit\",\"detected-strong\"]},\"family\":{\"type\":\"string\",\"minLength\":1},\"queryHexes\":{\"minItems\":1,\"type\":\"array\",\"items\":{\"type\":\"string\",\"pattern\":\"^#[0-9a-f]{6}$\"}},\"considered\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"kept\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"dropped\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"unknown\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"adjacent\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"abundance\":{\"type\":\"object\",\"properties\":{\"grain\":{\"type\":\"string\",\"enum\":[\"variant\",\"product\"]},\"requested\":{\"type\":\"integer\",\"exclusiveMinimum\":0,\"maximum\":9007199254740991},\"eligible\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"candidateWindow\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"sourceExhausted\":{\"type\":\"boolean\"},\"state\":{\"type\":\"string\",\"enum\":[\"filled\",\"corpus_scarcity\",\"evidence_unavailable\",\"retrieval_window_exhausted\"]}},\"required\":[\"grain\",\"requested\",\"eligible\",\"candidateWindow\",\"sourceExhausted\",\"state\"],\"additionalProperties\":false}},\"required\":[\"basis\",\"family\",\"queryHexes\",\"considered\",\"kept\",\"dropped\",\"unknown\",\"adjacent\"],\"additionalProperties\":false,\"description\":\"Eligibility for a compound text colour: counts cover the bounded candidate pool, `adjacent` passed ColorScope hue compatibility, and missing colour evidence failed closed. Absent means colour was not a hard constraint.\"},\"formConstraint\":{\"type\":\"object\",\"properties\":{\"instruction\":{\"type\":\"string\",\"enum\":[\"subway\",\"plank\",\"square\",\"hexagon\",\"penny-round\"]},\"considered\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"kept\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"dropped\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"contradicted\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"unknown\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"abundance\":{\"type\":\"object\",\"properties\":{\"grain\":{\"type\":\"string\",\"enum\":[\"variant\",\"product\"]},\"requested\":{\"type\":\"integer\",\"exclusiveMinimum\":0,\"maximum\":9007199254740991},\"eligible\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"candidateWindow\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"sourceExhausted\":{\"type\":\"boolean\"},\"state\":{\"type\":\"string\",\"enum\":[\"filled\",\"corpus_scarcity\",\"evidence_unavailable\",\"retrieval_window_exhausted\"]}},\"required\":[\"grain\",\"requested\",\"eligible\",\"candidateWindow\",\"sourceExhausted\",\"state\"],\"additionalProperties\":false}},\"required\":[\"instruction\",\"considered\",\"kept\",\"dropped\",\"contradicted\",\"unknown\",\"abundance\"],\"additionalProperties\":false,\"description\":\"Hard form eligibility for a compound text query. A candidate is kept only when governed size or body-type evidence supports the requested form; product and variant names are never proof.\"},\"maxScore\":{\"type\":\"number\"},\"counts\":{\"description\":\"Feed totals, present when `count:true` was requested OR when `collapse:\\\"product\\\"` grouped the results. `variants` is the total matching variants; `products` (only under `collapse`) the distinct product groups. Each total carries a `basis`: `exact` (true count, ",
46
- "up to the cap of 1000) or `lowerBound` (a floor — the cap was hit, an ANN-ranked pool bounds it, or the count degraded). The lexical arm counts exactly via the tsvector predicate; a single accepted spec term reads the `mv_attribute_facet_counts` materialized view (refreshed post-ingest, so a hair stale by design); ANN pools (image / colour / visual-text) and `products` are always `lowerBound`. The two totals describe the same result set, so `products` never exceeds `variants`: both are measured against the pool this page was drawn from, and a `variants` total that came out below that pool is reported as the pool-bounded floor instead — it counted a narrower universe than the results beside it.\",\"type\":\"object\",\"properties\":{\"variants\":{\"type\":\"object\",\"properties\":{\"value\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"basis\":{\"type\":\"string\",\"enum\":[\"exact\",\"lowerBound\"]}},\"required\":[\"value\",\"basis\"],\"additionalProperties\":false},\"products\":{\"type\":\"object\",\"properties\":{\"value\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"basis\":{\"type\":\"string\",\"enum\":[\"exact\",\"lowerBound\"]}},\"required\":[\"value\",\"basis\"],\"additionalProperties\":false}},\"required\":[\"variants\"],\"additionalProperties\":false},\"facets\":{\"description\":\"Per-query facet counts (MG-1010), present ONLY when the request sent `facetCounts`. Each requested facet's values are counted WITHIN this query's match set — a ranked query reports pool-derived floors (`basis:\\\"lowerBound\\\"`, `poolSize` stated), a filter-driven query true counts (`basis:\\\"exact\\\"`). See the `facetCounts` input field.\",\"type\":\"object\",\"properties\":{\"poolSize\":{\"description\":\"Present when the counts are POOL-DERIVED (every ranked query, and any filter-only query whose exact aggregation degraded): the size of the finalised candidate pool the per-value figures were counted over. Absent when the counts are exact under the query's whole predicate.\",\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"requested\":{\"type\":\"array\",\"items\":{\"type\":\"string\",\"minLength\":1},\"description\":\"The facet ids the request asked for, echoed verbatim.\"},\"absent\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"facetId\":{\"type\":\"string\",\"minLength\":1,\"description\":\"The requested facet id, verbatim.\"},\"reason\":{\"type\":\"string\",\"enum\":[\"unsupported\",\"timeout\"],\"description\":\"Why no counts were computed. `unsupported` — the id is KNOWN (a real registry attribute or virtual facet) but facet counting does not cover it: numeric attributes, non-facetable attributes, and (v1) the virtual certification programme. `timeout` — counting did not complete inside the facet stage's own time budget; the RESULTS are unaffected (facet counting can never take the page down), only the counts are missing. An id the registry has never heard of is a 400 naming it, never an `absent` entry.\"}},\"required\":[\"facetId\",\"reason\"],\"additionalProperties\":false},\"description\":\"Requested facets that were not counted, each with its reason — `unsupported` (the id is known but not countable) or `timeout` (the facet stage's own budget expired; the results are unaffected). Empty when every requested facet was counted.\"},\"counts\":{\"type\":\"object\",\"propertyNames\":{\"type\":\"string\"},\"additionalProperties\":{\"type\":\"object\",\"properties\":{\"values\":{\"maxItems\":25,\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"value\":{\"type\":\"string\",\"minLength\":1,\"description\":\"The CANONICAL token — a brand slug, a taxonomy product-type id, or a spec attribute's canonical term. Send it back verbatim as a filter term; never display-derive it.\"},\"label\":{\"type\":\"string\",\"minLength\":1,\"description\":\"Display label for the value — the brand's name, the product type's display name, or the registry dictionary label for a spec term. Falls back to the canonical token when the registry carries no label.\"},\"variants\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991,\"description\":\"Variants carrying this value within the counted set.\"}},\"required\":[\"value\",\"label\",\"variants\"],\"additionalProperties\":false},\"description\":\"Top values by varian",
47
- "t count, at most 25. An empty list is a facet with no values in the counted set, not an error.\"},\"truncated\":{\"type\":\"boolean\",\"description\":\"ALWAYS present. True when more distinct values existed beyond the reported list; false when the list is complete. Never absent-or-false.\"},\"basis\":{\"type\":\"string\",\"enum\":[\"exact\",\"lowerBound\"],\"description\":\"Exactly \\\"exact\\\" or \\\"lowerBound\\\" — nothing else, ever. `exact`: each `variants` figure is the true count under the query's whole predicate (filter-driven requests only). `lowerBound`: the figures were counted over the ranked candidate pool (`facets.poolSize`), so each is a floor of the true per-value total. Pool-counted values are NEVER `exact`.\"}},\"required\":[\"values\",\"truncated\",\"basis\"],\"additionalProperties\":false},\"description\":\"Per-facet value counts, keyed by the requested facet id. Every requested facet that is not in `absent` has an entry, including facets whose counted set held no values (`values: []`).\"}},\"required\":[\"requested\",\"absent\",\"counts\"],\"additionalProperties\":false},\"corpusScope\":{\"description\":\"The corpus this page AND its `counts` were drawn from — present only when the request narrowed it. ABSENT means the whole graph. Read it with `counts`: under a scope a short page is a small SCOPED corpus, not a truncated one.\",\"type\":\"object\",\"properties\":{\"scope\":{\"type\":\"string\",\"enum\":[\"all\",\"samplizeCatalogue\",\"materialBankOrderable\",\"materialBankNaOrderable\",\"materialBankNaAndCatalogue\",\"materialBankEuOrderable\",\"designShopOrderable\"],\"description\":\"Which corpus to answer from. samplizeCatalogue admits published US paint colours regardless of stock. ABSENT (or 'all') means the WHOLE GRAPH — every material MaterialGraph knows, orderable or not; absence is never a narrower default. Three transaction-oriented scopes gate on governed CHANNEL membership: 'materialBankNaOrderable' (Material Bank North America, ~277,000 materials), 'materialBankEuOrderable' (Material Bank Europe, ~65,000) and 'designShopOrderable' (DesignShop, ~34,000, with the products DesignShop hides from browse excluded). 'materialBankNaAndCatalogue' is the one-query union of Material Bank North America and the governed Catalogue editorial discovery corpus; that union does not by itself claim every member is transactable. 'materialBankOrderable' is NOT one of them: it selects materials carrying a Material Bank identity, which is a provenance signal rather than a channel one, and it admits 98.4% of the graph — use it to exclude crawl-sourced brands, not to mean orderable. A scope answers membership in its named governed corpus, never suitability: scope on channel, then refine on sector. Every scope is applied INSIDE the query: `limit` fills with eligible rows and `counts` describe the scoped corpus, which post-filtering a page cannot give you. An unknown value is a 400 naming it, never a silent fall back to the whole graph.\"},\"description\":{\"type\":\"string\",\"description\":\"What the scope admits, in one sentence, for a human reader.\"}},\"required\":[\"scope\",\"description\"],\"additionalProperties\":false},\"effectiveColours\":{\"description\":\"The `#rrggbb` colours the colour signal ACTUALLY ran on, after minor-proportion accents were dropped (MG-1549). Present whenever the request carried explicit `colours`. Compare against your request: a difference means the search that ran is not the search that was asked for.\",\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"droppedColours\":{\"description\":\"Colours removed from the query for carrying a `weight` below the 5% accent threshold. EMPTY is the normal case. Non-empty and the dropped colours are absent from colour retrieval and ranking entirely — they did not merely rank low. To keep a small accent in the query, give it `role: \\\"accent\\\"`.\",\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"note\":{\"type\":\"string\"},\"emptyOutcome\":{\"description\":\"Engine-authored explanation for an empty page: the strongest observed origin plus retrieved, after-prune, and final counts. Present only under explain.\",\"oneOf\":[{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"const\":\"proven-",
48
- "corpus-empty\"},\"basis\":{\"type\":\"string\",\"enum\":[\"spec-allowlist\",\"brand-scope\"]},\"exhaustive\":{\"type\":\"boolean\",\"const\":true},\"sourceEmpty\":{\"type\":[\"boolean\",\"null\"]},\"counts\":{\"type\":\"object\",\"properties\":{\"retrieved\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"afterPrune\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"final\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991}},\"required\":[\"retrieved\",\"afterPrune\",\"final\"],\"additionalProperties\":false}},\"required\":[\"kind\",\"basis\",\"exhaustive\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"const\":\"unproven-empty\"},\"reason\":{\"type\":\"string\",\"enum\":[\"allowlist-truncated\",\"retrieval-window-not-exhaustive\",\"source-empty\",\"post-retrieval-rejected\",\"pagination-empty\",\"no-authoritative-empty-proof\"]},\"sourceEmpty\":{\"type\":[\"boolean\",\"null\"]},\"counts\":{\"type\":\"object\",\"properties\":{\"retrieved\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"afterPrune\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"final\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991}},\"required\":[\"retrieved\",\"afterPrune\",\"final\"],\"additionalProperties\":false}},\"required\":[\"kind\",\"reason\"],\"additionalProperties\":false}],\"type\":\"object\"},\"termCoverage\":{\"description\":\"Lexical-projection coverage of substantive terms. Present only under explain.\",\"type\":\"object\",\"properties\":{\"status\":{\"type\":\"string\",\"enum\":[\"covered\",\"uncovered\",\"unavailable\",\"skipped\"]},\"terms\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"note\":{\"type\":\"string\"}},\"required\":[\"status\",\"terms\"],\"additionalProperties\":false},\"cursor\":{\"type\":\"string\"},\"window\":{\"type\":\"object\",\"properties\":{\"profile\":{\"type\":\"string\",\"const\":\"interactive-r1\"},\"candidateTier\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"retrievalRounds\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"retrievalMs\":{\"type\":\"number\",\"minimum\":0},\"exhaustive\":{\"type\":\"boolean\"},\"candidateCounts\":{\"type\":\"object\",\"properties\":{\"rawProviderMatches\":{\"anyOf\":[{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},{\"type\":\"null\"}]},\"selectedMatches\":{\"anyOf\":[{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},{\"type\":\"null\"}]},\"hydratedCanonicalCandidates\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991}},\"required\":[\"rawProviderMatches\",\"selectedMatches\",\"hydratedCanonicalCandidates\"],\"additionalProperties\":false}},\"required\":[\"profile\",\"candidateTier\",\"retrievalRounds\",\"retrievalMs\",\"exhaustive\"],\"additionalProperties\":false,\"description\":\"The single bounded candidate window that produced this page: versioned profile, requested depth, retrieval cost, and whether the selected source proved it was exhausted.\"},\"plan\":{\"description\":\"The executed query shape — result grain and the per-arm weight profile. Present only when `explain` is set. Diagnostic, not part of the ranking contract.\",\"type\":\"object\",\"properties\":{\"textLane\":{\"type\":\"string\",\"enum\":[\"image-text\",\"variant-text\"]},\"grain\":{\"type\":\"string\",\"enum\":[\"variant\",\"product\"],\"description\":\"What one result stands for: \\\"variant\\\" (the default — every matching variant is its own result) or \\\"product\\\" (`collapse: \\\"product\\\"` grouped variants, so each result fronts a product and hides siblings).\"},\"weights\":{\"type\":\"object\",\"properties\":{\"portrait\":{\"type\":\"number\"},\"image\":{\"type\":\"number\"},\"category\":{\"type\":\"number\"},\"colour\":{\"type\":\"number\"},\"lexical\":{\"type\":\"number\"},\"text\":{\"type\":\"number\"}},\"required\":[\"portrait\",\"image\",\"category\",\"colour\",\"lexical\",\"text\"],\"additionalProperties\":false,\"description\":\"The applied ordering-signal profile. At most one weight is non-zero: the selected mode's primary signal. Multiplying that signal by its weight reproduces `score`, and the weight is `maxScore`. Other signals are diagnostic or hard-gate receipts and never blend.\"},\"queryTransform\":{\"type\":\"array\",\"items\":{\"oneOf\":[{\"type\":\"object\",\"properties\":{\"stage\":{\"type\":\"string\",\"enum\":[\"normalise\",\"resolve-spec\",\"resolve-paint\",\"resolve-brand\",\"resolve-category\"]},\"applied\":{\"type\":\"boolean\"",
49
- ",\"const\":true}},\"required\":[\"stage\",\"applied\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"stage\":{\"type\":\"string\",\"enum\":[\"normalise\",\"resolve-spec\",\"resolve-paint\",\"resolve-brand\",\"resolve-category\"]},\"applied\":{\"type\":\"boolean\",\"const\":false},\"reason\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"stage\",\"applied\",\"reason\"],\"additionalProperties\":false}],\"type\":\"object\"},\"description\":\"The query-transformation stages that actually executed, in execution order. An applied stage changed execution; a skipped stage carries its reason. An empty array means this path ran no query transform.\"},\"steps\":{\"description\":\"Ordered request-understanding steps. Every entry says whether the step executed or skipped, why, and its measured wall-clock duration.\",\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"key\":{\"type\":\"string\",\"minLength\":1},\"status\":{\"type\":\"string\",\"enum\":[\"executed\",\"skipped\"]},\"reason\":{\"type\":\"string\",\"minLength\":1},\"durationMs\":{\"type\":\"number\",\"minimum\":0}},\"required\":[\"key\",\"status\",\"reason\",\"durationMs\"],\"additionalProperties\":false}},\"categoryAnchor\":{\"description\":\"Present only when the query NAMED a canonical product type. See `applied` for whether it gated the candidate pool.\",\"type\":\"object\",\"properties\":{\"productTypeId\":{\"type\":\"string\",\"description\":\"The leaf product-type id the query anchored to, e.g. \\\"modular_carpet\\\" — or, for a \\\"group\\\" anchor, the GROUP key, e.g. \\\"flooring\\\". A group key is a taxonomy node no variant is typed as; the ids actually gated on are in `productTypeIds`.\"},\"productTypeIds\":{\"description\":\"The leaf type ids a \\\"group\\\" anchor expanded to. Absent for the leaf sources, where `productTypeId` is already the single type gated on.\",\"readOnly\":true,\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"displayName\":{\"type\":\"string\",\"description\":\"The registry display name of that type.\"},\"similarity\":{\"type\":\"number\",\"description\":\"Cosine of the RAW (unanchored) query vector against the type vector for a \\\"vector\\\" anchor; 1 for the exact synonym / display-name passes.\"},\"margin\":{\"description\":\"Lead over the runner-up type for a \\\"vector\\\" anchor — the tie-break guard that stops an ambiguous query gating. Null for the exact passes.\",\"type\":[\"number\",\"null\"]},\"source\":{\"type\":\"string\",\"enum\":[\"taxonomy\",\"synonym\",\"display-name\",\"group\",\"head-noun\",\"vector\"],\"description\":\"How the anchor was established: a curated customer-language synonym, a registry display name appearing verbatim in the query, a public product-type group named by the query's trailing head noun, a bare canonical category noun whose leaf types cut across groups (\\\"tile\\\"), or the measured vector match.\"},\"candidateSource\":{\"description\":\"Provider-owned retrieval policy. When present, the hard category is applied inside lexical candidate selection before the result limit.\",\"type\":\"string\",\"const\":\"lexical\"},\"applied\":{\"type\":\"boolean\",\"description\":\"Whether the anchor actually gated the pool. False means the anchored category held nothing in the candidate pool, so the engine fell back to unanchored ranking (and said so in `note`) rather than returning an empty page.\"}},\"required\":[\"productTypeId\",\"displayName\",\"similarity\",\"margin\",\"source\",\"applied\"],\"additionalProperties\":false},\"brandAnchor\":{\"description\":\"Present only when the query NAMED a brand the catalogue sells. See `applied` for whether it gated retrieval or was superseded by the empty-result retreat.\",\"type\":\"object\",\"properties\":{\"brandSlug\":{\"type\":\"string\",\"description\":\"The brand the query named, e.g. \\\"daltile\\\" — its stable slug.\"},\"brandName\":{\"type\":\"string\",\"description\":\"The brand's name as catalogued.\"},\"confidence\":{\"type\":\"number\",\"description\":\"Resolver confidence in [0,1]. 1 means the query IS the brand's name or slug; the gate's floor is 0.88.\"},\"margin\":{\"description\":\"Lead over the runner-up brand — the tie-break guard that stops an ambiguous query gating. Null when there was no runner-up.\",\"type\":[\"number\",\"null\"]},\"tier\":{\"type\":\"string\",\"enum\":[\"exact_id\",\"exact_alias\",\"lexical\",\"semantic\"],\"description\":\"Which run",
50
- "g of the resolver ladder won. Only the identity rungs (`exact_id`, `exact_alias`) may gate — a `lexical` or `semantic` win means the query RESEMBLES a brand name rather than naming one.\"},\"variantCount\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991,\"description\":\"Catalogued variants behind the brand — the gated universe's size.\"},\"applied\":{\"type\":\"boolean\",\"description\":\"Whether the anchor actually gated retrieval. False means the brand held nothing retrievable, so the engine re-ran unanchored (and said so in `note`) rather than returning an empty page.\"}},\"required\":[\"brandSlug\",\"brandName\",\"confidence\",\"margin\",\"tier\",\"variantCount\",\"applied\"],\"additionalProperties\":false},\"brandSkipped\":{\"description\":\"Present only when the brand stage saw a real brand and REFUSED to gate on it. Mutually exclusive with `brandAnchor`.\",\"type\":\"object\",\"properties\":{\"reason\":{\"type\":\"string\",\"enum\":[\"ambiguous\",\"weak-tier\",\"too-broad\"],\"description\":\"Why the brand gate was declined. `ambiguous` = two brands inside the resolver's gap; `weak-tier` = the query resembles a brand name rather than naming one; `too-broad` = the brand exceeds the exact-scan ceiling.\"},\"candidates\":{\"description\":\"The brands that tied — populated for `ambiguous` only, so the response can name what it declined to choose between.\",\"type\":\"array\",\"items\":{\"type\":\"string\"}}},\"required\":[\"reason\"],\"additionalProperties\":false}},\"required\":[\"grain\",\"weights\",\"queryTransform\"],\"additionalProperties\":false},\"timings\":{\"description\":\"Per-stage wall-clock milliseconds (embed, retrieval, prune, constraints/enrichment, finalize, total). Present only when `explain` is set — diagnostic, not part of the ranking contract.\",\"type\":\"object\",\"propertyNames\":{\"type\":\"string\"},\"additionalProperties\":{\"type\":\"number\"}},\"query\":{\"type\":\"object\",\"propertyNames\":{\"type\":\"string\"},\"additionalProperties\":{}},\"seed\":{\"type\":\"object\",\"propertyNames\":{\"type\":\"string\"},\"additionalProperties\":{}}},\"required\":[\"view\",\"results\",\"suggestions\",\"mode\",\"maxScore\",\"query\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"view\":{\"type\":\"string\",\"const\":\"card\",\"description\":\"Projection echo — always 'card' on this envelope variant.\"},\"results\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"resultId\":{\"type\":\"string\",\"description\":\"This exact hit's attribution key — `<responseId>:<rank>`, where rank is 1-based within THIS response. Unique across pages, because each page is its own response with its own id. Store it verbatim next to the outcome; it is opaque, and `attribution.responseId` is the field to group by.\"},\"variantId\":{\"type\":\"string\"},\"productId\":{\"type\":[\"string\",\"null\"]},\"brandSlug\":{\"type\":[\"string\",\"null\"]},\"brand\":{\"type\":\"string\"},\"name\":{\"type\":\"string\",\"description\":\"Product name (the full item's `product`).\"},\"variantName\":{\"type\":[\"string\",\"null\"]},\"imageUrl\":{\"type\":[\"string\",\"null\"]},\"projectSaveCapability\":{\"type\":\"string\",\"const\":\"allowed\"},\"sampleable\":{\"description\":\"Whether a physical sample is explicitly known to be orderable. Absent means unknown, never unavailable.\",\"type\":\"boolean\"},\"sourceStatus\":{\"type\":\"string\",\"const\":\"provisional\"},\"score\":{\"type\":\"number\"},\"attributes\":{\"description\":\"Resolved attribute values for the ids sent in `returnAttributes`, keyed by attribute id. ABSENT entirely when `returnAttributes` was not sent. A key present with `null` means this variant carries no accepted value for that attribute — absent is not empty and neither is null: the three states are distinct on purpose. Values resolve with the normal variant-over-product precedence.\",\"type\":\"object\",\"propertyNames\":{\"type\":\"string\"},\"additionalProperties\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"array\",\"items\":{\"type\":\"string\"}},{\"type\":\"null\"}]}}},\"required\":[\"variantId\",\"productId\",\"brandSlug\",\"brand\",\"name\",\"variantName\",\"imageUrl\",\"score\"],\"additionalProperties\":false}},\"attribution\":{\"type\":\"object\",\"properties\":{\"responseId\":{\"type\":\"string\",\"description\":\"This response's stable id — the same value as the `X-Request-Id` header. Store it next to the outcome",
51
- " (an order, a save, a click) to join that outcome back to the ranking that produced it. Opaque: round-trip it verbatim, never parse it.\"},\"basis\":{\"type\":\"string\",\"enum\":[\"image\",\"colour\",\"text\",\"lexical\",\"entity-browse\",\"match\"],\"description\":\"The ranking basis, groupable across both ranked routes. On /api/v1/search this is the envelope's `mode` verbatim; /api/v1/search/match reports 'match'. Group your stored outcomes by this field.\"},\"issuedAt\":{\"type\":\"string\",\"description\":\"ISO-8601 timestamp of when this response was assembled, so a stored outcome carries its own age without a second lookup.\"}},\"required\":[\"responseId\",\"basis\",\"issuedAt\"],\"additionalProperties\":false,\"description\":\"Result attribution receipt. Present on every successful ranked response. Nothing on our side retains it — what you store IS the record.\"},\"refinement\":{\"description\":\"Inspectable receipt for narrowing this exact finalized candidate pool. The token is opaque and never exposes variant ids.\",\"type\":\"object\",\"properties\":{\"token\":{\"type\":\"string\"},\"expiresAt\":{\"type\":\"string\",\"format\":\"date-time\",\"pattern\":\"^(?:(?:\\\\d\\\\d[2468][048]|\\\\d\\\\d[13579][26]|\\\\d\\\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\\\d|30)|(?:02)-(?:0[1-9]|1\\\\d|2[0-8])))T(?:(?:[01]\\\\d|2[0-3]):[0-5]\\\\d:[0-5]\\\\d(?:\\\\.\\\\d+)?(?:Z))$\"},\"poolSize\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":800},\"schemaVersion\":{\"type\":\"string\"},\"projectionVersion\":{\"type\":\"integer\",\"minimum\":-9007199254740991,\"maximum\":9007199254740991}},\"required\":[\"token\",\"expiresAt\",\"poolSize\",\"schemaVersion\",\"projectionVersion\"],\"additionalProperties\":false},\"understanding\":{\"description\":\"How the ordinary search path interpreted the raw material-domain query: recognized senses, applied/refused/ambiguous outcomes, evidence, and the semantic text left after exact entity consumption. New standard-search responses include it; optional in the schema for rolling-deploy compatibility.\",\"type\":\"object\",\"properties\":{\"version\":{\"type\":\"string\",\"const\":\"1\"},\"rawQuery\":{\"type\":\"string\"},\"normalisedQuery\":{\"type\":\"string\"},\"residualText\":{\"type\":\"string\"},\"status\":{\"type\":\"string\",\"enum\":[\"resolved\",\"ambiguous\",\"partial\",\"unresolved\"]},\"mode\":{\"type\":\"string\",\"enum\":[\"image\",\"colour\",\"text\",\"lexical\",\"entity-browse\"]},\"senses\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"id\":{\"type\":\"string\",\"minLength\":1},\"kind\":{\"type\":\"string\",\"enum\":[\"brand\",\"product\",\"paint_identity\",\"category\",\"material\",\"colour\",\"attribute\",\"attribute_value\",\"certification\"]},\"label\":{\"type\":\"string\",\"minLength\":1},\"outcome\":{\"type\":\"string\",\"enum\":[\"applied\",\"ambiguous\",\"recognized\",\"refused\"]},\"effect\":{\"type\":\"string\",\"enum\":[\"restrict\",\"prioritize\",\"rerank\",\"offer\",\"none\"]},\"confidence\":{\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"evidence\":{\"type\":\"object\",\"properties\":{\"source\":{\"type\":\"string\",\"minLength\":1},\"matchedText\":{\"type\":\"string\",\"minLength\":1},\"span\":{\"type\":\"object\",\"properties\":{\"start\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"end\":{\"type\":\"integer\",\"exclusiveMinimum\":0,\"maximum\":9007199254740991}},\"required\":[\"start\",\"end\"],\"additionalProperties\":false}},\"required\":[\"source\",\"matchedText\"],\"additionalProperties\":false},\"entity\":{\"type\":\"object\",\"properties\":{\"brandSlug\":{\"type\":\"string\"},\"productId\":{\"type\":\"string\"},\"variantId\":{\"type\":\"string\"},\"materialBankId\":{\"type\":\"string\"},\"productTypeId\":{\"type\":\"string\"},\"paintCode\":{\"type\":\"string\"},\"hex\":{\"type\":\"string\"}},\"additionalProperties\":false},\"reason\":{\"type\":\"string\"}},\"required\":[\"id\",\"kind\",\"label\",\"outcome\",\"effect\",\"confidence\",\"evidence\"],\"additionalProperties\":false}},\"canonicalLexical\":{\"type\":\"object\",\"properties\":{\"original\":{\"type\":\"string\"},\"outcome\":{\"type\":\"string\",\"enum\":[\"resolved\",\"ambiguous\",\"declined\",\"partial\",\"unresolved\"]},\"matches\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"original\":{\"type\":\"string\"},\"canonical\":{\"type\":\"string\"},\"rule\":{\"type\":\"string\",\"enum\":[\"exact\",\"plural-to-singular\",\"typo-to-canonical\"]},\"span\":{\"type\":\"object\"",
52
- ",\"properties\":{\"start\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"end\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991}},\"required\":[\"start\",\"end\"],\"additionalProperties\":false},\"coverage\":{\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"readings\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"authority\":{\"type\":\"string\",\"enum\":[\"taxonomy\",\"colorscope\"]},\"identity\":{\"type\":\"string\",\"minLength\":1},\"outcome\":{\"type\":\"string\",\"enum\":[\"equivalent\",\"ambiguous\",\"declined\"]},\"executionStatus\":{\"type\":\"string\",\"enum\":[\"applied\",\"suppressed\",\"executable\",\"recognized-but-unexecutable\",\"declined\"],\"description\":\"MaterialGraph's execution verdict for this provider-owned reading. Taxonomy adapters report 'applied' only when their category or criterion contributed to this query; an adapter-known ambiguous or conflicting reading is 'suppressed'. 'recognized-but-unexecutable' means no current mapping can apply it.\"},\"role\":{\"anyOf\":[{\"type\":\"string\",\"enum\":[\"primary\",\"secondary\"]},{\"type\":\"null\"}]},\"kind\":{\"type\":\"string\",\"enum\":[\"domain\",\"core-colour\",\"paint\"]},\"label\":{\"type\":\"string\"},\"libraryId\":{\"type\":\"string\"},\"code\":{\"type\":\"string\"},\"aliasCount\":{\"type\":\"integer\",\"exclusiveMinimum\":0,\"maximum\":9007199254740991},\"matchForm\":{\"type\":\"string\",\"enum\":[\"name\",\"code\",\"authoritative-term\"]},\"collisionCount\":{\"type\":\"integer\",\"exclusiveMinimum\":0,\"maximum\":9007199254740991},\"familyId\":{\"type\":\"string\"},\"hex\":{\"type\":\"string\",\"pattern\":\"^#[0-9a-f]{6}$\"},\"level\":{\"type\":\"string\",\"enum\":[\"family\",\"tone-band\",\"branch\",\"concept\"]},\"lifecycle\":{\"type\":\"object\",\"properties\":{\"introducedAt\":{\"type\":\"string\",\"minLength\":1},\"reviewedAt\":{\"type\":\"string\",\"minLength\":1},\"status\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"introducedAt\",\"status\"],\"additionalProperties\":false},\"provenance\":{\"type\":\"object\",\"properties\":{\"basis\":{\"type\":\"string\",\"minLength\":1},\"snapshot\":{\"type\":\"string\",\"minLength\":1},\"source\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"basis\",\"source\"],\"additionalProperties\":false}},\"required\":[\"authority\",\"identity\",\"outcome\",\"executionStatus\",\"role\"],\"additionalProperties\":false}},\"receipt\":{\"type\":\"object\",\"properties\":{\"original\":{\"type\":\"string\"},\"canonical\":{\"type\":\"string\"},\"rule\":{\"type\":\"string\",\"enum\":[\"exact\",\"plural-to-singular\",\"typo-to-canonical\"]},\"authorities\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"authority\":{\"type\":\"string\",\"enum\":[\"taxonomy\",\"colorscope\"]},\"version\":{\"type\":\"string\",\"minLength\":1},\"revision\":{\"type\":\"string\",\"minLength\":1},\"digest\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"authority\",\"version\",\"revision\"],\"additionalProperties\":false}}},\"required\":[\"original\",\"canonical\",\"rule\",\"authorities\"],\"additionalProperties\":false}},\"required\":[\"original\",\"canonical\",\"rule\",\"span\",\"coverage\",\"readings\",\"receipt\"],\"additionalProperties\":false}},\"unresolved\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"original\":{\"type\":\"string\"},\"canonical\":{\"type\":\"string\"},\"rule\":{\"type\":\"string\",\"enum\":[\"exact\",\"plural-to-singular\",\"typo-to-canonical\"]},\"span\":{\"type\":\"object\",\"properties\":{\"start\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"end\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991}},\"required\":[\"start\",\"end\"],\"additionalProperties\":false},\"coverage\":{\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"readings\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"authority\":{\"type\":\"string\",\"enum\":[\"taxonomy\",\"colorscope\"]},\"identity\":{\"type\":\"string\",\"minLength\":1},\"outcome\":{\"type\":\"string\",\"enum\":[\"equivalent\",\"ambiguous\",\"declined\"]},\"executionStatus\":{\"type\":\"string\",\"enum\":[\"applied\",\"suppressed\",\"executable\",\"recognized-but-unexecutable\",\"declined\"],\"description\":\"MaterialGraph's execution verdict for this provider-owned reading. Taxonomy adapters report 'applied' only when their category or criterion contributed to this query; an adapter-known ambiguous or conflicting reading is 'suppressed'. 'recognized-but-unexecutable' means no current mapping can apply it.\"},\"role\":{\"anyOf\":[{\"type\":\"string\",\"enum\":[\"primary\",\"second",
53
- "ary\"]},{\"type\":\"null\"}]},\"kind\":{\"type\":\"string\",\"enum\":[\"domain\",\"core-colour\",\"paint\"]},\"label\":{\"type\":\"string\"},\"libraryId\":{\"type\":\"string\"},\"code\":{\"type\":\"string\"},\"aliasCount\":{\"type\":\"integer\",\"exclusiveMinimum\":0,\"maximum\":9007199254740991},\"matchForm\":{\"type\":\"string\",\"enum\":[\"name\",\"code\",\"authoritative-term\"]},\"collisionCount\":{\"type\":\"integer\",\"exclusiveMinimum\":0,\"maximum\":9007199254740991},\"familyId\":{\"type\":\"string\"},\"hex\":{\"type\":\"string\",\"pattern\":\"^#[0-9a-f]{6}$\"},\"level\":{\"type\":\"string\",\"enum\":[\"family\",\"tone-band\",\"branch\",\"concept\"]},\"lifecycle\":{\"type\":\"object\",\"properties\":{\"introducedAt\":{\"type\":\"string\",\"minLength\":1},\"reviewedAt\":{\"type\":\"string\",\"minLength\":1},\"status\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"introducedAt\",\"status\"],\"additionalProperties\":false},\"provenance\":{\"type\":\"object\",\"properties\":{\"basis\":{\"type\":\"string\",\"minLength\":1},\"snapshot\":{\"type\":\"string\",\"minLength\":1},\"source\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"basis\",\"source\"],\"additionalProperties\":false}},\"required\":[\"authority\",\"identity\",\"outcome\",\"executionStatus\",\"role\"],\"additionalProperties\":false}},\"receipt\":{\"type\":\"object\",\"properties\":{\"original\":{\"type\":\"string\"},\"canonical\":{\"type\":\"string\"},\"rule\":{\"type\":\"string\",\"enum\":[\"exact\",\"plural-to-singular\",\"typo-to-canonical\"]},\"authorities\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"authority\":{\"type\":\"string\",\"enum\":[\"taxonomy\",\"colorscope\"]},\"version\":{\"type\":\"string\",\"minLength\":1},\"revision\":{\"type\":\"string\",\"minLength\":1},\"digest\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"authority\",\"version\",\"revision\"],\"additionalProperties\":false}}},\"required\":[\"original\",\"canonical\",\"rule\",\"authorities\"],\"additionalProperties\":false}},\"required\":[\"original\",\"canonical\",\"rule\",\"span\",\"coverage\",\"readings\",\"receipt\"],\"additionalProperties\":false}}},\"required\":[\"original\",\"outcome\",\"matches\",\"unresolved\"],\"additionalProperties\":false},\"criteria\":{\"type\":\"object\",\"properties\":{\"applied\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"criterion\":{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, e.g. \\\"primary_material\\\" or \\\"fire_rating\\\" — or the special id \\\"brand\\\", which filters on the brand RELATION (a join, carried by every variant) rather than on any attribute.\"},\"term\":{\"type\":\"string\",\"description\":\"Canonical term the attribute must equal, e.g. \\\"textile\\\". For \\\"brand\\\", a brand slug (\\\"kvadrat\\\") or display name (\\\"Kvadrat\\\"), case-insensitive — exactly the `value` a `brand` facet reports. An unrecognised brand is a 400 naming it, never an empty page.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"term\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, or the special id \\\"brand\\\" for the brand relation.\"},\"terms\":{\"minItems\":1,\"type\":\"array\",\"items\":{\"type\":\"string\"},\"description\":\"Canonical terms, ANY ONE of which satisfies the filter — this is how OR is expressed. OR lives inside one entry; separate `filters[]` entries AND together. Used for ordinal scales, where a stricter class satisfies a looser one, and for a multi-select rail — `{attributeId:\\\"brand\\\", terms:[\\\"kvadrat\\\",\\\"rubelli\\\"]}` returns materials from either brand.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"descriptio",
54
- "n\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"terms\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\"},\"excludesTerm\":{\"type\":\"string\",\"minLength\":1},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"excludesTerm\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id that must have an accepted value, e.g. \\\"sample_image_mm_per_px\\\".\"},\"hasValue\":{\"type\":\"boolean\",\"const\":true,\"description\":\"Require any accepted value for this attribute.\"},\"asOf\":{\"description\":\"Reference date for EPD expiration, exclusive: the value must remain valid after this date.\",\"type\":\"string\",\"format\":\"date\",\"pattern\":\"^(?:(?:\\\\d\\\\d[2468][048]|\\\\d\\\\d[13579][26]|\\\\d\\\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\\\d|30)|(?:02)-(?:0[1-9]|1\\\\d|2[0-8])))$\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"hasValue\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id holding a NUMBER, e.g. \\\"light_reflectance_value\\\" or \\\"lead_time_days\\\".\"},\"min\":{\"description\":\"Inclusive lower bound. Omit for a ceiling-only range.\",\"type\":\"number\"},\"max\":{\"description\":\"Inclusive upper bound. Omit for a floor-only range.\",\"type\":\"number\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\"],\"additionalProperties\":false}]},\"lane\":{\"type\":\"string\",\"enum\":[\"filter\",\"preference\"]},\"lexicalSignal\":{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"minLength\":1},\"canonical\":{\"type\":\"string\",\"minLength\":1},\"identity\":{\"type\":\"string\",\"minLength\":1},\"original\":{\"type\":\"string\",\"minLength\":1},\"relation\":{\"type\":\"string\",\"enum\":[\"equivalent\",\"broader-storage-term\"]},\"span\":{\"type\":\"object\",\"properties\":{\"start\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"end\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991}},\"required\":[\"start\",\"end",
55
- "\"],\"additionalProperties\":false},\"storedTerm\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"attributeId\",\"canonical\",\"identity\",\"original\",\"relation\",\"span\",\"storedTerm\"],\"additionalProperties\":false},\"source\":{\"type\":\"string\",\"enum\":[\"compiled\",\"taxonomy-adapter\"]},\"span\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"criterion\",\"lane\",\"source\",\"span\"],\"additionalProperties\":false}},\"conflicts\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"minLength\":1},\"caller\":{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, e.g. \\\"primary_material\\\" or \\\"fire_rating\\\" — or the special id \\\"brand\\\", which filters on the brand RELATION (a join, carried by every variant) rather than on any attribute.\"},\"term\":{\"type\":\"string\",\"description\":\"Canonical term the attribute must equal, e.g. \\\"textile\\\". For \\\"brand\\\", a brand slug (\\\"kvadrat\\\") or display name (\\\"Kvadrat\\\"), case-insensitive — exactly the `value` a `brand` facet reports. An unrecognised brand is a 400 naming it, never an empty page.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"term\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, or the special id \\\"brand\\\" for the brand relation.\"},\"terms\":{\"minItems\":1,\"type\":\"array\",\"items\":{\"type\":\"string\"},\"description\":\"Canonical terms, ANY ONE of which satisfies the filter — this is how OR is expressed. OR lives inside one entry; separate `filters[]` entries AND together. Used for ordinal scales, where a stricter class satisfies a looser one, and for a multi-select rail — `{attributeId:\\\"brand\\\", terms:[\\\"kvadrat\\\",\\\"rubelli\\\"]}` returns materials from either brand.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"terms\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\"},\"excludesTerm\":{\"type\":\"string\",\"minLength\":1},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"excludesTerm\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id that must have an accepted value, e.g. \\\"sample_image_mm_per_px\\\".\"},\"hasValue\":{\"type\":\"boolean\",\"const\":true,\"description\":\"Require any accepted value for this attribute.\"},\"asOf\":{\"description\":\"Reference date for EPD expirat",
56
- "ion, exclusive: the value must remain valid after this date.\",\"type\":\"string\",\"format\":\"date\",\"pattern\":\"^(?:(?:\\\\d\\\\d[2468][048]|\\\\d\\\\d[13579][26]|\\\\d\\\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\\\d|30)|(?:02)-(?:0[1-9]|1\\\\d|2[0-8])))$\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"hasValue\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id holding a NUMBER, e.g. \\\"light_reflectance_value\\\" or \\\"lead_time_days\\\".\"},\"min\":{\"description\":\"Inclusive lower bound. Omit for a ceiling-only range.\",\"type\":\"number\"},\"max\":{\"description\":\"Inclusive upper bound. Omit for a floor-only range.\",\"type\":\"number\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\"],\"additionalProperties\":false}]},\"compiled\":{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, e.g. \\\"primary_material\\\" or \\\"fire_rating\\\" — or the special id \\\"brand\\\", which filters on the brand RELATION (a join, carried by every variant) rather than on any attribute.\"},\"term\":{\"type\":\"string\",\"description\":\"Canonical term the attribute must equal, e.g. \\\"textile\\\". For \\\"brand\\\", a brand slug (\\\"kvadrat\\\") or display name (\\\"Kvadrat\\\"), case-insensitive — exactly the `value` a `brand` facet reports. An unrecognised brand is a 400 naming it, never an empty page.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"term\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, or the special id \\\"brand\\\" for the brand relation.\"},\"terms\":{\"minItems\":1,\"type\":\"array\",\"items\":{\"type\":\"string\"},\"description\":\"Canonical terms, ANY ONE of which satisfies the filter — this is how OR is expressed. OR lives inside one entry; separate `filters[]` entries AND together. Used for ordinal scales, where a stricter class satisfies a looser one, and for a multi-select rail — `{attributeId:\\\"brand\\\", terms:[\\\"kvadrat\\\",\\\"rubelli\\\"]}` returns materials from either brand.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, decla",
57
- "red, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"terms\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\"},\"excludesTerm\":{\"type\":\"string\",\"minLength\":1},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"excludesTerm\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id that must have an accepted value, e.g. \\\"sample_image_mm_per_px\\\".\"},\"hasValue\":{\"type\":\"boolean\",\"const\":true,\"description\":\"Require any accepted value for this attribute.\"},\"asOf\":{\"description\":\"Reference date for EPD expiration, exclusive: the value must remain valid after this date.\",\"type\":\"string\",\"format\":\"date\",\"pattern\":\"^(?:(?:\\\\d\\\\d[2468][048]|\\\\d\\\\d[13579][26]|\\\\d\\\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\\\d|30)|(?:02)-(?:0[1-9]|1\\\\d|2[0-8])))$\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"hasValue\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id holding a NUMBER, e.g. \\\"light_reflectance_value\\\" or \\\"lead_time_days\\\".\"},\"min\":{\"description\":\"Inclusive lower bound. Omit for a ceiling-only range.\",\"type\":\"number\"},\"max\":{\"description\":\"Inclusive upper bound. Omit for a floor-only range.\",\"type\":\"number\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\"],\"additionalProperties\":false}]},\"reason\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"attributeId\",\"caller\",\"compiled\",\"reason\"],\"additionalProperties\":false}},\"exclusions\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"criterion\":{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, e.g. \\\"primary_material\\\" or \\\"fire_rating\\\" — or the special id \\\"brand\\\", which filters on the brand RELATION (a join, carried by every variant) rather than on any attribute.\"},\"term\":{\"type\":\"string\",\"description\":\"Canonical term the attribute must equal, e.g. \\\"textile\\\". For \\\"brand\\\", a",
58
- " brand slug (\\\"kvadrat\\\") or display name (\\\"Kvadrat\\\"), case-insensitive — exactly the `value` a `brand` facet reports. An unrecognised brand is a 400 naming it, never an empty page.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"term\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, or the special id \\\"brand\\\" for the brand relation.\"},\"terms\":{\"minItems\":1,\"type\":\"array\",\"items\":{\"type\":\"string\"},\"description\":\"Canonical terms, ANY ONE of which satisfies the filter — this is how OR is expressed. OR lives inside one entry; separate `filters[]` entries AND together. Used for ordinal scales, where a stricter class satisfies a looser one, and for a multi-select rail — `{attributeId:\\\"brand\\\", terms:[\\\"kvadrat\\\",\\\"rubelli\\\"]}` returns materials from either brand.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"terms\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\"},\"excludesTerm\":{\"type\":\"string\",\"minLength\":1},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"excludesTerm\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id that must have an accepted value, e.g. \\\"sample_image_mm_per_px\\\".\"},\"hasValue\":{\"type\":\"boolean\",\"const\":true,\"description\":\"Require any accepted value for this attribute.\"},\"asOf\":{\"description\":\"Reference date for EPD expiration, exclusive: the value must remain valid after this date.\",\"type\":\"string\",\"format\":\"date\",\"pattern\":\"^(?:(?:\\\\d\\\\d[2468][048]|\\\\d\\\\d[13579][26]|\\\\d\\\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\\\d|30)|(?:02)-(?:0[1-9]|1\\\\d|2[0-8])))$\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"",
59
- "required\":[\"attributeId\",\"hasValue\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id holding a NUMBER, e.g. \\\"light_reflectance_value\\\" or \\\"lead_time_days\\\".\"},\"min\":{\"description\":\"Inclusive lower bound. Omit for a ceiling-only range.\",\"type\":\"number\"},\"max\":{\"description\":\"Inclusive upper bound. Omit for a floor-only range.\",\"type\":\"number\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\"],\"additionalProperties\":false}]},\"execution\":{\"type\":\"string\",\"const\":\"blocked-positive-only-contract\"},\"lane\":{\"type\":\"string\",\"enum\":[\"hard\",\"soft\"]},\"missingValuePolicy\":{\"type\":\"string\",\"const\":\"retain-unknown\"},\"span\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"criterion\",\"execution\",\"lane\",\"missingValuePolicy\",\"span\"],\"additionalProperties\":false}},\"preferences\":{\"type\":\"array\",\"items\":{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, e.g. \\\"primary_material\\\" or \\\"fire_rating\\\" — or the special id \\\"brand\\\", which filters on the brand RELATION (a join, carried by every variant) rather than on any attribute.\"},\"term\":{\"type\":\"string\",\"description\":\"Canonical term the attribute must equal, e.g. \\\"textile\\\". For \\\"brand\\\", a brand slug (\\\"kvadrat\\\") or display name (\\\"Kvadrat\\\"), case-insensitive — exactly the `value` a `brand` facet reports. An unrecognised brand is a 400 naming it, never an empty page.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"term\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, or the special id \\\"brand\\\" for the brand relation.\"},\"terms\":{\"minItems\":1,\"type\":\"array\",\"items\":{\"type\":\"string\"},\"description\":\"Canonical terms, ANY ONE of which satisfies the filter — this is how OR is expressed. OR lives inside one entry; separate `filters[]` entries AND together. Used for ordinal scales, where a stricter class satisfies a looser one, and for a multi-select rail — `{attributeId:\\\"brand\\\", terms:[\\\"kvadrat\\\",\\\"rubelli\\\"]}` returns materials from either brand.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"terms\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\"},\"excludesTerm\":{\"type\":\"string\",\"minLength\":1},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{",
60
- "\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"excludesTerm\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id that must have an accepted value, e.g. \\\"sample_image_mm_per_px\\\".\"},\"hasValue\":{\"type\":\"boolean\",\"const\":true,\"description\":\"Require any accepted value for this attribute.\"},\"asOf\":{\"description\":\"Reference date for EPD expiration, exclusive: the value must remain valid after this date.\",\"type\":\"string\",\"format\":\"date\",\"pattern\":\"^(?:(?:\\\\d\\\\d[2468][048]|\\\\d\\\\d[13579][26]|\\\\d\\\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\\\d|30)|(?:02)-(?:0[1-9]|1\\\\d|2[0-8])))$\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"hasValue\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id holding a NUMBER, e.g. \\\"light_reflectance_value\\\" or \\\"lead_time_days\\\".\"},\"min\":{\"description\":\"Inclusive lower bound. Omit for a ceiling-only range.\",\"type\":\"number\"},\"max\":{\"description\":\"Inclusive upper bound. Omit for a floor-only range.\",\"type\":\"number\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\"],\"additionalProperties\":false}]}},\"residualText\":{\"type\":[\"string\",\"null\"]},\"unresolved\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"code\":{\"type\":\"string\",\"enum\":[\"missing-searchable-attribute\",\"missing-dimension-pair-contract\",\"missing-explicit-comparator\",\"negative-criterion-contract-blocked\",\"registry-contract-drift\",\"unsupported-numeric-format\",\"unsupported-unit\",\"unsupported-strict-comparator\"]},\"reason\":{\"type\":\"string\",\"minLength\":1},\"span\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"code\",\"reason\",\"span\"],\"additionalProperties\":false}}},\"required\":[\"applied\",\"conflicts\",\"exclusions\",\"preferences\",\"residualText\",\"unresolved\"],\"additionalProperties\":false},\"productType\":{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"enum\":[\"leaf\",\"group\",\"vector\",\"ambiguous\",\"none\"]},\"gate\":{\"type\":\"string\",\"enum\":[\"hard\",\"constrain\",\"none\"]},\"pass\":{\"type\":\"string\",\"enum\":[\"taxonomy\",\"synonym\",\"display-name\",\"group\",\"head-noun\",\"vector\",\"none\"]},\"reason\":{\"type\":\"string\",\"minLength\":1},\"displayName\":{\"type\":[\"string\",\"null\"]},\"productTypeId\":{\"type\":[\"string\",\"null\"]},\"groupKey\":{\"type\":[\"string\",\"null\"]},\"productTypeIds\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"similar",
61
- "ity\":{\"type\":[\"number\",\"null\"]},\"margin\":{\"type\":[\"number\",\"null\"]},\"residualText\":{\"type\":\"string\"},\"vectorPassRan\":{\"type\":\"boolean\"},\"registryVersion\":{\"type\":\"string\",\"minLength\":1},\"durationMs\":{\"type\":\"number\",\"minimum\":0},\"candidates\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"productTypeId\":{\"type\":\"string\",\"minLength\":1},\"displayName\":{\"type\":\"string\",\"minLength\":1},\"similarity\":{\"type\":\"number\"}},\"required\":[\"productTypeId\",\"displayName\",\"similarity\"],\"additionalProperties\":false}}},\"required\":[\"kind\",\"gate\",\"pass\",\"reason\",\"displayName\",\"productTypeId\",\"groupKey\",\"productTypeIds\",\"similarity\",\"margin\",\"residualText\",\"vectorPassRan\",\"registryVersion\",\"durationMs\",\"candidates\"],\"additionalProperties\":false},\"arm\":{\"type\":\"object\",\"properties\":{\"arm\":{\"anyOf\":[{\"type\":\"string\",\"enum\":[\"dense\",\"lexical\",\"colour\"]},{\"type\":\"null\"}]},\"reason\":{\"type\":\"string\",\"minLength\":1},\"reasonDetail\":{\"type\":\"string\",\"enum\":[\"derived-colour-veto\",\"typo-recovery\",\"literal-compound-without-type\"]},\"outcome\":{\"type\":\"string\",\"enum\":[\"dense\",\"lexical\",\"colour\",\"browse\",\"criteria-only\",\"ambiguous\"]},\"decidedBy\":{\"type\":\"string\",\"enum\":[\"deterministic\",\"model\"]},\"productTypeGate\":{\"type\":\"string\",\"enum\":[\"hard\",\"constrain\",\"none\"]},\"productTypeIds\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"residual\":{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"enum\":[\"compound\",\"descriptive\",\"ambiguous\",\"empty\"]},\"tokens\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"resolvedTokens\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"unresolvedTokens\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"resolvedRatio\":{\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"comparisonFrame\":{\"type\":\"boolean\"}},\"required\":[\"kind\",\"tokens\",\"resolvedTokens\",\"unresolvedTokens\",\"resolvedRatio\",\"comparisonFrame\"],\"additionalProperties\":false},\"registryVersion\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"arm\",\"reason\",\"outcome\",\"decidedBy\",\"productTypeGate\",\"productTypeIds\",\"residual\",\"registryVersion\"],\"additionalProperties\":false},\"ambiguity\":{\"type\":\"object\",\"properties\":{\"outcome\":{\"type\":\"string\",\"enum\":[\"applied\",\"abstained\",\"refused\",\"timeout\"]},\"modelId\":{\"type\":[\"string\",\"null\"]},\"providerRan\":{\"type\":\"boolean\"},\"durationMs\":{\"type\":\"number\",\"minimum\":0},\"timeoutMs\":{\"type\":\"number\",\"exclusiveMinimum\":0},\"validation\":{\"type\":\"string\",\"enum\":[\"valid\",\"invalid\",\"not-run\"]},\"refusalCode\":{\"type\":[\"string\",\"null\"]},\"confidence\":{\"type\":[\"number\",\"null\"]},\"routing\":{\"anyOf\":[{\"type\":\"string\",\"enum\":[\"compound\",\"descriptive\"]},{\"type\":\"null\"}]},\"productTypeIds\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"changedArm\":{\"type\":\"boolean\"}},\"required\":[\"outcome\",\"modelId\",\"providerRan\",\"durationMs\",\"timeoutMs\",\"validation\",\"refusalCode\",\"confidence\",\"routing\",\"productTypeIds\",\"changedArm\"],\"additionalProperties\":false},\"categoryScope\":{\"type\":\"object\",\"properties\":{\"source\":{\"type\":\"string\",\"enum\":[\"caller\",\"anchor\"]},\"mechanism\":{\"type\":\"string\",\"enum\":[\"sql-predicate\",\"allowlist\",\"prune\"]},\"variantCount\":{\"anyOf\":[{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},{\"type\":\"null\"}]},\"ceiling\":{\"type\":\"integer\",\"exclusiveMinimum\":0,\"maximum\":9007199254740991},\"specScanBounded\":{\"type\":\"boolean\"}},\"required\":[\"source\",\"mechanism\",\"variantCount\",\"ceiling\"],\"additionalProperties\":{}},\"specAllowlist\":{\"type\":\"object\",\"properties\":{\"limit\":{\"type\":\"integer\",\"exclusiveMinimum\":0,\"maximum\":9007199254740991},\"returned\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"retrieved\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"truncated\":{\"type\":\"boolean\"},\"emptyMeansScarcity\":{\"type\":\"boolean\"},\"outcome\":{\"type\":\"string\",\"enum\":[\"corpus-scarcity\",\"retrieval-miss\",\"unknown\"]}},\"required\":[\"limit\",\"returned\",\"truncated\",\"emptyMeansScarcity\"],\"additionalProperties\":false},\"unsatisfiableNarrowing\":{\"type\":\"object\",\"properties\":{\"narrowing\":{\"type\":\"string\",\"enum\":[\"product-type\"]},\"gate\":{\"type\":\"string\",\"enum\":[\"hard\"]},\"productTypeId\":{\"type\":\"string\",\"minLength\":1},\"displayName\":{\"type\":\"string\",\"minLength\":1},\"productTypeIds\":{\"type\":\"array\",\"it",
62
- "ems\":{\"type\":\"string\",\"minLength\":1}},\"pass\":{\"type\":\"string\",\"minLength\":1},\"evidence\":{\"type\":\"string\",\"enum\":[\"zero-accepted-variants\"]},\"relaxedTo\":{\"type\":\"string\",\"enum\":[\"none\"]}},\"required\":[\"narrowing\",\"gate\",\"productTypeId\",\"displayName\",\"productTypeIds\",\"pass\",\"evidence\",\"relaxedTo\"],\"additionalProperties\":false}},\"required\":[\"version\",\"rawQuery\",\"normalisedQuery\",\"residualText\",\"status\",\"mode\",\"senses\"],\"additionalProperties\":false},\"suggestions\":{\"default\":[],\"description\":\"Bounded structured refinements. Each request is a complete executable replacement for the current search request.\",\"maxItems\":6,\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"label\":{\"type\":\"string\",\"minLength\":1,\"description\":\"Human-readable description of the refinement.\"},\"request\":{\"type\":\"object\",\"properties\":{\"text\":{\"description\":\"Free-text query, matched VISUALLY against material imagery (\\\"striped wallpaper\\\", \\\"warm terrazzo\\\"). A bare colour word (\\\"sage\\\") automatically runs as a colour-only search.\",\"type\":\"string\",\"maxLength\":500},\"intent\":{\"default\":\"search\",\"description\":\"Budget profile for the search primitive. Default \\\"search\\\" is the full engine. \\\"typeahead\\\" is the as-you-type profile: lexical-only with prefix match on the final token, forced `view:\\\"card\\\"`, `limit` capped at 24, and no mood/colour detection tiers, semantic fallback, category boost, secondary scoring, refinement suggestions, counts, or meanings. `textModes` is ignored under typeahead. Recommended wire shape for library typeahead UIs.\",\"type\":\"string\",\"enum\":[\"search\",\"typeahead\"]},\"textModes\":{\"description\":\"How to interpret `text`. Default [\\\"visual\\\"] — a text→image VISUAL search. \\\"lexical\\\" (opt-in) runs a BM25 ATTRIBUTE-lens search over material names + canonical attribute terms (find by what it's called / what it IS); detected colour words are stripped from the lexical terms and retained only as diagnostic or hard-constraint evidence. Requesting BOTH [\\\"visual\\\",\\\"lexical\\\"] runs the visual search and notes it — compose a separate lexical search if both result sets are required. Ignored when `intent` is \\\"typeahead\\\".\",\"type\":\"array\",\"items\":{\"type\":\"string\",\"enum\":[\"visual\",\"lexical\"]}},\"colours\":{\"description\":\"Explicit colour palette to match. Each entry is EITHER a `hex` OR a `name` (a descriptive or brand paint name resolved server-side — never invent a hex for a name). PRECEDENCE: when set, these override any colour word detected in `text`. Unresolvable names fail with a typed error listing them, so you can correct and retry.\",\"maxItems\":8,\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"hex\":{\"description\":\"The colour to match, as 3- or 6-digit hex. Provide EITHER `hex` OR `name`, never both. Only pass `hex` when you already hold an EXACT hex — for a colour NAME use `name` and let the server resolve it.\",\"type\":\"string\",\"pattern\":\"^#?(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$\"},\"name\":{\"description\":\"A colour NAME to match — a descriptive name ('sage green', 'terracotta', 'warm sand') or a brand paint name ('Gauntlet Gray', 'Hale Navy', 'White Dove'). Resolved server-side to its exact library hex (misspellings are fuzzy-matched). NEVER invent a hex for a name — pass the name here. Provide EITHER `name` OR `hex`, never both.\",\"type\":\"string\",\"minLength\":1,\"maxLength\":120},\"weight\":{\"description\":\"Relative proportion of this colour in the desired palette (0–1). Weights are normalised across the palette; omit for equal parts.\",\"type\":\"number\",\"exclusiveMinimum\":0,\"maximum\":1},\"role\":{\"type\":\"string\",\"enum\":[\"any\",\"is\",\"accent\"],\"description\":\"Per-colour swatch eligibility (default 'any'). PRECEDENCE: `colourLogic` governs cross-colour combination (all/any); `role` governs which swatch of a product may satisfy THIS colour, and ANDs on top of the logic. 'is' = the colour must be the product's DOMINANT swatch; 'accent' = the colour must be a minor swatch (proportion 0.05–0.35) and is excluded from the proportion-match term; 'any' = today's nearest-swatch match.\"},\"sourceName\":{\"description\":\"Human name the colour came from (e.g. \\\"sage\\",
63
- "\", \\\"Elephant's Breath\\\") — echoed back for display; never affects matching. (For a name you want RESOLVED to a hex, use `name` instead.)\",\"type\":\"string\",\"maxLength\":120}},\"additionalProperties\":false}},\"colourLogic\":{\"description\":\"\\\"all\\\" (default): results should contain the whole palette, ranked by average distance + proportion match. \\\"any\\\": results matching ANY palette colour, ranked by their best single-colour distance.\",\"type\":\"string\",\"enum\":[\"all\",\"any\"]},\"colourTolerance\":{\"description\":\"How close a colour must be to count as a match. 'normal' (default) is today's behaviour; 'tight' narrows the OKLab match threshold and prunes far single-colour matches; 'loose' widens the threshold.\",\"type\":\"string\",\"enum\":[\"tight\",\"normal\",\"loose\"]},\"colourAppearance\":{\"type\":\"object\",\"properties\":{\"lightness\":{\"description\":\"OKLab lightness window (L ∈ [0,1]); dark ≈ 0, light ≈ 1.\",\"type\":\"object\",\"properties\":{\"min\":{\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"max\":{\"type\":\"number\",\"minimum\":0,\"maximum\":1}},\"required\":[\"min\",\"max\"],\"additionalProperties\":false},\"warmth\":{\"description\":\"OKLab warmth window (b axis); cool < 0 < warm, ≈ −0.1..0.1.\",\"type\":\"object\",\"properties\":{\"min\":{\"type\":\"number\",\"minimum\":-0.5,\"maximum\":0.5},\"max\":{\"type\":\"number\",\"minimum\":-0.5,\"maximum\":0.5}},\"required\":[\"min\",\"max\"],\"additionalProperties\":false}},\"additionalProperties\":false,\"description\":\"HARD appearance gate on the material's DOMINANT colour, independent of the colour palette — works on a text-only query too. Variants without colour extraction are EXCLUDED whenever any axis is set (NULL scalars never match).\"},\"mood\":{\"type\":\"string\",\"enum\":[\"warm\",\"cool\",\"earthy\",\"moody\",\"bold\",\"light-airy\",\"natural\",\"neutral\",\"romantic\",\"coastal\",\"industrial\",\"luxe\",\"vintage\",\"serene\",\"scandi\",\"cosy\",\"heritage\",\"mid-century\",\"pastel\"],\"description\":\"Mood-language colour entry — resolves to a curated checked-in colour palette. Use it for evocative mood queries ('coastal', 'moody', 'earthy') rather than a single colour. A mood ALONE runs as a colour search; with text it remains diagnostic or hard-constraint evidence and does not blend into the text mode's ordering score. One of: warm (Warm — Reds, oranges, yellows - inviting and cozy); cool (Cool — Blues, teals, cool grays - calm and professional); earthy (Earthy — Browns, tans, terracotta - natural and grounded); moody (Moody — Deep, saturated, dramatic - rich and atmospheric); bold (Bold — High chroma, vibrant - energetic and eye-catching); light-airy (Light + Airy — Pastels, pale tones - soft and spacious); natural (Natural — Greens, wood tones, stone - organic and balanced); neutral (Neutral — Whites, grays, blacks - versatile and timeless); romantic (Romantic — Soft pinks, mauves, blush - tender and elegant); coastal (Coastal — Sandy beiges, ocean blues, seafoam - relaxed seaside); industrial (Industrial — Cool grays, concrete, metallic - urban and modern); luxe (Luxe — Deep jewel tones, rich emeralds, sapphires - opulent); vintage (Vintage — Muted, dusty tones - nostalgic and timeworn); serene (Serene — Soft blues, sage, muted lavender - calm and peaceful); scandi (Scandi — Warm whites, pale woods, cream - Scandinavian minimalism); cosy (Cosy — Deep plaster, amber, tobacco - enveloping warmth); heritage (Heritage — Rich historical tones with saturated depth - classic and grounded); mid-century (Mid-Century — Olive, mustard, teal, terracotta - retro modern); pastel (Pastel — Sage, duck egg, blush - soft colors with body).\"},\"sort\":{\"type\":\"string\",\"enum\":[\"relevance\",\"light\",\"dark\",\"vivid\",\"muted\",\"colourful\",\"name\",\"name_desc\",\"recent\"],\"description\":\"Re-order the final scored results (default 'relevance' = the selected mode's primary score). Two families. COLOUR RAMPS: 'light'/'dark' by dominant lightness, 'vivid'/'muted' by chroma, 'colourful' by overall vibrancy — variants without colour extraction sink to the end. CATALOGUE ORDERS: 'name' (A→Z), 'name_desc' (Z→A) and 'recent' (newest first), which read the same `name` / `created_at` columns, in the same directions, that /api/v1/search/brows",
64
- "e orders on — one word, one order, on both lanes. Every sort REORDERS the pool the search signals already retrieved; it never widens or substitutes for one. Retired materials sort below visible ones under every non-relevance sort. For an unranked walk of the whole corpus in any of these orders, use the browse surface instead.\"},\"orderBy\":{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"minLength\":1,\"description\":\"The attribute to sort by. Only attributes with a DECLARED value ladder can be ordered — ordering is never inferred from a value's spelling — and anything else is refused with the orderable set named. Orderable today: price_tier.\"},\"direction\":{\"default\":\"asc\",\"description\":\"'asc' (default) walks the ladder from its first rung.\",\"type\":\"string\",\"enum\":[\"asc\",\"desc\"]}},\"required\":[\"attributeId\",\"direction\"],\"additionalProperties\":false,\"description\":\"Sort results along ONE attribute's declared ladder — a separate axis from `sort`, which stays the closed relevance/colour/catalogue vocabulary. Sent together, `orderBy` wins and the response `note` says so. A variant whose value is off the ladder, or which carries no value at all, sorts LAST in both directions: it has no rung, and it is still a real row, so it is placed at the end rather than dropped or guessed at.\"},\"returnAttributes\":{\"maxItems\":8,\"type\":\"array\",\"items\":{\"type\":\"string\",\"minLength\":1},\"description\":\"Attribute ids whose RESOLVED VALUE should ride each result, under `attributes` keyed by attribute id. Values resolve with the normal variant-over-product precedence. THREE STATES, and they are different: an attribute you did not ask for is not a key at all; an attribute you asked for that this variant carries no accepted value for is `null`; anything else is the value (a string, or an array for a multi-valued attribute). An unknown attribute id is refused naming it, never silently dropped. Pair it with `filters` to show what you filtered on.\"},\"similarTo\":{\"description\":\"Find materials that LOOK like a seed image. Supply `variantId` to seed from an existing variant's primary image, `materialBankId` to seed from a Material Bank SKU — the ONE EXTERNAL identifier this API accepts, so a caller already holding a SKU needs no resolve round trip — or `vector`, a pre-computed halfvec literal (`[a,b,…]`) from an externally-embedded image, to drive the image arm directly. A `materialBankId` that names no variant is a 400 naming the offending value, never an empty result set and never an unseeded search. PRECEDENCE: wins over `text` (the response carries a note when both were given).\",\"anyOf\":[{\"type\":\"object\",\"properties\":{\"variantId\":{\"type\":\"string\"}},\"required\":[\"variantId\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"materialBankId\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"materialBankId\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"vector\":{\"type\":\"string\"}},\"required\":[\"vector\"],\"additionalProperties\":false}]},\"excludeVariantIds\":{\"description\":\"Canonical variant ids that must not appear in this result. Applied before the returned page is cut, so the page refills from the deeper ranked pool instead of being shortened by client-side filtering. IDs are opaque; duplicates are harmless and deduplicated by the engine.\",\"maxItems\":256,\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"image\":{\"description\":\"A raw incoming image to search by (URL or base64). The HTTP adapter embeds it and sets `similarTo: { vector }`; wins over `text`.\",\"oneOf\":[{\"type\":\"object\",\"properties\":{\"type\":{\"type\":\"string\",\"const\":\"url\"},\"url\":{\"type\":\"string\",\"format\":\"uri\",\"description\":\"HTTP(S) URL of the query image. MUST be an https URL.\"}},\"required\":[\"type\",\"url\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"type\":{\"type\":\"string\",\"const\":\"base64\"},\"data\":{\"type\":\"string\",\"minLength\":1,\"description\":\"Base64-encoded image bytes (or a data URI) the caller already holds — e.g. a form upload — so there is nothing to host.\"}},\"required\":[\"type\",\"data\"],\"additionalProperties\":false}],\"type\":\"object\"},\"category\":{\"type\":\"obje",
65
- "ct\",\"properties\":{\"boost\":{\"description\":\"Soft category preference — results of this product type score higher but others still appear.\",\"type\":\"string\"},\"filter\":{\"description\":\"HARD gate: only these public taxonomy categories are returned. Values are category-group or product-type display names (case-insensitive), or canonical taxonomy keys. A group expands to every child product type; an unknown value errors listing every valid name.\",\"type\":\"array\",\"items\":{\"type\":\"string\"}}},\"additionalProperties\":false},\"weights\":{\"type\":\"object\",\"properties\":{\"image\":{\"type\":\"number\",\"minimum\":0},\"category\":{\"type\":\"number\",\"minimum\":0},\"colour\":{\"type\":\"number\",\"minimum\":0},\"lexical\":{\"type\":\"number\",\"minimum\":0},\"text\":{\"type\":\"number\",\"minimum\":0}},\"additionalProperties\":false,\"description\":\"Optional multiplier for the selected mode's one ordering signal. At most one applied weight is non-zero: colour→colour, lexical/entity-browse→lexical, variant-text→portrait, image-text/image→image, and filter-only→none. Overrides may scale only that live signal and cannot activate any other. Other similarities are diagnostic or hard-gate receipts and do not blend into score.\"},\"filters\":{\"description\":\"Typed filters applied as a hard restriction before ranking. Each entry is a term match (`attributeId` + `term`), a term SET (`attributeId` + `terms` — ANY one of them satisfies it), a presence check (`attributeId` + `hasValue: true`), or a numeric range (`attributeId` + `min`/`max`). AND/OR: entries AND together, and OR is expressed WITHIN one entry via `terms`. BRAND: `attributeId: \\\"brand\\\"` filters on the brand RELATION — a join every one of the catalogue's variants carries — taking a brand slug or display name, exactly the `value` a `brand` facet reports. Do NOT use the `brand_name` ATTRIBUTE for this: it is a sparse spec value present on about 1.5% of variants and absent entirely from every catalog-ingested brand, so filtering on it returns a near-empty page that reads as a corpus gap. Presence filters always route as hard spec gates. Aesthetic/meaning term filters (pattern, texture, directionality, and the other visual-meaning attributes) are applied as a pool-bounded post-filter over the visual candidate pool rather than a whole-catalog restriction. Each entry may carry a `trust` bar the matching value must clear.\",\"type\":\"array\",\"items\":{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, e.g. \\\"primary_material\\\" or \\\"fire_rating\\\" — or the special id \\\"brand\\\", which filters on the brand RELATION (a join, carried by every variant) rather than on any attribute.\"},\"term\":{\"type\":\"string\",\"description\":\"Canonical term the attribute must equal, e.g. \\\"textile\\\". For \\\"brand\\\", a brand slug (\\\"kvadrat\\\") or display name (\\\"Kvadrat\\\"), case-insensitive — exactly the `value` a `brand` facet reports. An unrecognised brand is a 400 naming it, never an empty page.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"term\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, or the special id \\\"brand\\\" for the brand relation.\"},\"terms\":{\"minItems\":1,\"type\":\"array\",\"items\":{\"type\":\"string\"},\"description\":\"Canonical terms, ANY ONE of which satisfies the filter — this is how OR is expressed. OR lives inside one entry; separate `filters[]` entries AND together. Used for ordinal scales, where a stricter class satisfies a looser one, and for a multi-select rai",
66
- "l — `{attributeId:\\\"brand\\\", terms:[\\\"kvadrat\\\",\\\"rubelli\\\"]}` returns materials from either brand.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"terms\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\"},\"excludesTerm\":{\"type\":\"string\",\"minLength\":1},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"excludesTerm\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id that must have an accepted value, e.g. \\\"sample_image_mm_per_px\\\".\"},\"hasValue\":{\"type\":\"boolean\",\"const\":true,\"description\":\"Require any accepted value for this attribute.\"},\"asOf\":{\"description\":\"Reference date for EPD expiration, exclusive: the value must remain valid after this date.\",\"type\":\"string\",\"format\":\"date\",\"pattern\":\"^(?:(?:\\\\d\\\\d[2468][048]|\\\\d\\\\d[13579][26]|\\\\d\\\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\\\d|30)|(?:02)-(?:0[1-9]|1\\\\d|2[0-8])))$\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"hasValue\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id holding a NUMBER, e.g. \\\"light_reflectance_value\\\" or \\\"lead_time_days\\\".\"},\"min\":{\"description\":\"Inclusive lower bound. Omit for a ceiling-only range.\",\"type\":\"number\"},\"max\":{\"description\":\"Inclusive upper bound. Omit for a floor-only range.\",\"type\":\"number\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\"],\"additionalProperties\":false}]}},\"preferences\":{\"description\":\"SOFT spec criteria — rank-never-exclude. Same shape as `filters`, but a preference NEVER removes a result: each hit's `verdict` gains a `preferred` cell, and a failed/missing preferred cell only DEMOTES the hit WITHIN ",
67
- "its page (the cursor is from the pre-demotion order).\",\"maxItems\":8,\"type\":\"array\",\"items\":{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, e.g. \\\"primary_material\\\" or \\\"fire_rating\\\" — or the special id \\\"brand\\\", which filters on the brand RELATION (a join, carried by every variant) rather than on any attribute.\"},\"term\":{\"type\":\"string\",\"description\":\"Canonical term the attribute must equal, e.g. \\\"textile\\\". For \\\"brand\\\", a brand slug (\\\"kvadrat\\\") or display name (\\\"Kvadrat\\\"), case-insensitive — exactly the `value` a `brand` facet reports. An unrecognised brand is a 400 naming it, never an empty page.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"term\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, or the special id \\\"brand\\\" for the brand relation.\"},\"terms\":{\"minItems\":1,\"type\":\"array\",\"items\":{\"type\":\"string\"},\"description\":\"Canonical terms, ANY ONE of which satisfies the filter — this is how OR is expressed. OR lives inside one entry; separate `filters[]` entries AND together. Used for ordinal scales, where a stricter class satisfies a looser one, and for a multi-select rail — `{attributeId:\\\"brand\\\", terms:[\\\"kvadrat\\\",\\\"rubelli\\\"]}` returns materials from either brand.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"terms\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\"},\"excludesTerm\":{\"type\":\"string\",\"minLength\":1},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"excludesTerm\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id that must have an accepted value, e.g. \\\"sample_image_mm_per_px\\\".\"},\"hasValue\":{\"type\":\"boolean\",\"const\":true,\"description\":\"Require any accepted value for this attribute.\"},\"asOf\":{\"description\":\"Reference date for EPD expiration, exclusive: the value must remain valid after this date.\",\"type\":\"string\",\"format\":\"date\",\"pattern\":\"^(?:(?:\\\\d\\\\d[2468][048]|\\\\d\\\\d[13579][26]|\\\\d\\\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\\\d|30)|(?:02)-(?:0[1-9]|1\\\\d|2[0-8])))$\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor",
68
- " ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"hasValue\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id holding a NUMBER, e.g. \\\"light_reflectance_value\\\" or \\\"lead_time_days\\\".\"},\"min\":{\"description\":\"Inclusive lower bound. Omit for a ceiling-only range.\",\"type\":\"number\"},\"max\":{\"description\":\"Inclusive upper bound. Omit for a floor-only range.\",\"type\":\"number\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\"],\"additionalProperties\":false}]}},\"verdicts\":{\"description\":\"Opt-in seed-relative verdict cells. Only meaningful with `similarTo: { variantId }`: costs one seed fetch + one page read to diff each hit's accepted COMPARABLE attributes (numeric + a colour axis) against the seed's, emitting `preferred` `verdict` cells with directional deviations (`above`/`below`, e.g. 2 mm thicker, warmer). Ignored on the default path; a `similarTo: { vector }` / raw-image seed carries no seed variant, so the response notes that seed cells were skipped.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Results per page (default 24, max 48).\",\"type\":\"integer\",\"minimum\":1,\"maximum\":48},\"cursor\":{\"description\":\"Opaque pagination cursor from a previous response. NOTE: ANN search is approximate — tail items may reorder slightly between pages.\",\"type\":\"string\"},\"explain\":{\"description\":\"Include per-signal scores on each result (`signals`).\",\"type\":\"boolean\"},\"collapse\":{\"description\":\"Group results by product before pagination. \\\"product\\\" returns one representative (highest-ranked) variant per product, with hidden siblings counted in `siblingCount`. The engine and GET adapter default to \\\"none\\\"; public POST requests default to \\\"product\\\" and may request \\\"none\\\" explicitly for variant-grain results.\",\"type\":\"string\",\"enum\":[\"none\",\"product\"]},\"count\":{\"default\":false,\"description\":\"Opt-in feed total. When true the response carries `counts.variants` (and `counts.products` under `collapse:\\\"product\\\"`). The variant total is EXACT up to a cap of 1000 and reported as `basis:\\\"lowerBound\\\"` beyond it — and ALWAYS `lowerBound` for ANN-ranked pools (image / colour / visual-text / visual-meaning post-filters), where the finite candidate pool is the only honest figure. Counts are never silently approximated: an exact count is labelled `exact`, a capped or pool-bounded one `lowerBound`. Default false (skip the count query entirely).\",\"type\":\"boolean\"},\"facetCounts\":{\"description\":\"Opt-in per-query facet counts (MG-1010). Name up to 10 facet ids — `brand`, `category`, or spec attribute ids — and the response carries a `facets` block counting each facet's values WITHIN this query's match set. Absent means no `facets` in the response and no counting cost. An id the registry does not know is a 400 naming it (with `didYouMean` candidates); a known but uncountable id lands in `facets.absent` with a reason. Counts follow the same honesty discipline as `count`: ranked (ANN) queries report pool-derived floors labelled `lowerBound` with `poolSize` stated; only ",
69
- "a filter-driven query earns `exact`.\",\"maxItems\":10,\"type\":\"array\",\"items\":{\"type\":\"string\",\"minLength\":1}},\"refinementToken\":{\"description\":\"Opaque five-minute receipt from a prior pool-derived faceted search. Resubmitting it narrows that exact finalized pool instead of running a new candidate source.\",\"type\":\"string\",\"minLength\":1,\"maxLength\":131072},\"hasImages\":{\"default\":false,\"description\":\"When true, only return results that have at least one catalog image (a servable card thumbnail). Off by default so search stays honest about the FULL catalog, including image-less records. It filters the LEXICAL attribute lens — the one source that can surface an image-less record; the visual arms (image / colour / text→image) are inherently image-backed, so the flag is a no-op there. The opt-in `counts.variants` feed total honours it too (image-less records are excluded from the count). Default false.\",\"type\":\"boolean\"},\"sampleableOnly\":{\"description\":\"Require an accepted sampleable=true variant fact before pagination. False or absent leaves the corpus unchanged.\",\"type\":\"boolean\"},\"view\":{\"default\":\"full\",\"description\":\"Result projection. \\\"full\\\" (default) returns the complete `searchResultItemSchema` (palette, colourMeta, hasImage, siblings, explain signals/meanings). \\\"card\\\" returns the lean `searchCardItemSchema` (identity + name + thumbnail + score) for grid rendering, skipping the per-page palette / appearance / meaning reads. The response echoes the chosen `view`, which discriminates the `results` union.\",\"type\":\"string\",\"enum\":[\"full\",\"card\"]},\"corpusScope\":{\"description\":\"Which corpus to answer from. samplizeCatalogue admits published US Samplize paint colours regardless of stock. ABSENT means the WHOLE GRAPH — every material MaterialGraph knows, orderable or not; absence is never a narrower default. Three transaction-oriented scopes gate on governed CHANNEL membership: 'materialBankNaOrderable' (Material Bank North America, about 277,000 materials), 'materialBankEuOrderable' (Material Bank Europe, about 65,000) and 'designShopOrderable' (DesignShop, about 34,000, excluding what DesignShop hides from browse). 'materialBankNaAndCatalogue' is a governed discovery corpus over Material Bank NA or Catalogue editorial membership; it is not a buyability claim, and references alone do not confer membership. 'materialBankOrderable' instead restricts retrieval to materials carrying a Material Bank identity, which is a provenance signal rather than a channel one, and admits about 98% of the graph. Scope is distribution/visibility membership, never suitability; its receipt states whether that means transaction or editorial inclusion. Every scope is applied INSIDE the query: `limit` fills with eligible rows, `counts` describe the SCOPED corpus, and the response echoes the scope under `corpusScope`. This is what post-filtering a returned page cannot give you — a client-side filter shortens the page exactly when the query drifts toward the corpus MaterialGraph carries beyond the Material Bank feed, which is unpredictable from outside. An unknown value is a 400 naming it.\",\"type\":\"string\",\"enum\":[\"all\",\"samplizeCatalogue\",\"materialBankOrderable\",\"materialBankNaOrderable\",\"materialBankNaAndCatalogue\",\"materialBankEuOrderable\",\"designShopOrderable\"]},\"includeSamplingArtifacts\":{\"description\":\"OPS/AUDIT escape hatch — DO NOT use for normal search. Default behaviour (absent or false) EXCLUDES sampling-artifact products (multi-swatch sample cards — orderable samples, never specifiable materials) from every result, count, and candidate pool. Set true only to surface them for ops inspection or an audit.\",\"type\":\"boolean\"}},\"required\":[\"intent\",\"count\",\"hasImages\",\"view\"],\"additionalProperties\":false,\"description\":\"Complete replacement search request. It can be parsed and executed directly; it is not a delta.\"}},\"required\":[\"label\",\"request\"],\"additionalProperties\":false}},\"disambiguation\":{\"description\":\"Present when the query has multiple valid readings — a colour⇄material word ('walnut'), a certification phrase wearing colour words ('red ",
70
- "list free'), or a pinned paint chip that is also a colour to look for across materials, or an exact paint name shared by several makers. The reading that ran carries `isDefault`; for a refused paint collision every reading is false. Every reading is a complete executable request. ABSENT means the query was unambiguous, never that the alternative was checked and found empty.\",\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"enum\":[\"compliance-phrase\",\"colour-material\",\"paint-colour\",\"paint-identity\"],\"description\":\"Governed ambiguity class.\"},\"term\":{\"type\":\"string\",\"minLength\":1,\"description\":\"The ambiguous term or phrase.\"},\"interpretations\":{\"minItems\":2,\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"id\":{\"type\":\"string\",\"minLength\":1,\"description\":\"Stable id of this reading within the clarification; exact paints include maker, identity, and swatch.\"},\"label\":{\"type\":\"string\",\"minLength\":1,\"description\":\"Human-facing name for the reading, e.g. 'the colour walnut'.\"},\"kind\":{\"type\":\"string\",\"minLength\":1,\"description\":\"Short noun for what this reading searches.\"},\"isDefault\":{\"type\":\"boolean\",\"description\":\"True for the reading THIS response ran. Every reading is false when the engine intentionally refused to choose, such as an exact cross-library paint collision.\"},\"hex\":{\"description\":\"Representative swatch, present when the reading is a colour.\",\"type\":\"string\"},\"request\":{\"type\":\"object\",\"properties\":{\"text\":{\"description\":\"Free-text query, matched VISUALLY against material imagery (\\\"striped wallpaper\\\", \\\"warm terrazzo\\\"). A bare colour word (\\\"sage\\\") automatically runs as a colour-only search.\",\"type\":\"string\",\"maxLength\":500},\"intent\":{\"default\":\"search\",\"description\":\"Budget profile for the search primitive. Default \\\"search\\\" is the full engine. \\\"typeahead\\\" is the as-you-type profile: lexical-only with prefix match on the final token, forced `view:\\\"card\\\"`, `limit` capped at 24, and no mood/colour detection tiers, semantic fallback, category boost, secondary scoring, refinement suggestions, counts, or meanings. `textModes` is ignored under typeahead. Recommended wire shape for library typeahead UIs.\",\"type\":\"string\",\"enum\":[\"search\",\"typeahead\"]},\"textModes\":{\"description\":\"How to interpret `text`. Default [\\\"visual\\\"] — a text→image VISUAL search. \\\"lexical\\\" (opt-in) runs a BM25 ATTRIBUTE-lens search over material names + canonical attribute terms (find by what it's called / what it IS); detected colour words are stripped from the lexical terms and retained only as diagnostic or hard-constraint evidence. Requesting BOTH [\\\"visual\\\",\\\"lexical\\\"] runs the visual search and notes it — compose a separate lexical search if both result sets are required. Ignored when `intent` is \\\"typeahead\\\".\",\"type\":\"array\",\"items\":{\"type\":\"string\",\"enum\":[\"visual\",\"lexical\"]}},\"colours\":{\"description\":\"Explicit colour palette to match. Each entry is EITHER a `hex` OR a `name` (a descriptive or brand paint name resolved server-side — never invent a hex for a name). PRECEDENCE: when set, these override any colour word detected in `text`. Unresolvable names fail with a typed error listing them, so you can correct and retry.\",\"maxItems\":8,\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"hex\":{\"description\":\"The colour to match, as 3- or 6-digit hex. Provide EITHER `hex` OR `name`, never both. Only pass `hex` when you already hold an EXACT hex — for a colour NAME use `name` and let the server resolve it.\",\"type\":\"string\",\"pattern\":\"^#?(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$\"},\"name\":{\"description\":\"A colour NAME to match — a descriptive name ('sage green', 'terracotta', 'warm sand') or a brand paint name ('Gauntlet Gray', 'Hale Navy', 'White Dove'). Resolved server-side to its exact library hex (misspellings are fuzzy-matched). NEVER invent a hex for a name — pass the name here. Provide EITHER `name` OR `hex`, never both.\",\"type\":\"string\",\"minLength\":1,\"maxLength\":120},\"weight\":{\"description\":\"Relative proportion of this colour in the desired palette (0–1). Weights are normalised across the palette; omit for equal",
71
- " parts.\",\"type\":\"number\",\"exclusiveMinimum\":0,\"maximum\":1},\"role\":{\"type\":\"string\",\"enum\":[\"any\",\"is\",\"accent\"],\"description\":\"Per-colour swatch eligibility (default 'any'). PRECEDENCE: `colourLogic` governs cross-colour combination (all/any); `role` governs which swatch of a product may satisfy THIS colour, and ANDs on top of the logic. 'is' = the colour must be the product's DOMINANT swatch; 'accent' = the colour must be a minor swatch (proportion 0.05–0.35) and is excluded from the proportion-match term; 'any' = today's nearest-swatch match.\"},\"sourceName\":{\"description\":\"Human name the colour came from (e.g. \\\"sage\\\", \\\"Elephant's Breath\\\") — echoed back for display; never affects matching. (For a name you want RESOLVED to a hex, use `name` instead.)\",\"type\":\"string\",\"maxLength\":120}},\"additionalProperties\":false}},\"colourLogic\":{\"description\":\"\\\"all\\\" (default): results should contain the whole palette, ranked by average distance + proportion match. \\\"any\\\": results matching ANY palette colour, ranked by their best single-colour distance.\",\"type\":\"string\",\"enum\":[\"all\",\"any\"]},\"colourTolerance\":{\"description\":\"How close a colour must be to count as a match. 'normal' (default) is today's behaviour; 'tight' narrows the OKLab match threshold and prunes far single-colour matches; 'loose' widens the threshold.\",\"type\":\"string\",\"enum\":[\"tight\",\"normal\",\"loose\"]},\"colourAppearance\":{\"type\":\"object\",\"properties\":{\"lightness\":{\"description\":\"OKLab lightness window (L ∈ [0,1]); dark ≈ 0, light ≈ 1.\",\"type\":\"object\",\"properties\":{\"min\":{\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"max\":{\"type\":\"number\",\"minimum\":0,\"maximum\":1}},\"required\":[\"min\",\"max\"],\"additionalProperties\":false},\"warmth\":{\"description\":\"OKLab warmth window (b axis); cool < 0 < warm, ≈ −0.1..0.1.\",\"type\":\"object\",\"properties\":{\"min\":{\"type\":\"number\",\"minimum\":-0.5,\"maximum\":0.5},\"max\":{\"type\":\"number\",\"minimum\":-0.5,\"maximum\":0.5}},\"required\":[\"min\",\"max\"],\"additionalProperties\":false}},\"additionalProperties\":false,\"description\":\"HARD appearance gate on the material's DOMINANT colour, independent of the colour palette — works on a text-only query too. Variants without colour extraction are EXCLUDED whenever any axis is set (NULL scalars never match).\"},\"mood\":{\"type\":\"string\",\"enum\":[\"warm\",\"cool\",\"earthy\",\"moody\",\"bold\",\"light-airy\",\"natural\",\"neutral\",\"romantic\",\"coastal\",\"industrial\",\"luxe\",\"vintage\",\"serene\",\"scandi\",\"cosy\",\"heritage\",\"mid-century\",\"pastel\"],\"description\":\"Mood-language colour entry — resolves to a curated checked-in colour palette. Use it for evocative mood queries ('coastal', 'moody', 'earthy') rather than a single colour. A mood ALONE runs as a colour search; with text it remains diagnostic or hard-constraint evidence and does not blend into the text mode's ordering score. One of: warm (Warm — Reds, oranges, yellows - inviting and cozy); cool (Cool — Blues, teals, cool grays - calm and professional); earthy (Earthy — Browns, tans, terracotta - natural and grounded); moody (Moody — Deep, saturated, dramatic - rich and atmospheric); bold (Bold — High chroma, vibrant - energetic and eye-catching); light-airy (Light + Airy — Pastels, pale tones - soft and spacious); natural (Natural — Greens, wood tones, stone - organic and balanced); neutral (Neutral — Whites, grays, blacks - versatile and timeless); romantic (Romantic — Soft pinks, mauves, blush - tender and elegant); coastal (Coastal — Sandy beiges, ocean blues, seafoam - relaxed seaside); industrial (Industrial — Cool grays, concrete, metallic - urban and modern); luxe (Luxe — Deep jewel tones, rich emeralds, sapphires - opulent); vintage (Vintage — Muted, dusty tones - nostalgic and timeworn); serene (Serene — Soft blues, sage, muted lavender - calm and peaceful); scandi (Scandi — Warm whites, pale woods, cream - Scandinavian minimalism); cosy (Cosy — Deep plaster, amber, tobacco - enveloping warmth); heritage (Heritage — Rich historical tones with saturated depth - classic and grounded); mid-century (Mid-Century — Olive, mustard, teal, terracotta - retro modern); pastel (Pastel — Sage,",
72
- " duck egg, blush - soft colors with body).\"},\"sort\":{\"type\":\"string\",\"enum\":[\"relevance\",\"light\",\"dark\",\"vivid\",\"muted\",\"colourful\",\"name\",\"name_desc\",\"recent\"],\"description\":\"Re-order the final scored results (default 'relevance' = the selected mode's primary score). Two families. COLOUR RAMPS: 'light'/'dark' by dominant lightness, 'vivid'/'muted' by chroma, 'colourful' by overall vibrancy — variants without colour extraction sink to the end. CATALOGUE ORDERS: 'name' (A→Z), 'name_desc' (Z→A) and 'recent' (newest first), which read the same `name` / `created_at` columns, in the same directions, that /api/v1/search/browse orders on — one word, one order, on both lanes. Every sort REORDERS the pool the search signals already retrieved; it never widens or substitutes for one. Retired materials sort below visible ones under every non-relevance sort. For an unranked walk of the whole corpus in any of these orders, use the browse surface instead.\"},\"orderBy\":{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"minLength\":1,\"description\":\"The attribute to sort by. Only attributes with a DECLARED value ladder can be ordered — ordering is never inferred from a value's spelling — and anything else is refused with the orderable set named. Orderable today: price_tier.\"},\"direction\":{\"default\":\"asc\",\"description\":\"'asc' (default) walks the ladder from its first rung.\",\"type\":\"string\",\"enum\":[\"asc\",\"desc\"]}},\"required\":[\"attributeId\",\"direction\"],\"additionalProperties\":false,\"description\":\"Sort results along ONE attribute's declared ladder — a separate axis from `sort`, which stays the closed relevance/colour/catalogue vocabulary. Sent together, `orderBy` wins and the response `note` says so. A variant whose value is off the ladder, or which carries no value at all, sorts LAST in both directions: it has no rung, and it is still a real row, so it is placed at the end rather than dropped or guessed at.\"},\"returnAttributes\":{\"maxItems\":8,\"type\":\"array\",\"items\":{\"type\":\"string\",\"minLength\":1},\"description\":\"Attribute ids whose RESOLVED VALUE should ride each result, under `attributes` keyed by attribute id. Values resolve with the normal variant-over-product precedence. THREE STATES, and they are different: an attribute you did not ask for is not a key at all; an attribute you asked for that this variant carries no accepted value for is `null`; anything else is the value (a string, or an array for a multi-valued attribute). An unknown attribute id is refused naming it, never silently dropped. Pair it with `filters` to show what you filtered on.\"},\"similarTo\":{\"description\":\"Find materials that LOOK like a seed image. Supply `variantId` to seed from an existing variant's primary image, `materialBankId` to seed from a Material Bank SKU — the ONE EXTERNAL identifier this API accepts, so a caller already holding a SKU needs no resolve round trip — or `vector`, a pre-computed halfvec literal (`[a,b,…]`) from an externally-embedded image, to drive the image arm directly. A `materialBankId` that names no variant is a 400 naming the offending value, never an empty result set and never an unseeded search. PRECEDENCE: wins over `text` (the response carries a note when both were given).\",\"anyOf\":[{\"type\":\"object\",\"properties\":{\"variantId\":{\"type\":\"string\"}},\"required\":[\"variantId\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"materialBankId\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"materialBankId\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"vector\":{\"type\":\"string\"}},\"required\":[\"vector\"],\"additionalProperties\":false}]},\"excludeVariantIds\":{\"description\":\"Canonical variant ids that must not appear in this result. Applied before the returned page is cut, so the page refills from the deeper ranked pool instead of being shortened by client-side filtering. IDs are opaque; duplicates are harmless and deduplicated by the engine.\",\"maxItems\":256,\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"image\":{\"description\":\"A raw incoming image to search by (URL or base64). The HTTP adapter embeds it and sets `similarTo",
73
- ": { vector }`; wins over `text`.\",\"oneOf\":[{\"type\":\"object\",\"properties\":{\"type\":{\"type\":\"string\",\"const\":\"url\"},\"url\":{\"type\":\"string\",\"format\":\"uri\",\"description\":\"HTTP(S) URL of the query image. MUST be an https URL.\"}},\"required\":[\"type\",\"url\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"type\":{\"type\":\"string\",\"const\":\"base64\"},\"data\":{\"type\":\"string\",\"minLength\":1,\"description\":\"Base64-encoded image bytes (or a data URI) the caller already holds — e.g. a form upload — so there is nothing to host.\"}},\"required\":[\"type\",\"data\"],\"additionalProperties\":false}],\"type\":\"object\"},\"category\":{\"type\":\"object\",\"properties\":{\"boost\":{\"description\":\"Soft category preference — results of this product type score higher but others still appear.\",\"type\":\"string\"},\"filter\":{\"description\":\"HARD gate: only these public taxonomy categories are returned. Values are category-group or product-type display names (case-insensitive), or canonical taxonomy keys. A group expands to every child product type; an unknown value errors listing every valid name.\",\"type\":\"array\",\"items\":{\"type\":\"string\"}}},\"additionalProperties\":false},\"weights\":{\"type\":\"object\",\"properties\":{\"image\":{\"type\":\"number\",\"minimum\":0},\"category\":{\"type\":\"number\",\"minimum\":0},\"colour\":{\"type\":\"number\",\"minimum\":0},\"lexical\":{\"type\":\"number\",\"minimum\":0},\"text\":{\"type\":\"number\",\"minimum\":0}},\"additionalProperties\":false,\"description\":\"Optional multiplier for the selected mode's one ordering signal. At most one applied weight is non-zero: colour→colour, lexical/entity-browse→lexical, variant-text→portrait, image-text/image→image, and filter-only→none. Overrides may scale only that live signal and cannot activate any other. Other similarities are diagnostic or hard-gate receipts and do not blend into score.\"},\"filters\":{\"description\":\"Typed filters applied as a hard restriction before ranking. Each entry is a term match (`attributeId` + `term`), a term SET (`attributeId` + `terms` — ANY one of them satisfies it), a presence check (`attributeId` + `hasValue: true`), or a numeric range (`attributeId` + `min`/`max`). AND/OR: entries AND together, and OR is expressed WITHIN one entry via `terms`. BRAND: `attributeId: \\\"brand\\\"` filters on the brand RELATION — a join every one of the catalogue's variants carries — taking a brand slug or display name, exactly the `value` a `brand` facet reports. Do NOT use the `brand_name` ATTRIBUTE for this: it is a sparse spec value present on about 1.5% of variants and absent entirely from every catalog-ingested brand, so filtering on it returns a near-empty page that reads as a corpus gap. Presence filters always route as hard spec gates. Aesthetic/meaning term filters (pattern, texture, directionality, and the other visual-meaning attributes) are applied as a pool-bounded post-filter over the visual candidate pool rather than a whole-catalog restriction. Each entry may carry a `trust` bar the matching value must clear.\",\"type\":\"array\",\"items\":{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, e.g. \\\"primary_material\\\" or \\\"fire_rating\\\" — or the special id \\\"brand\\\", which filters on the brand RELATION (a join, carried by every variant) rather than on any attribute.\"},\"term\":{\"type\":\"string\",\"description\":\"Canonical term the attribute must equal, e.g. \\\"textile\\\". For \\\"brand\\\", a brand slug (\\\"kvadrat\\\") or display name (\\\"Kvadrat\\\"), case-insensitive — exactly the `value` a `brand` facet reports. An unrecognised brand is a 400 naming it, never an empty page.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met",
74
- " vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"term\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, or the special id \\\"brand\\\" for the brand relation.\"},\"terms\":{\"minItems\":1,\"type\":\"array\",\"items\":{\"type\":\"string\"},\"description\":\"Canonical terms, ANY ONE of which satisfies the filter — this is how OR is expressed. OR lives inside one entry; separate `filters[]` entries AND together. Used for ordinal scales, where a stricter class satisfies a looser one, and for a multi-select rail — `{attributeId:\\\"brand\\\", terms:[\\\"kvadrat\\\",\\\"rubelli\\\"]}` returns materials from either brand.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"terms\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\"},\"excludesTerm\":{\"type\":\"string\",\"minLength\":1},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"excludesTerm\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id that must have an accepted value, e.g. \\\"sample_image_mm_per_px\\\".\"},\"hasValue\":{\"type\":\"boolean\",\"const\":true,\"description\":\"Require any accepted value for this attribute.\"},\"asOf\":{\"description\":\"Reference date for EPD expiration, exclusive: the value must remain valid after this date.\",\"type\":\"string\",\"format\":\"date\",\"pattern\":\"^(?:(?:\\\\d\\\\d[2468][048]|\\\\d\\\\d[13579][26]|\\\\d\\\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\\\d|30)|(?:02)-(?:0[1-9]|1\\\\d|2[0-8])))$\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"hasValue\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id holding a NUMBER, e.g. \\\"light_reflectance_value\\\" or \\\"lead_time_days\\\".\"},\"min\":{\"description\":\"Inclusive lower bound. Omit for a ceiling-only range.\",\"type\":\"number\"},\"max\":{\"description\":\"Inclusive upper bound. Omit for a floor-only range.\",\"type\":\"number\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, deri",
75
- "ved).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\"],\"additionalProperties\":false}]}},\"preferences\":{\"description\":\"SOFT spec criteria — rank-never-exclude. Same shape as `filters`, but a preference NEVER removes a result: each hit's `verdict` gains a `preferred` cell, and a failed/missing preferred cell only DEMOTES the hit WITHIN its page (the cursor is from the pre-demotion order).\",\"maxItems\":8,\"type\":\"array\",\"items\":{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, e.g. \\\"primary_material\\\" or \\\"fire_rating\\\" — or the special id \\\"brand\\\", which filters on the brand RELATION (a join, carried by every variant) rather than on any attribute.\"},\"term\":{\"type\":\"string\",\"description\":\"Canonical term the attribute must equal, e.g. \\\"textile\\\". For \\\"brand\\\", a brand slug (\\\"kvadrat\\\") or display name (\\\"Kvadrat\\\"), case-insensitive — exactly the `value` a `brand` facet reports. An unrecognised brand is a 400 naming it, never an empty page.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"term\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, or the special id \\\"brand\\\" for the brand relation.\"},\"terms\":{\"minItems\":1,\"type\":\"array\",\"items\":{\"type\":\"string\"},\"description\":\"Canonical terms, ANY ONE of which satisfies the filter — this is how OR is expressed. OR lives inside one entry; separate `filters[]` entries AND together. Used for ordinal scales, where a stricter class satisfies a looser one, and for a multi-select rail — `{attributeId:\\\"brand\\\", terms:[\\\"kvadrat\\\",\\\"rubelli\\\"]}` returns materials from either brand.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"terms\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\"},\"excludesTerm\":{\"type\":\"string\",\"minLength\":1},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"excludesTerm\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id that must have an ",
76
- "accepted value, e.g. \\\"sample_image_mm_per_px\\\".\"},\"hasValue\":{\"type\":\"boolean\",\"const\":true,\"description\":\"Require any accepted value for this attribute.\"},\"asOf\":{\"description\":\"Reference date for EPD expiration, exclusive: the value must remain valid after this date.\",\"type\":\"string\",\"format\":\"date\",\"pattern\":\"^(?:(?:\\\\d\\\\d[2468][048]|\\\\d\\\\d[13579][26]|\\\\d\\\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\\\d|30)|(?:02)-(?:0[1-9]|1\\\\d|2[0-8])))$\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"hasValue\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id holding a NUMBER, e.g. \\\"light_reflectance_value\\\" or \\\"lead_time_days\\\".\"},\"min\":{\"description\":\"Inclusive lower bound. Omit for a ceiling-only range.\",\"type\":\"number\"},\"max\":{\"description\":\"Inclusive upper bound. Omit for a floor-only range.\",\"type\":\"number\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\"],\"additionalProperties\":false}]}},\"verdicts\":{\"description\":\"Opt-in seed-relative verdict cells. Only meaningful with `similarTo: { variantId }`: costs one seed fetch + one page read to diff each hit's accepted COMPARABLE attributes (numeric + a colour axis) against the seed's, emitting `preferred` `verdict` cells with directional deviations (`above`/`below`, e.g. 2 mm thicker, warmer). Ignored on the default path; a `similarTo: { vector }` / raw-image seed carries no seed variant, so the response notes that seed cells were skipped.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Results per page (default 24, max 48).\",\"type\":\"integer\",\"minimum\":1,\"maximum\":48},\"cursor\":{\"description\":\"Opaque pagination cursor from a previous response. NOTE: ANN search is approximate — tail items may reorder slightly between pages.\",\"type\":\"string\"},\"explain\":{\"description\":\"Include per-signal scores on each result (`signals`).\",\"type\":\"boolean\"},\"collapse\":{\"description\":\"Group results by product before pagination. \\\"product\\\" returns one representative (highest-ranked) variant per product, with hidden siblings counted in `siblingCount`. The engine and GET adapter default to \\\"none\\\"; public POST requests default to \\\"product\\\" and may request \\\"none\\\" explicitly for variant-grain results.\",\"type\":\"string\",\"enum\":[\"none\",\"product\"]},\"count\":{\"default\":false,\"description\":\"Opt-in feed total. When true the response carries `counts.variants` (and `counts.products` under `collapse:\\\"product\\\"`). The variant total is EXACT up to a cap of 1000 and reported as `basis:\\\"lowerBound\\\"` beyond it — and ALWAYS `lowerBound` for ANN-ranked pools (image / colour / visual-text / visual-meaning post-filters), where the finite candidate pool is the only honest figure. Counts are never silently approximated: an exact count is labelled `exact`, a capped or pool-bounded one `lowerBound`. Default false (skip the count query entirely).\",\"t",
77
- "ype\":\"boolean\"},\"facetCounts\":{\"description\":\"Opt-in per-query facet counts (MG-1010). Name up to 10 facet ids — `brand`, `category`, or spec attribute ids — and the response carries a `facets` block counting each facet's values WITHIN this query's match set. Absent means no `facets` in the response and no counting cost. An id the registry does not know is a 400 naming it (with `didYouMean` candidates); a known but uncountable id lands in `facets.absent` with a reason. Counts follow the same honesty discipline as `count`: ranked (ANN) queries report pool-derived floors labelled `lowerBound` with `poolSize` stated; only a filter-driven query earns `exact`.\",\"maxItems\":10,\"type\":\"array\",\"items\":{\"type\":\"string\",\"minLength\":1}},\"refinementToken\":{\"description\":\"Opaque five-minute receipt from a prior pool-derived faceted search. Resubmitting it narrows that exact finalized pool instead of running a new candidate source.\",\"type\":\"string\",\"minLength\":1,\"maxLength\":131072},\"hasImages\":{\"default\":false,\"description\":\"When true, only return results that have at least one catalog image (a servable card thumbnail). Off by default so search stays honest about the FULL catalog, including image-less records. It filters the LEXICAL attribute lens — the one source that can surface an image-less record; the visual arms (image / colour / text→image) are inherently image-backed, so the flag is a no-op there. The opt-in `counts.variants` feed total honours it too (image-less records are excluded from the count). Default false.\",\"type\":\"boolean\"},\"sampleableOnly\":{\"description\":\"Require an accepted sampleable=true variant fact before pagination. False or absent leaves the corpus unchanged.\",\"type\":\"boolean\"},\"view\":{\"default\":\"full\",\"description\":\"Result projection. \\\"full\\\" (default) returns the complete `searchResultItemSchema` (palette, colourMeta, hasImage, siblings, explain signals/meanings). \\\"card\\\" returns the lean `searchCardItemSchema` (identity + name + thumbnail + score) for grid rendering, skipping the per-page palette / appearance / meaning reads. The response echoes the chosen `view`, which discriminates the `results` union.\",\"type\":\"string\",\"enum\":[\"full\",\"card\"]},\"corpusScope\":{\"description\":\"Which corpus to answer from. samplizeCatalogue admits published US Samplize paint colours regardless of stock. ABSENT means the WHOLE GRAPH — every material MaterialGraph knows, orderable or not; absence is never a narrower default. Three transaction-oriented scopes gate on governed CHANNEL membership: 'materialBankNaOrderable' (Material Bank North America, about 277,000 materials), 'materialBankEuOrderable' (Material Bank Europe, about 65,000) and 'designShopOrderable' (DesignShop, about 34,000, excluding what DesignShop hides from browse). 'materialBankNaAndCatalogue' is a governed discovery corpus over Material Bank NA or Catalogue editorial membership; it is not a buyability claim, and references alone do not confer membership. 'materialBankOrderable' instead restricts retrieval to materials carrying a Material Bank identity, which is a provenance signal rather than a channel one, and admits about 98% of the graph. Scope is distribution/visibility membership, never suitability; its receipt states whether that means transaction or editorial inclusion. Every scope is applied INSIDE the query: `limit` fills with eligible rows, `counts` describe the SCOPED corpus, and the response echoes the scope under `corpusScope`. This is what post-filtering a returned page cannot give you — a client-side filter shortens the page exactly when the query drifts toward the corpus MaterialGraph carries beyond the Material Bank feed, which is unpredictable from outside. An unknown value is a 400 naming it.\",\"type\":\"string\",\"enum\":[\"all\",\"samplizeCatalogue\",\"materialBankOrderable\",\"materialBankNaOrderable\",\"materialBankNaAndCatalogue\",\"materialBankEuOrderable\",\"designShopOrderable\"]},\"includeSamplingArtifacts\":{\"description\":\"OPS/AUDIT escape hatch — DO NOT use for normal search. Default behaviour (absent or false) EXCLUDES sampling-artifact products (mu",
78
- "lti-swatch sample cards — orderable samples, never specifiable materials) from every result, count, and candidate pool. Set true only to surface them for ops inspection or an audit.\",\"type\":\"boolean\"}},\"required\":[\"intent\",\"count\",\"hasImages\",\"view\"],\"additionalProperties\":false,\"description\":\"Complete replacement search request that runs this reading. Executable directly; it is not a delta.\"}},\"required\":[\"id\",\"label\",\"kind\",\"isDefault\",\"request\"],\"additionalProperties\":false},\"description\":\"Every valid reading. The one that ran is flagged `isDefault`; all are false when the engine intentionally chose none. Each is a complete executable request.\"}},\"required\":[\"kind\",\"term\",\"interpretations\"],\"additionalProperties\":false},\"supersededMoodId\":{\"description\":\"A mood term detected in the query but overridden by a stronger explicit colour reading. Absent means no mood was superseded, not that mood detection ran and found none.\",\"type\":\"string\",\"enum\":[\"warm\",\"cool\",\"earthy\",\"moody\",\"bold\",\"light-airy\",\"natural\",\"neutral\",\"romantic\",\"coastal\",\"industrial\",\"luxe\",\"vintage\",\"serene\",\"scandi\",\"cosy\",\"heritage\",\"mid-century\",\"pastel\"]},\"moodInterpretation\":{\"type\":\"object\",\"properties\":{\"query\":{\"description\":\"The original query text, or null without text.\",\"type\":[\"string\",\"null\"]},\"moodId\":{\"anyOf\":[{\"type\":\"string\",\"enum\":[\"warm\",\"cool\",\"earthy\",\"moody\",\"bold\",\"light-airy\",\"natural\",\"neutral\",\"romantic\",\"coastal\",\"industrial\",\"luxe\",\"vintage\",\"serene\",\"scandi\",\"cosy\",\"heritage\",\"mid-century\",\"pastel\"],\"description\":\"Mood-language colour entry — resolves to a curated checked-in colour palette. Use it for evocative mood queries ('coastal', 'moody', 'earthy') rather than a single colour. A mood ALONE runs as a colour search; with text it remains diagnostic or hard-constraint evidence and does not blend into the text mode's ordering score. One of: warm (Warm — Reds, oranges, yellows - inviting and cozy); cool (Cool — Blues, teals, cool grays - calm and professional); earthy (Earthy — Browns, tans, terracotta - natural and grounded); moody (Moody — Deep, saturated, dramatic - rich and atmospheric); bold (Bold — High chroma, vibrant - energetic and eye-catching); light-airy (Light + Airy — Pastels, pale tones - soft and spacious); natural (Natural — Greens, wood tones, stone - organic and balanced); neutral (Neutral — Whites, grays, blacks - versatile and timeless); romantic (Romantic — Soft pinks, mauves, blush - tender and elegant); coastal (Coastal — Sandy beiges, ocean blues, seafoam - relaxed seaside); industrial (Industrial — Cool grays, concrete, metallic - urban and modern); luxe (Luxe — Deep jewel tones, rich emeralds, sapphires - opulent); vintage (Vintage — Muted, dusty tones - nostalgic and timeworn); serene (Serene — Soft blues, sage, muted lavender - calm and peaceful); scandi (Scandi — Warm whites, pale woods, cream - Scandinavian minimalism); cosy (Cosy — Deep plaster, amber, tobacco - enveloping warmth); heritage (Heritage — Rich historical tones with saturated depth - classic and grounded); mid-century (Mid-Century — Olive, mustard, teal, terracotta - retro modern); pastel (Pastel — Sage, duck egg, blush - soft colors with body).\"},{\"type\":\"null\"}],\"description\":\"The recognized mood id, or null when none was selected.\"},\"supersededMoodId\":{\"anyOf\":[{\"type\":\"string\",\"enum\":[\"warm\",\"cool\",\"earthy\",\"moody\",\"bold\",\"light-airy\",\"natural\",\"neutral\",\"romantic\",\"coastal\",\"industrial\",\"luxe\",\"vintage\",\"serene\",\"scandi\",\"cosy\",\"heritage\",\"mid-century\",\"pastel\"],\"description\":\"Mood-language colour entry — resolves to a curated checked-in colour palette. Use it for evocative mood queries ('coastal', 'moody', 'earthy') rather than a single colour. A mood ALONE runs as a colour search; with text it remains diagnostic or hard-constraint evidence and does not blend into the text mode's ordering score. One of: warm (Warm — Reds, oranges, yellows - inviting and cozy); cool (Cool — Blues, teals, cool grays - calm and professional); earthy (Earthy — Browns, tans, terracotta - natural and grounded); moody (Moody — Deep, saturated, dram",
79
- "atic - rich and atmospheric); bold (Bold — High chroma, vibrant - energetic and eye-catching); light-airy (Light + Airy — Pastels, pale tones - soft and spacious); natural (Natural — Greens, wood tones, stone - organic and balanced); neutral (Neutral — Whites, grays, blacks - versatile and timeless); romantic (Romantic — Soft pinks, mauves, blush - tender and elegant); coastal (Coastal — Sandy beiges, ocean blues, seafoam - relaxed seaside); industrial (Industrial — Cool grays, concrete, metallic - urban and modern); luxe (Luxe — Deep jewel tones, rich emeralds, sapphires - opulent); vintage (Vintage — Muted, dusty tones - nostalgic and timeworn); serene (Serene — Soft blues, sage, muted lavender - calm and peaceful); scandi (Scandi — Warm whites, pale woods, cream - Scandinavian minimalism); cosy (Cosy — Deep plaster, amber, tobacco - enveloping warmth); heritage (Heritage — Rich historical tones with saturated depth - classic and grounded); mid-century (Mid-Century — Olive, mustard, teal, terracotta - retro modern); pastel (Pastel — Sage, duck egg, blush - soft colors with body).\"},{\"type\":\"null\"}],\"description\":\"A recognized mood displaced by a stronger colour reading.\"}},\"required\":[\"query\",\"moodId\",\"supersededMoodId\"],\"additionalProperties\":false,\"description\":\"Mood interpretation receipt. Present when the standard search evaluated mood; null ids explicitly mean no mood was selected. Absent means mood was not evaluated or the response came from an older server.\"},\"mode\":{\"type\":\"string\",\"enum\":[\"image\",\"colour\",\"text\",\"lexical\",\"entity-browse\"],\"description\":\"The primary ranking source the query resolved to: 'image' (look-alike), 'colour' (the colour arm — pure), 'text' (visual text→image, the default), 'lexical' (BM25 over the attribute/name projection), or 'entity-browse' (the query names a brand and nothing else, so the brand's own catalogue in browse order is returned instead of a similarity ranking). A fully governed hard form + colour query filters the primary source's one candidate pool; that source still owns order and remains the reported mode.\"},\"textLane\":{\"type\":\"string\",\"enum\":[\"image-text\",\"variant-text\"],\"description\":\"Which dense text lane answered: 'image-text' (the query carries evidence it describes appearance, so it is ranked against image vectors) or 'variant-text' (ranked against each variant's written portrait). Absent when the answer did not come from the dense text arm — absence is unknown, not a default.\"},\"rerank\":{\"type\":\"object\",\"properties\":{\"colour\":{\"type\":\"string\",\"enum\":[\"ok\",\"failed\",\"skipped\"]},\"colourWeight\":{\"type\":\"number\"},\"colourLabel\":{\"type\":\"string\"}},\"additionalProperties\":false},\"colourConstraint\":{\"type\":\"object\",\"properties\":{\"basis\":{\"type\":\"string\",\"enum\":[\"explicit\",\"detected-strong\"]},\"family\":{\"type\":\"string\",\"minLength\":1},\"queryHexes\":{\"minItems\":1,\"type\":\"array\",\"items\":{\"type\":\"string\",\"pattern\":\"^#[0-9a-f]{6}$\"}},\"considered\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"kept\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"dropped\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"unknown\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"adjacent\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"abundance\":{\"type\":\"object\",\"properties\":{\"grain\":{\"type\":\"string\",\"enum\":[\"variant\",\"product\"]},\"requested\":{\"type\":\"integer\",\"exclusiveMinimum\":0,\"maximum\":9007199254740991},\"eligible\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"candidateWindow\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"sourceExhausted\":{\"type\":\"boolean\"},\"state\":{\"type\":\"string\",\"enum\":[\"filled\",\"corpus_scarcity\",\"evidence_unavailable\",\"retrieval_window_exhausted\"]}},\"required\":[\"grain\",\"requested\",\"eligible\",\"candidateWindow\",\"sourceExhausted\",\"state\"],\"additionalProperties\":false}},\"required\":[\"basis\",\"family\",\"queryHexes\",\"considered\",\"kept\",\"dropped\",\"unknown\",\"adjacent\"],\"additionalProperties\":false,\"description\":\"Eligibility for a compound text colour: counts cover the bounded candidate pool, `adjacent` pass",
80
- "ed ColorScope hue compatibility, and missing colour evidence failed closed. Absent means colour was not a hard constraint.\"},\"formConstraint\":{\"type\":\"object\",\"properties\":{\"instruction\":{\"type\":\"string\",\"enum\":[\"subway\",\"plank\",\"square\",\"hexagon\",\"penny-round\"]},\"considered\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"kept\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"dropped\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"contradicted\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"unknown\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"abundance\":{\"type\":\"object\",\"properties\":{\"grain\":{\"type\":\"string\",\"enum\":[\"variant\",\"product\"]},\"requested\":{\"type\":\"integer\",\"exclusiveMinimum\":0,\"maximum\":9007199254740991},\"eligible\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"candidateWindow\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"sourceExhausted\":{\"type\":\"boolean\"},\"state\":{\"type\":\"string\",\"enum\":[\"filled\",\"corpus_scarcity\",\"evidence_unavailable\",\"retrieval_window_exhausted\"]}},\"required\":[\"grain\",\"requested\",\"eligible\",\"candidateWindow\",\"sourceExhausted\",\"state\"],\"additionalProperties\":false}},\"required\":[\"instruction\",\"considered\",\"kept\",\"dropped\",\"contradicted\",\"unknown\",\"abundance\"],\"additionalProperties\":false,\"description\":\"Hard form eligibility for a compound text query. A candidate is kept only when governed size or body-type evidence supports the requested form; product and variant names are never proof.\"},\"maxScore\":{\"type\":\"number\"},\"counts\":{\"description\":\"Feed totals, present when `count:true` was requested OR when `collapse:\\\"product\\\"` grouped the results. `variants` is the total matching variants; `products` (only under `collapse`) the distinct product groups. Each total carries a `basis`: `exact` (true count, up to the cap of 1000) or `lowerBound` (a floor — the cap was hit, an ANN-ranked pool bounds it, or the count degraded). The lexical arm counts exactly via the tsvector predicate; a single accepted spec term reads the `mv_attribute_facet_counts` materialized view (refreshed post-ingest, so a hair stale by design); ANN pools (image / colour / visual-text) and `products` are always `lowerBound`. The two totals describe the same result set, so `products` never exceeds `variants`: both are measured against the pool this page was drawn from, and a `variants` total that came out below that pool is reported as the pool-bounded floor instead — it counted a narrower universe than the results beside it.\",\"type\":\"object\",\"properties\":{\"variants\":{\"type\":\"object\",\"properties\":{\"value\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"basis\":{\"type\":\"string\",\"enum\":[\"exact\",\"lowerBound\"]}},\"required\":[\"value\",\"basis\"],\"additionalProperties\":false},\"products\":{\"type\":\"object\",\"properties\":{\"value\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"basis\":{\"type\":\"string\",\"enum\":[\"exact\",\"lowerBound\"]}},\"required\":[\"value\",\"basis\"],\"additionalProperties\":false}},\"required\":[\"variants\"],\"additionalProperties\":false},\"facets\":{\"description\":\"Per-query facet counts (MG-1010), present ONLY when the request sent `facetCounts`. Each requested facet's values are counted WITHIN this query's match set — a ranked query reports pool-derived floors (`basis:\\\"lowerBound\\\"`, `poolSize` stated), a filter-driven query true counts (`basis:\\\"exact\\\"`). See the `facetCounts` input field.\",\"type\":\"object\",\"properties\":{\"poolSize\":{\"description\":\"Present when the counts are POOL-DERIVED (every ranked query, and any filter-only query whose exact aggregation degraded): the size of the finalised candidate pool the per-value figures were counted over. Absent when the counts are exact under the query's whole predicate.\",\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"requested\":{\"type\":\"array\",\"items\":{\"type\":\"string\",\"minLength\":1},\"description\":\"The facet ids the request asked for, echoed verbatim.\"},\"absent\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"facetId\":{\"type\":\"string\",\"minLength\":1,\"description\":\"The ",
81
- "requested facet id, verbatim.\"},\"reason\":{\"type\":\"string\",\"enum\":[\"unsupported\",\"timeout\"],\"description\":\"Why no counts were computed. `unsupported` — the id is KNOWN (a real registry attribute or virtual facet) but facet counting does not cover it: numeric attributes, non-facetable attributes, and (v1) the virtual certification programme. `timeout` — counting did not complete inside the facet stage's own time budget; the RESULTS are unaffected (facet counting can never take the page down), only the counts are missing. An id the registry has never heard of is a 400 naming it, never an `absent` entry.\"}},\"required\":[\"facetId\",\"reason\"],\"additionalProperties\":false},\"description\":\"Requested facets that were not counted, each with its reason — `unsupported` (the id is known but not countable) or `timeout` (the facet stage's own budget expired; the results are unaffected). Empty when every requested facet was counted.\"},\"counts\":{\"type\":\"object\",\"propertyNames\":{\"type\":\"string\"},\"additionalProperties\":{\"type\":\"object\",\"properties\":{\"values\":{\"maxItems\":25,\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"value\":{\"type\":\"string\",\"minLength\":1,\"description\":\"The CANONICAL token — a brand slug, a taxonomy product-type id, or a spec attribute's canonical term. Send it back verbatim as a filter term; never display-derive it.\"},\"label\":{\"type\":\"string\",\"minLength\":1,\"description\":\"Display label for the value — the brand's name, the product type's display name, or the registry dictionary label for a spec term. Falls back to the canonical token when the registry carries no label.\"},\"variants\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991,\"description\":\"Variants carrying this value within the counted set.\"}},\"required\":[\"value\",\"label\",\"variants\"],\"additionalProperties\":false},\"description\":\"Top values by variant count, at most 25. An empty list is a facet with no values in the counted set, not an error.\"},\"truncated\":{\"type\":\"boolean\",\"description\":\"ALWAYS present. True when more distinct values existed beyond the reported list; false when the list is complete. Never absent-or-false.\"},\"basis\":{\"type\":\"string\",\"enum\":[\"exact\",\"lowerBound\"],\"description\":\"Exactly \\\"exact\\\" or \\\"lowerBound\\\" — nothing else, ever. `exact`: each `variants` figure is the true count under the query's whole predicate (filter-driven requests only). `lowerBound`: the figures were counted over the ranked candidate pool (`facets.poolSize`), so each is a floor of the true per-value total. Pool-counted values are NEVER `exact`.\"}},\"required\":[\"values\",\"truncated\",\"basis\"],\"additionalProperties\":false},\"description\":\"Per-facet value counts, keyed by the requested facet id. Every requested facet that is not in `absent` has an entry, including facets whose counted set held no values (`values: []`).\"}},\"required\":[\"requested\",\"absent\",\"counts\"],\"additionalProperties\":false},\"corpusScope\":{\"description\":\"The corpus this page AND its `counts` were drawn from — present only when the request narrowed it. ABSENT means the whole graph. Read it with `counts`: under a scope a short page is a small SCOPED corpus, not a truncated one.\",\"type\":\"object\",\"properties\":{\"scope\":{\"type\":\"string\",\"enum\":[\"all\",\"samplizeCatalogue\",\"materialBankOrderable\",\"materialBankNaOrderable\",\"materialBankNaAndCatalogue\",\"materialBankEuOrderable\",\"designShopOrderable\"],\"description\":\"Which corpus to answer from. samplizeCatalogue admits published US paint colours regardless of stock. ABSENT (or 'all') means the WHOLE GRAPH — every material MaterialGraph knows, orderable or not; absence is never a narrower default. Three transaction-oriented scopes gate on governed CHANNEL membership: 'materialBankNaOrderable' (Material Bank North America, ~277,000 materials), 'materialBankEuOrderable' (Material Bank Europe, ~65,000) and 'designShopOrderable' (DesignShop, ~34,000, with the products DesignShop hides from browse excluded). 'materialBankNaAndCatalogue' is the one-query union of Material Bank North America and the governed Catalogue editorial discovery corpus; that union does not by it",
82
- "self claim every member is transactable. 'materialBankOrderable' is NOT one of them: it selects materials carrying a Material Bank identity, which is a provenance signal rather than a channel one, and it admits 98.4% of the graph — use it to exclude crawl-sourced brands, not to mean orderable. A scope answers membership in its named governed corpus, never suitability: scope on channel, then refine on sector. Every scope is applied INSIDE the query: `limit` fills with eligible rows and `counts` describe the scoped corpus, which post-filtering a page cannot give you. An unknown value is a 400 naming it, never a silent fall back to the whole graph.\"},\"description\":{\"type\":\"string\",\"description\":\"What the scope admits, in one sentence, for a human reader.\"}},\"required\":[\"scope\",\"description\"],\"additionalProperties\":false},\"effectiveColours\":{\"description\":\"The `#rrggbb` colours the colour signal ACTUALLY ran on, after minor-proportion accents were dropped (MG-1549). Present whenever the request carried explicit `colours`. Compare against your request: a difference means the search that ran is not the search that was asked for.\",\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"droppedColours\":{\"description\":\"Colours removed from the query for carrying a `weight` below the 5% accent threshold. EMPTY is the normal case. Non-empty and the dropped colours are absent from colour retrieval and ranking entirely — they did not merely rank low. To keep a small accent in the query, give it `role: \\\"accent\\\"`.\",\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"note\":{\"type\":\"string\"},\"emptyOutcome\":{\"description\":\"Engine-authored explanation for an empty page: the strongest observed origin plus retrieved, after-prune, and final counts. Present only under explain.\",\"oneOf\":[{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"const\":\"proven-corpus-empty\"},\"basis\":{\"type\":\"string\",\"enum\":[\"spec-allowlist\",\"brand-scope\"]},\"exhaustive\":{\"type\":\"boolean\",\"const\":true},\"sourceEmpty\":{\"type\":[\"boolean\",\"null\"]},\"counts\":{\"type\":\"object\",\"properties\":{\"retrieved\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"afterPrune\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"final\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991}},\"required\":[\"retrieved\",\"afterPrune\",\"final\"],\"additionalProperties\":false}},\"required\":[\"kind\",\"basis\",\"exhaustive\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"const\":\"unproven-empty\"},\"reason\":{\"type\":\"string\",\"enum\":[\"allowlist-truncated\",\"retrieval-window-not-exhaustive\",\"source-empty\",\"post-retrieval-rejected\",\"pagination-empty\",\"no-authoritative-empty-proof\"]},\"sourceEmpty\":{\"type\":[\"boolean\",\"null\"]},\"counts\":{\"type\":\"object\",\"properties\":{\"retrieved\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"afterPrune\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"final\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991}},\"required\":[\"retrieved\",\"afterPrune\",\"final\"],\"additionalProperties\":false}},\"required\":[\"kind\",\"reason\"],\"additionalProperties\":false}],\"type\":\"object\"},\"termCoverage\":{\"description\":\"Lexical-projection coverage of substantive terms. Present only under explain.\",\"type\":\"object\",\"properties\":{\"status\":{\"type\":\"string\",\"enum\":[\"covered\",\"uncovered\",\"unavailable\",\"skipped\"]},\"terms\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"note\":{\"type\":\"string\"}},\"required\":[\"status\",\"terms\"],\"additionalProperties\":false},\"cursor\":{\"type\":\"string\"},\"window\":{\"type\":\"object\",\"properties\":{\"profile\":{\"type\":\"string\",\"const\":\"interactive-r1\"},\"candidateTier\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"retrievalRounds\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"retrievalMs\":{\"type\":\"number\",\"minimum\":0},\"exhaustive\":{\"type\":\"boolean\"},\"candidateCounts\":{\"type\":\"object\",\"properties\":{\"rawProviderMatches\":{\"anyOf\":[{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},{\"type\":\"null\"}]},\"selectedMatches\":{\"anyOf\":[{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},{\"type\":\"null\"}]},\"hydratedCano",
83
- "nicalCandidates\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991}},\"required\":[\"rawProviderMatches\",\"selectedMatches\",\"hydratedCanonicalCandidates\"],\"additionalProperties\":false}},\"required\":[\"profile\",\"candidateTier\",\"retrievalRounds\",\"retrievalMs\",\"exhaustive\"],\"additionalProperties\":false,\"description\":\"The single bounded candidate window that produced this page: versioned profile, requested depth, retrieval cost, and whether the selected source proved it was exhausted.\"},\"plan\":{\"description\":\"The executed query shape — result grain and the per-arm weight profile. Present only when `explain` is set. Diagnostic, not part of the ranking contract.\",\"type\":\"object\",\"properties\":{\"textLane\":{\"type\":\"string\",\"enum\":[\"image-text\",\"variant-text\"]},\"grain\":{\"type\":\"string\",\"enum\":[\"variant\",\"product\"],\"description\":\"What one result stands for: \\\"variant\\\" (the default — every matching variant is its own result) or \\\"product\\\" (`collapse: \\\"product\\\"` grouped variants, so each result fronts a product and hides siblings).\"},\"weights\":{\"type\":\"object\",\"properties\":{\"portrait\":{\"type\":\"number\"},\"image\":{\"type\":\"number\"},\"category\":{\"type\":\"number\"},\"colour\":{\"type\":\"number\"},\"lexical\":{\"type\":\"number\"},\"text\":{\"type\":\"number\"}},\"required\":[\"portrait\",\"image\",\"category\",\"colour\",\"lexical\",\"text\"],\"additionalProperties\":false,\"description\":\"The applied ordering-signal profile. At most one weight is non-zero: the selected mode's primary signal. Multiplying that signal by its weight reproduces `score`, and the weight is `maxScore`. Other signals are diagnostic or hard-gate receipts and never blend.\"},\"queryTransform\":{\"type\":\"array\",\"items\":{\"oneOf\":[{\"type\":\"object\",\"properties\":{\"stage\":{\"type\":\"string\",\"enum\":[\"normalise\",\"resolve-spec\",\"resolve-paint\",\"resolve-brand\",\"resolve-category\"]},\"applied\":{\"type\":\"boolean\",\"const\":true}},\"required\":[\"stage\",\"applied\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"stage\":{\"type\":\"string\",\"enum\":[\"normalise\",\"resolve-spec\",\"resolve-paint\",\"resolve-brand\",\"resolve-category\"]},\"applied\":{\"type\":\"boolean\",\"const\":false},\"reason\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"stage\",\"applied\",\"reason\"],\"additionalProperties\":false}],\"type\":\"object\"},\"description\":\"The query-transformation stages that actually executed, in execution order. An applied stage changed execution; a skipped stage carries its reason. An empty array means this path ran no query transform.\"},\"steps\":{\"description\":\"Ordered request-understanding steps. Every entry says whether the step executed or skipped, why, and its measured wall-clock duration.\",\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"key\":{\"type\":\"string\",\"minLength\":1},\"status\":{\"type\":\"string\",\"enum\":[\"executed\",\"skipped\"]},\"reason\":{\"type\":\"string\",\"minLength\":1},\"durationMs\":{\"type\":\"number\",\"minimum\":0}},\"required\":[\"key\",\"status\",\"reason\",\"durationMs\"],\"additionalProperties\":false}},\"categoryAnchor\":{\"description\":\"Present only when the query NAMED a canonical product type. See `applied` for whether it gated the candidate pool.\",\"type\":\"object\",\"properties\":{\"productTypeId\":{\"type\":\"string\",\"description\":\"The leaf product-type id the query anchored to, e.g. \\\"modular_carpet\\\" — or, for a \\\"group\\\" anchor, the GROUP key, e.g. \\\"flooring\\\". A group key is a taxonomy node no variant is typed as; the ids actually gated on are in `productTypeIds`.\"},\"productTypeIds\":{\"description\":\"The leaf type ids a \\\"group\\\" anchor expanded to. Absent for the leaf sources, where `productTypeId` is already the single type gated on.\",\"readOnly\":true,\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"displayName\":{\"type\":\"string\",\"description\":\"The registry display name of that type.\"},\"similarity\":{\"type\":\"number\",\"description\":\"Cosine of the RAW (unanchored) query vector against the type vector for a \\\"vector\\\" anchor; 1 for the exact synonym / display-name passes.\"},\"margin\":{\"description\":\"Lead over the runner-up type for a \\\"vector\\\" anchor — the tie-break guard that stops an ambiguous query gating. Null for the exact passes.\",\"type\":[\"number\",\"n",
84
- "ull\"]},\"source\":{\"type\":\"string\",\"enum\":[\"taxonomy\",\"synonym\",\"display-name\",\"group\",\"head-noun\",\"vector\"],\"description\":\"How the anchor was established: a curated customer-language synonym, a registry display name appearing verbatim in the query, a public product-type group named by the query's trailing head noun, a bare canonical category noun whose leaf types cut across groups (\\\"tile\\\"), or the measured vector match.\"},\"candidateSource\":{\"description\":\"Provider-owned retrieval policy. When present, the hard category is applied inside lexical candidate selection before the result limit.\",\"type\":\"string\",\"const\":\"lexical\"},\"applied\":{\"type\":\"boolean\",\"description\":\"Whether the anchor actually gated the pool. False means the anchored category held nothing in the candidate pool, so the engine fell back to unanchored ranking (and said so in `note`) rather than returning an empty page.\"}},\"required\":[\"productTypeId\",\"displayName\",\"similarity\",\"margin\",\"source\",\"applied\"],\"additionalProperties\":false},\"brandAnchor\":{\"description\":\"Present only when the query NAMED a brand the catalogue sells. See `applied` for whether it gated retrieval or was superseded by the empty-result retreat.\",\"type\":\"object\",\"properties\":{\"brandSlug\":{\"type\":\"string\",\"description\":\"The brand the query named, e.g. \\\"daltile\\\" — its stable slug.\"},\"brandName\":{\"type\":\"string\",\"description\":\"The brand's name as catalogued.\"},\"confidence\":{\"type\":\"number\",\"description\":\"Resolver confidence in [0,1]. 1 means the query IS the brand's name or slug; the gate's floor is 0.88.\"},\"margin\":{\"description\":\"Lead over the runner-up brand — the tie-break guard that stops an ambiguous query gating. Null when there was no runner-up.\",\"type\":[\"number\",\"null\"]},\"tier\":{\"type\":\"string\",\"enum\":[\"exact_id\",\"exact_alias\",\"lexical\",\"semantic\"],\"description\":\"Which rung of the resolver ladder won. Only the identity rungs (`exact_id`, `exact_alias`) may gate — a `lexical` or `semantic` win means the query RESEMBLES a brand name rather than naming one.\"},\"variantCount\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991,\"description\":\"Catalogued variants behind the brand — the gated universe's size.\"},\"applied\":{\"type\":\"boolean\",\"description\":\"Whether the anchor actually gated retrieval. False means the brand held nothing retrievable, so the engine re-ran unanchored (and said so in `note`) rather than returning an empty page.\"}},\"required\":[\"brandSlug\",\"brandName\",\"confidence\",\"margin\",\"tier\",\"variantCount\",\"applied\"],\"additionalProperties\":false},\"brandSkipped\":{\"description\":\"Present only when the brand stage saw a real brand and REFUSED to gate on it. Mutually exclusive with `brandAnchor`.\",\"type\":\"object\",\"properties\":{\"reason\":{\"type\":\"string\",\"enum\":[\"ambiguous\",\"weak-tier\",\"too-broad\"],\"description\":\"Why the brand gate was declined. `ambiguous` = two brands inside the resolver's gap; `weak-tier` = the query resembles a brand name rather than naming one; `too-broad` = the brand exceeds the exact-scan ceiling.\"},\"candidates\":{\"description\":\"The brands that tied — populated for `ambiguous` only, so the response can name what it declined to choose between.\",\"type\":\"array\",\"items\":{\"type\":\"string\"}}},\"required\":[\"reason\"],\"additionalProperties\":false}},\"required\":[\"grain\",\"weights\",\"queryTransform\"],\"additionalProperties\":false},\"timings\":{\"description\":\"Per-stage wall-clock milliseconds (embed, retrieval, prune, constraints/enrichment, finalize, total). Present only when `explain` is set — diagnostic, not part of the ranking contract.\",\"type\":\"object\",\"propertyNames\":{\"type\":\"string\"},\"additionalProperties\":{\"type\":\"number\"}},\"query\":{\"type\":\"object\",\"propertyNames\":{\"type\":\"string\"},\"additionalProperties\":{}},\"seed\":{\"type\":\"object\",\"propertyNames\":{\"type\":\"string\"},\"additionalProperties\":{}}},\"required\":[\"view\",\"results\",\"suggestions\",\"mode\",\"maxScore\",\"query\"],\"additionalProperties\":false}],\"type\":\"object\"},\"meta\":{\"type\":\"object\",\"properties\":{\"operationId\":{\"type\":\"string\",\"enum\":[\"search_materials_post\",\"search_materials_get\"]},\"requestId\":{\"type\":\"string\",\"minLe",
85
- "ngth\":1}},\"required\":[\"operationId\",\"requestId\"],\"additionalProperties\":false},\"links\":{\"type\":\"object\",\"properties\":{},\"additionalProperties\":false},\"warnings\":{\"maxItems\":0,\"type\":\"array\",\"items\":{\"not\":{}}}},\"required\":[\"data\",\"meta\",\"links\",\"warnings\"],\"additionalProperties\":false}}},\"annotations\":{\"readOnlyHint\":true,\"untrustedContentHint\":true},\"requiresVisibleConfirmation\":false,\"requiredHeaders\":[]},{\"name\":\"compare\",\"operationId\":\"retrieval.compare\",\"path\":\"/api/v1/compare\",\"method\":\"POST\",\"description\":\"Reads 2–10 available canonical Variants and compares their stated values across the selected blocks or attributes. Unknown and conflicting cells remain explicit; no canonical data is changed. Requires the `mg:read` bearer scope.\",\"inputSchema\":{\"$ref\":\"#/$defs/schema0\",\"$defs\":{\"schema0\":{\"type\":\"object\",\"properties\":{\"variantIds\":{\"minItems\":2,\"maxItems\":10,\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"selection\":{\"oneOf\":[{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"const\":\"blocks\"},\"blockIds\":{\"minItems\":1,\"maxItems\":31,\"type\":\"array\",\"items\":{\"type\":\"string\"}}},\"required\":[\"kind\",\"blockIds\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"const\":\"attributes\"},\"attributeIds\":{\"minItems\":1,\"maxItems\":278,\"type\":\"array\",\"items\":{\"type\":\"string\"}}},\"required\":[\"kind\",\"attributeIds\"],\"additionalProperties\":false}],\"type\":\"object\"}},\"required\":[\"variantIds\"],\"additionalProperties\":false}}},\"outputSchema\":{\"$ref\":\"#/$defs/schema0\",\"$defs\":{\"schema0\":{\"type\":\"object\",\"properties\":{\"data\":{\"type\":\"object\",\"properties\":{\"targets\":{\"minItems\":2,\"maxItems\":10,\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"variantId\":{\"type\":\"string\"},\"version\":{\"type\":\"string\"},\"freshness\":{\"type\":\"object\",\"properties\":{\"oldestAt\":{\"type\":[\"string\",\"null\"]},\"newestAt\":{\"type\":[\"string\",\"null\"]}},\"required\":[\"oldestAt\",\"newestAt\"],\"additionalProperties\":false},\"identity\":{\"type\":\"object\",\"properties\":{\"variantId\":{\"type\":\"string\"},\"variantName\":{\"type\":[\"string\",\"null\"]},\"brand\":{\"type\":\"string\"},\"brandSlug\":{\"type\":\"string\"},\"product\":{\"type\":\"string\"},\"productId\":{\"type\":\"string\"},\"colourName\":{\"type\":[\"string\",\"null\"]},\"category\":{\"type\":[\"string\",\"null\"]},\"categoryPath\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"imageUrl\":{\"type\":[\"string\",\"null\"]},\"installationImageUrl\":{\"type\":[\"string\",\"null\"]},\"dominantHex\":{\"type\":[\"string\",\"null\"]},\"siblingCount\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991}},\"required\":[\"variantId\",\"variantName\",\"brand\",\"brandSlug\",\"product\",\"productId\",\"colourName\",\"category\",\"categoryPath\",\"imageUrl\",\"installationImageUrl\",\"dominantHex\",\"siblingCount\"],\"additionalProperties\":false},\"availability\":{\"type\":\"object\",\"properties\":{\"state\":{\"type\":\"string\",\"enum\":[\"available\",\"discontinued\",\"superseded\",\"unknown\"],\"description\":\"Orderability: 'available' (listed in the catalog of record), 'discontinued' (withdrawn from that catalog — not a reviewed manufacturer-discontinuation claim), 'superseded' (a reviewed successor relationship exists and the material is no longer listed), 'unknown' (never checked).\"},\"label\":{\"type\":[\"string\",\"null\"]},\"supersededBy\":{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"variantId\":{\"type\":\"string\"},\"brandSlug\":{\"type\":\"string\"},\"productId\":{\"type\":\"string\"},\"brand\":{\"type\":\"string\"},\"product\":{\"type\":\"string\"},\"variant\":{\"type\":\"string\"},\"color\":{\"type\":[\"string\",\"null\"]},\"availability\":{\"type\":\"string\",\"enum\":[\"available\",\"discontinued\",\"superseded\",\"unknown\"],\"description\":\"Orderability: 'available' (listed in the catalog of record), 'discontinued' (withdrawn from that catalog — not a reviewed manufacturer-discontinuation claim), 'superseded' (a reviewed successor relationship exists and the material is no longer listed), 'unknown' (never checked).\"}},\"required\":[\"variantId\",\"brandSlug\",\"productId\",\"brand\",\"product\",\"variant\",\"color\",\"availability\"],\"additionalProperties\":false},{\"type\":\"null\"}]}},\"required\":[\"state\",\"label\",\"supersededBy\"],\"additionalProperties\":false},\"sampleability\":{\"type\":[\"boolean\",\"null\"]}},\"requ",
86
- "ired\":[\"variantId\",\"version\",\"freshness\",\"identity\",\"availability\",\"sampleability\"],\"additionalProperties\":false}},\"rows\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"blockId\":{\"type\":\"string\"},\"blockName\":{\"type\":\"string\"},\"attributeId\":{\"type\":\"string\"},\"label\":{\"type\":\"string\"},\"outcome\":{\"type\":\"string\",\"enum\":[\"same\",\"different\",\"incomplete\",\"indeterminate\",\"not_applicable\"]},\"cells\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"variantId\":{\"type\":\"string\"},\"state\":{\"type\":\"string\",\"enum\":[\"known\",\"unknown\",\"conflicting\",\"inapplicable\"]},\"recordState\":{\"type\":\"string\",\"enum\":[\"stated\",\"conflicting\",\"stale\",\"unknown\",\"missing\",\"not_applicable\",\"not_evaluated\"]},\"value\":{\"anyOf\":[{},{\"type\":\"null\"}]},\"unit\":{\"type\":[\"string\",\"null\"]},\"verification\":{\"type\":\"string\",\"enum\":[\"verified\",\"unverified\",\"unbacked\"]},\"confidence\":{\"anyOf\":[{\"type\":\"number\",\"minimum\":0,\"maximum\":1},{\"type\":\"null\"}]},\"sourceBasis\":{\"type\":[\"string\",\"null\"]},\"sourceRef\":{\"type\":[\"string\",\"null\"]},\"freshnessAt\":{\"type\":[\"string\",\"null\"]},\"claimScope\":{\"anyOf\":[{\"type\":\"string\",\"enum\":[\"variant\",\"product\",\"brand\"]},{\"type\":\"null\"}]},\"competingClaims\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"scope\":{\"type\":\"string\",\"enum\":[\"variant\",\"product\",\"brand\"]},\"value\":{\"anyOf\":[{},{\"type\":\"null\"}]},\"sourceRef\":{\"type\":[\"string\",\"null\"]}},\"required\":[\"scope\",\"value\",\"sourceRef\"],\"additionalProperties\":false}}},\"required\":[\"variantId\",\"state\",\"recordState\",\"value\",\"unit\",\"verification\",\"confidence\",\"sourceBasis\",\"sourceRef\",\"freshnessAt\",\"claimScope\",\"competingClaims\"],\"additionalProperties\":false}}},\"required\":[\"blockId\",\"blockName\",\"attributeId\",\"label\",\"outcome\",\"cells\"],\"additionalProperties\":false}}},\"required\":[\"targets\",\"rows\"],\"additionalProperties\":false},\"meta\":{\"type\":\"object\",\"properties\":{\"operationId\":{\"type\":\"string\",\"const\":\"retrieval.compare\"},\"requestId\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"operationId\",\"requestId\"],\"additionalProperties\":false},\"links\":{\"type\":\"object\",\"properties\":{},\"additionalProperties\":false},\"warnings\":{\"maxItems\":0,\"type\":\"array\",\"items\":{\"not\":{}}}},\"required\":[\"data\",\"meta\",\"links\",\"warnings\"],\"additionalProperties\":false}}},\"annotations\":{\"readOnlyHint\":true,\"untrustedContentHint\":true},\"requiresVisibleConfirmation\":false,\"requiredHeaders\":[]},{\"name\":\"create_correction\",\"operationId\":\"corrections.create\",\"path\":\"/api/v1/corrections\",\"method\":\"POST\",\"description\":\"Records page and graph evidence as a caller-owned review proposal. The submission does not change accepted catalog truth. Requires `mg:read` and `mg:corrections`, plus a distinct caller principal.\",\"inputSchema\":{\"$ref\":\"#/$defs/schema0\",\"$defs\":{\"schema1\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"number\"},{\"type\":\"boolean\"},{\"type\":\"null\"},{\"type\":\"array\",\"items\":{\"$ref\":\"#/$defs/schema1\"}},{\"type\":\"object\",\"propertyNames\":{\"type\":\"string\"},\"additionalProperties\":{\"$ref\":\"#/$defs/schema1\"}}]},\"schema2\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"number\"},{\"type\":\"boolean\"},{\"type\":\"null\"},{\"type\":\"array\",\"items\":{\"$ref\":\"#/$defs/schema2\"}},{\"type\":\"object\",\"propertyNames\":{\"type\":\"string\"},\"additionalProperties\":{\"$ref\":\"#/$defs/schema2\"}}]},\"schema0\":{\"type\":\"object\",\"properties\":{\"target\":{\"oneOf\":[{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"const\":\"identity\"},\"subject\":{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"enum\":[\"brand\",\"product\",\"variant\"]},\"id\":{\"type\":\"string\",\"minLength\":1,\"maxLength\":300}},\"required\":[\"kind\",\"id\"],\"additionalProperties\":false}},\"required\":[\"kind\",\"subject\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"const\":\"attribute\"},\"subject\":{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"enum\":[\"brand\",\"product\",\"variant\"]},\"id\":{\"type\":\"string\",\"minLength\":1,\"maxLength\":300}},\"required\":[\"kind\",\"id\"],\"additionalProperties\":false},\"blockId\":{\"type\":\"string\",\"minLength\":1,\"maxLength\":300},\"attributeId\":{\"type\":\"string\",\"minLength\":1,\"maxLength\":300}},\"required\":[\"kind\",\"subject\",\"blockId\",\"attributeId\"],\"additionalProperties\":f",
87
- "alse},{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"const\":\"relationship\"},\"subject\":{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"enum\":[\"brand\",\"product\",\"variant\"]},\"id\":{\"type\":\"string\",\"minLength\":1,\"maxLength\":300}},\"required\":[\"kind\",\"id\"],\"additionalProperties\":false},\"relationship\":{\"type\":\"string\",\"minLength\":1,\"maxLength\":300},\"relatedSubject\":{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"enum\":[\"brand\",\"product\",\"variant\"]},\"id\":{\"type\":\"string\",\"minLength\":1,\"maxLength\":300}},\"required\":[\"kind\",\"id\"],\"additionalProperties\":false}},\"required\":[\"kind\",\"subject\",\"relationship\",\"relatedSubject\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"const\":\"source_interpretation\"},\"sourceMaterialId\":{\"type\":\"string\",\"minLength\":1,\"maxLength\":300},\"subject\":{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"enum\":[\"brand\",\"product\",\"variant\"]},\"id\":{\"type\":\"string\",\"minLength\":1,\"maxLength\":300}},\"required\":[\"kind\",\"id\"],\"additionalProperties\":false}},\"required\":[\"kind\",\"sourceMaterialId\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"const\":\"missing_record\"},\"url\":{\"type\":\"string\",\"maxLength\":2048,\"format\":\"uri\"}},\"required\":[\"kind\",\"url\"],\"additionalProperties\":false}],\"type\":\"object\"},\"pageEvidence\":{\"type\":\"object\",\"properties\":{\"state\":{\"type\":\"string\",\"enum\":[\"known\",\"unknown\",\"conflicting\",\"not_applicable\"]},\"value\":{\"$ref\":\"#/$defs/schema1\"},\"sourceUrl\":{\"type\":\"string\",\"maxLength\":2048,\"format\":\"uri\"},\"note\":{\"type\":\"string\",\"maxLength\":2000}},\"required\":[\"state\"],\"additionalProperties\":false},\"graphEvidence\":{\"type\":\"object\",\"properties\":{\"state\":{\"type\":\"string\",\"enum\":[\"known\",\"unknown\",\"conflicting\",\"not_applicable\"]},\"value\":{\"$ref\":\"#/$defs/schema1\"},\"sourceUrl\":{\"type\":\"string\",\"maxLength\":2048,\"format\":\"uri\"},\"note\":{\"type\":\"string\",\"maxLength\":2000}},\"required\":[\"state\"],\"additionalProperties\":false},\"proposedChange\":{\"$ref\":\"#/$defs/schema2\"},\"rationale\":{\"type\":\"string\",\"minLength\":1,\"maxLength\":2000}},\"required\":[\"target\",\"pageEvidence\",\"graphEvidence\",\"proposedChange\",\"rationale\"],\"additionalProperties\":false}}},\"outputSchema\":{\"$ref\":\"#/$defs/schema0\",\"$defs\":{\"schema1\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"number\"},{\"type\":\"boolean\"},{\"type\":\"null\"},{\"type\":\"array\",\"items\":{\"$ref\":\"#/$defs/schema1\"}},{\"type\":\"object\",\"propertyNames\":{\"type\":\"string\"},\"additionalProperties\":{\"$ref\":\"#/$defs/schema1\"}}]},\"schema2\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"number\"},{\"type\":\"boolean\"},{\"type\":\"null\"},{\"type\":\"array\",\"items\":{\"$ref\":\"#/$defs/schema2\"}},{\"type\":\"object\",\"propertyNames\":{\"type\":\"string\"},\"additionalProperties\":{\"$ref\":\"#/$defs/schema2\"}}]},\"schema0\":{\"type\":\"object\",\"properties\":{\"data\":{\"type\":\"object\",\"properties\":{\"id\":{\"type\":\"string\",\"minLength\":1,\"maxLength\":300},\"submission\":{\"type\":\"object\",\"properties\":{\"target\":{\"oneOf\":[{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"const\":\"identity\"},\"subject\":{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"enum\":[\"brand\",\"product\",\"variant\"]},\"id\":{\"type\":\"string\",\"minLength\":1,\"maxLength\":300}},\"required\":[\"kind\",\"id\"],\"additionalProperties\":false}},\"required\":[\"kind\",\"subject\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"const\":\"attribute\"},\"subject\":{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"enum\":[\"brand\",\"product\",\"variant\"]},\"id\":{\"type\":\"string\",\"minLength\":1,\"maxLength\":300}},\"required\":[\"kind\",\"id\"],\"additionalProperties\":false},\"blockId\":{\"type\":\"string\",\"minLength\":1,\"maxLength\":300},\"attributeId\":{\"type\":\"string\",\"minLength\":1,\"maxLength\":300}},\"required\":[\"kind\",\"subject\",\"blockId\",\"attributeId\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"const\":\"relationship\"},\"subject\":{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"enum\":[\"brand\",\"product\",\"variant\"]},\"id\":{\"type\":\"string\",\"minLength\":1,\"maxLength\":300}},\"required\":[\"kind\",\"id\"],\"additionalProperties\":false},\"relationship\":{\"type\":\"string\",\"minLengt",
88
- "h\":1,\"maxLength\":300},\"relatedSubject\":{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"enum\":[\"brand\",\"product\",\"variant\"]},\"id\":{\"type\":\"string\",\"minLength\":1,\"maxLength\":300}},\"required\":[\"kind\",\"id\"],\"additionalProperties\":false}},\"required\":[\"kind\",\"subject\",\"relationship\",\"relatedSubject\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"const\":\"source_interpretation\"},\"sourceMaterialId\":{\"type\":\"string\",\"minLength\":1,\"maxLength\":300},\"subject\":{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"enum\":[\"brand\",\"product\",\"variant\"]},\"id\":{\"type\":\"string\",\"minLength\":1,\"maxLength\":300}},\"required\":[\"kind\",\"id\"],\"additionalProperties\":false}},\"required\":[\"kind\",\"sourceMaterialId\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"const\":\"missing_record\"},\"url\":{\"type\":\"string\",\"maxLength\":2048,\"format\":\"uri\"}},\"required\":[\"kind\",\"url\"],\"additionalProperties\":false}],\"type\":\"object\"},\"pageEvidence\":{\"type\":\"object\",\"properties\":{\"state\":{\"type\":\"string\",\"enum\":[\"known\",\"unknown\",\"conflicting\",\"not_applicable\"]},\"value\":{\"$ref\":\"#/$defs/schema1\"},\"sourceUrl\":{\"type\":\"string\",\"maxLength\":2048,\"format\":\"uri\"},\"note\":{\"type\":\"string\",\"maxLength\":2000}},\"required\":[\"state\"],\"additionalProperties\":false},\"graphEvidence\":{\"type\":\"object\",\"properties\":{\"state\":{\"type\":\"string\",\"enum\":[\"known\",\"unknown\",\"conflicting\",\"not_applicable\"]},\"value\":{\"$ref\":\"#/$defs/schema1\"},\"sourceUrl\":{\"type\":\"string\",\"maxLength\":2048,\"format\":\"uri\"},\"note\":{\"type\":\"string\",\"maxLength\":2000}},\"required\":[\"state\"],\"additionalProperties\":false},\"proposedChange\":{\"$ref\":\"#/$defs/schema2\"},\"rationale\":{\"type\":\"string\",\"minLength\":1,\"maxLength\":2000}},\"required\":[\"target\",\"pageEvidence\",\"graphEvidence\",\"proposedChange\",\"rationale\"],\"additionalProperties\":false},\"status\":{\"type\":\"string\",\"enum\":[\"submitted\",\"in_review\",\"resolved\",\"withdrawn\"]},\"resolution\":{\"anyOf\":[{\"type\":\"string\",\"enum\":[\"substantiated\",\"not_substantiated\",\"duplicate\",\"superseded\",\"cannot_determine\"]},{\"type\":\"null\"}]},\"createdAt\":{\"type\":\"string\",\"format\":\"date-time\",\"pattern\":\"^(?:(?:\\\\d\\\\d[2468][048]|\\\\d\\\\d[13579][26]|\\\\d\\\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\\\d|30)|(?:02)-(?:0[1-9]|1\\\\d|2[0-8])))T(?:(?:[01]\\\\d|2[0-3]):[0-5]\\\\d:[0-5]\\\\d(?:\\\\.\\\\d+)?(?:Z))$\"},\"updatedAt\":{\"type\":\"string\",\"format\":\"date-time\",\"pattern\":\"^(?:(?:\\\\d\\\\d[2468][048]|\\\\d\\\\d[13579][26]|\\\\d\\\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\\\d|30)|(?:02)-(?:0[1-9]|1\\\\d|2[0-8])))T(?:(?:[01]\\\\d|2[0-3]):[0-5]\\\\d:[0-5]\\\\d(?:\\\\.\\\\d+)?(?:Z))$\"},\"acceptedTruthChanged\":{\"type\":\"boolean\",\"const\":false}},\"required\":[\"id\",\"submission\",\"status\",\"resolution\",\"createdAt\",\"updatedAt\",\"acceptedTruthChanged\"],\"additionalProperties\":false},\"meta\":{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"operationId\":{\"type\":\"string\",\"const\":\"corrections.create\"},\"requestId\":{\"type\":\"string\",\"minLength\":1},\"idempotency\":{\"type\":\"object\",\"properties\":{\"replayed\":{\"type\":\"boolean\"}},\"required\":[\"replayed\"],\"additionalProperties\":false}},\"required\":[\"operationId\",\"requestId\",\"idempotency\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"operationId\":{\"type\":\"string\",\"const\":\"corrections.get\"},\"requestId\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"operationId\",\"requestId\"],\"additionalProperties\":false}]},\"links\":{\"type\":\"object\",\"properties\":{\"self\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"self\"],\"additionalProperties\":false},\"warnings\":{\"maxItems\":0,\"type\":\"array\",\"items\":{\"not\":{}}}},\"required\":[\"data\",\"meta\",\"links\",\"warnings\"],\"additionalProperties\":false}}},\"annotations\":{\"readOnlyHint\":false,\"untrustedContentHint\":true},\"requiresVisibleConfirmation\":true,\"requiredHeaders\":[\"Idempotency-Key\"]}]",
4
+ "ore. A seeded search ALWAYS carries a `visual_match` verdict cell on each hit (baseline `{ kind: \\\"image\\\", source: \\\"variant\\\"|\\\"vector\\\", variantId }`): `matched` at or above an image cosine of 0.55, else `deviated` with a `looser visual match` deviation. Its `value` is the cosine as a percentage and its `confidence` is deliberately null — a similarity is not a trust confidence. `verdicts: true` (opt-in, full view) is meaningful only with `similarTo: { variantId }`: each hit's `verdict` gains `preferred` SEED cells (baseline `{ kind: \\\"seed\\\" }`) diffing its comparable attributes — numeric plus a virtual `colour` axis — against the seed's, `matched`/`deviated` with a directional `deviation` (`above`/`below`, e.g. 2 mm thicker, warmer); a `vector`/raw-image seed carries no seed variant, so the response `note` says seed cells were skipped. POST defaults to product grain: omitted `collapse` groups results to one representative (highest-ranked) variant per product, reporting hidden variants on that result's `siblingCount` plus a pool-bounded `counts` (variants + products, both `basis:\\\"lowerBound\\\"`) on the envelope. Send `collapse: \\\"none\\\"` explicitly for individual variants. `count: true` adds an opt-in `counts.variants` FEED total — EXACT up to a cap of 1,000 (`basis:\\\"exact\\\"`), `basis:\\\"lowerBound\\\"` beyond the cap and always for ANN-ranked pools (image / colour / visual-text); requests with `excludeVariantIds` also report the honest pool-bounded lower bound because the exact count shapes do not include the exclusion set. `view: \\\"card\\\"` returns the lean `searchCardItemSchema` grid projection instead of the full items; the response echoes `view`, which discriminates the `results` union. `hasImages: true` returns only materials with at least one catalog image (default false keeps the full catalog, and the `count` total honours it). It is applied on EVERY retrieval arm as a post-filter over the candidate pool, sharing one SQL definition of image presence with the count and the displayed card, so a short page means the pool ran out of image-backed matches rather than that some arm ignored the flag. Each result's `visualMeanings` (existing accepted/measured meanings — descriptive evidence, never a ranking cause) is included only when `explain` is set and at least one meaning exists. Returns the engine result verbatim (`mode`, `results[]`, `rerank`, `maxScore`, `counts`) plus a `query` echo (the raw `image` bytes/vector are never echoed back — only `{ type }`). Search is dynamic, so success responses carry `Cache-Control: no-store`. Requires the `mg:read` bearer scope.\",\"inputSchema\":{\"$ref\":\"#/$defs/schema0\",\"$defs\":{\"schema0\":{\"type\":\"object\",\"properties\":{\"text\":{\"description\":\"Free-text query, matched VISUALLY against material imagery (\\\"striped wallpaper\\\", \\\"warm terrazzo\\\"). A bare colour word (\\\"sage\\\") automatically runs as a colour-only search.\",\"type\":\"string\",\"maxLength\":500},\"intent\":{\"default\":\"search\",\"description\":\"Budget profile for the search primitive. Default \\\"search\\\" is the full engine. \\\"typeahead\\\" is the as-you-type profile: lexical-only with prefix match on the final token, forced `view:\\\"card\\\"`, `limit` capped at 24, and no mood/colour detection tiers, semantic fallback, category boost, secondary scoring, refinement suggestions, counts, or meanings. `textModes` is ignored under typeahead. Recommended wire shape for library typeahead UIs.\",\"type\":\"string\",\"enum\":[\"search\",\"typeahead\"]},\"textModes\":{\"description\":\"How to interpret `text`. Default [\\\"visual\\\"] — a text→image VISUAL search. \\\"lexical\\\" (opt-in) runs a BM25 ATTRIBUTE-lens search over material names + canonical attribute terms (find by what it's called / what it IS); detected colour words are stripped from the lexical terms and retained only as diagnostic or hard-constraint evidence. Requesting BOTH [\\\"visual\\\",\\\"lexical\\\"] runs the visual search and notes it — compose a separate lexical search if both result sets are required. Ignored when `intent` is \\\"typeahead\\\".\",\"type\":\"array\",\"items\":{\"type\":\"string\",\"enum\":[\"visual\",\"lexica",
5
+ "l\"]}},\"colours\":{\"description\":\"Explicit colour palette to match. Each entry is EITHER a `hex` OR a `name` (a descriptive or brand paint name resolved server-side — never invent a hex for a name). PRECEDENCE: when set, these override any colour word detected in `text`. Unresolvable names fail with a typed error listing them, so you can correct and retry.\",\"maxItems\":8,\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"hex\":{\"description\":\"The colour to match, as 3- or 6-digit hex. Provide EITHER `hex` OR `name`, never both. Only pass `hex` when you already hold an EXACT hex — for a colour NAME use `name` and let the server resolve it.\",\"type\":\"string\",\"pattern\":\"^#?(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$\"},\"name\":{\"description\":\"A colour NAME to match — a descriptive name ('sage green', 'terracotta', 'warm sand') or a brand paint name ('Gauntlet Gray', 'Hale Navy', 'White Dove'). Resolved server-side to its exact library hex (misspellings are fuzzy-matched). NEVER invent a hex for a name — pass the name here. Provide EITHER `name` OR `hex`, never both.\",\"type\":\"string\",\"minLength\":1,\"maxLength\":120},\"weight\":{\"description\":\"Relative proportion of this colour in the desired palette (0–1). Weights are normalised across the palette; omit for equal parts.\",\"type\":\"number\",\"exclusiveMinimum\":0,\"maximum\":1},\"role\":{\"type\":\"string\",\"enum\":[\"any\",\"is\",\"accent\"],\"description\":\"Per-colour swatch eligibility (default 'any'). PRECEDENCE: `colourLogic` governs cross-colour combination (all/any); `role` governs which swatch of a product may satisfy THIS colour, and ANDs on top of the logic. 'is' = the colour must be the product's DOMINANT swatch; 'accent' = the colour must be a minor share (proportion 0.05–0.35; all shades in a named colour region count together) and is excluded from the proportion-match term; 'any' = today's nearest-swatch match.\"},\"sourceName\":{\"description\":\"Human name the colour came from (e.g. \\\"sage\\\", \\\"Elephant's Breath\\\") — echoed back for display; never affects matching. (For a name you want RESOLVED to a hex, use `name` instead.)\",\"type\":\"string\",\"maxLength\":120}}}},\"colourLogic\":{\"description\":\"\\\"all\\\" (default): results should contain the whole palette, ranked by average distance + proportion match. \\\"any\\\": results matching ANY palette colour, ranked by their best single-colour distance.\",\"type\":\"string\",\"enum\":[\"all\",\"any\"]},\"colourTolerance\":{\"description\":\"How close a colour must be to count as a match. 'normal' (default) is today's behaviour; 'tight' narrows the OKLab match threshold and prunes far single-colour matches; 'loose' widens the threshold.\",\"type\":\"string\",\"enum\":[\"tight\",\"normal\",\"loose\"]},\"colourAppearance\":{\"type\":\"object\",\"properties\":{\"lightness\":{\"description\":\"OKLab lightness window (L ∈ [0,1]); dark ≈ 0, light ≈ 1.\",\"type\":\"object\",\"properties\":{\"min\":{\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"max\":{\"type\":\"number\",\"minimum\":0,\"maximum\":1}},\"required\":[\"min\",\"max\"]},\"warmth\":{\"description\":\"OKLab warmth window (b axis); cool < 0 < warm, ≈ −0.1..0.1.\",\"type\":\"object\",\"properties\":{\"min\":{\"type\":\"number\",\"minimum\":-0.5,\"maximum\":0.5},\"max\":{\"type\":\"number\",\"minimum\":-0.5,\"maximum\":0.5}},\"required\":[\"min\",\"max\"]}},\"description\":\"HARD appearance gate on the material's DOMINANT colour, independent of the colour palette — works on a text-only query too. Variants without colour extraction are EXCLUDED whenever any axis is set (NULL scalars never match).\"},\"mood\":{\"type\":\"string\",\"enum\":[\"warm\",\"cool\",\"earthy\",\"moody\",\"bold\",\"light-airy\",\"natural\",\"neutral\",\"romantic\",\"coastal\",\"industrial\",\"luxe\",\"vintage\",\"serene\",\"scandi\",\"cosy\",\"heritage\",\"mid-century\",\"pastel\"],\"description\":\"Mood-language colour entry — resolves to a curated checked-in colour palette. Use it for evocative mood queries ('coastal', 'moody', 'earthy') rather than a single colour. A mood ALONE runs as a colour search; with text it remains diagnostic or hard-constraint evidence and does not blend into the text mode's ordering score. One of: warm (Warm — Reds, oranges, yellows - inviting and cozy); cool (Cool — Blues, teals",
6
+ ", cool grays - calm and professional); earthy (Earthy — Browns, tans, terracotta - natural and grounded); moody (Moody — Deep, saturated, dramatic - rich and atmospheric); bold (Bold — High chroma, vibrant - energetic and eye-catching); light-airy (Light + Airy — Pastels, pale tones - soft and spacious); natural (Natural — Greens, wood tones, stone - organic and balanced); neutral (Neutral — Whites, grays, blacks - versatile and timeless); romantic (Romantic — Soft pinks, mauves, blush - tender and elegant); coastal (Coastal — Sandy beiges, ocean blues, seafoam - relaxed seaside); industrial (Industrial — Cool grays, concrete, metallic - urban and modern); luxe (Luxe — Deep jewel tones, rich emeralds, sapphires - opulent); vintage (Vintage — Muted, dusty tones - nostalgic and timeworn); serene (Serene — Soft blues, sage, muted lavender - calm and peaceful); scandi (Scandi — Warm whites, pale woods, cream - Scandinavian minimalism); cosy (Cosy — Deep plaster, amber, tobacco - enveloping warmth); heritage (Heritage — Rich historical tones with saturated depth - classic and grounded); mid-century (Mid-Century — Olive, mustard, teal, terracotta - retro modern); pastel (Pastel — Sage, duck egg, blush - soft colors with body).\"},\"sort\":{\"type\":\"string\",\"enum\":[\"relevance\",\"light\",\"dark\",\"vivid\",\"muted\",\"colourful\",\"name\",\"name_desc\",\"recent\"],\"description\":\"Re-order the final scored results (default 'relevance' = the selected mode's primary score). Two families. COLOUR RAMPS: 'light'/'dark' by dominant lightness, 'vivid'/'muted' by chroma, 'colourful' by overall vibrancy — variants without colour extraction sink to the end. CATALOGUE ORDERS: 'name' (A→Z), 'name_desc' (Z→A) and 'recent' (newest first), which read the same `name` / `created_at` columns, in the same directions, that /api/v1/search/browse orders on — one word, one order, on both lanes. Every sort REORDERS the pool the search signals already retrieved; it never widens or substitutes for one. Retired materials sort below visible ones under every non-relevance sort. For an unranked walk of the whole corpus in any of these orders, use the browse surface instead.\"},\"orderBy\":{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"minLength\":1,\"description\":\"The attribute to sort by. Only attributes with a DECLARED value ladder can be ordered — ordering is never inferred from a value's spelling — and anything else is refused with the orderable set named. Orderable today: price_tier.\"},\"direction\":{\"default\":\"asc\",\"description\":\"'asc' (default) walks the ladder from its first rung.\",\"type\":\"string\",\"enum\":[\"asc\",\"desc\"]}},\"required\":[\"attributeId\"],\"description\":\"Sort results along ONE attribute's declared ladder — a separate axis from `sort`, which stays the closed relevance/colour/catalogue vocabulary. Sent together, `orderBy` wins and the response `note` says so. A variant whose value is off the ladder, or which carries no value at all, sorts LAST in both directions: it has no rung, and it is still a real row, so it is placed at the end rather than dropped or guessed at.\"},\"returnAttributes\":{\"maxItems\":8,\"type\":\"array\",\"items\":{\"type\":\"string\",\"minLength\":1},\"description\":\"Attribute ids whose RESOLVED VALUE should ride each result, under `attributes` keyed by attribute id. Values resolve with the normal variant-over-product precedence. THREE STATES, and they are different: an attribute you did not ask for is not a key at all; an attribute you asked for that this variant carries no accepted value for is `null`; anything else is the value (a string, or an array for a multi-valued attribute). An unknown attribute id is refused naming it, never silently dropped. Pair it with `filters` to show what you filtered on.\"},\"similarTo\":{\"description\":\"Find materials that LOOK like a seed image. Supply `variantId` to seed from an existing variant's primary image, `materialBankId` to seed from a Material Bank SKU — the ONE EXTERNAL identifier this API accepts, so a caller already holding a SKU needs no resolve round trip — or `vector`, a pre-computed halfvec literal (`[a,b,",
7
+ "…]`) from an externally-embedded image, to drive the image arm directly. A `materialBankId` that names no variant is a 400 naming the offending value, never an empty result set and never an unseeded search. PRECEDENCE: wins over `text` (the response carries a note when both were given).\",\"anyOf\":[{\"type\":\"object\",\"properties\":{\"variantId\":{\"type\":\"string\"}},\"required\":[\"variantId\"]},{\"type\":\"object\",\"properties\":{\"materialBankId\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"materialBankId\"]},{\"type\":\"object\",\"properties\":{\"vector\":{\"type\":\"string\"}},\"required\":[\"vector\"]}]},\"excludeVariantIds\":{\"description\":\"Canonical variant ids that must not appear in this result. Applied before the returned page is cut, so the page refills from the deeper ranked pool instead of being shortened by client-side filtering. IDs are opaque; duplicates are harmless and deduplicated by the engine.\",\"maxItems\":256,\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"image\":{\"description\":\"A raw incoming image to search by (URL or base64). The HTTP adapter embeds it and sets `similarTo: { vector }`; wins over `text`.\",\"oneOf\":[{\"type\":\"object\",\"properties\":{\"type\":{\"type\":\"string\",\"const\":\"url\"},\"url\":{\"type\":\"string\",\"format\":\"uri\",\"description\":\"HTTP(S) URL of the query image. MUST be an https URL.\"}},\"required\":[\"type\",\"url\"]},{\"type\":\"object\",\"properties\":{\"type\":{\"type\":\"string\",\"const\":\"base64\"},\"data\":{\"type\":\"string\",\"minLength\":1,\"description\":\"Base64-encoded image bytes (or a data URI) the caller already holds — e.g. a form upload — so there is nothing to host.\"}},\"required\":[\"type\",\"data\"]}],\"type\":\"object\"},\"category\":{\"type\":\"object\",\"properties\":{\"boost\":{\"description\":\"Soft category preference — results of this product type score higher but others still appear.\",\"type\":\"string\"},\"filter\":{\"description\":\"HARD gate: only these public taxonomy categories are returned. Values are category-group or product-type display names (case-insensitive), or canonical taxonomy keys. A group expands to every child product type; an unknown value errors listing every valid name.\",\"type\":\"array\",\"items\":{\"type\":\"string\"}}}},\"weights\":{\"type\":\"object\",\"properties\":{\"image\":{\"type\":\"number\",\"minimum\":0},\"category\":{\"type\":\"number\",\"minimum\":0},\"colour\":{\"type\":\"number\",\"minimum\":0},\"lexical\":{\"type\":\"number\",\"minimum\":0},\"text\":{\"type\":\"number\",\"minimum\":0}},\"description\":\"Optional multiplier for the selected mode's one ordering signal. At most one applied weight is non-zero: colour→colour, lexical/entity-browse→lexical, variant-text→portrait, image-text/image→image, and filter-only→none. Overrides may scale only that live signal and cannot activate any other. Other similarities are diagnostic or hard-gate receipts and do not blend into score.\"},\"filters\":{\"description\":\"Typed filters applied as a hard restriction before ranking. Each entry is a term match (`attributeId` + `term`), a term SET (`attributeId` + `terms` — ANY one of them satisfies it), a presence check (`attributeId` + `hasValue: true`), or a numeric range (`attributeId` + `min`/`max`). AND/OR: entries AND together, and OR is expressed WITHIN one entry via `terms`. BRAND: `attributeId: \\\"brand\\\"` filters on the brand RELATION — a join every one of the catalogue's variants carries — taking a brand slug or display name, exactly the `value` a `brand` facet reports. Do NOT use the `brand_name` ATTRIBUTE for this: it is a sparse spec value present on about 1.5% of variants and absent entirely from every catalog-ingested brand, so filtering on it returns a near-empty page that reads as a corpus gap. Presence filters always route as hard spec gates. Aesthetic/meaning term filters (pattern, texture, directionality, and the other visual-meaning attributes) are applied as a pool-bounded post-filter over the visual candidate pool rather than a whole-catalog restriction. Each entry may carry a `trust` bar the matching value must clear.\",\"type\":\"array\",\"items\":{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, e.g. \\\"primary_material\\\" or \\\"fire_rat",
8
+ "ing\\\" — or the special id \\\"brand\\\", which filters on the brand RELATION (a join, carried by every variant) rather than on any attribute.\"},\"term\":{\"type\":\"string\",\"description\":\"Canonical term the attribute must equal, e.g. \\\"textile\\\". For \\\"brand\\\", a brand slug (\\\"kvadrat\\\") or display name (\\\"Kvadrat\\\"), case-insensitive — exactly the `value` a `brand` facet reports. An unrecognised brand is a 400 naming it, never an empty page.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"term\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, or the special id \\\"brand\\\" for the brand relation.\"},\"terms\":{\"minItems\":1,\"type\":\"array\",\"items\":{\"type\":\"string\"},\"description\":\"Canonical terms, ANY ONE of which satisfies the filter — this is how OR is expressed. OR lives inside one entry; separate `filters[]` entries AND together. Used for ordinal scales, where a stricter class satisfies a looser one, and for a multi-select rail — `{attributeId:\\\"brand\\\", terms:[\\\"kvadrat\\\",\\\"rubelli\\\"]}` returns materials from either brand.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"terms\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\"},\"excludesTerm\":{\"type\":\"string\",\"minLength\":1},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"excludesTerm\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id that must have an accepted value, e.g. \\\"sample_image_mm_per_px\\\".\"},\"hasValue\":{\"type\":\"boolean\",\"const\":true,\"description\":\"Require any accepted value for this attribute.\"},\"asOf\":{\"description\":\"Reference date for EPD expiration, exclusive: the value must remain valid after this date.\",\"type\":\"string\",\"format\":\"date\",\"pattern\":\"^(?:(?:\\\\d\\\\d[2468][048]|\\\\d\\\\d[13579][26]|\\\\d\\\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\\\d|30)|(?:02)-(?:0[1-9]|1\\\\d|2[0-8])))$\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"",
9
+ "]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"hasValue\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id holding a NUMBER, e.g. \\\"light_reflectance_value\\\" or \\\"lead_time_days\\\".\"},\"min\":{\"description\":\"Inclusive lower bound. Omit for a ceiling-only range.\",\"type\":\"number\"},\"max\":{\"description\":\"Inclusive upper bound. Omit for a floor-only range.\",\"type\":\"number\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\"],\"additionalProperties\":false}]}},\"preferences\":{\"description\":\"SOFT spec criteria — rank-never-exclude. Same shape as `filters`, but a preference NEVER removes a result: each hit's `verdict` gains a `preferred` cell, and a failed/missing preferred cell only DEMOTES the hit WITHIN its page (the cursor is from the pre-demotion order).\",\"maxItems\":8,\"type\":\"array\",\"items\":{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, e.g. \\\"primary_material\\\" or \\\"fire_rating\\\" — or the special id \\\"brand\\\", which filters on the brand RELATION (a join, carried by every variant) rather than on any attribute.\"},\"term\":{\"type\":\"string\",\"description\":\"Canonical term the attribute must equal, e.g. \\\"textile\\\". For \\\"brand\\\", a brand slug (\\\"kvadrat\\\") or display name (\\\"Kvadrat\\\"), case-insensitive — exactly the `value` a `brand` facet reports. An unrecognised brand is a 400 naming it, never an empty page.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"term\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, or the special id \\\"brand\\\" for the brand relation.\"},\"terms\":{\"minItems\":1,\"type\":\"array\",\"items\":{\"type\":\"string\"},\"description\":\"Canonical terms, ANY ONE of which satisfies the filter — this is how OR is expressed. OR lives inside one entry; separate `filters[]` entries AND together. Used for ordinal scales, where a stricter class satisfies a looser one, and for a multi-select rail — `{attributeId:\\\"brand\\\", terms:[\\\"kvadrat\\\",\\\"rubelli\\\"]}` returns materials from either brand.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"",
10
+ "required\":[\"attributeId\",\"terms\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\"},\"excludesTerm\":{\"type\":\"string\",\"minLength\":1},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"excludesTerm\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id that must have an accepted value, e.g. \\\"sample_image_mm_per_px\\\".\"},\"hasValue\":{\"type\":\"boolean\",\"const\":true,\"description\":\"Require any accepted value for this attribute.\"},\"asOf\":{\"description\":\"Reference date for EPD expiration, exclusive: the value must remain valid after this date.\",\"type\":\"string\",\"format\":\"date\",\"pattern\":\"^(?:(?:\\\\d\\\\d[2468][048]|\\\\d\\\\d[13579][26]|\\\\d\\\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\\\d|30)|(?:02)-(?:0[1-9]|1\\\\d|2[0-8])))$\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"hasValue\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id holding a NUMBER, e.g. \\\"light_reflectance_value\\\" or \\\"lead_time_days\\\".\"},\"min\":{\"description\":\"Inclusive lower bound. Omit for a ceiling-only range.\",\"type\":\"number\"},\"max\":{\"description\":\"Inclusive upper bound. Omit for a floor-only range.\",\"type\":\"number\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\"],\"additionalProperties\":false}]}},\"verdicts\":{\"description\":\"Opt-in seed-relative verdict cells. Only meaningful with `similarTo: { variantId }`: costs one seed fetch + one page read to diff each hit's accepted COMPARABLE attributes (numeric + a colour axis) against the seed's, emitting `preferred` `verdict` cells with directional deviations (`above`/`below`, e.g. 2 mm thicker, warmer). Ignored on the default path; a `similarTo: { vector }` / raw-image seed carries no seed variant, so the response notes that seed cells were skipped.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Results per page (default 60, max 60).\",\"type\":\"integer\",\"minimum\":1,\"maximum\":60},\"cursor\":{\"description\":\"Opaque pagination cursor from a previous response. NOTE: ANN search is approximate — tail items may reorder slightly between pages.\",\"type\":\"string\"},\"explain\":{\"description\":\"Include per-signal scores on each result (`signals`).\",\"type\":\"boolean\"},\"collapse\":{\"description\":\"Group res",
11
+ "ults by product before pagination. \\\"product\\\" returns one representative (highest-ranked) variant per product, with hidden siblings counted in `siblingCount`. The engine and GET adapter default to \\\"none\\\"; public POST requests default to \\\"product\\\" and may request \\\"none\\\" explicitly for variant-grain results.\",\"type\":\"string\",\"enum\":[\"none\",\"product\"]},\"count\":{\"default\":false,\"description\":\"Opt-in feed total. When true the response carries `counts.variants` (and `counts.products` under `collapse:\\\"product\\\"`). The variant total is EXACT up to a cap of 1000 and reported as `basis:\\\"lowerBound\\\"` beyond it — and ALWAYS `lowerBound` for ANN-ranked pools (image / colour / visual-text / visual-meaning post-filters), where the finite candidate pool is the only honest figure. Counts are never silently approximated: an exact count is labelled `exact`, a capped or pool-bounded one `lowerBound`. Default false (skip the count query entirely).\",\"type\":\"boolean\"},\"facetCounts\":{\"description\":\"Opt-in per-query facet counts (MG-1010). Name up to 10 facet ids — `brand`, `category`, or spec attribute ids — and the response carries a `facets` block counting each facet's values WITHIN this query's match set. Absent means no `facets` in the response and no counting cost. An id the registry does not know is a 400 naming it (with `didYouMean` candidates); a known but uncountable id lands in `facets.absent` with a reason. Counts follow the same honesty discipline as `count`: ranked (ANN) queries report pool-derived floors labelled `lowerBound` with `poolSize` stated; only a filter-driven query earns `exact`.\",\"maxItems\":10,\"type\":\"array\",\"items\":{\"type\":\"string\",\"minLength\":1}},\"refinementToken\":{\"description\":\"Opaque five-minute receipt from a prior pool-derived faceted search. Resubmitting it narrows that exact finalized pool instead of running a new candidate source.\",\"type\":\"string\",\"minLength\":1,\"maxLength\":131072},\"hasImages\":{\"default\":false,\"description\":\"When true, only return results that have at least one catalog image (a servable card thumbnail). Off by default so search stays honest about the FULL catalog, including image-less records. Applied on EVERY retrieval arm as a post-filter over the candidate pool, so a short page means the pool ran out of image-backed matches rather than that some arm ignored the flag. The opt-in `counts.variants` feed total honours it too, and filter, count and display share one SQL definition of image presence. Default false.\",\"type\":\"boolean\"},\"sampleableOnly\":{\"description\":\"Require an accepted sampleable=true variant fact before pagination. False or absent leaves the corpus unchanged.\",\"type\":\"boolean\"},\"view\":{\"default\":\"full\",\"description\":\"Result projection. \\\"full\\\" (default) returns the complete `searchResultItemSchema` (palette, colourMeta, hasImage, siblings, explain signals/meanings). \\\"card\\\" returns the lean `searchCardItemSchema` (identity + name + thumbnail + score) for grid rendering, skipping the per-page palette / appearance / meaning reads. The response echoes the chosen `view`, which discriminates the `results` union.\",\"type\":\"string\",\"enum\":[\"full\",\"card\"]},\"corpusScope\":{\"description\":\"Which corpus to answer from. samplizeCatalogue admits published US Samplize paint colours regardless of stock. ABSENT means the WHOLE GRAPH — every material MaterialGraph knows, orderable or not; absence is never a narrower default. Three transaction-oriented scopes gate on governed CHANNEL membership: 'materialBankNaOrderable' (Material Bank North America, about 277,000 materials), 'materialBankEuOrderable' (Material Bank Europe, about 65,000) and 'designShopOrderable' (DesignShop, about 34,000, excluding what DesignShop hides from browse). 'materialBankNaAndCatalogue' is a governed discovery corpus over Material Bank NA or Catalogue editorial membership; it is not a buyability claim, and references alone do not confer membership. 'materialBankOrderable' instead restricts retrieval to materials carrying a Material Bank identity, which is a provenance signal rather than a channel one, and admit",
12
+ "s about 98% of the graph. Scope is distribution/visibility membership, never suitability; its receipt states whether that means transaction or editorial inclusion. Every scope is applied INSIDE the query: `limit` fills with eligible rows, `counts` describe the SCOPED corpus, and the response echoes the scope under `corpusScope`. This is what post-filtering a returned page cannot give you — a client-side filter shortens the page exactly when the query drifts toward the corpus MaterialGraph carries beyond the Material Bank feed, which is unpredictable from outside. An unknown value is a 400 naming it.\",\"type\":\"string\",\"enum\":[\"all\",\"samplizeCatalogue\",\"materialBankOrderable\",\"materialBankNaOrderable\",\"materialBankNaAndCatalogue\",\"materialBankEuOrderable\",\"designShopOrderable\"]},\"includeSamplingArtifacts\":{\"description\":\"OPS/AUDIT escape hatch — DO NOT use for normal search. Default behaviour (absent or false) EXCLUDES sampling-artifact products (multi-swatch sample cards — orderable samples, never specifiable materials) from every result, count, and candidate pool. Set true only to surface them for ops inspection or an audit.\",\"type\":\"boolean\"}},\"additionalProperties\":false}}},\"outputSchema\":{\"$ref\":\"#/$defs/schema0\",\"$defs\":{\"schema0\":{\"type\":\"object\",\"properties\":{\"data\":{\"oneOf\":[{\"type\":\"object\",\"properties\":{\"view\":{\"default\":\"full\",\"description\":\"Projection echo — always 'full' on this envelope variant.\",\"type\":\"string\",\"const\":\"full\"},\"results\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"resultId\":{\"type\":\"string\",\"description\":\"This exact hit's attribution key — `<responseId>:<rank>`, where rank is 1-based within THIS response. Unique across pages, because each page is its own response with its own id. Store it verbatim next to the outcome; it is opaque, and `attribution.responseId` is the field to group by.\"},\"variantId\":{\"type\":\"string\"},\"brand\":{\"type\":\"string\"},\"product\":{\"type\":\"string\"},\"brandSlug\":{\"type\":[\"string\",\"null\"]},\"productId\":{\"type\":[\"string\",\"null\"]},\"materialBankId\":{\"description\":\"The variant's Material Bank SKU — the ONE EXTERNAL identifier on a result, in contrast to the OPAQUE `variantId`: it is the id you are expected to match against Material Bank's own catalogue (carts, ordering and analytics all key on it), so a result list can be joined without a second lookup. Populated on EVERY lane — all four search arms and both Match lanes. ABSENT means no MB identity is known for the variant, never that the SKU cannot be ordered; presence is the orderability tell, absence is silence.\",\"type\":\"string\"},\"manufacturerCode\":{\"description\":\"The governed manufacturer/source-native item code from `variants.code` (for example \\\"GADLC02\\\"). Populated when the full-result retrieval source carries that canonical field; Match always hydrates it from the variant row. ABSENT means unknown or an older external serving projection, never an invented code. Independent of materialBankId.\",\"type\":\"string\"},\"projectSaveCapability\":{\"description\":\"Explicit source-governed permission to save this exact source-coded variant to a Project. ABSENT means unknown/not allowed; it is never inferred from the brand or name.\",\"type\":\"string\",\"const\":\"allowed\"},\"sampleable\":{\"description\":\"Whether a physical sample is explicitly known to be orderable. Absent means unknown, never unavailable.\",\"type\":\"boolean\"},\"sourceStatus\":{\"description\":\"The governed data status supporting Project capture. ABSENT means unknown. Provisional data remains source-backed but is not a verified catalogue claim.\",\"type\":\"string\",\"const\":\"provisional\"},\"productType\":{\"type\":\"string\"},\"color\":{\"type\":[\"string\",\"null\"]},\"variantName\":{\"type\":[\"string\",\"null\"]},\"imageUrl\":{\"type\":[\"string\",\"null\"]},\"hasImage\":{\"type\":\"boolean\"},\"palette\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"hex\":{\"type\":\"string\",\"description\":\"`#rrggbb` of the swatch, as extracted from the product image.\"},\"proportion\":{\"type\":\"number\",\"description\":\"The swatch's share of the analysed image, in [0,1].\"},\"name\":{\"description\":\"Stored human colour name for the s",
13
+ "watch, when one was extracted.\",\"type\":[\"string\",\"null\"]}},\"required\":[\"hex\",\"proportion\"],\"additionalProperties\":false},\"description\":\"The material's main-image palette — the FULL stored palette, uncapped and in stored order. Colours are EXTRACTED FROM PRODUCT IMAGERY (visual likeness, never a declared colour specification). A one-swatch palette means the extractor found one colour — solid-colour materials are genuinely monochrome — never that the list was trimmed. This array is always present: EMPTY means nothing is on record (unknown), not 'this material has no colours'.\"},\"colourMeta\":{\"type\":\"object\",\"properties\":{\"mood\":{\"type\":\"string\"},\"dominantFamily\":{\"type\":\"string\"},\"dominantHex\":{\"type\":\"string\"}},\"additionalProperties\":false},\"colourChip\":{\"type\":\"object\",\"properties\":{\"hex\":{\"type\":\"string\",\"description\":\"`#rrggbb` to fill the chip with. EXTRACTED FROM the product image (a visual likeness), never a manufacturer-declared colour specification — surfaces must not present it as one.\"}},\"required\":[\"hex\"],\"additionalProperties\":false,\"description\":\"Present when this material's product imagery carries nothing beyond one colour, so a surface should render that colour rather than the photograph. Set for COATINGS (paints, primers, stains, floor coatings) whose stored palette quantises to a single swatch — a paint's photo is a flat fill, and the colour we hold structured beats a JPEG of it. ABSENT means the question was not established (unknown product type, unextracted or multi-swatch palette) and the image should be rendered; it NEVER asserts 'this is not a coating'.\"},\"attributes\":{\"description\":\"Resolved attribute values for the ids sent in `returnAttributes`, keyed by attribute id. ABSENT entirely when `returnAttributes` was not sent. A key present with `null` means this variant carries no accepted value for that attribute — absent is not empty and neither is null: the three states are distinct on purpose. Values resolve with the normal variant-over-product precedence.\",\"type\":\"object\",\"propertyNames\":{\"type\":\"string\"},\"additionalProperties\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"array\",\"items\":{\"type\":\"string\"}},{\"type\":\"null\"}]}},\"score\":{\"type\":\"number\"},\"confidence\":{\"description\":\"Match only (MG-1031). How close this material is to the query, as an ABSOLUTE image-space similarity in [0,1] adjusted for colour — the same unit as `qualityFloor` and `comparison.bestRejected`, so all three can be drawn on one axis. Never normalised against the page, so the number means the same thing across runs. It is the TRUE value and may reach 1; rendering it so that no card claims '100%' is the consumer's job (round toward zero, or show '99%+'). Capping it here would put it on a different ceiling from `bestRejected`, which publishes the same unit uncapped. Distinct from `score`, which is the selected mode's weighted primary ranking value on the `[0, maxScore]` scale. Absent outside Match.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"confidenceBand\":{\"description\":\"`confidence` as a word, banded on the engine's measured readings (strong >= 0.85, good >= 0.72, fair >= 0.55 = the default quality floor, weak below it and reachable only under `includeBelowFloor`). Present exactly when `confidence` is.\",\"type\":\"string\",\"enum\":[\"strong\",\"good\",\"fair\",\"weak\"]},\"colourDistance\":{\"description\":\"Match only. Proportion-weighted OKLab distance from the query crop's palette to this material's. `null` means the material carries no stored palette — UNKNOWN colour, never 'no colour' and never 'distant'. Absent when the colour stage did not run.\",\"type\":[\"number\",\"null\"]},\"colourBand\":{\"description\":\"`colourDistance` as a word (identical / very close / close / related / different), or null when unmeasurable. Absent when the colour stage did not run.\",\"type\":[\"string\",\"null\"]},\"signals\":{\"type\":\"object\",\"properties\":{\"portrait\":{\"type\":[\"number\",\"null\"]},\"image\":{\"type\":[\"number\",\"null\"]},\"category\":{\"type\":[\"number\",\"null\"]},\"colour\":{\"type\":[\"number\",\"null\"]},\"lexical\":{\"type\":[\"number\",\"null\"]},\"text\":{\"type\":[\"number\",\"null\"]}},\"required\":[\"",
14
+ "image\",\"category\",\"colour\",\"lexical\",\"text\"],\"additionalProperties\":false},\"retired\":{\"description\":\"MB lists the SKU as not visible/retired; demoted in ranking. Omitted when false.\",\"type\":\"boolean\"},\"belowFloor\":{\"description\":\"Present (true) only on quality-floor rejects returned under includeBelowFloor — the item failed the match image-quality floor and would be dropped by default.\",\"type\":\"boolean\"},\"admittedOnColour\":{\"description\":\"Present (true) only on Match rows the colour probe admitted: this material is as close in colour as the catalogue gets, and its image similarity is BELOW the match quality floor by design. It is on the page because no image-retrieved row is nearer in colour, not because it cleared the floor — so a consumer can present it as a colour answer or suppress it knowingly, and a quality check can tell a designed admission from a genuine floor leak. Absent (never false) on rows that came through image retrieval. Distinct from `belowFloor`, which marks rejects returned only under `includeBelowFloor`.\",\"type\":\"boolean\"},\"siblingCount\":{\"description\":\"Additional variants of the same product hidden by `collapse: \\\"product\\\"`. Present only when grouping hid at least one sibling. It is the COMPLETE tally, and stays so when `variants` is capped.\",\"type\":\"integer\",\"minimum\":1,\"maximum\":9007199254740991},\"variants\":{\"description\":\"The product's matching variants, MOST RELEVANT FIRST — present exactly when `collapse: \\\"product\\\"` was requested, and then on every result. This is what makes the product-shaped view usable without a second call per card: the twelve colourways of one tile arrive with the product instead of being reduced to a number. Under the default variant grain (`collapse` absent or `\\\"none\\\"`) the key is ABSENT — every matching variant is already its own result, so there is no group to carry. A one-entry array means the product matched with exactly one variant, which is an answer rather than a gap. ORDER is the ranking pipeline's own and is never re-sorted: `variants[0]` is the group's RANKING representative — the highest-scoring member, whose score is the group's cursor key. If the display face differs from that representative, `explain` names those two identities apart in `anatomy`. CAPPED at 12 entries, with `siblingCount` left as the complete tally — so truncation is decidable from the response alone: `(siblingCount ?? 0) + 1 > variants.length`. A full-length array is never on its own evidence that the product holds no further variants.\",\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"variantId\":{\"type\":\"string\",\"description\":\"The variant's OPAQUE id — round-trip it verbatim, never parse it.\"},\"variantName\":{\"description\":\"The variant's own label — for paint, the colourway name. Null when unnamed.\",\"type\":[\"string\",\"null\"]},\"color\":{\"description\":\"The variant's coarse colour family.\",\"type\":[\"string\",\"null\"]},\"hex\":{\"description\":\"`#rrggbb` dominant colour, extracted from the variant's image (visual likeness, never a declared specification). ABSENT means no palette is on record — unknown, never 'this variant has no colour'.\",\"type\":\"string\"},\"imageUrl\":{\"description\":\"The variant's main image.\",\"type\":[\"string\",\"null\"]},\"materialBankId\":{\"description\":\"The variant's Material Bank SKU, on the same contract as the result item's: ABSENT means no MB identity is on record, never that it cannot be ordered.\",\"type\":\"string\"},\"score\":{\"type\":\"number\",\"description\":\"The variant's own mode-primary, retired-demoted score — the value this array is ordered by, comparable with the result item's `score` and with the other entries here. It is what makes 'most relevant first' checkable.\"}},\"required\":[\"variantId\",\"variantName\",\"color\",\"imageUrl\",\"score\"],\"additionalProperties\":false}},\"visualMeanings\":{\"description\":\"Existing accepted/measured visual meanings — descriptive evidence, not ranking causes. Present only when `explain` was set and at least one meaning exists.\",\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"id\":{\"type\":\"string\",\"description\":\"Registry attribute id, or a measure",
15
+ "d-meaning id like `colour_mood`.\"},\"label\":{\"type\":\"string\",\"description\":\"Human label — registry presentation label for canonical meanings.\"},\"values\":{\"minItems\":1,\"type\":\"array\",\"items\":{\"type\":\"string\"},\"description\":\"Human-readable value labels.\"},\"basis\":{\"type\":\"string\",\"enum\":[\"canonical_attribute\",\"image_colour_analysis\"],\"description\":\"Where the meaning comes from: an accepted canonical attribute value, or measured image colour analysis. NEVER derived from vector similarity or scores.\"},\"confidence\":{\"type\":\"number\"},\"sourceBasis\":{\"type\":\"string\"},\"version\":{\"description\":\"Extraction version for measured meanings.\",\"type\":\"string\"},\"appliedAt\":{\"description\":\"ISO timestamp the accepted value was applied.\",\"type\":\"string\"}},\"required\":[\"id\",\"label\",\"values\",\"basis\"],\"additionalProperties\":false}},\"verdict\":{\"description\":\"The shared report card for this hit against the request's `filters` (required cells) and `preferences` (preferred cells): one cell per criterion with its state (matched / unverified / failed / missing), value, per-cell trust, a grade, and the weakest confidence. With `verdicts: true` and a `similarTo: { variantId }` seed it ALSO carries `preferred` SEED cells (baseline `{ kind: \\\"seed\\\" }`) — one per comparable attribute the seed holds, plus a `colour` axis — `matched`/`deviated` with a directional deviation. Present (full view only) whenever a spec filter, preference, or seed verdict produced a cell.\",\"type\":\"object\",\"properties\":{\"cells\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"minLength\":1},\"state\":{\"type\":\"string\",\"enum\":[\"matched\",\"deviated\",\"failed\",\"unverified\",\"missing\",\"not_evaluated\"]},\"strength\":{\"type\":\"string\",\"enum\":[\"required\",\"preferred\"]},\"baseline\":{\"oneOf\":[{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"const\":\"term\"},\"term\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"kind\",\"term\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"const\":\"presence\"}},\"required\":[\"kind\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"const\":\"seed\"},\"variantId\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"kind\",\"variantId\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"const\":\"image\"},\"source\":{\"type\":\"string\",\"enum\":[\"variant\",\"vector\"]},\"variantId\":{\"anyOf\":[{\"type\":\"string\",\"minLength\":1},{\"type\":\"null\"}]}},\"required\":[\"kind\",\"source\",\"variantId\"],\"additionalProperties\":false}],\"type\":\"object\"},\"value\":{\"type\":[\"string\",\"null\"]},\"deviation\":{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"direction\":{\"type\":\"string\",\"enum\":[\"above\",\"below\",\"other\"]},\"delta\":{\"type\":[\"number\",\"null\"]},\"unit\":{\"type\":[\"string\",\"null\"]},\"label\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"direction\",\"delta\",\"unit\",\"label\"],\"additionalProperties\":false},{\"type\":\"null\"}]},\"confidence\":{\"anyOf\":[{\"type\":\"number\",\"minimum\":0,\"maximum\":1},{\"type\":\"null\"}]},\"sourceBasis\":{\"type\":[\"string\",\"null\"]},\"trustGap\":{\"anyOf\":[{\"type\":\"string\",\"enum\":[\"below_confidence\",\"basis_unmet\",\"proposed\"]},{\"type\":\"null\"}]},\"citation\":{\"type\":\"object\",\"properties\":{\"citationId\":{\"type\":\"string\",\"minLength\":1},\"sourceType\":{\"type\":\"string\",\"minLength\":1},\"count\":{\"type\":\"integer\",\"exclusiveMinimum\":0,\"maximum\":9007199254740991}},\"required\":[\"citationId\",\"sourceType\",\"count\"],\"additionalProperties\":false}},\"required\":[\"attributeId\",\"state\",\"strength\",\"baseline\",\"value\",\"deviation\",\"confidence\",\"sourceBasis\",\"trustGap\"],\"additionalProperties\":false}},\"grade\":{\"type\":\"string\",\"enum\":[\"meets_all\",\"close\",\"hard_no\"]},\"minConfidence\":{\"anyOf\":[{\"type\":\"number\",\"minimum\":0,\"maximum\":1},{\"type\":\"null\"}]}},\"required\":[\"cells\",\"grade\",\"minConfidence\"],\"additionalProperties\":false},\"anatomy\":{\"description\":\"How this hit was assembled — the ranking representative behind the displayed variant, and the siblings a product collapse hid. Present only when `explain` was set AND the hit actually holds one of those (a distinct representative, or at least one hidden sibling); a plain uncollap",
16
+ "sed variant hit has no anatomy to report.\",\"type\":\"object\",\"properties\":{\"representative\":{\"description\":\"The RANKING identity behind this hit, when it differs from the displayed variant. Its score is the group's cursor key and the item's `signals` describe IT, not the card's photo. Omitted when the face IS the representative (no collapse, or the collapse fronted the top-ranked member).\",\"type\":\"object\",\"properties\":{\"variantId\":{\"type\":\"string\"},\"variantName\":{\"type\":[\"string\",\"null\"]},\"color\":{\"type\":[\"string\",\"null\"]},\"score\":{\"type\":\"number\"},\"signals\":{\"type\":\"object\",\"properties\":{\"portrait\":{\"type\":[\"number\",\"null\"]},\"image\":{\"type\":[\"number\",\"null\"]},\"category\":{\"type\":[\"number\",\"null\"]},\"colour\":{\"type\":[\"number\",\"null\"]},\"lexical\":{\"type\":[\"number\",\"null\"]},\"text\":{\"type\":[\"number\",\"null\"]}},\"required\":[\"image\",\"category\",\"colour\",\"lexical\",\"text\"],\"additionalProperties\":false}},\"required\":[\"variantId\",\"variantName\",\"color\",\"score\",\"signals\"],\"additionalProperties\":false},\"siblings\":{\"description\":\"Variants of the same product that also matched but were HIDDEN by `collapse: \\\"product\\\"` — the named form of `siblingCount`. Each carries its own per-arm signals, so an operator can see what each sibling matched on. Capped at 12 entries; `siblingCount` is always the complete tally.\",\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"variantId\":{\"type\":\"string\"},\"variantName\":{\"type\":[\"string\",\"null\"]},\"color\":{\"type\":[\"string\",\"null\"]},\"score\":{\"type\":\"number\"},\"signals\":{\"type\":\"object\",\"properties\":{\"portrait\":{\"type\":[\"number\",\"null\"]},\"image\":{\"type\":[\"number\",\"null\"]},\"category\":{\"type\":[\"number\",\"null\"]},\"colour\":{\"type\":[\"number\",\"null\"]},\"lexical\":{\"type\":[\"number\",\"null\"]},\"text\":{\"type\":[\"number\",\"null\"]}},\"required\":[\"image\",\"category\",\"colour\",\"lexical\",\"text\"],\"additionalProperties\":false}},\"required\":[\"variantId\",\"variantName\",\"color\",\"score\",\"signals\"],\"additionalProperties\":false}},\"siblingsTruncated\":{\"description\":\"Present (true) only when more than 12 siblings were hidden and `siblings` lists the highest-ranked slice.\",\"type\":\"boolean\"}},\"additionalProperties\":false}},\"required\":[\"variantId\",\"brand\",\"product\",\"brandSlug\",\"productId\",\"color\",\"variantName\",\"imageUrl\",\"hasImage\",\"palette\",\"score\"],\"additionalProperties\":false}},\"attribution\":{\"type\":\"object\",\"properties\":{\"responseId\":{\"type\":\"string\",\"description\":\"This response's stable id — the same value as the `X-Request-Id` header. Store it next to the outcome (an order, a save, a click) to join that outcome back to the ranking that produced it. Opaque: round-trip it verbatim, never parse it.\"},\"basis\":{\"type\":\"string\",\"enum\":[\"image\",\"colour\",\"text\",\"lexical\",\"entity-browse\",\"match\"],\"description\":\"The ranking basis, groupable across both ranked routes. On /api/v1/search this is the envelope's `mode` verbatim; /api/v1/search/match reports 'match'. Group your stored outcomes by this field.\"},\"issuedAt\":{\"type\":\"string\",\"description\":\"ISO-8601 timestamp of when this response was assembled, so a stored outcome carries its own age without a second lookup.\"}},\"required\":[\"responseId\",\"basis\",\"issuedAt\"],\"additionalProperties\":false,\"description\":\"Result attribution receipt. Present on every successful ranked response. Nothing on our side retains it — what you store IS the record.\"},\"refinement\":{\"description\":\"Inspectable receipt for narrowing this exact finalized candidate pool. The token is opaque and never exposes variant ids.\",\"type\":\"object\",\"properties\":{\"token\":{\"type\":\"string\"},\"expiresAt\":{\"type\":\"string\",\"format\":\"date-time\",\"pattern\":\"^(?:(?:\\\\d\\\\d[2468][048]|\\\\d\\\\d[13579][26]|\\\\d\\\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\\\d|30)|(?:02)-(?:0[1-9]|1\\\\d|2[0-8])))T(?:(?:[01]\\\\d|2[0-3]):[0-5]\\\\d:[0-5]\\\\d(?:\\\\.\\\\d+)?(?:Z))$\"},\"poolSize\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":800},\"schemaVersion\":{\"type\":\"string\"},\"projectionVersion\":{\"type\":\"integer\",\"minimum\":-9007199254740991,\"maximum\":9007199254740991}},\"required\":[\"token\",\"expiresAt\",\"poolSize\",\"schem",
17
+ "aVersion\",\"projectionVersion\"],\"additionalProperties\":false},\"understanding\":{\"description\":\"How the ordinary search path interpreted the raw material-domain query: recognized senses, applied/refused/ambiguous outcomes, evidence, and the semantic text left after exact entity consumption. New standard-search responses include it; optional in the schema for rolling-deploy compatibility.\",\"type\":\"object\",\"properties\":{\"version\":{\"type\":\"string\",\"const\":\"1\"},\"rawQuery\":{\"type\":\"string\"},\"normalisedQuery\":{\"type\":\"string\"},\"residualText\":{\"type\":\"string\"},\"status\":{\"type\":\"string\",\"enum\":[\"resolved\",\"ambiguous\",\"partial\",\"unresolved\"]},\"mode\":{\"type\":\"string\",\"enum\":[\"image\",\"colour\",\"text\",\"lexical\",\"entity-browse\"]},\"senses\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"id\":{\"type\":\"string\",\"minLength\":1},\"kind\":{\"type\":\"string\",\"enum\":[\"brand\",\"product\",\"paint_identity\",\"category\",\"material\",\"colour\",\"attribute\",\"attribute_value\",\"certification\"]},\"label\":{\"type\":\"string\",\"minLength\":1},\"outcome\":{\"type\":\"string\",\"enum\":[\"applied\",\"ambiguous\",\"recognized\",\"refused\"]},\"effect\":{\"type\":\"string\",\"enum\":[\"restrict\",\"prioritize\",\"rerank\",\"offer\",\"none\"]},\"confidence\":{\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"evidence\":{\"type\":\"object\",\"properties\":{\"source\":{\"type\":\"string\",\"minLength\":1},\"matchedText\":{\"type\":\"string\",\"minLength\":1},\"span\":{\"type\":\"object\",\"properties\":{\"start\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"end\":{\"type\":\"integer\",\"exclusiveMinimum\":0,\"maximum\":9007199254740991}},\"required\":[\"start\",\"end\"],\"additionalProperties\":false}},\"required\":[\"source\",\"matchedText\"],\"additionalProperties\":false},\"entity\":{\"type\":\"object\",\"properties\":{\"brandSlug\":{\"type\":\"string\"},\"productId\":{\"type\":\"string\"},\"variantId\":{\"type\":\"string\"},\"materialBankId\":{\"type\":\"string\"},\"productTypeId\":{\"type\":\"string\"},\"paintCode\":{\"type\":\"string\"},\"hex\":{\"type\":\"string\"}},\"additionalProperties\":false},\"reason\":{\"type\":\"string\"}},\"required\":[\"id\",\"kind\",\"label\",\"outcome\",\"effect\",\"confidence\",\"evidence\"],\"additionalProperties\":false}},\"canonicalLexical\":{\"type\":\"object\",\"properties\":{\"original\":{\"type\":\"string\"},\"outcome\":{\"type\":\"string\",\"enum\":[\"resolved\",\"ambiguous\",\"declined\",\"partial\",\"unresolved\"]},\"matches\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"original\":{\"type\":\"string\"},\"canonical\":{\"type\":\"string\"},\"rule\":{\"type\":\"string\",\"enum\":[\"exact\",\"plural-to-singular\",\"typo-to-canonical\"]},\"span\":{\"type\":\"object\",\"properties\":{\"start\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"end\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991}},\"required\":[\"start\",\"end\"],\"additionalProperties\":false},\"coverage\":{\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"readings\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"matchTarget\":{\"oneOf\":[{\"type\":\"object\",\"properties\":{\"type\":{\"type\":\"string\",\"const\":\"none\"},\"hasSelection\":{\"type\":\"boolean\",\"const\":false}},\"required\":[\"type\",\"hasSelection\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"type\":{\"type\":\"string\",\"const\":\"oklab\"},\"hasSelection\":{\"type\":\"boolean\",\"const\":true},\"L\":{\"type\":\"number\"},\"a\":{\"type\":\"number\"},\"b\":{\"type\":\"number\"},\"label\":{\"type\":\"string\"},\"previewColors\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}}},\"required\":[\"type\",\"hasSelection\",\"L\",\"a\",\"b\",\"label\",\"previewColors\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"type\":{\"type\":\"string\",\"const\":\"range\"},\"hasSelection\":{\"type\":\"boolean\",\"const\":true},\"L\":{\"type\":\"number\"},\"a\":{\"type\":\"number\"},\"b\":{\"type\":\"number\"},\"label\":{\"type\":\"string\"},\"previewColors\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"range\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"chroma\":{\"type\":\"array\",\"prefixItems\":[{\"type\":\"number\",\"minimum\":0},{\"type\":\"number\",\"minimum\":0}],\"items\":false,\"minItems\":2,\"maxItems\":2},\"hue\":{\"anyOf\":[{\"type\":\"array\",\"prefixItems\":[{\"type\":\"number\",\"minimum\":0,\"maximum\":360},{\"type\":\"number\",\"minimum\":0,\"maximum\":360}],\"items\":false,\"minItems\":2,\"maxItems\":2},{\"type\":\"null\"}]},\"lightness\":{\"type\":\"array\",\"prefixItem",
18
+ "s\":[{\"type\":\"number\",\"minimum\":0,\"maximum\":1},{\"type\":\"number\",\"minimum\":0,\"maximum\":1}],\"items\":false,\"minItems\":2,\"maxItems\":2}},\"required\":[\"chroma\",\"hue\",\"lightness\"],\"additionalProperties\":false}},\"representative\":{\"type\":\"object\",\"properties\":{\"hex\":{\"type\":\"string\",\"pattern\":\"^#[0-9a-f]{6}$\"}},\"required\":[\"hex\"],\"additionalProperties\":false}},\"required\":[\"type\",\"hasSelection\",\"L\",\"a\",\"b\",\"label\",\"previewColors\",\"range\",\"representative\"],\"additionalProperties\":false}],\"type\":\"object\"},\"colorResolution\":{\"oneOf\":[{\"type\":\"object\",\"properties\":{\"confidence\":{\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"input\":{\"type\":\"string\"},\"kind\":{\"type\":\"string\",\"const\":\"resolved\"},\"others\":{\"maxItems\":4,\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"band\":{\"type\":\"string\",\"enum\":[\"inside\",\"near\",\"far\"]},\"family\":{\"type\":\"string\",\"enum\":[\"white\",\"cream\",\"gray\",\"black\",\"beige\",\"taupe\",\"brown\",\"red\",\"orange\",\"yellow\",\"green\",\"teal\",\"blue\",\"purple\",\"pink\"]},\"hex\":{\"type\":\"string\",\"pattern\":\"^#[0-9a-f]{6}$\"},\"id\":{\"type\":\"string\",\"minLength\":1},\"name\":{\"type\":\"string\",\"minLength\":1},\"range\":{\"type\":\"object\",\"properties\":{\"chroma\":{\"type\":\"array\",\"prefixItems\":[{\"type\":\"number\",\"minimum\":0},{\"type\":\"number\",\"minimum\":0}],\"items\":false,\"minItems\":2,\"maxItems\":2},\"hue\":{\"anyOf\":[{\"type\":\"array\",\"prefixItems\":[{\"type\":\"number\",\"minimum\":0,\"maximum\":360},{\"type\":\"number\",\"minimum\":0,\"maximum\":360}],\"items\":false,\"minItems\":2,\"maxItems\":2},{\"type\":\"null\"}]},\"lightness\":{\"type\":\"array\",\"prefixItems\":[{\"type\":\"number\",\"minimum\":0,\"maximum\":1},{\"type\":\"number\",\"minimum\":0,\"maximum\":1}],\"items\":false,\"minItems\":2,\"maxItems\":2}},\"required\":[\"chroma\",\"hue\",\"lightness\"],\"additionalProperties\":false}},\"required\":[\"band\",\"family\",\"hex\",\"id\",\"name\",\"range\"],\"additionalProperties\":false}},\"principal\":{\"type\":\"object\",\"properties\":{\"band\":{\"type\":\"string\",\"enum\":[\"inside\",\"near\",\"far\"]},\"family\":{\"type\":\"string\",\"enum\":[\"white\",\"cream\",\"gray\",\"black\",\"beige\",\"taupe\",\"brown\",\"red\",\"orange\",\"yellow\",\"green\",\"teal\",\"blue\",\"purple\",\"pink\"]},\"hex\":{\"type\":\"string\",\"pattern\":\"^#[0-9a-f]{6}$\"},\"id\":{\"type\":\"string\",\"minLength\":1},\"name\":{\"type\":\"string\",\"minLength\":1},\"range\":{\"type\":\"object\",\"properties\":{\"chroma\":{\"type\":\"array\",\"prefixItems\":[{\"type\":\"number\",\"minimum\":0},{\"type\":\"number\",\"minimum\":0}],\"items\":false,\"minItems\":2,\"maxItems\":2},\"hue\":{\"anyOf\":[{\"type\":\"array\",\"prefixItems\":[{\"type\":\"number\",\"minimum\":0,\"maximum\":360},{\"type\":\"number\",\"minimum\":0,\"maximum\":360}],\"items\":false,\"minItems\":2,\"maxItems\":2},{\"type\":\"null\"}]},\"lightness\":{\"type\":\"array\",\"prefixItems\":[{\"type\":\"number\",\"minimum\":0,\"maximum\":1},{\"type\":\"number\",\"minimum\":0,\"maximum\":1}],\"items\":false,\"minItems\":2,\"maxItems\":2}},\"required\":[\"chroma\",\"hue\",\"lightness\"],\"additionalProperties\":false}},\"required\":[\"band\",\"family\",\"hex\",\"id\",\"name\",\"range\"],\"additionalProperties\":false},\"source\":{\"type\":\"string\",\"enum\":[\"name\",\"family\",\"composed\",\"hex\",\"reference\",\"semantic\"]}},\"required\":[\"confidence\",\"input\",\"kind\",\"others\",\"principal\",\"source\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"input\":{\"type\":\"string\"},\"kind\":{\"type\":\"string\",\"const\":\"unresolved\"},\"reason\":{\"type\":\"string\",\"enum\":[\"unknown-term\",\"ambiguous-modifier\",\"contradictory-modifiers\",\"unsupported-base\",\"empty-range\",\"unrepresentable-color\"]},\"terms\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}}},\"required\":[\"input\",\"kind\",\"reason\",\"terms\"],\"additionalProperties\":false}],\"type\":\"object\"},\"authority\":{\"type\":\"string\",\"enum\":[\"taxonomy\",\"colorscope\"]},\"identity\":{\"type\":\"string\",\"minLength\":1},\"outcome\":{\"type\":\"string\",\"enum\":[\"equivalent\",\"ambiguous\",\"declined\"]},\"executionStatus\":{\"type\":\"string\",\"enum\":[\"applied\",\"suppressed\",\"executable\",\"recognized-but-unexecutable\",\"declined\"],\"description\":\"MaterialGraph's execution verdict for this provider-owned reading. Taxonomy adapters report 'applied' only when their category or criterion contributed to this query; an adapter-known ambiguous or conflicting reading is 'suppressed'. 'recognized-but-unexecutable' means no current mapping can apply it.\"},\"r",
19
+ "ole\":{\"anyOf\":[{\"type\":\"string\",\"enum\":[\"primary\",\"secondary\"]},{\"type\":\"null\"}]},\"kind\":{\"type\":\"string\",\"enum\":[\"domain\",\"core-colour\",\"paint\"]},\"label\":{\"type\":\"string\"},\"libraryId\":{\"type\":\"string\"},\"code\":{\"type\":\"string\"},\"aliasCount\":{\"type\":\"integer\",\"exclusiveMinimum\":0,\"maximum\":9007199254740991},\"matchForm\":{\"type\":\"string\",\"enum\":[\"name\",\"code\",\"authoritative-term\"]},\"collisionCount\":{\"type\":\"integer\",\"exclusiveMinimum\":0,\"maximum\":9007199254740991},\"familyId\":{\"type\":\"string\"},\"hex\":{\"type\":\"string\",\"pattern\":\"^#[0-9a-f]{6}$\"},\"level\":{\"type\":\"string\",\"enum\":[\"family\",\"tone-band\",\"branch\",\"concept\"]},\"lifecycle\":{\"type\":\"object\",\"properties\":{\"introducedAt\":{\"type\":\"string\",\"minLength\":1},\"reviewedAt\":{\"type\":\"string\",\"minLength\":1},\"status\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"introducedAt\",\"status\"],\"additionalProperties\":false},\"provenance\":{\"type\":\"object\",\"properties\":{\"basis\":{\"type\":\"string\",\"minLength\":1},\"snapshot\":{\"type\":\"string\",\"minLength\":1},\"source\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"basis\",\"source\"],\"additionalProperties\":false}},\"required\":[\"authority\",\"identity\",\"outcome\",\"executionStatus\",\"role\"],\"additionalProperties\":false}},\"receipt\":{\"type\":\"object\",\"properties\":{\"original\":{\"type\":\"string\"},\"canonical\":{\"type\":\"string\"},\"rule\":{\"type\":\"string\",\"enum\":[\"exact\",\"plural-to-singular\",\"typo-to-canonical\"]},\"authorities\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"authority\":{\"type\":\"string\",\"enum\":[\"taxonomy\",\"colorscope\"]},\"version\":{\"type\":\"string\",\"minLength\":1},\"revision\":{\"type\":\"string\",\"minLength\":1},\"digest\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"authority\",\"version\",\"revision\"],\"additionalProperties\":false}}},\"required\":[\"original\",\"canonical\",\"rule\",\"authorities\"],\"additionalProperties\":false}},\"required\":[\"original\",\"canonical\",\"rule\",\"span\",\"coverage\",\"readings\",\"receipt\"],\"additionalProperties\":false}},\"unresolved\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"original\":{\"type\":\"string\"},\"canonical\":{\"type\":\"string\"},\"rule\":{\"type\":\"string\",\"enum\":[\"exact\",\"plural-to-singular\",\"typo-to-canonical\"]},\"span\":{\"type\":\"object\",\"properties\":{\"start\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"end\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991}},\"required\":[\"start\",\"end\"],\"additionalProperties\":false},\"coverage\":{\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"readings\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"matchTarget\":{\"oneOf\":[{\"type\":\"object\",\"properties\":{\"type\":{\"type\":\"string\",\"const\":\"none\"},\"hasSelection\":{\"type\":\"boolean\",\"const\":false}},\"required\":[\"type\",\"hasSelection\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"type\":{\"type\":\"string\",\"const\":\"oklab\"},\"hasSelection\":{\"type\":\"boolean\",\"const\":true},\"L\":{\"type\":\"number\"},\"a\":{\"type\":\"number\"},\"b\":{\"type\":\"number\"},\"label\":{\"type\":\"string\"},\"previewColors\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}}},\"required\":[\"type\",\"hasSelection\",\"L\",\"a\",\"b\",\"label\",\"previewColors\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"type\":{\"type\":\"string\",\"const\":\"range\"},\"hasSelection\":{\"type\":\"boolean\",\"const\":true},\"L\":{\"type\":\"number\"},\"a\":{\"type\":\"number\"},\"b\":{\"type\":\"number\"},\"label\":{\"type\":\"string\"},\"previewColors\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"range\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"chroma\":{\"type\":\"array\",\"prefixItems\":[{\"type\":\"number\",\"minimum\":0},{\"type\":\"number\",\"minimum\":0}],\"items\":false,\"minItems\":2,\"maxItems\":2},\"hue\":{\"anyOf\":[{\"type\":\"array\",\"prefixItems\":[{\"type\":\"number\",\"minimum\":0,\"maximum\":360},{\"type\":\"number\",\"minimum\":0,\"maximum\":360}],\"items\":false,\"minItems\":2,\"maxItems\":2},{\"type\":\"null\"}]},\"lightness\":{\"type\":\"array\",\"prefixItems\":[{\"type\":\"number\",\"minimum\":0,\"maximum\":1},{\"type\":\"number\",\"minimum\":0,\"maximum\":1}],\"items\":false,\"minItems\":2,\"maxItems\":2}},\"required\":[\"chroma\",\"hue\",\"lightness\"],\"additionalProperties\":false}},\"representative\":{\"type\":\"object\",\"properties\":{\"hex\":{\"type\":\"string\",\"pattern\":\"^#[0-9a-f]{6}$\"}},\"required\":[\"hex\"],\"additionalP",
20
+ "roperties\":false}},\"required\":[\"type\",\"hasSelection\",\"L\",\"a\",\"b\",\"label\",\"previewColors\",\"range\",\"representative\"],\"additionalProperties\":false}],\"type\":\"object\"},\"colorResolution\":{\"oneOf\":[{\"type\":\"object\",\"properties\":{\"confidence\":{\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"input\":{\"type\":\"string\"},\"kind\":{\"type\":\"string\",\"const\":\"resolved\"},\"others\":{\"maxItems\":4,\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"band\":{\"type\":\"string\",\"enum\":[\"inside\",\"near\",\"far\"]},\"family\":{\"type\":\"string\",\"enum\":[\"white\",\"cream\",\"gray\",\"black\",\"beige\",\"taupe\",\"brown\",\"red\",\"orange\",\"yellow\",\"green\",\"teal\",\"blue\",\"purple\",\"pink\"]},\"hex\":{\"type\":\"string\",\"pattern\":\"^#[0-9a-f]{6}$\"},\"id\":{\"type\":\"string\",\"minLength\":1},\"name\":{\"type\":\"string\",\"minLength\":1},\"range\":{\"type\":\"object\",\"properties\":{\"chroma\":{\"type\":\"array\",\"prefixItems\":[{\"type\":\"number\",\"minimum\":0},{\"type\":\"number\",\"minimum\":0}],\"items\":false,\"minItems\":2,\"maxItems\":2},\"hue\":{\"anyOf\":[{\"type\":\"array\",\"prefixItems\":[{\"type\":\"number\",\"minimum\":0,\"maximum\":360},{\"type\":\"number\",\"minimum\":0,\"maximum\":360}],\"items\":false,\"minItems\":2,\"maxItems\":2},{\"type\":\"null\"}]},\"lightness\":{\"type\":\"array\",\"prefixItems\":[{\"type\":\"number\",\"minimum\":0,\"maximum\":1},{\"type\":\"number\",\"minimum\":0,\"maximum\":1}],\"items\":false,\"minItems\":2,\"maxItems\":2}},\"required\":[\"chroma\",\"hue\",\"lightness\"],\"additionalProperties\":false}},\"required\":[\"band\",\"family\",\"hex\",\"id\",\"name\",\"range\"],\"additionalProperties\":false}},\"principal\":{\"type\":\"object\",\"properties\":{\"band\":{\"type\":\"string\",\"enum\":[\"inside\",\"near\",\"far\"]},\"family\":{\"type\":\"string\",\"enum\":[\"white\",\"cream\",\"gray\",\"black\",\"beige\",\"taupe\",\"brown\",\"red\",\"orange\",\"yellow\",\"green\",\"teal\",\"blue\",\"purple\",\"pink\"]},\"hex\":{\"type\":\"string\",\"pattern\":\"^#[0-9a-f]{6}$\"},\"id\":{\"type\":\"string\",\"minLength\":1},\"name\":{\"type\":\"string\",\"minLength\":1},\"range\":{\"type\":\"object\",\"properties\":{\"chroma\":{\"type\":\"array\",\"prefixItems\":[{\"type\":\"number\",\"minimum\":0},{\"type\":\"number\",\"minimum\":0}],\"items\":false,\"minItems\":2,\"maxItems\":2},\"hue\":{\"anyOf\":[{\"type\":\"array\",\"prefixItems\":[{\"type\":\"number\",\"minimum\":0,\"maximum\":360},{\"type\":\"number\",\"minimum\":0,\"maximum\":360}],\"items\":false,\"minItems\":2,\"maxItems\":2},{\"type\":\"null\"}]},\"lightness\":{\"type\":\"array\",\"prefixItems\":[{\"type\":\"number\",\"minimum\":0,\"maximum\":1},{\"type\":\"number\",\"minimum\":0,\"maximum\":1}],\"items\":false,\"minItems\":2,\"maxItems\":2}},\"required\":[\"chroma\",\"hue\",\"lightness\"],\"additionalProperties\":false}},\"required\":[\"band\",\"family\",\"hex\",\"id\",\"name\",\"range\"],\"additionalProperties\":false},\"source\":{\"type\":\"string\",\"enum\":[\"name\",\"family\",\"composed\",\"hex\",\"reference\",\"semantic\"]}},\"required\":[\"confidence\",\"input\",\"kind\",\"others\",\"principal\",\"source\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"input\":{\"type\":\"string\"},\"kind\":{\"type\":\"string\",\"const\":\"unresolved\"},\"reason\":{\"type\":\"string\",\"enum\":[\"unknown-term\",\"ambiguous-modifier\",\"contradictory-modifiers\",\"unsupported-base\",\"empty-range\",\"unrepresentable-color\"]},\"terms\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}}},\"required\":[\"input\",\"kind\",\"reason\",\"terms\"],\"additionalProperties\":false}],\"type\":\"object\"},\"authority\":{\"type\":\"string\",\"enum\":[\"taxonomy\",\"colorscope\"]},\"identity\":{\"type\":\"string\",\"minLength\":1},\"outcome\":{\"type\":\"string\",\"enum\":[\"equivalent\",\"ambiguous\",\"declined\"]},\"executionStatus\":{\"type\":\"string\",\"enum\":[\"applied\",\"suppressed\",\"executable\",\"recognized-but-unexecutable\",\"declined\"],\"description\":\"MaterialGraph's execution verdict for this provider-owned reading. Taxonomy adapters report 'applied' only when their category or criterion contributed to this query; an adapter-known ambiguous or conflicting reading is 'suppressed'. 'recognized-but-unexecutable' means no current mapping can apply it.\"},\"role\":{\"anyOf\":[{\"type\":\"string\",\"enum\":[\"primary\",\"secondary\"]},{\"type\":\"null\"}]},\"kind\":{\"type\":\"string\",\"enum\":[\"domain\",\"core-colour\",\"paint\"]},\"label\":{\"type\":\"string\"},\"libraryId\":{\"type\":\"string\"},\"code\":{\"type\":\"string\"},\"aliasCount\":{\"type\":\"integer\",\"exclusiveMinimum\":0,\"maximum\":9007199254740991},\"matchForm\":{\"type\":\"stri",
21
+ "ng\",\"enum\":[\"name\",\"code\",\"authoritative-term\"]},\"collisionCount\":{\"type\":\"integer\",\"exclusiveMinimum\":0,\"maximum\":9007199254740991},\"familyId\":{\"type\":\"string\"},\"hex\":{\"type\":\"string\",\"pattern\":\"^#[0-9a-f]{6}$\"},\"level\":{\"type\":\"string\",\"enum\":[\"family\",\"tone-band\",\"branch\",\"concept\"]},\"lifecycle\":{\"type\":\"object\",\"properties\":{\"introducedAt\":{\"type\":\"string\",\"minLength\":1},\"reviewedAt\":{\"type\":\"string\",\"minLength\":1},\"status\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"introducedAt\",\"status\"],\"additionalProperties\":false},\"provenance\":{\"type\":\"object\",\"properties\":{\"basis\":{\"type\":\"string\",\"minLength\":1},\"snapshot\":{\"type\":\"string\",\"minLength\":1},\"source\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"basis\",\"source\"],\"additionalProperties\":false}},\"required\":[\"authority\",\"identity\",\"outcome\",\"executionStatus\",\"role\"],\"additionalProperties\":false}},\"receipt\":{\"type\":\"object\",\"properties\":{\"original\":{\"type\":\"string\"},\"canonical\":{\"type\":\"string\"},\"rule\":{\"type\":\"string\",\"enum\":[\"exact\",\"plural-to-singular\",\"typo-to-canonical\"]},\"authorities\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"authority\":{\"type\":\"string\",\"enum\":[\"taxonomy\",\"colorscope\"]},\"version\":{\"type\":\"string\",\"minLength\":1},\"revision\":{\"type\":\"string\",\"minLength\":1},\"digest\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"authority\",\"version\",\"revision\"],\"additionalProperties\":false}}},\"required\":[\"original\",\"canonical\",\"rule\",\"authorities\"],\"additionalProperties\":false}},\"required\":[\"original\",\"canonical\",\"rule\",\"span\",\"coverage\",\"readings\",\"receipt\"],\"additionalProperties\":false}}},\"required\":[\"original\",\"outcome\",\"matches\",\"unresolved\"],\"additionalProperties\":false},\"criteria\":{\"type\":\"object\",\"properties\":{\"applied\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"criterion\":{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, e.g. \\\"primary_material\\\" or \\\"fire_rating\\\" — or the special id \\\"brand\\\", which filters on the brand RELATION (a join, carried by every variant) rather than on any attribute.\"},\"term\":{\"type\":\"string\",\"description\":\"Canonical term the attribute must equal, e.g. \\\"textile\\\". For \\\"brand\\\", a brand slug (\\\"kvadrat\\\") or display name (\\\"Kvadrat\\\"), case-insensitive — exactly the `value` a `brand` facet reports. An unrecognised brand is a 400 naming it, never an empty page.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"term\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, or the special id \\\"brand\\\" for the brand relation.\"},\"terms\":{\"minItems\":1,\"type\":\"array\",\"items\":{\"type\":\"string\"},\"description\":\"Canonical terms, ANY ONE of which satisfies the filter — this is how OR is expressed. OR lives inside one entry; separate `filters[]` entries AND together. Used for ordinal scales, where a stricter class satisfies a looser one, and for a multi-select rail — `{attributeId:\\\"brand\\\", terms:[\\\"kvadrat\\\",\\\"rubelli\\\"]}` returns materials from either brand.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does",
22
+ " NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"terms\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\"},\"excludesTerm\":{\"type\":\"string\",\"minLength\":1},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"excludesTerm\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id that must have an accepted value, e.g. \\\"sample_image_mm_per_px\\\".\"},\"hasValue\":{\"type\":\"boolean\",\"const\":true,\"description\":\"Require any accepted value for this attribute.\"},\"asOf\":{\"description\":\"Reference date for EPD expiration, exclusive: the value must remain valid after this date.\",\"type\":\"string\",\"format\":\"date\",\"pattern\":\"^(?:(?:\\\\d\\\\d[2468][048]|\\\\d\\\\d[13579][26]|\\\\d\\\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\\\d|30)|(?:02)-(?:0[1-9]|1\\\\d|2[0-8])))$\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"hasValue\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id holding a NUMBER, e.g. \\\"light_reflectance_value\\\" or \\\"lead_time_days\\\".\"},\"min\":{\"description\":\"Inclusive lower bound. Omit for a ceiling-only range.\",\"type\":\"number\"},\"max\":{\"description\":\"Inclusive upper bound. Omit for a floor-only range.\",\"type\":\"number\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\"],\"additionalProperties\":false}]},\"lane\":{\"type\":\"string\",\"enum\":[\"filter\",\"preference\"]},\"lexicalSignal\":{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"minLength\":1},\"canonical\":{\"type\":\"string\",\"minLength\":1},\"identity\":{\"type\":\"string\",\"minLength\":1},\"original\":{\"type\":\"string\",\"minLength\":1},\"relation\":{\"type\":\"string\",\"enum\":[\"equivalent\",\"broader-storage-term\"]},\"span\":{\"type\":\"object\",\"properties\":{\"start\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"end\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991}},\"required\":[\"start\",\"end\"],\"additionalProperties\":false},\"storedTerm\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"attributeId\",\"canonical\",\"identity\",\"original\",\"relation\",\"span\",\"storedTerm\"],\"additionalProperties\":false},\"source\":{\"type\":\"string\",\"enum\":[\"compiled\",\"taxonomy-adapter\"]},\"span\":{\"",
23
+ "type\":\"string\",\"minLength\":1}},\"required\":[\"criterion\",\"lane\",\"source\",\"span\"],\"additionalProperties\":false}},\"conflicts\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"minLength\":1},\"caller\":{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, e.g. \\\"primary_material\\\" or \\\"fire_rating\\\" — or the special id \\\"brand\\\", which filters on the brand RELATION (a join, carried by every variant) rather than on any attribute.\"},\"term\":{\"type\":\"string\",\"description\":\"Canonical term the attribute must equal, e.g. \\\"textile\\\". For \\\"brand\\\", a brand slug (\\\"kvadrat\\\") or display name (\\\"Kvadrat\\\"), case-insensitive — exactly the `value` a `brand` facet reports. An unrecognised brand is a 400 naming it, never an empty page.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"term\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, or the special id \\\"brand\\\" for the brand relation.\"},\"terms\":{\"minItems\":1,\"type\":\"array\",\"items\":{\"type\":\"string\"},\"description\":\"Canonical terms, ANY ONE of which satisfies the filter — this is how OR is expressed. OR lives inside one entry; separate `filters[]` entries AND together. Used for ordinal scales, where a stricter class satisfies a looser one, and for a multi-select rail — `{attributeId:\\\"brand\\\", terms:[\\\"kvadrat\\\",\\\"rubelli\\\"]}` returns materials from either brand.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"terms\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\"},\"excludesTerm\":{\"type\":\"string\",\"minLength\":1},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"excludesTerm\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id that must have an accepted value, e.g. \\\"sample_image_mm_per_px\\\".\"},\"hasValue\":{\"type\":\"boolean\",\"const\":true,\"description\":\"Require any accepted value for this attribute.\"},\"asOf\":{\"description\":\"Reference date for EPD expiration, exclusive: the value must remain valid after this date.\",\"type\":\"string\",\"format\":\"date\",\"pattern\":\"^(?:(?:\\\\d\\\\d[2468][048]|\\\\d\\\\d[13579][26]|\\\\d\\\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[1",
24
+ "2]\\\\d|30)|(?:02)-(?:0[1-9]|1\\\\d|2[0-8])))$\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"hasValue\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id holding a NUMBER, e.g. \\\"light_reflectance_value\\\" or \\\"lead_time_days\\\".\"},\"min\":{\"description\":\"Inclusive lower bound. Omit for a ceiling-only range.\",\"type\":\"number\"},\"max\":{\"description\":\"Inclusive upper bound. Omit for a floor-only range.\",\"type\":\"number\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\"],\"additionalProperties\":false}]},\"compiled\":{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, e.g. \\\"primary_material\\\" or \\\"fire_rating\\\" — or the special id \\\"brand\\\", which filters on the brand RELATION (a join, carried by every variant) rather than on any attribute.\"},\"term\":{\"type\":\"string\",\"description\":\"Canonical term the attribute must equal, e.g. \\\"textile\\\". For \\\"brand\\\", a brand slug (\\\"kvadrat\\\") or display name (\\\"Kvadrat\\\"), case-insensitive — exactly the `value` a `brand` facet reports. An unrecognised brand is a 400 naming it, never an empty page.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"term\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, or the special id \\\"brand\\\" for the brand relation.\"},\"terms\":{\"minItems\":1,\"type\":\"array\",\"items\":{\"type\":\"string\"},\"description\":\"Canonical terms, ANY ONE of which satisfies the filter — this is how OR is expressed. OR lives inside one entry; separate `filters[]` entries AND together. Used for ordinal scales, where a stricter class satisfies a looser one, and for a multi-select rail — `{attributeId:\\\"brand\\\", terms:[\\\"kvadrat\\\",\\\"rubelli\\\"]}` returns materials from either brand.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boo",
25
+ "lean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"terms\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\"},\"excludesTerm\":{\"type\":\"string\",\"minLength\":1},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"excludesTerm\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id that must have an accepted value, e.g. \\\"sample_image_mm_per_px\\\".\"},\"hasValue\":{\"type\":\"boolean\",\"const\":true,\"description\":\"Require any accepted value for this attribute.\"},\"asOf\":{\"description\":\"Reference date for EPD expiration, exclusive: the value must remain valid after this date.\",\"type\":\"string\",\"format\":\"date\",\"pattern\":\"^(?:(?:\\\\d\\\\d[2468][048]|\\\\d\\\\d[13579][26]|\\\\d\\\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\\\d|30)|(?:02)-(?:0[1-9]|1\\\\d|2[0-8])))$\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"hasValue\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id holding a NUMBER, e.g. \\\"light_reflectance_value\\\" or \\\"lead_time_days\\\".\"},\"min\":{\"description\":\"Inclusive lower bound. Omit for a ceiling-only range.\",\"type\":\"number\"},\"max\":{\"description\":\"Inclusive upper bound. Omit for a floor-only range.\",\"type\":\"number\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\"],\"additionalProperties\":false}]},\"reason\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"attributeId\",\"caller\",\"compiled\",\"reason\"],\"additionalProperties\":false}},\"exclusions\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"criterion\":{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, e.g. \\\"primary_material\\\" or \\\"fire_rating\\\" — or the special id \\\"brand\\\", which filters on the brand RELATION (a join, carried by every variant) rather than on any attribute.\"},\"term\":{\"type\":\"string\",\"description\":\"Canonical term the attribute must equal, e.g. \\\"textile\\\". For \\\"brand\\\", a brand slug (\\\"kvadrat\\\") or display name (\\\"Kvadrat\\\"), case-insensitive — exactly the `value` a `brand` facet reports. An unrecognised brand is a 400 naming it, never an empty page.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence",
26
+ " floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"term\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, or the special id \\\"brand\\\" for the brand relation.\"},\"terms\":{\"minItems\":1,\"type\":\"array\",\"items\":{\"type\":\"string\"},\"description\":\"Canonical terms, ANY ONE of which satisfies the filter — this is how OR is expressed. OR lives inside one entry; separate `filters[]` entries AND together. Used for ordinal scales, where a stricter class satisfies a looser one, and for a multi-select rail — `{attributeId:\\\"brand\\\", terms:[\\\"kvadrat\\\",\\\"rubelli\\\"]}` returns materials from either brand.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"terms\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\"},\"excludesTerm\":{\"type\":\"string\",\"minLength\":1},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"excludesTerm\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id that must have an accepted value, e.g. \\\"sample_image_mm_per_px\\\".\"},\"hasValue\":{\"type\":\"boolean\",\"const\":true,\"description\":\"Require any accepted value for this attribute.\"},\"asOf\":{\"description\":\"Reference date for EPD expiration, exclusive: the value must remain valid after this date.\",\"type\":\"string\",\"format\":\"date\",\"pattern\":\"^(?:(?:\\\\d\\\\d[2468][048]|\\\\d\\\\d[13579][26]|\\\\d\\\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\\\d|30)|(?:02)-(?:0[1-9]|1\\\\d|2[0-8])))$\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"hasValue\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id holding a NUMBER, e.g. \\\"light_reflectance_value\\\" or \\\"lead_time_days\\\".\"},\"min\":{\"description\":\"Inclusive l",
27
+ "ower bound. Omit for a ceiling-only range.\",\"type\":\"number\"},\"max\":{\"description\":\"Inclusive upper bound. Omit for a floor-only range.\",\"type\":\"number\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\"],\"additionalProperties\":false}]},\"execution\":{\"type\":\"string\",\"const\":\"blocked-positive-only-contract\"},\"lane\":{\"type\":\"string\",\"enum\":[\"hard\",\"soft\"]},\"missingValuePolicy\":{\"type\":\"string\",\"const\":\"retain-unknown\"},\"span\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"criterion\",\"execution\",\"lane\",\"missingValuePolicy\",\"span\"],\"additionalProperties\":false}},\"preferences\":{\"type\":\"array\",\"items\":{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, e.g. \\\"primary_material\\\" or \\\"fire_rating\\\" — or the special id \\\"brand\\\", which filters on the brand RELATION (a join, carried by every variant) rather than on any attribute.\"},\"term\":{\"type\":\"string\",\"description\":\"Canonical term the attribute must equal, e.g. \\\"textile\\\". For \\\"brand\\\", a brand slug (\\\"kvadrat\\\") or display name (\\\"Kvadrat\\\"), case-insensitive — exactly the `value` a `brand` facet reports. An unrecognised brand is a 400 naming it, never an empty page.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"term\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, or the special id \\\"brand\\\" for the brand relation.\"},\"terms\":{\"minItems\":1,\"type\":\"array\",\"items\":{\"type\":\"string\"},\"description\":\"Canonical terms, ANY ONE of which satisfies the filter — this is how OR is expressed. OR lives inside one entry; separate `filters[]` entries AND together. Used for ordinal scales, where a stricter class satisfies a looser one, and for a multi-select rail — `{attributeId:\\\"brand\\\", terms:[\\\"kvadrat\\\",\\\"rubelli\\\"]}` returns materials from either brand.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"terms\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\"},\"excludesTerm\":{\"type\":\"string\",\"minLength\":1},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"der",
28
+ "ived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"excludesTerm\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id that must have an accepted value, e.g. \\\"sample_image_mm_per_px\\\".\"},\"hasValue\":{\"type\":\"boolean\",\"const\":true,\"description\":\"Require any accepted value for this attribute.\"},\"asOf\":{\"description\":\"Reference date for EPD expiration, exclusive: the value must remain valid after this date.\",\"type\":\"string\",\"format\":\"date\",\"pattern\":\"^(?:(?:\\\\d\\\\d[2468][048]|\\\\d\\\\d[13579][26]|\\\\d\\\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\\\d|30)|(?:02)-(?:0[1-9]|1\\\\d|2[0-8])))$\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"hasValue\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id holding a NUMBER, e.g. \\\"light_reflectance_value\\\" or \\\"lead_time_days\\\".\"},\"min\":{\"description\":\"Inclusive lower bound. Omit for a ceiling-only range.\",\"type\":\"number\"},\"max\":{\"description\":\"Inclusive upper bound. Omit for a floor-only range.\",\"type\":\"number\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\"],\"additionalProperties\":false}]}},\"residualText\":{\"type\":[\"string\",\"null\"]},\"unresolved\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"code\":{\"type\":\"string\",\"enum\":[\"missing-searchable-attribute\",\"missing-dimension-pair-contract\",\"missing-explicit-comparator\",\"negative-criterion-contract-blocked\",\"registry-contract-drift\",\"unsupported-numeric-format\",\"unsupported-unit\",\"unsupported-strict-comparator\"]},\"reason\":{\"type\":\"string\",\"minLength\":1},\"span\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"code\",\"reason\",\"span\"],\"additionalProperties\":false}}},\"required\":[\"applied\",\"conflicts\",\"exclusions\",\"preferences\",\"residualText\",\"unresolved\"],\"additionalProperties\":false},\"productType\":{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"enum\":[\"leaf\",\"group\",\"vector\",\"ambiguous\",\"none\"]},\"gate\":{\"type\":\"string\",\"enum\":[\"hard\",\"constrain\",\"none\"]},\"pass\":{\"type\":\"string\",\"enum\":[\"taxonomy\",\"synonym\",\"display-name\",\"group\",\"head-noun\",\"vector\",\"none\"]},\"reason\":{\"type\":\"string\",\"minLength\":1},\"displayName\":{\"type\":[\"string\",\"null\"]},\"productTypeId\":{\"type\":[\"string\",\"null\"]},\"groupKey\":{\"type\":[\"string\",\"null\"]},\"productTypeIds\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"similarity\":{\"type\":[\"number\",\"null\"]},\"margin\":{\"type\":[\"number\",\"null\"]},\"residualText\":{\"type\":\"string\"},\"vectorPassRan\":{\"type\":\"boolean\"},\"registryVersion\":{\"type\":\"string\",\"minLength\":1},\"durationMs\":{\"type\":\"number\",\"minimum\":0},\"candidates\":{\"type\":\"array\",\"items\":{\"type\":\"o",
29
+ "bject\",\"properties\":{\"productTypeId\":{\"type\":\"string\",\"minLength\":1},\"displayName\":{\"type\":\"string\",\"minLength\":1},\"similarity\":{\"type\":\"number\"}},\"required\":[\"productTypeId\",\"displayName\",\"similarity\"],\"additionalProperties\":false}}},\"required\":[\"kind\",\"gate\",\"pass\",\"reason\",\"displayName\",\"productTypeId\",\"groupKey\",\"productTypeIds\",\"similarity\",\"margin\",\"residualText\",\"vectorPassRan\",\"registryVersion\",\"durationMs\",\"candidates\"],\"additionalProperties\":false},\"arm\":{\"type\":\"object\",\"properties\":{\"arm\":{\"anyOf\":[{\"type\":\"string\",\"enum\":[\"dense\",\"lexical\",\"colour\"]},{\"type\":\"null\"}]},\"reason\":{\"type\":\"string\",\"minLength\":1},\"reasonDetail\":{\"type\":\"string\",\"enum\":[\"derived-colour-veto\",\"typo-recovery\",\"literal-compound-without-type\"]},\"outcome\":{\"type\":\"string\",\"enum\":[\"dense\",\"lexical\",\"colour\",\"browse\",\"criteria-only\",\"ambiguous\"]},\"decidedBy\":{\"type\":\"string\",\"enum\":[\"deterministic\",\"model\"]},\"productTypeGate\":{\"type\":\"string\",\"enum\":[\"hard\",\"constrain\",\"none\"]},\"productTypeIds\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"residual\":{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"enum\":[\"compound\",\"descriptive\",\"ambiguous\",\"empty\"]},\"tokens\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"resolvedTokens\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"unresolvedTokens\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"resolvedRatio\":{\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"comparisonFrame\":{\"type\":\"boolean\"}},\"required\":[\"kind\",\"tokens\",\"resolvedTokens\",\"unresolvedTokens\",\"resolvedRatio\",\"comparisonFrame\"],\"additionalProperties\":false},\"registryVersion\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"arm\",\"reason\",\"outcome\",\"decidedBy\",\"productTypeGate\",\"productTypeIds\",\"residual\",\"registryVersion\"],\"additionalProperties\":false},\"ambiguity\":{\"type\":\"object\",\"properties\":{\"outcome\":{\"type\":\"string\",\"enum\":[\"applied\",\"abstained\",\"refused\",\"timeout\"]},\"modelId\":{\"type\":[\"string\",\"null\"]},\"providerRan\":{\"type\":\"boolean\"},\"durationMs\":{\"type\":\"number\",\"minimum\":0},\"timeoutMs\":{\"type\":\"number\",\"exclusiveMinimum\":0},\"validation\":{\"type\":\"string\",\"enum\":[\"valid\",\"invalid\",\"not-run\"]},\"refusalCode\":{\"type\":[\"string\",\"null\"]},\"confidence\":{\"type\":[\"number\",\"null\"]},\"routing\":{\"anyOf\":[{\"type\":\"string\",\"enum\":[\"compound\",\"descriptive\"]},{\"type\":\"null\"}]},\"productTypeIds\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"changedArm\":{\"type\":\"boolean\"}},\"required\":[\"outcome\",\"modelId\",\"providerRan\",\"durationMs\",\"timeoutMs\",\"validation\",\"refusalCode\",\"confidence\",\"routing\",\"productTypeIds\",\"changedArm\"],\"additionalProperties\":false},\"categoryScope\":{\"type\":\"object\",\"properties\":{\"source\":{\"type\":\"string\",\"enum\":[\"caller\",\"anchor\"]},\"mechanism\":{\"type\":\"string\",\"enum\":[\"sql-predicate\",\"provider-filter\",\"allowlist\",\"prune\"]},\"variantCount\":{\"anyOf\":[{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},{\"type\":\"null\"}]},\"ceiling\":{\"type\":\"integer\",\"exclusiveMinimum\":0,\"maximum\":9007199254740991},\"specScanBounded\":{\"type\":\"boolean\"}},\"required\":[\"source\",\"mechanism\",\"variantCount\",\"ceiling\"],\"additionalProperties\":{}},\"specAllowlist\":{\"type\":\"object\",\"properties\":{\"limit\":{\"type\":\"integer\",\"exclusiveMinimum\":0,\"maximum\":9007199254740991},\"returned\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"retrieved\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"truncated\":{\"type\":\"boolean\"},\"emptyMeansScarcity\":{\"type\":\"boolean\"},\"outcome\":{\"type\":\"string\",\"enum\":[\"corpus-scarcity\",\"retrieval-miss\",\"unknown\"]}},\"required\":[\"limit\",\"returned\",\"truncated\",\"emptyMeansScarcity\"],\"additionalProperties\":false},\"unsatisfiableNarrowing\":{\"type\":\"object\",\"properties\":{\"narrowing\":{\"type\":\"string\",\"enum\":[\"product-type\"]},\"gate\":{\"type\":\"string\",\"enum\":[\"hard\"]},\"productTypeId\":{\"type\":\"string\",\"minLength\":1},\"displayName\":{\"type\":\"string\",\"minLength\":1},\"productTypeIds\":{\"type\":\"array\",\"items\":{\"type\":\"string\",\"minLength\":1}},\"pass\":{\"type\":\"string\",\"minLength\":1},\"evidence\":{\"type\":\"string\",\"enum\":[\"zero-accepted-variants\"]},\"relaxedTo\":{\"type\":\"string\",\"enum\":[\"none\"]}},\"required\":[\"narrowing\",\"gate\",\"productTypeId\",\"displayName\",\"productTy",
30
+ "peIds\",\"pass\",\"evidence\",\"relaxedTo\"],\"additionalProperties\":false}},\"required\":[\"version\",\"rawQuery\",\"normalisedQuery\",\"residualText\",\"status\",\"mode\",\"senses\"],\"additionalProperties\":false},\"suggestions\":{\"default\":[],\"description\":\"Bounded structured refinements. Each request is a complete executable replacement for the current search request.\",\"maxItems\":6,\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"label\":{\"type\":\"string\",\"minLength\":1,\"description\":\"Human-readable description of the refinement.\"},\"request\":{\"type\":\"object\",\"properties\":{\"text\":{\"description\":\"Free-text query, matched VISUALLY against material imagery (\\\"striped wallpaper\\\", \\\"warm terrazzo\\\"). A bare colour word (\\\"sage\\\") automatically runs as a colour-only search.\",\"type\":\"string\",\"maxLength\":500},\"intent\":{\"default\":\"search\",\"description\":\"Budget profile for the search primitive. Default \\\"search\\\" is the full engine. \\\"typeahead\\\" is the as-you-type profile: lexical-only with prefix match on the final token, forced `view:\\\"card\\\"`, `limit` capped at 24, and no mood/colour detection tiers, semantic fallback, category boost, secondary scoring, refinement suggestions, counts, or meanings. `textModes` is ignored under typeahead. Recommended wire shape for library typeahead UIs.\",\"type\":\"string\",\"enum\":[\"search\",\"typeahead\"]},\"textModes\":{\"description\":\"How to interpret `text`. Default [\\\"visual\\\"] — a text→image VISUAL search. \\\"lexical\\\" (opt-in) runs a BM25 ATTRIBUTE-lens search over material names + canonical attribute terms (find by what it's called / what it IS); detected colour words are stripped from the lexical terms and retained only as diagnostic or hard-constraint evidence. Requesting BOTH [\\\"visual\\\",\\\"lexical\\\"] runs the visual search and notes it — compose a separate lexical search if both result sets are required. Ignored when `intent` is \\\"typeahead\\\".\",\"type\":\"array\",\"items\":{\"type\":\"string\",\"enum\":[\"visual\",\"lexical\"]}},\"colours\":{\"description\":\"Explicit colour palette to match. Each entry is EITHER a `hex` OR a `name` (a descriptive or brand paint name resolved server-side — never invent a hex for a name). PRECEDENCE: when set, these override any colour word detected in `text`. Unresolvable names fail with a typed error listing them, so you can correct and retry.\",\"maxItems\":8,\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"hex\":{\"description\":\"The colour to match, as 3- or 6-digit hex. Provide EITHER `hex` OR `name`, never both. Only pass `hex` when you already hold an EXACT hex — for a colour NAME use `name` and let the server resolve it.\",\"type\":\"string\",\"pattern\":\"^#?(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$\"},\"name\":{\"description\":\"A colour NAME to match — a descriptive name ('sage green', 'terracotta', 'warm sand') or a brand paint name ('Gauntlet Gray', 'Hale Navy', 'White Dove'). Resolved server-side to its exact library hex (misspellings are fuzzy-matched). NEVER invent a hex for a name — pass the name here. Provide EITHER `name` OR `hex`, never both.\",\"type\":\"string\",\"minLength\":1,\"maxLength\":120},\"weight\":{\"description\":\"Relative proportion of this colour in the desired palette (0–1). Weights are normalised across the palette; omit for equal parts.\",\"type\":\"number\",\"exclusiveMinimum\":0,\"maximum\":1},\"role\":{\"type\":\"string\",\"enum\":[\"any\",\"is\",\"accent\"],\"description\":\"Per-colour swatch eligibility (default 'any'). PRECEDENCE: `colourLogic` governs cross-colour combination (all/any); `role` governs which swatch of a product may satisfy THIS colour, and ANDs on top of the logic. 'is' = the colour must be the product's DOMINANT swatch; 'accent' = the colour must be a minor share (proportion 0.05–0.35; all shades in a named colour region count together) and is excluded from the proportion-match term; 'any' = today's nearest-swatch match.\"},\"sourceName\":{\"description\":\"Human name the colour came from (e.g. \\\"sage\\\", \\\"Elephant's Breath\\\") — echoed back for display; never affects matching. (For a name you want RESOLVED to a hex, use `name` instead.)\",\"type\":\"string\",\"maxLength\":120}},\"additionalProperties\":false}},\"co",
31
+ "lourLogic\":{\"description\":\"\\\"all\\\" (default): results should contain the whole palette, ranked by average distance + proportion match. \\\"any\\\": results matching ANY palette colour, ranked by their best single-colour distance.\",\"type\":\"string\",\"enum\":[\"all\",\"any\"]},\"colourTolerance\":{\"description\":\"How close a colour must be to count as a match. 'normal' (default) is today's behaviour; 'tight' narrows the OKLab match threshold and prunes far single-colour matches; 'loose' widens the threshold.\",\"type\":\"string\",\"enum\":[\"tight\",\"normal\",\"loose\"]},\"colourAppearance\":{\"type\":\"object\",\"properties\":{\"lightness\":{\"description\":\"OKLab lightness window (L ∈ [0,1]); dark ≈ 0, light ≈ 1.\",\"type\":\"object\",\"properties\":{\"min\":{\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"max\":{\"type\":\"number\",\"minimum\":0,\"maximum\":1}},\"required\":[\"min\",\"max\"],\"additionalProperties\":false},\"warmth\":{\"description\":\"OKLab warmth window (b axis); cool < 0 < warm, ≈ −0.1..0.1.\",\"type\":\"object\",\"properties\":{\"min\":{\"type\":\"number\",\"minimum\":-0.5,\"maximum\":0.5},\"max\":{\"type\":\"number\",\"minimum\":-0.5,\"maximum\":0.5}},\"required\":[\"min\",\"max\"],\"additionalProperties\":false}},\"additionalProperties\":false,\"description\":\"HARD appearance gate on the material's DOMINANT colour, independent of the colour palette — works on a text-only query too. Variants without colour extraction are EXCLUDED whenever any axis is set (NULL scalars never match).\"},\"mood\":{\"type\":\"string\",\"enum\":[\"warm\",\"cool\",\"earthy\",\"moody\",\"bold\",\"light-airy\",\"natural\",\"neutral\",\"romantic\",\"coastal\",\"industrial\",\"luxe\",\"vintage\",\"serene\",\"scandi\",\"cosy\",\"heritage\",\"mid-century\",\"pastel\"],\"description\":\"Mood-language colour entry — resolves to a curated checked-in colour palette. Use it for evocative mood queries ('coastal', 'moody', 'earthy') rather than a single colour. A mood ALONE runs as a colour search; with text it remains diagnostic or hard-constraint evidence and does not blend into the text mode's ordering score. One of: warm (Warm — Reds, oranges, yellows - inviting and cozy); cool (Cool — Blues, teals, cool grays - calm and professional); earthy (Earthy — Browns, tans, terracotta - natural and grounded); moody (Moody — Deep, saturated, dramatic - rich and atmospheric); bold (Bold — High chroma, vibrant - energetic and eye-catching); light-airy (Light + Airy — Pastels, pale tones - soft and spacious); natural (Natural — Greens, wood tones, stone - organic and balanced); neutral (Neutral — Whites, grays, blacks - versatile and timeless); romantic (Romantic — Soft pinks, mauves, blush - tender and elegant); coastal (Coastal — Sandy beiges, ocean blues, seafoam - relaxed seaside); industrial (Industrial — Cool grays, concrete, metallic - urban and modern); luxe (Luxe — Deep jewel tones, rich emeralds, sapphires - opulent); vintage (Vintage — Muted, dusty tones - nostalgic and timeworn); serene (Serene — Soft blues, sage, muted lavender - calm and peaceful); scandi (Scandi — Warm whites, pale woods, cream - Scandinavian minimalism); cosy (Cosy — Deep plaster, amber, tobacco - enveloping warmth); heritage (Heritage — Rich historical tones with saturated depth - classic and grounded); mid-century (Mid-Century — Olive, mustard, teal, terracotta - retro modern); pastel (Pastel — Sage, duck egg, blush - soft colors with body).\"},\"sort\":{\"type\":\"string\",\"enum\":[\"relevance\",\"light\",\"dark\",\"vivid\",\"muted\",\"colourful\",\"name\",\"name_desc\",\"recent\"],\"description\":\"Re-order the final scored results (default 'relevance' = the selected mode's primary score). Two families. COLOUR RAMPS: 'light'/'dark' by dominant lightness, 'vivid'/'muted' by chroma, 'colourful' by overall vibrancy — variants without colour extraction sink to the end. CATALOGUE ORDERS: 'name' (A→Z), 'name_desc' (Z→A) and 'recent' (newest first), which read the same `name` / `created_at` columns, in the same directions, that /api/v1/search/browse orders on — one word, one order, on both lanes. Every sort REORDERS the pool the search signals already retrieved; it never widens or substitutes for one. Retired materials sort below visible ones under ev",
32
+ "ery non-relevance sort. For an unranked walk of the whole corpus in any of these orders, use the browse surface instead.\"},\"orderBy\":{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"minLength\":1,\"description\":\"The attribute to sort by. Only attributes with a DECLARED value ladder can be ordered — ordering is never inferred from a value's spelling — and anything else is refused with the orderable set named. Orderable today: price_tier.\"},\"direction\":{\"default\":\"asc\",\"description\":\"'asc' (default) walks the ladder from its first rung.\",\"type\":\"string\",\"enum\":[\"asc\",\"desc\"]}},\"required\":[\"attributeId\",\"direction\"],\"additionalProperties\":false,\"description\":\"Sort results along ONE attribute's declared ladder — a separate axis from `sort`, which stays the closed relevance/colour/catalogue vocabulary. Sent together, `orderBy` wins and the response `note` says so. A variant whose value is off the ladder, or which carries no value at all, sorts LAST in both directions: it has no rung, and it is still a real row, so it is placed at the end rather than dropped or guessed at.\"},\"returnAttributes\":{\"maxItems\":8,\"type\":\"array\",\"items\":{\"type\":\"string\",\"minLength\":1},\"description\":\"Attribute ids whose RESOLVED VALUE should ride each result, under `attributes` keyed by attribute id. Values resolve with the normal variant-over-product precedence. THREE STATES, and they are different: an attribute you did not ask for is not a key at all; an attribute you asked for that this variant carries no accepted value for is `null`; anything else is the value (a string, or an array for a multi-valued attribute). An unknown attribute id is refused naming it, never silently dropped. Pair it with `filters` to show what you filtered on.\"},\"similarTo\":{\"description\":\"Find materials that LOOK like a seed image. Supply `variantId` to seed from an existing variant's primary image, `materialBankId` to seed from a Material Bank SKU — the ONE EXTERNAL identifier this API accepts, so a caller already holding a SKU needs no resolve round trip — or `vector`, a pre-computed halfvec literal (`[a,b,…]`) from an externally-embedded image, to drive the image arm directly. A `materialBankId` that names no variant is a 400 naming the offending value, never an empty result set and never an unseeded search. PRECEDENCE: wins over `text` (the response carries a note when both were given).\",\"anyOf\":[{\"type\":\"object\",\"properties\":{\"variantId\":{\"type\":\"string\"}},\"required\":[\"variantId\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"materialBankId\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"materialBankId\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"vector\":{\"type\":\"string\"}},\"required\":[\"vector\"],\"additionalProperties\":false}]},\"excludeVariantIds\":{\"description\":\"Canonical variant ids that must not appear in this result. Applied before the returned page is cut, so the page refills from the deeper ranked pool instead of being shortened by client-side filtering. IDs are opaque; duplicates are harmless and deduplicated by the engine.\",\"maxItems\":256,\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"image\":{\"description\":\"A raw incoming image to search by (URL or base64). The HTTP adapter embeds it and sets `similarTo: { vector }`; wins over `text`.\",\"oneOf\":[{\"type\":\"object\",\"properties\":{\"type\":{\"type\":\"string\",\"const\":\"url\"},\"url\":{\"type\":\"string\",\"format\":\"uri\",\"description\":\"HTTP(S) URL of the query image. MUST be an https URL.\"}},\"required\":[\"type\",\"url\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"type\":{\"type\":\"string\",\"const\":\"base64\"},\"data\":{\"type\":\"string\",\"minLength\":1,\"description\":\"Base64-encoded image bytes (or a data URI) the caller already holds — e.g. a form upload — so there is nothing to host.\"}},\"required\":[\"type\",\"data\"],\"additionalProperties\":false}],\"type\":\"object\"},\"category\":{\"type\":\"object\",\"properties\":{\"boost\":{\"description\":\"Soft category preference — results of this product type score higher but others still appear.\",\"type\":\"string\"},\"filter\":{\"description\":\"HARD gate: only these public",
33
+ " taxonomy categories are returned. Values are category-group or product-type display names (case-insensitive), or canonical taxonomy keys. A group expands to every child product type; an unknown value errors listing every valid name.\",\"type\":\"array\",\"items\":{\"type\":\"string\"}}},\"additionalProperties\":false},\"weights\":{\"type\":\"object\",\"properties\":{\"image\":{\"type\":\"number\",\"minimum\":0},\"category\":{\"type\":\"number\",\"minimum\":0},\"colour\":{\"type\":\"number\",\"minimum\":0},\"lexical\":{\"type\":\"number\",\"minimum\":0},\"text\":{\"type\":\"number\",\"minimum\":0}},\"additionalProperties\":false,\"description\":\"Optional multiplier for the selected mode's one ordering signal. At most one applied weight is non-zero: colour→colour, lexical/entity-browse→lexical, variant-text→portrait, image-text/image→image, and filter-only→none. Overrides may scale only that live signal and cannot activate any other. Other similarities are diagnostic or hard-gate receipts and do not blend into score.\"},\"filters\":{\"description\":\"Typed filters applied as a hard restriction before ranking. Each entry is a term match (`attributeId` + `term`), a term SET (`attributeId` + `terms` — ANY one of them satisfies it), a presence check (`attributeId` + `hasValue: true`), or a numeric range (`attributeId` + `min`/`max`). AND/OR: entries AND together, and OR is expressed WITHIN one entry via `terms`. BRAND: `attributeId: \\\"brand\\\"` filters on the brand RELATION — a join every one of the catalogue's variants carries — taking a brand slug or display name, exactly the `value` a `brand` facet reports. Do NOT use the `brand_name` ATTRIBUTE for this: it is a sparse spec value present on about 1.5% of variants and absent entirely from every catalog-ingested brand, so filtering on it returns a near-empty page that reads as a corpus gap. Presence filters always route as hard spec gates. Aesthetic/meaning term filters (pattern, texture, directionality, and the other visual-meaning attributes) are applied as a pool-bounded post-filter over the visual candidate pool rather than a whole-catalog restriction. Each entry may carry a `trust` bar the matching value must clear.\",\"type\":\"array\",\"items\":{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, e.g. \\\"primary_material\\\" or \\\"fire_rating\\\" — or the special id \\\"brand\\\", which filters on the brand RELATION (a join, carried by every variant) rather than on any attribute.\"},\"term\":{\"type\":\"string\",\"description\":\"Canonical term the attribute must equal, e.g. \\\"textile\\\". For \\\"brand\\\", a brand slug (\\\"kvadrat\\\") or display name (\\\"Kvadrat\\\"), case-insensitive — exactly the `value` a `brand` facet reports. An unrecognised brand is a 400 naming it, never an empty page.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"term\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, or the special id \\\"brand\\\" for the brand relation.\"},\"terms\":{\"minItems\":1,\"type\":\"array\",\"items\":{\"type\":\"string\"},\"description\":\"Canonical terms, ANY ONE of which satisfies the filter — this is how OR is expressed. OR lives inside one entry; separate `filters[]` entries AND together. Used for ordinal scales, where a stricter class satisfies a looser one, and for a multi-select rail — `{attributeId:\\\"brand\\\", terms:[\\\"kvadrat\\\",\\\"rubelli\\\"]}` returns materials from either brand.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] ",
34
+ "the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"terms\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\"},\"excludesTerm\":{\"type\":\"string\",\"minLength\":1},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"excludesTerm\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id that must have an accepted value, e.g. \\\"sample_image_mm_per_px\\\".\"},\"hasValue\":{\"type\":\"boolean\",\"const\":true,\"description\":\"Require any accepted value for this attribute.\"},\"asOf\":{\"description\":\"Reference date for EPD expiration, exclusive: the value must remain valid after this date.\",\"type\":\"string\",\"format\":\"date\",\"pattern\":\"^(?:(?:\\\\d\\\\d[2468][048]|\\\\d\\\\d[13579][26]|\\\\d\\\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\\\d|30)|(?:02)-(?:0[1-9]|1\\\\d|2[0-8])))$\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"hasValue\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id holding a NUMBER, e.g. \\\"light_reflectance_value\\\" or \\\"lead_time_days\\\".\"},\"min\":{\"description\":\"Inclusive lower bound. Omit for a ceiling-only range.\",\"type\":\"number\"},\"max\":{\"description\":\"Inclusive upper bound. Omit for a floor-only range.\",\"type\":\"number\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\"],\"additionalProperties\":false}]}},\"preferences\":{\"description\":\"SOFT spec criteria — rank-never-exclude. Same shape as `filters`, but a preference NEVER removes a result: each hit's `verdict` gains a `preferred` cell, and a failed/missing preferred cell only DEMOTES the hit WITHIN its page (the cursor is from the pre-demotion order).\",\"maxItems\":8,\"type\":\"array\",\"items\":{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, e.g. ",
35
+ "\\\"primary_material\\\" or \\\"fire_rating\\\" — or the special id \\\"brand\\\", which filters on the brand RELATION (a join, carried by every variant) rather than on any attribute.\"},\"term\":{\"type\":\"string\",\"description\":\"Canonical term the attribute must equal, e.g. \\\"textile\\\". For \\\"brand\\\", a brand slug (\\\"kvadrat\\\") or display name (\\\"Kvadrat\\\"), case-insensitive — exactly the `value` a `brand` facet reports. An unrecognised brand is a 400 naming it, never an empty page.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"term\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, or the special id \\\"brand\\\" for the brand relation.\"},\"terms\":{\"minItems\":1,\"type\":\"array\",\"items\":{\"type\":\"string\"},\"description\":\"Canonical terms, ANY ONE of which satisfies the filter — this is how OR is expressed. OR lives inside one entry; separate `filters[]` entries AND together. Used for ordinal scales, where a stricter class satisfies a looser one, and for a multi-select rail — `{attributeId:\\\"brand\\\", terms:[\\\"kvadrat\\\",\\\"rubelli\\\"]}` returns materials from either brand.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"terms\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\"},\"excludesTerm\":{\"type\":\"string\",\"minLength\":1},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"excludesTerm\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id that must have an accepted value, e.g. \\\"sample_image_mm_per_px\\\".\"},\"hasValue\":{\"type\":\"boolean\",\"const\":true,\"description\":\"Require any accepted value for this attribute.\"},\"asOf\":{\"description\":\"Reference date for EPD expiration, exclusive: the value must remain valid after this date.\",\"type\":\"string\",\"format\":\"date\",\"pattern\":\"^(?:(?:\\\\d\\\\d[2468][048]|\\\\d\\\\d[13579][26]|\\\\d\\\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\\\d|30)|(?:02)-(?:0[1-9]|1\\\\d|2[0-8])))$\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"",
36
+ "catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"hasValue\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id holding a NUMBER, e.g. \\\"light_reflectance_value\\\" or \\\"lead_time_days\\\".\"},\"min\":{\"description\":\"Inclusive lower bound. Omit for a ceiling-only range.\",\"type\":\"number\"},\"max\":{\"description\":\"Inclusive upper bound. Omit for a floor-only range.\",\"type\":\"number\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\"],\"additionalProperties\":false}]}},\"verdicts\":{\"description\":\"Opt-in seed-relative verdict cells. Only meaningful with `similarTo: { variantId }`: costs one seed fetch + one page read to diff each hit's accepted COMPARABLE attributes (numeric + a colour axis) against the seed's, emitting `preferred` `verdict` cells with directional deviations (`above`/`below`, e.g. 2 mm thicker, warmer). Ignored on the default path; a `similarTo: { vector }` / raw-image seed carries no seed variant, so the response notes that seed cells were skipped.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Results per page (default 60, max 60).\",\"type\":\"integer\",\"minimum\":1,\"maximum\":60},\"cursor\":{\"description\":\"Opaque pagination cursor from a previous response. NOTE: ANN search is approximate — tail items may reorder slightly between pages.\",\"type\":\"string\"},\"explain\":{\"description\":\"Include per-signal scores on each result (`signals`).\",\"type\":\"boolean\"},\"collapse\":{\"description\":\"Group results by product before pagination. \\\"product\\\" returns one representative (highest-ranked) variant per product, with hidden siblings counted in `siblingCount`. The engine and GET adapter default to \\\"none\\\"; public POST requests default to \\\"product\\\" and may request \\\"none\\\" explicitly for variant-grain results.\",\"type\":\"string\",\"enum\":[\"none\",\"product\"]},\"count\":{\"default\":false,\"description\":\"Opt-in feed total. When true the response carries `counts.variants` (and `counts.products` under `collapse:\\\"product\\\"`). The variant total is EXACT up to a cap of 1000 and reported as `basis:\\\"lowerBound\\\"` beyond it — and ALWAYS `lowerBound` for ANN-ranked pools (image / colour / visual-text / visual-meaning post-filters), where the finite candidate pool is the only honest figure. Counts are never silently approximated: an exact count is labelled `exact`, a capped or pool-bounded one `lowerBound`. Default false (skip the count query entirely).\",\"type\":\"boolean\"},\"facetCounts\":{\"description\":\"Opt-in per-query facet counts (MG-1010). Name up to 10 facet ids — `brand`, `category`, or spec attribute ids — and the response carries a `facets` block counting each facet's values WITHIN this query's match set. Absent means no `facets` in the response and no counting cost. An id the registry does not know is a 400 naming it (with `didYouMean` candidates); a known but uncountable id lands in `facets.absent` with a reason. Counts follow the same honesty discipline as `count`: ranked (ANN) queries report pool-derived floors labelled `lowerBound` with `poolSize` stated; only a filter-driven query earns `exact`.\",\"maxItems\":10,\"type\":\"array\",\"items\":{\"type\":\"string\",\"minLength\":1}},\"refinementToken\":{\"description\":\"Opaque five-minute receipt from a prior pool-derived faceted sear",
37
+ "ch. Resubmitting it narrows that exact finalized pool instead of running a new candidate source.\",\"type\":\"string\",\"minLength\":1,\"maxLength\":131072},\"hasImages\":{\"default\":false,\"description\":\"When true, only return results that have at least one catalog image (a servable card thumbnail). Off by default so search stays honest about the FULL catalog, including image-less records. Applied on EVERY retrieval arm as a post-filter over the candidate pool, so a short page means the pool ran out of image-backed matches rather than that some arm ignored the flag. The opt-in `counts.variants` feed total honours it too, and filter, count and display share one SQL definition of image presence. Default false.\",\"type\":\"boolean\"},\"sampleableOnly\":{\"description\":\"Require an accepted sampleable=true variant fact before pagination. False or absent leaves the corpus unchanged.\",\"type\":\"boolean\"},\"view\":{\"default\":\"full\",\"description\":\"Result projection. \\\"full\\\" (default) returns the complete `searchResultItemSchema` (palette, colourMeta, hasImage, siblings, explain signals/meanings). \\\"card\\\" returns the lean `searchCardItemSchema` (identity + name + thumbnail + score) for grid rendering, skipping the per-page palette / appearance / meaning reads. The response echoes the chosen `view`, which discriminates the `results` union.\",\"type\":\"string\",\"enum\":[\"full\",\"card\"]},\"corpusScope\":{\"description\":\"Which corpus to answer from. samplizeCatalogue admits published US Samplize paint colours regardless of stock. ABSENT means the WHOLE GRAPH — every material MaterialGraph knows, orderable or not; absence is never a narrower default. Three transaction-oriented scopes gate on governed CHANNEL membership: 'materialBankNaOrderable' (Material Bank North America, about 277,000 materials), 'materialBankEuOrderable' (Material Bank Europe, about 65,000) and 'designShopOrderable' (DesignShop, about 34,000, excluding what DesignShop hides from browse). 'materialBankNaAndCatalogue' is a governed discovery corpus over Material Bank NA or Catalogue editorial membership; it is not a buyability claim, and references alone do not confer membership. 'materialBankOrderable' instead restricts retrieval to materials carrying a Material Bank identity, which is a provenance signal rather than a channel one, and admits about 98% of the graph. Scope is distribution/visibility membership, never suitability; its receipt states whether that means transaction or editorial inclusion. Every scope is applied INSIDE the query: `limit` fills with eligible rows, `counts` describe the SCOPED corpus, and the response echoes the scope under `corpusScope`. This is what post-filtering a returned page cannot give you — a client-side filter shortens the page exactly when the query drifts toward the corpus MaterialGraph carries beyond the Material Bank feed, which is unpredictable from outside. An unknown value is a 400 naming it.\",\"type\":\"string\",\"enum\":[\"all\",\"samplizeCatalogue\",\"materialBankOrderable\",\"materialBankNaOrderable\",\"materialBankNaAndCatalogue\",\"materialBankEuOrderable\",\"designShopOrderable\"]},\"includeSamplingArtifacts\":{\"description\":\"OPS/AUDIT escape hatch — DO NOT use for normal search. Default behaviour (absent or false) EXCLUDES sampling-artifact products (multi-swatch sample cards — orderable samples, never specifiable materials) from every result, count, and candidate pool. Set true only to surface them for ops inspection or an audit.\",\"type\":\"boolean\"}},\"required\":[\"intent\",\"count\",\"hasImages\",\"view\"],\"additionalProperties\":false,\"description\":\"Complete replacement search request. It can be parsed and executed directly; it is not a delta.\"}},\"required\":[\"label\",\"request\"],\"additionalProperties\":false}},\"disambiguation\":{\"description\":\"Present when the query has multiple valid readings — a colour⇄material word ('walnut'), a certification phrase wearing colour words ('red list free'), or a pinned paint chip that is also a colour to look for across materials, or an exact paint name shared by several makers. The reading that ran carries `isDefault`; for a refused paint co",
38
+ "llision every reading is false. Every reading is a complete executable request. ABSENT means the query was unambiguous, never that the alternative was checked and found empty.\",\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"enum\":[\"compliance-phrase\",\"colour-material\",\"paint-colour\",\"paint-identity\"],\"description\":\"Governed ambiguity class.\"},\"term\":{\"type\":\"string\",\"minLength\":1,\"description\":\"The ambiguous term or phrase.\"},\"interpretations\":{\"minItems\":2,\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"id\":{\"type\":\"string\",\"minLength\":1,\"description\":\"Stable id of this reading within the clarification; exact paints include maker, identity, and swatch.\"},\"label\":{\"type\":\"string\",\"minLength\":1,\"description\":\"Human-facing name for the reading, e.g. 'the colour walnut'.\"},\"kind\":{\"type\":\"string\",\"minLength\":1,\"description\":\"Short noun for what this reading searches.\"},\"isDefault\":{\"type\":\"boolean\",\"description\":\"True for the reading THIS response ran. Every reading is false when the engine intentionally refused to choose, such as an exact cross-library paint collision.\"},\"hex\":{\"description\":\"Representative swatch, present when the reading is a colour.\",\"type\":\"string\"},\"request\":{\"type\":\"object\",\"properties\":{\"text\":{\"description\":\"Free-text query, matched VISUALLY against material imagery (\\\"striped wallpaper\\\", \\\"warm terrazzo\\\"). A bare colour word (\\\"sage\\\") automatically runs as a colour-only search.\",\"type\":\"string\",\"maxLength\":500},\"intent\":{\"default\":\"search\",\"description\":\"Budget profile for the search primitive. Default \\\"search\\\" is the full engine. \\\"typeahead\\\" is the as-you-type profile: lexical-only with prefix match on the final token, forced `view:\\\"card\\\"`, `limit` capped at 24, and no mood/colour detection tiers, semantic fallback, category boost, secondary scoring, refinement suggestions, counts, or meanings. `textModes` is ignored under typeahead. Recommended wire shape for library typeahead UIs.\",\"type\":\"string\",\"enum\":[\"search\",\"typeahead\"]},\"textModes\":{\"description\":\"How to interpret `text`. Default [\\\"visual\\\"] — a text→image VISUAL search. \\\"lexical\\\" (opt-in) runs a BM25 ATTRIBUTE-lens search over material names + canonical attribute terms (find by what it's called / what it IS); detected colour words are stripped from the lexical terms and retained only as diagnostic or hard-constraint evidence. Requesting BOTH [\\\"visual\\\",\\\"lexical\\\"] runs the visual search and notes it — compose a separate lexical search if both result sets are required. Ignored when `intent` is \\\"typeahead\\\".\",\"type\":\"array\",\"items\":{\"type\":\"string\",\"enum\":[\"visual\",\"lexical\"]}},\"colours\":{\"description\":\"Explicit colour palette to match. Each entry is EITHER a `hex` OR a `name` (a descriptive or brand paint name resolved server-side — never invent a hex for a name). PRECEDENCE: when set, these override any colour word detected in `text`. Unresolvable names fail with a typed error listing them, so you can correct and retry.\",\"maxItems\":8,\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"hex\":{\"description\":\"The colour to match, as 3- or 6-digit hex. Provide EITHER `hex` OR `name`, never both. Only pass `hex` when you already hold an EXACT hex — for a colour NAME use `name` and let the server resolve it.\",\"type\":\"string\",\"pattern\":\"^#?(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$\"},\"name\":{\"description\":\"A colour NAME to match — a descriptive name ('sage green', 'terracotta', 'warm sand') or a brand paint name ('Gauntlet Gray', 'Hale Navy', 'White Dove'). Resolved server-side to its exact library hex (misspellings are fuzzy-matched). NEVER invent a hex for a name — pass the name here. Provide EITHER `name` OR `hex`, never both.\",\"type\":\"string\",\"minLength\":1,\"maxLength\":120},\"weight\":{\"description\":\"Relative proportion of this colour in the desired palette (0–1). Weights are normalised across the palette; omit for equal parts.\",\"type\":\"number\",\"exclusiveMinimum\":0,\"maximum\":1},\"role\":{\"type\":\"string\",\"enum\":[\"any\",\"is\",\"accent\"],\"description\":\"Per-colour swatch eligibility (default 'any'). PRECEDENCE: `colourLogic` g",
39
+ "overns cross-colour combination (all/any); `role` governs which swatch of a product may satisfy THIS colour, and ANDs on top of the logic. 'is' = the colour must be the product's DOMINANT swatch; 'accent' = the colour must be a minor share (proportion 0.05–0.35; all shades in a named colour region count together) and is excluded from the proportion-match term; 'any' = today's nearest-swatch match.\"},\"sourceName\":{\"description\":\"Human name the colour came from (e.g. \\\"sage\\\", \\\"Elephant's Breath\\\") — echoed back for display; never affects matching. (For a name you want RESOLVED to a hex, use `name` instead.)\",\"type\":\"string\",\"maxLength\":120}},\"additionalProperties\":false}},\"colourLogic\":{\"description\":\"\\\"all\\\" (default): results should contain the whole palette, ranked by average distance + proportion match. \\\"any\\\": results matching ANY palette colour, ranked by their best single-colour distance.\",\"type\":\"string\",\"enum\":[\"all\",\"any\"]},\"colourTolerance\":{\"description\":\"How close a colour must be to count as a match. 'normal' (default) is today's behaviour; 'tight' narrows the OKLab match threshold and prunes far single-colour matches; 'loose' widens the threshold.\",\"type\":\"string\",\"enum\":[\"tight\",\"normal\",\"loose\"]},\"colourAppearance\":{\"type\":\"object\",\"properties\":{\"lightness\":{\"description\":\"OKLab lightness window (L ∈ [0,1]); dark ≈ 0, light ≈ 1.\",\"type\":\"object\",\"properties\":{\"min\":{\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"max\":{\"type\":\"number\",\"minimum\":0,\"maximum\":1}},\"required\":[\"min\",\"max\"],\"additionalProperties\":false},\"warmth\":{\"description\":\"OKLab warmth window (b axis); cool < 0 < warm, ≈ −0.1..0.1.\",\"type\":\"object\",\"properties\":{\"min\":{\"type\":\"number\",\"minimum\":-0.5,\"maximum\":0.5},\"max\":{\"type\":\"number\",\"minimum\":-0.5,\"maximum\":0.5}},\"required\":[\"min\",\"max\"],\"additionalProperties\":false}},\"additionalProperties\":false,\"description\":\"HARD appearance gate on the material's DOMINANT colour, independent of the colour palette — works on a text-only query too. Variants without colour extraction are EXCLUDED whenever any axis is set (NULL scalars never match).\"},\"mood\":{\"type\":\"string\",\"enum\":[\"warm\",\"cool\",\"earthy\",\"moody\",\"bold\",\"light-airy\",\"natural\",\"neutral\",\"romantic\",\"coastal\",\"industrial\",\"luxe\",\"vintage\",\"serene\",\"scandi\",\"cosy\",\"heritage\",\"mid-century\",\"pastel\"],\"description\":\"Mood-language colour entry — resolves to a curated checked-in colour palette. Use it for evocative mood queries ('coastal', 'moody', 'earthy') rather than a single colour. A mood ALONE runs as a colour search; with text it remains diagnostic or hard-constraint evidence and does not blend into the text mode's ordering score. One of: warm (Warm — Reds, oranges, yellows - inviting and cozy); cool (Cool — Blues, teals, cool grays - calm and professional); earthy (Earthy — Browns, tans, terracotta - natural and grounded); moody (Moody — Deep, saturated, dramatic - rich and atmospheric); bold (Bold — High chroma, vibrant - energetic and eye-catching); light-airy (Light + Airy — Pastels, pale tones - soft and spacious); natural (Natural — Greens, wood tones, stone - organic and balanced); neutral (Neutral — Whites, grays, blacks - versatile and timeless); romantic (Romantic — Soft pinks, mauves, blush - tender and elegant); coastal (Coastal — Sandy beiges, ocean blues, seafoam - relaxed seaside); industrial (Industrial — Cool grays, concrete, metallic - urban and modern); luxe (Luxe — Deep jewel tones, rich emeralds, sapphires - opulent); vintage (Vintage — Muted, dusty tones - nostalgic and timeworn); serene (Serene — Soft blues, sage, muted lavender - calm and peaceful); scandi (Scandi — Warm whites, pale woods, cream - Scandinavian minimalism); cosy (Cosy — Deep plaster, amber, tobacco - enveloping warmth); heritage (Heritage — Rich historical tones with saturated depth - classic and grounded); mid-century (Mid-Century — Olive, mustard, teal, terracotta - retro modern); pastel (Pastel — Sage, duck egg, blush - soft colors with body).\"},\"sort\":{\"type\":\"string\",\"enum\":[\"relevance\",\"light\",\"dark\",\"vivid\",\"muted\",\"colourful\",\"name\",\"name_desc\"",
40
+ ",\"recent\"],\"description\":\"Re-order the final scored results (default 'relevance' = the selected mode's primary score). Two families. COLOUR RAMPS: 'light'/'dark' by dominant lightness, 'vivid'/'muted' by chroma, 'colourful' by overall vibrancy — variants without colour extraction sink to the end. CATALOGUE ORDERS: 'name' (A→Z), 'name_desc' (Z→A) and 'recent' (newest first), which read the same `name` / `created_at` columns, in the same directions, that /api/v1/search/browse orders on — one word, one order, on both lanes. Every sort REORDERS the pool the search signals already retrieved; it never widens or substitutes for one. Retired materials sort below visible ones under every non-relevance sort. For an unranked walk of the whole corpus in any of these orders, use the browse surface instead.\"},\"orderBy\":{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"minLength\":1,\"description\":\"The attribute to sort by. Only attributes with a DECLARED value ladder can be ordered — ordering is never inferred from a value's spelling — and anything else is refused with the orderable set named. Orderable today: price_tier.\"},\"direction\":{\"default\":\"asc\",\"description\":\"'asc' (default) walks the ladder from its first rung.\",\"type\":\"string\",\"enum\":[\"asc\",\"desc\"]}},\"required\":[\"attributeId\",\"direction\"],\"additionalProperties\":false,\"description\":\"Sort results along ONE attribute's declared ladder — a separate axis from `sort`, which stays the closed relevance/colour/catalogue vocabulary. Sent together, `orderBy` wins and the response `note` says so. A variant whose value is off the ladder, or which carries no value at all, sorts LAST in both directions: it has no rung, and it is still a real row, so it is placed at the end rather than dropped or guessed at.\"},\"returnAttributes\":{\"maxItems\":8,\"type\":\"array\",\"items\":{\"type\":\"string\",\"minLength\":1},\"description\":\"Attribute ids whose RESOLVED VALUE should ride each result, under `attributes` keyed by attribute id. Values resolve with the normal variant-over-product precedence. THREE STATES, and they are different: an attribute you did not ask for is not a key at all; an attribute you asked for that this variant carries no accepted value for is `null`; anything else is the value (a string, or an array for a multi-valued attribute). An unknown attribute id is refused naming it, never silently dropped. Pair it with `filters` to show what you filtered on.\"},\"similarTo\":{\"description\":\"Find materials that LOOK like a seed image. Supply `variantId` to seed from an existing variant's primary image, `materialBankId` to seed from a Material Bank SKU — the ONE EXTERNAL identifier this API accepts, so a caller already holding a SKU needs no resolve round trip — or `vector`, a pre-computed halfvec literal (`[a,b,…]`) from an externally-embedded image, to drive the image arm directly. A `materialBankId` that names no variant is a 400 naming the offending value, never an empty result set and never an unseeded search. PRECEDENCE: wins over `text` (the response carries a note when both were given).\",\"anyOf\":[{\"type\":\"object\",\"properties\":{\"variantId\":{\"type\":\"string\"}},\"required\":[\"variantId\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"materialBankId\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"materialBankId\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"vector\":{\"type\":\"string\"}},\"required\":[\"vector\"],\"additionalProperties\":false}]},\"excludeVariantIds\":{\"description\":\"Canonical variant ids that must not appear in this result. Applied before the returned page is cut, so the page refills from the deeper ranked pool instead of being shortened by client-side filtering. IDs are opaque; duplicates are harmless and deduplicated by the engine.\",\"maxItems\":256,\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"image\":{\"description\":\"A raw incoming image to search by (URL or base64). The HTTP adapter embeds it and sets `similarTo: { vector }`; wins over `text`.\",\"oneOf\":[{\"type\":\"object\",\"properties\":{\"type\":{\"type\":\"string\",\"const\":\"url\"},\"url\":{\"type\":\"string\",\"format\":\"uri\"",
41
+ ",\"description\":\"HTTP(S) URL of the query image. MUST be an https URL.\"}},\"required\":[\"type\",\"url\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"type\":{\"type\":\"string\",\"const\":\"base64\"},\"data\":{\"type\":\"string\",\"minLength\":1,\"description\":\"Base64-encoded image bytes (or a data URI) the caller already holds — e.g. a form upload — so there is nothing to host.\"}},\"required\":[\"type\",\"data\"],\"additionalProperties\":false}],\"type\":\"object\"},\"category\":{\"type\":\"object\",\"properties\":{\"boost\":{\"description\":\"Soft category preference — results of this product type score higher but others still appear.\",\"type\":\"string\"},\"filter\":{\"description\":\"HARD gate: only these public taxonomy categories are returned. Values are category-group or product-type display names (case-insensitive), or canonical taxonomy keys. A group expands to every child product type; an unknown value errors listing every valid name.\",\"type\":\"array\",\"items\":{\"type\":\"string\"}}},\"additionalProperties\":false},\"weights\":{\"type\":\"object\",\"properties\":{\"image\":{\"type\":\"number\",\"minimum\":0},\"category\":{\"type\":\"number\",\"minimum\":0},\"colour\":{\"type\":\"number\",\"minimum\":0},\"lexical\":{\"type\":\"number\",\"minimum\":0},\"text\":{\"type\":\"number\",\"minimum\":0}},\"additionalProperties\":false,\"description\":\"Optional multiplier for the selected mode's one ordering signal. At most one applied weight is non-zero: colour→colour, lexical/entity-browse→lexical, variant-text→portrait, image-text/image→image, and filter-only→none. Overrides may scale only that live signal and cannot activate any other. Other similarities are diagnostic or hard-gate receipts and do not blend into score.\"},\"filters\":{\"description\":\"Typed filters applied as a hard restriction before ranking. Each entry is a term match (`attributeId` + `term`), a term SET (`attributeId` + `terms` — ANY one of them satisfies it), a presence check (`attributeId` + `hasValue: true`), or a numeric range (`attributeId` + `min`/`max`). AND/OR: entries AND together, and OR is expressed WITHIN one entry via `terms`. BRAND: `attributeId: \\\"brand\\\"` filters on the brand RELATION — a join every one of the catalogue's variants carries — taking a brand slug or display name, exactly the `value` a `brand` facet reports. Do NOT use the `brand_name` ATTRIBUTE for this: it is a sparse spec value present on about 1.5% of variants and absent entirely from every catalog-ingested brand, so filtering on it returns a near-empty page that reads as a corpus gap. Presence filters always route as hard spec gates. Aesthetic/meaning term filters (pattern, texture, directionality, and the other visual-meaning attributes) are applied as a pool-bounded post-filter over the visual candidate pool rather than a whole-catalog restriction. Each entry may carry a `trust` bar the matching value must clear.\",\"type\":\"array\",\"items\":{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, e.g. \\\"primary_material\\\" or \\\"fire_rating\\\" — or the special id \\\"brand\\\", which filters on the brand RELATION (a join, carried by every variant) rather than on any attribute.\"},\"term\":{\"type\":\"string\",\"description\":\"Canonical term the attribute must equal, e.g. \\\"textile\\\". For \\\"brand\\\", a brand slug (\\\"kvadrat\\\") or display name (\\\"Kvadrat\\\"), case-insensitive — exactly the `value` a `brand` facet reports. An unrecognised brand is a 400 naming it, never an empty page.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"term\"],\"additionalProperties\":false},{\"type\":\"o",
42
+ "bject\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, or the special id \\\"brand\\\" for the brand relation.\"},\"terms\":{\"minItems\":1,\"type\":\"array\",\"items\":{\"type\":\"string\"},\"description\":\"Canonical terms, ANY ONE of which satisfies the filter — this is how OR is expressed. OR lives inside one entry; separate `filters[]` entries AND together. Used for ordinal scales, where a stricter class satisfies a looser one, and for a multi-select rail — `{attributeId:\\\"brand\\\", terms:[\\\"kvadrat\\\",\\\"rubelli\\\"]}` returns materials from either brand.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"terms\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\"},\"excludesTerm\":{\"type\":\"string\",\"minLength\":1},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"excludesTerm\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id that must have an accepted value, e.g. \\\"sample_image_mm_per_px\\\".\"},\"hasValue\":{\"type\":\"boolean\",\"const\":true,\"description\":\"Require any accepted value for this attribute.\"},\"asOf\":{\"description\":\"Reference date for EPD expiration, exclusive: the value must remain valid after this date.\",\"type\":\"string\",\"format\":\"date\",\"pattern\":\"^(?:(?:\\\\d\\\\d[2468][048]|\\\\d\\\\d[13579][26]|\\\\d\\\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\\\d|30)|(?:02)-(?:0[1-9]|1\\\\d|2[0-8])))$\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"hasValue\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id holding a NUMBER, e.g. \\\"light_reflectance_value\\\" or \\\"lead_time_days\\\".\"},\"min\":{\"description\":\"Inclusive lower bound. Omit for a ceiling-only range.\",\"type\":\"number\"},\"max\":{\"description\":\"Inclusive upper bound. Omit for a floor-only range.\",\"type\":\"number\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REG",
43
+ "ISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\"],\"additionalProperties\":false}]}},\"preferences\":{\"description\":\"SOFT spec criteria — rank-never-exclude. Same shape as `filters`, but a preference NEVER removes a result: each hit's `verdict` gains a `preferred` cell, and a failed/missing preferred cell only DEMOTES the hit WITHIN its page (the cursor is from the pre-demotion order).\",\"maxItems\":8,\"type\":\"array\",\"items\":{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, e.g. \\\"primary_material\\\" or \\\"fire_rating\\\" — or the special id \\\"brand\\\", which filters on the brand RELATION (a join, carried by every variant) rather than on any attribute.\"},\"term\":{\"type\":\"string\",\"description\":\"Canonical term the attribute must equal, e.g. \\\"textile\\\". For \\\"brand\\\", a brand slug (\\\"kvadrat\\\") or display name (\\\"Kvadrat\\\"), case-insensitive — exactly the `value` a `brand` facet reports. An unrecognised brand is a 400 naming it, never an empty page.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"term\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, or the special id \\\"brand\\\" for the brand relation.\"},\"terms\":{\"minItems\":1,\"type\":\"array\",\"items\":{\"type\":\"string\"},\"description\":\"Canonical terms, ANY ONE of which satisfies the filter — this is how OR is expressed. OR lives inside one entry; separate `filters[]` entries AND together. Used for ordinal scales, where a stricter class satisfies a looser one, and for a multi-select rail — `{attributeId:\\\"brand\\\", terms:[\\\"kvadrat\\\",\\\"rubelli\\\"]}` returns materials from either brand.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"terms\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\"},\"excludesTerm\":{\"type\":\"string\",\"minLength\":1},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"excludesTerm\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id that must have an accepted value, e.g. \\\"sample_image_mm_per_px\\\".\"},\"hasValue\":{\"type\":\"boolean\",\"const\":true,\"description\":\"Require any accepted value for this attrib",
44
+ "ute.\"},\"asOf\":{\"description\":\"Reference date for EPD expiration, exclusive: the value must remain valid after this date.\",\"type\":\"string\",\"format\":\"date\",\"pattern\":\"^(?:(?:\\\\d\\\\d[2468][048]|\\\\d\\\\d[13579][26]|\\\\d\\\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\\\d|30)|(?:02)-(?:0[1-9]|1\\\\d|2[0-8])))$\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"hasValue\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id holding a NUMBER, e.g. \\\"light_reflectance_value\\\" or \\\"lead_time_days\\\".\"},\"min\":{\"description\":\"Inclusive lower bound. Omit for a ceiling-only range.\",\"type\":\"number\"},\"max\":{\"description\":\"Inclusive upper bound. Omit for a floor-only range.\",\"type\":\"number\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\"],\"additionalProperties\":false}]}},\"verdicts\":{\"description\":\"Opt-in seed-relative verdict cells. Only meaningful with `similarTo: { variantId }`: costs one seed fetch + one page read to diff each hit's accepted COMPARABLE attributes (numeric + a colour axis) against the seed's, emitting `preferred` `verdict` cells with directional deviations (`above`/`below`, e.g. 2 mm thicker, warmer). Ignored on the default path; a `similarTo: { vector }` / raw-image seed carries no seed variant, so the response notes that seed cells were skipped.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Results per page (default 60, max 60).\",\"type\":\"integer\",\"minimum\":1,\"maximum\":60},\"cursor\":{\"description\":\"Opaque pagination cursor from a previous response. NOTE: ANN search is approximate — tail items may reorder slightly between pages.\",\"type\":\"string\"},\"explain\":{\"description\":\"Include per-signal scores on each result (`signals`).\",\"type\":\"boolean\"},\"collapse\":{\"description\":\"Group results by product before pagination. \\\"product\\\" returns one representative (highest-ranked) variant per product, with hidden siblings counted in `siblingCount`. The engine and GET adapter default to \\\"none\\\"; public POST requests default to \\\"product\\\" and may request \\\"none\\\" explicitly for variant-grain results.\",\"type\":\"string\",\"enum\":[\"none\",\"product\"]},\"count\":{\"default\":false,\"description\":\"Opt-in feed total. When true the response carries `counts.variants` (and `counts.products` under `collapse:\\\"product\\\"`). The variant total is EXACT up to a cap of 1000 and reported as `basis:\\\"lowerBound\\\"` beyond it — and ALWAYS `lowerBound` for ANN-ranked pools (image / colour / visual-text / visual-meaning post-filters), where the finite candidate pool is the only honest figure. Counts are never silently approximated: an exact count is labelled `exact`, a capped or pool-bounded one `lowerBound`. Default false (skip the count query entirely).\",\"type\":\"boolean\"},\"facetCounts\":{\"description\":\"Opt-in per-query facet counts (MG-1010). Name up to 10 facet ids — `brand`, `category`, or spec attribut",
45
+ "e ids — and the response carries a `facets` block counting each facet's values WITHIN this query's match set. Absent means no `facets` in the response and no counting cost. An id the registry does not know is a 400 naming it (with `didYouMean` candidates); a known but uncountable id lands in `facets.absent` with a reason. Counts follow the same honesty discipline as `count`: ranked (ANN) queries report pool-derived floors labelled `lowerBound` with `poolSize` stated; only a filter-driven query earns `exact`.\",\"maxItems\":10,\"type\":\"array\",\"items\":{\"type\":\"string\",\"minLength\":1}},\"refinementToken\":{\"description\":\"Opaque five-minute receipt from a prior pool-derived faceted search. Resubmitting it narrows that exact finalized pool instead of running a new candidate source.\",\"type\":\"string\",\"minLength\":1,\"maxLength\":131072},\"hasImages\":{\"default\":false,\"description\":\"When true, only return results that have at least one catalog image (a servable card thumbnail). Off by default so search stays honest about the FULL catalog, including image-less records. Applied on EVERY retrieval arm as a post-filter over the candidate pool, so a short page means the pool ran out of image-backed matches rather than that some arm ignored the flag. The opt-in `counts.variants` feed total honours it too, and filter, count and display share one SQL definition of image presence. Default false.\",\"type\":\"boolean\"},\"sampleableOnly\":{\"description\":\"Require an accepted sampleable=true variant fact before pagination. False or absent leaves the corpus unchanged.\",\"type\":\"boolean\"},\"view\":{\"default\":\"full\",\"description\":\"Result projection. \\\"full\\\" (default) returns the complete `searchResultItemSchema` (palette, colourMeta, hasImage, siblings, explain signals/meanings). \\\"card\\\" returns the lean `searchCardItemSchema` (identity + name + thumbnail + score) for grid rendering, skipping the per-page palette / appearance / meaning reads. The response echoes the chosen `view`, which discriminates the `results` union.\",\"type\":\"string\",\"enum\":[\"full\",\"card\"]},\"corpusScope\":{\"description\":\"Which corpus to answer from. samplizeCatalogue admits published US Samplize paint colours regardless of stock. ABSENT means the WHOLE GRAPH — every material MaterialGraph knows, orderable or not; absence is never a narrower default. Three transaction-oriented scopes gate on governed CHANNEL membership: 'materialBankNaOrderable' (Material Bank North America, about 277,000 materials), 'materialBankEuOrderable' (Material Bank Europe, about 65,000) and 'designShopOrderable' (DesignShop, about 34,000, excluding what DesignShop hides from browse). 'materialBankNaAndCatalogue' is a governed discovery corpus over Material Bank NA or Catalogue editorial membership; it is not a buyability claim, and references alone do not confer membership. 'materialBankOrderable' instead restricts retrieval to materials carrying a Material Bank identity, which is a provenance signal rather than a channel one, and admits about 98% of the graph. Scope is distribution/visibility membership, never suitability; its receipt states whether that means transaction or editorial inclusion. Every scope is applied INSIDE the query: `limit` fills with eligible rows, `counts` describe the SCOPED corpus, and the response echoes the scope under `corpusScope`. This is what post-filtering a returned page cannot give you — a client-side filter shortens the page exactly when the query drifts toward the corpus MaterialGraph carries beyond the Material Bank feed, which is unpredictable from outside. An unknown value is a 400 naming it.\",\"type\":\"string\",\"enum\":[\"all\",\"samplizeCatalogue\",\"materialBankOrderable\",\"materialBankNaOrderable\",\"materialBankNaAndCatalogue\",\"materialBankEuOrderable\",\"designShopOrderable\"]},\"includeSamplingArtifacts\":{\"description\":\"OPS/AUDIT escape hatch — DO NOT use for normal search. Default behaviour (absent or false) EXCLUDES sampling-artifact products (multi-swatch sample cards — orderable samples, never specifiable materials) from every result, count, and candidate pool. Set true only to surface",
46
+ " them for ops inspection or an audit.\",\"type\":\"boolean\"}},\"required\":[\"intent\",\"count\",\"hasImages\",\"view\"],\"additionalProperties\":false,\"description\":\"Complete replacement search request that runs this reading. Executable directly; it is not a delta.\"}},\"required\":[\"id\",\"label\",\"kind\",\"isDefault\",\"request\"],\"additionalProperties\":false},\"description\":\"Every valid reading. The one that ran is flagged `isDefault`; all are false when the engine intentionally chose none. Each is a complete executable request.\"}},\"required\":[\"kind\",\"term\",\"interpretations\"],\"additionalProperties\":false},\"supersededMoodId\":{\"description\":\"A mood term detected in the query but overridden by a stronger explicit colour reading. Absent means no mood was superseded, not that mood detection ran and found none.\",\"type\":\"string\",\"enum\":[\"warm\",\"cool\",\"earthy\",\"moody\",\"bold\",\"light-airy\",\"natural\",\"neutral\",\"romantic\",\"coastal\",\"industrial\",\"luxe\",\"vintage\",\"serene\",\"scandi\",\"cosy\",\"heritage\",\"mid-century\",\"pastel\"]},\"moodInterpretation\":{\"type\":\"object\",\"properties\":{\"query\":{\"description\":\"The original query text, or null without text.\",\"type\":[\"string\",\"null\"]},\"moodId\":{\"anyOf\":[{\"type\":\"string\",\"enum\":[\"warm\",\"cool\",\"earthy\",\"moody\",\"bold\",\"light-airy\",\"natural\",\"neutral\",\"romantic\",\"coastal\",\"industrial\",\"luxe\",\"vintage\",\"serene\",\"scandi\",\"cosy\",\"heritage\",\"mid-century\",\"pastel\"],\"description\":\"Mood-language colour entry — resolves to a curated checked-in colour palette. Use it for evocative mood queries ('coastal', 'moody', 'earthy') rather than a single colour. A mood ALONE runs as a colour search; with text it remains diagnostic or hard-constraint evidence and does not blend into the text mode's ordering score. One of: warm (Warm — Reds, oranges, yellows - inviting and cozy); cool (Cool — Blues, teals, cool grays - calm and professional); earthy (Earthy — Browns, tans, terracotta - natural and grounded); moody (Moody — Deep, saturated, dramatic - rich and atmospheric); bold (Bold — High chroma, vibrant - energetic and eye-catching); light-airy (Light + Airy — Pastels, pale tones - soft and spacious); natural (Natural — Greens, wood tones, stone - organic and balanced); neutral (Neutral — Whites, grays, blacks - versatile and timeless); romantic (Romantic — Soft pinks, mauves, blush - tender and elegant); coastal (Coastal — Sandy beiges, ocean blues, seafoam - relaxed seaside); industrial (Industrial — Cool grays, concrete, metallic - urban and modern); luxe (Luxe — Deep jewel tones, rich emeralds, sapphires - opulent); vintage (Vintage — Muted, dusty tones - nostalgic and timeworn); serene (Serene — Soft blues, sage, muted lavender - calm and peaceful); scandi (Scandi — Warm whites, pale woods, cream - Scandinavian minimalism); cosy (Cosy — Deep plaster, amber, tobacco - enveloping warmth); heritage (Heritage — Rich historical tones with saturated depth - classic and grounded); mid-century (Mid-Century — Olive, mustard, teal, terracotta - retro modern); pastel (Pastel — Sage, duck egg, blush - soft colors with body).\"},{\"type\":\"null\"}],\"description\":\"The recognized mood id, or null when none was selected.\"},\"supersededMoodId\":{\"anyOf\":[{\"type\":\"string\",\"enum\":[\"warm\",\"cool\",\"earthy\",\"moody\",\"bold\",\"light-airy\",\"natural\",\"neutral\",\"romantic\",\"coastal\",\"industrial\",\"luxe\",\"vintage\",\"serene\",\"scandi\",\"cosy\",\"heritage\",\"mid-century\",\"pastel\"],\"description\":\"Mood-language colour entry — resolves to a curated checked-in colour palette. Use it for evocative mood queries ('coastal', 'moody', 'earthy') rather than a single colour. A mood ALONE runs as a colour search; with text it remains diagnostic or hard-constraint evidence and does not blend into the text mode's ordering score. One of: warm (Warm — Reds, oranges, yellows - inviting and cozy); cool (Cool — Blues, teals, cool grays - calm and professional); earthy (Earthy — Browns, tans, terracotta - natural and grounded); moody (Moody — Deep, saturated, dramatic - rich and atmospheric); bold (Bold — High chroma, vibrant - energetic and eye-catching); light-airy (Light + Airy — Pastels, pale tones - ",
47
+ "soft and spacious); natural (Natural — Greens, wood tones, stone - organic and balanced); neutral (Neutral — Whites, grays, blacks - versatile and timeless); romantic (Romantic — Soft pinks, mauves, blush - tender and elegant); coastal (Coastal — Sandy beiges, ocean blues, seafoam - relaxed seaside); industrial (Industrial — Cool grays, concrete, metallic - urban and modern); luxe (Luxe — Deep jewel tones, rich emeralds, sapphires - opulent); vintage (Vintage — Muted, dusty tones - nostalgic and timeworn); serene (Serene — Soft blues, sage, muted lavender - calm and peaceful); scandi (Scandi — Warm whites, pale woods, cream - Scandinavian minimalism); cosy (Cosy — Deep plaster, amber, tobacco - enveloping warmth); heritage (Heritage — Rich historical tones with saturated depth - classic and grounded); mid-century (Mid-Century — Olive, mustard, teal, terracotta - retro modern); pastel (Pastel — Sage, duck egg, blush - soft colors with body).\"},{\"type\":\"null\"}],\"description\":\"A recognized mood displaced by a stronger colour reading.\"}},\"required\":[\"query\",\"moodId\",\"supersededMoodId\"],\"additionalProperties\":false,\"description\":\"Mood interpretation receipt. Present when the standard search evaluated mood; null ids explicitly mean no mood was selected. Absent means mood was not evaluated or the response came from an older server.\"},\"mode\":{\"type\":\"string\",\"enum\":[\"image\",\"colour\",\"text\",\"lexical\",\"entity-browse\"],\"description\":\"The primary ranking source the query resolved to: 'image' (look-alike), 'colour' (the colour arm — pure), 'text' (visual text→image, the default), 'lexical' (BM25 over the attribute/name projection), or 'entity-browse' (the query names a brand and nothing else, so the brand's own catalogue in browse order is returned instead of a similarity ranking). A fully governed hard form + colour query filters the primary source's one candidate pool; that source still owns order and remains the reported mode.\"},\"textLane\":{\"type\":\"string\",\"enum\":[\"image-text\",\"variant-text\"],\"description\":\"Which dense text lane answered: 'image-text' (the query carries evidence it describes appearance, so it is ranked against image vectors) or 'variant-text' (ranked against each variant's written portrait). Absent when the answer did not come from the dense text arm — absence is unknown, not a default.\"},\"rerank\":{\"type\":\"object\",\"properties\":{\"colour\":{\"type\":\"string\",\"enum\":[\"ok\",\"failed\",\"skipped\"]},\"colourWeight\":{\"type\":\"number\"},\"colourLabel\":{\"type\":\"string\"}},\"additionalProperties\":false},\"colourConstraint\":{\"type\":\"object\",\"properties\":{\"basis\":{\"type\":\"string\",\"enum\":[\"explicit\",\"detected-strong\"]},\"family\":{\"type\":\"string\",\"minLength\":1},\"queryHexes\":{\"minItems\":1,\"type\":\"array\",\"items\":{\"type\":\"string\",\"pattern\":\"^#[0-9a-f]{6}$\"}},\"toneBandIds\":{\"minItems\":1,\"type\":\"array\",\"items\":{\"type\":\"string\",\"minLength\":1}},\"toneBandDropped\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"considered\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"kept\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"dropped\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"unknown\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"adjacent\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"abundance\":{\"type\":\"object\",\"properties\":{\"grain\":{\"type\":\"string\",\"enum\":[\"variant\",\"product\"]},\"requested\":{\"type\":\"integer\",\"exclusiveMinimum\":0,\"maximum\":9007199254740991},\"eligible\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"candidateWindow\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"sourceExhausted\":{\"type\":\"boolean\"},\"state\":{\"type\":\"string\",\"enum\":[\"filled\",\"corpus_scarcity\",\"evidence_unavailable\",\"retrieval_window_exhausted\"]}},\"required\":[\"grain\",\"requested\",\"eligible\",\"candidateWindow\",\"sourceExhausted\",\"state\"],\"additionalProperties\":false}},\"required\":[\"basis\",\"family\",\"queryHexes\",\"considered\",\"kept\",\"dropped\",\"unknown\",\"adjacent\"],\"additionalProperties\":false,\"description\":\"Eligibility for a compound text colour: counts cover the bounded candidate pool,",
48
+ " `adjacent` passed ColorScope hue compatibility, and missing colour evidence failed closed. Absent means colour was not a hard constraint.\"},\"formConstraint\":{\"type\":\"object\",\"properties\":{\"instruction\":{\"type\":\"string\",\"enum\":[\"subway\",\"plank\",\"square\",\"hexagon\",\"penny-round\"]},\"considered\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"kept\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"dropped\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"contradicted\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"unknown\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"abundance\":{\"type\":\"object\",\"properties\":{\"grain\":{\"type\":\"string\",\"enum\":[\"variant\",\"product\"]},\"requested\":{\"type\":\"integer\",\"exclusiveMinimum\":0,\"maximum\":9007199254740991},\"eligible\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"candidateWindow\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"sourceExhausted\":{\"type\":\"boolean\"},\"state\":{\"type\":\"string\",\"enum\":[\"filled\",\"corpus_scarcity\",\"evidence_unavailable\",\"retrieval_window_exhausted\"]}},\"required\":[\"grain\",\"requested\",\"eligible\",\"candidateWindow\",\"sourceExhausted\",\"state\"],\"additionalProperties\":false}},\"required\":[\"instruction\",\"considered\",\"kept\",\"dropped\",\"contradicted\",\"unknown\",\"abundance\"],\"additionalProperties\":false,\"description\":\"Hard form eligibility for a compound text query. A candidate is kept only when governed size or body-type evidence supports the requested form; product and variant names are never proof.\"},\"maxScore\":{\"type\":\"number\"},\"counts\":{\"description\":\"Feed totals, present when `count:true` was requested OR when `collapse:\\\"product\\\"` grouped the results. `variants` is the total matching variants; `products` (only under `collapse`) the distinct product groups. Each total carries a `basis`: `exact` (true count, up to the cap of 1000) or `lowerBound` (a floor — the cap was hit, an ANN-ranked pool bounds it, or the count degraded). The lexical arm counts exactly via the tsvector predicate; a single accepted spec term reads the `mv_attribute_facet_counts` materialized view (refreshed post-ingest, so a hair stale by design); ANN pools (image / colour / visual-text) and `products` are always `lowerBound`. The two totals describe the same result set, so `products` never exceeds `variants`: both are measured against the pool this page was drawn from, and a `variants` total that came out below that pool is reported as the pool-bounded floor instead — it counted a narrower universe than the results beside it.\",\"type\":\"object\",\"properties\":{\"variants\":{\"type\":\"object\",\"properties\":{\"value\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"basis\":{\"type\":\"string\",\"enum\":[\"exact\",\"lowerBound\"]}},\"required\":[\"value\",\"basis\"],\"additionalProperties\":false},\"products\":{\"type\":\"object\",\"properties\":{\"value\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"basis\":{\"type\":\"string\",\"enum\":[\"exact\",\"lowerBound\"]}},\"required\":[\"value\",\"basis\"],\"additionalProperties\":false}},\"required\":[\"variants\"],\"additionalProperties\":false},\"facets\":{\"description\":\"Per-query facet counts (MG-1010), present ONLY when the request sent `facetCounts`. Each requested facet's values are counted WITHIN this query's match set — a ranked query reports pool-derived floors (`basis:\\\"lowerBound\\\"`, `poolSize` stated), a filter-driven query true counts (`basis:\\\"exact\\\"`). See the `facetCounts` input field.\",\"type\":\"object\",\"properties\":{\"poolSize\":{\"description\":\"Present when the counts are POOL-DERIVED (every ranked query, and any filter-only query whose exact aggregation degraded): the size of the finalised candidate pool the per-value figures were counted over. Absent when the counts are exact under the query's whole predicate.\",\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"requested\":{\"type\":\"array\",\"items\":{\"type\":\"string\",\"minLength\":1},\"description\":\"The facet ids the request asked for, echoed verbatim.\"},\"absent\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"facetId\":{\"type\":\"string\",\"minLength\":1,\"de",
49
+ "scription\":\"The requested facet id, verbatim.\"},\"reason\":{\"type\":\"string\",\"enum\":[\"unsupported\",\"timeout\"],\"description\":\"Why no counts were computed. `unsupported` — the id is KNOWN (a real registry attribute or virtual facet) but facet counting does not cover it: numeric attributes, non-facetable attributes, and (v1) the virtual certification programme. `timeout` — counting did not complete inside the facet stage's own time budget; the RESULTS are unaffected (facet counting can never take the page down), only the counts are missing. An id the registry has never heard of is a 400 naming it, never an `absent` entry.\"}},\"required\":[\"facetId\",\"reason\"],\"additionalProperties\":false},\"description\":\"Requested facets that were not counted, each with its reason — `unsupported` (the id is known but not countable) or `timeout` (the facet stage's own budget expired; the results are unaffected). Empty when every requested facet was counted.\"},\"counts\":{\"type\":\"object\",\"propertyNames\":{\"type\":\"string\"},\"additionalProperties\":{\"type\":\"object\",\"properties\":{\"values\":{\"maxItems\":25,\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"value\":{\"type\":\"string\",\"minLength\":1,\"description\":\"The CANONICAL token — a brand slug, a taxonomy product-type id, or a spec attribute's canonical term. Send it back verbatim as a filter term; never display-derive it.\"},\"label\":{\"type\":\"string\",\"minLength\":1,\"description\":\"Display label for the value — the brand's name, the product type's display name, or the registry dictionary label for a spec term. Falls back to the canonical token when the registry carries no label.\"},\"variants\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991,\"description\":\"Variants carrying this value within the counted set.\"}},\"required\":[\"value\",\"label\",\"variants\"],\"additionalProperties\":false},\"description\":\"Top values by variant count, at most 25. An empty list is a facet with no values in the counted set, not an error.\"},\"truncated\":{\"type\":\"boolean\",\"description\":\"ALWAYS present. True when more distinct values existed beyond the reported list; false when the list is complete. Never absent-or-false.\"},\"basis\":{\"type\":\"string\",\"enum\":[\"exact\",\"lowerBound\"],\"description\":\"Exactly \\\"exact\\\" or \\\"lowerBound\\\" — nothing else, ever. `exact`: each `variants` figure is the true count under the query's whole predicate (filter-driven requests only). `lowerBound`: the figures were counted over the ranked candidate pool (`facets.poolSize`), so each is a floor of the true per-value total. Pool-counted values are NEVER `exact`.\"}},\"required\":[\"values\",\"truncated\",\"basis\"],\"additionalProperties\":false},\"description\":\"Per-facet value counts, keyed by the requested facet id. Every requested facet that is not in `absent` has an entry, including facets whose counted set held no values (`values: []`).\"}},\"required\":[\"requested\",\"absent\",\"counts\"],\"additionalProperties\":false},\"corpusScope\":{\"description\":\"The corpus this page AND its `counts` were drawn from — present only when the request narrowed it. ABSENT means the whole graph. Read it with `counts`: under a scope a short page is a small SCOPED corpus, not a truncated one.\",\"type\":\"object\",\"properties\":{\"scope\":{\"type\":\"string\",\"enum\":[\"all\",\"samplizeCatalogue\",\"materialBankOrderable\",\"materialBankNaOrderable\",\"materialBankNaAndCatalogue\",\"materialBankEuOrderable\",\"designShopOrderable\"],\"description\":\"Which corpus to answer from. samplizeCatalogue admits published US paint colours regardless of stock. ABSENT (or 'all') means the WHOLE GRAPH — every material MaterialGraph knows, orderable or not; absence is never a narrower default. Three transaction-oriented scopes gate on governed CHANNEL membership: 'materialBankNaOrderable' (Material Bank North America, ~277,000 materials), 'materialBankEuOrderable' (Material Bank Europe, ~65,000) and 'designShopOrderable' (DesignShop, ~34,000, with the products DesignShop hides from browse excluded). 'materialBankNaAndCatalogue' is the one-query union of Material Bank North America and the governed Catalogue editorial discovery corpus; that unio",
50
+ "n does not by itself claim every member is transactable. 'materialBankOrderable' is NOT one of them: it selects materials carrying a Material Bank identity, which is a provenance signal rather than a channel one, and it admits 98.4% of the graph — use it to exclude crawl-sourced brands, not to mean orderable. A scope answers membership in its named governed corpus, never suitability: scope on channel, then refine on sector. Every scope is applied INSIDE the query: `limit` fills with eligible rows and `counts` describe the scoped corpus, which post-filtering a page cannot give you. An unknown value is a 400 naming it, never a silent fall back to the whole graph.\"},\"description\":{\"type\":\"string\",\"description\":\"What the scope admits, in one sentence, for a human reader.\"}},\"required\":[\"scope\",\"description\"],\"additionalProperties\":false},\"effectiveColours\":{\"description\":\"The `#rrggbb` colours the colour signal ACTUALLY ran on, after minor-proportion accents were dropped (MG-1549). Present whenever the request carried explicit `colours`. Compare against your request: a difference means the search that ran is not the search that was asked for.\",\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"droppedColours\":{\"description\":\"Colours removed from the query for carrying a `weight` below the 5% accent threshold. EMPTY is the normal case. Non-empty and the dropped colours are absent from colour retrieval and ranking entirely — they did not merely rank low. To keep a small accent in the query, give it `role: \\\"accent\\\"`.\",\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"note\":{\"type\":\"string\"},\"degraded\":{\"description\":\"Which parts of the search did not answer, on EVERY response rather than only under `explain`. A named part says that check failed and the search carried on without it, so the page may be missing results or a warning it would otherwise have carried. An empty `parts` says no part reported a failure; it does NOT promise every check ran, because a check the request never needed is skipped rather than failed. ABSENT means the server does not report this, never that nothing was degraded. The vocabulary is open: read it with a default branch.\",\"type\":\"object\",\"properties\":{\"parts\":{\"type\":\"array\",\"items\":{\"type\":\"string\",\"enum\":[\"filter-coverage\",\"term-coverage\"]}}},\"required\":[\"parts\"],\"additionalProperties\":false},\"emptyOutcome\":{\"description\":\"Why this page carries no results, on EVERY empty response rather than only under `explain`. `proven-corpus-empty` means the catalogue was checked and holds nothing matching — safe to tell someone there is nothing. `unproven-empty` means the search ended with nothing and could not prove the catalogue is empty, so it is never grounds for a claim about the catalogue. `search-degraded` means part of the search did not answer; retry before reading anything into the empty page. ABSENT means the page carries results.\",\"oneOf\":[{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"const\":\"proven-corpus-empty\"},\"basis\":{\"type\":\"string\",\"enum\":[\"spec-allowlist\",\"brand-scope\",\"brand-product-type\"]},\"exhaustive\":{\"type\":\"boolean\",\"const\":true},\"sourceEmpty\":{\"type\":[\"boolean\",\"null\"]},\"counts\":{\"type\":\"object\",\"properties\":{\"retrieved\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"afterPrune\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"final\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991}},\"required\":[\"retrieved\",\"afterPrune\",\"final\"],\"additionalProperties\":false}},\"required\":[\"kind\",\"basis\",\"exhaustive\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"const\":\"unproven-empty\"},\"reason\":{\"type\":\"string\",\"enum\":[\"allowlist-truncated\",\"retrieval-window-not-exhaustive\",\"source-empty\",\"post-retrieval-rejected\",\"pagination-empty\",\"no-authoritative-empty-proof\"]},\"sourceEmpty\":{\"type\":[\"boolean\",\"null\"]},\"counts\":{\"type\":\"object\",\"properties\":{\"retrieved\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"afterPrune\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"final\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007",
51
+ "199254740991}},\"required\":[\"retrieved\",\"afterPrune\",\"final\"],\"additionalProperties\":false}},\"required\":[\"kind\",\"reason\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"const\":\"search-degraded\"},\"parts\":{\"minItems\":1,\"type\":\"array\",\"items\":{\"type\":\"string\",\"enum\":[\"filter-coverage\",\"term-coverage\"]}},\"sourceEmpty\":{\"type\":[\"boolean\",\"null\"]},\"counts\":{\"type\":\"object\",\"properties\":{\"retrieved\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"afterPrune\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"final\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991}},\"required\":[\"retrieved\",\"afterPrune\",\"final\"],\"additionalProperties\":false}},\"required\":[\"kind\",\"parts\"],\"additionalProperties\":false}],\"type\":\"object\"},\"termCoverage\":{\"description\":\"Lexical-projection coverage of substantive terms. Present only under explain.\",\"type\":\"object\",\"properties\":{\"status\":{\"type\":\"string\",\"enum\":[\"covered\",\"uncovered\",\"unavailable\",\"skipped\"]},\"terms\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"note\":{\"type\":\"string\"}},\"required\":[\"status\",\"terms\"],\"additionalProperties\":false},\"cursor\":{\"type\":\"string\"},\"window\":{\"type\":\"object\",\"properties\":{\"profile\":{\"type\":\"string\",\"const\":\"interactive-r1\"},\"candidateTier\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"retrievalRounds\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"retrievalMs\":{\"type\":\"number\",\"minimum\":0},\"exhaustive\":{\"type\":\"boolean\"},\"candidateCounts\":{\"type\":\"object\",\"properties\":{\"rawProviderMatches\":{\"anyOf\":[{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},{\"type\":\"null\"}]},\"selectedMatches\":{\"anyOf\":[{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},{\"type\":\"null\"}]},\"hydratedCanonicalCandidates\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991}},\"required\":[\"rawProviderMatches\",\"selectedMatches\",\"hydratedCanonicalCandidates\"],\"additionalProperties\":false}},\"required\":[\"profile\",\"candidateTier\",\"retrievalRounds\",\"retrievalMs\",\"exhaustive\"],\"additionalProperties\":false,\"description\":\"The single bounded candidate window that produced this page: versioned profile, requested depth, retrieval cost, and whether the selected source proved it was exhausted.\"},\"plan\":{\"description\":\"The executed query shape — result grain and the per-arm weight profile. Present only when `explain` is set. Diagnostic, not part of the ranking contract.\",\"type\":\"object\",\"properties\":{\"textLane\":{\"type\":\"string\",\"enum\":[\"image-text\",\"variant-text\"]},\"grain\":{\"type\":\"string\",\"enum\":[\"variant\",\"product\"],\"description\":\"What one result stands for: \\\"variant\\\" (the default — every matching variant is its own result) or \\\"product\\\" (`collapse: \\\"product\\\"` grouped variants, so each result fronts a product and hides siblings).\"},\"weights\":{\"type\":\"object\",\"properties\":{\"portrait\":{\"type\":\"number\"},\"image\":{\"type\":\"number\"},\"category\":{\"type\":\"number\"},\"colour\":{\"type\":\"number\"},\"lexical\":{\"type\":\"number\"},\"text\":{\"type\":\"number\"}},\"required\":[\"portrait\",\"image\",\"category\",\"colour\",\"lexical\",\"text\"],\"additionalProperties\":false,\"description\":\"The applied ordering-signal profile. At most one weight is non-zero: the selected mode's primary signal. Multiplying that signal by its weight reproduces `score`, and the weight is `maxScore`. Other signals are diagnostic or hard-gate receipts and never blend.\"},\"queryTransform\":{\"type\":\"array\",\"items\":{\"oneOf\":[{\"type\":\"object\",\"properties\":{\"stage\":{\"type\":\"string\",\"enum\":[\"normalise\",\"resolve-spec\",\"resolve-paint\",\"resolve-brand\",\"resolve-category\"]},\"applied\":{\"type\":\"boolean\",\"const\":true}},\"required\":[\"stage\",\"applied\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"stage\":{\"type\":\"string\",\"enum\":[\"normalise\",\"resolve-spec\",\"resolve-paint\",\"resolve-brand\",\"resolve-category\"]},\"applied\":{\"type\":\"boolean\",\"const\":false},\"reason\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"stage\",\"applied\",\"reason\"],\"additionalProperties\":false}],\"type\":\"object\"},\"description\":\"The query-transformation stages that actually executed, in execution order. An a",
52
+ "pplied stage changed execution; a skipped stage carries its reason. An empty array means this path ran no query transform.\"},\"steps\":{\"description\":\"Ordered request-understanding steps. Every entry says whether the step executed or skipped, why, and its measured wall-clock duration.\",\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"key\":{\"type\":\"string\",\"minLength\":1},\"status\":{\"type\":\"string\",\"enum\":[\"executed\",\"skipped\"]},\"reason\":{\"type\":\"string\",\"minLength\":1},\"durationMs\":{\"type\":\"number\",\"minimum\":0}},\"required\":[\"key\",\"status\",\"reason\",\"durationMs\"],\"additionalProperties\":false}},\"categoryAnchor\":{\"description\":\"Present only when the query NAMED a canonical product type. See `applied` for whether it gated the candidate pool.\",\"type\":\"object\",\"properties\":{\"productTypeId\":{\"type\":\"string\",\"description\":\"The leaf product-type id the query anchored to, e.g. \\\"modular_carpet\\\" — or, for a \\\"group\\\" anchor, the GROUP key, e.g. \\\"flooring\\\". A group key is a taxonomy node no variant is typed as; the ids actually gated on are in `productTypeIds`.\"},\"productTypeIds\":{\"description\":\"The leaf type ids a \\\"group\\\" anchor expanded to. Absent for the leaf sources, where `productTypeId` is already the single type gated on.\",\"readOnly\":true,\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"displayName\":{\"type\":\"string\",\"description\":\"The registry display name of that type.\"},\"similarity\":{\"type\":\"number\",\"description\":\"Cosine of the RAW (unanchored) query vector against the type vector for a \\\"vector\\\" anchor; 1 for the exact synonym / display-name passes.\"},\"margin\":{\"description\":\"Lead over the runner-up type for a \\\"vector\\\" anchor — the tie-break guard that stops an ambiguous query gating. Null for the exact passes.\",\"type\":[\"number\",\"null\"]},\"source\":{\"type\":\"string\",\"enum\":[\"taxonomy\",\"synonym\",\"display-name\",\"group\",\"head-noun\",\"vector\"],\"description\":\"How the anchor was established: a curated customer-language synonym, a registry display name appearing verbatim in the query, a public product-type group named by the query's trailing head noun, a bare canonical category noun whose leaf types cut across groups (\\\"tile\\\"), or the measured vector match.\"},\"candidateSource\":{\"description\":\"Provider-owned retrieval policy. When present, the hard category is applied inside lexical candidate selection before the result limit.\",\"type\":\"string\",\"const\":\"lexical\"},\"applied\":{\"type\":\"boolean\",\"description\":\"Whether the anchor actually gated the pool. False means the anchored category held nothing in the candidate pool, so the engine fell back to unanchored ranking (and said so in `note`) rather than returning an empty page.\"}},\"required\":[\"productTypeId\",\"displayName\",\"similarity\",\"margin\",\"source\",\"applied\"],\"additionalProperties\":false},\"brandAnchor\":{\"description\":\"Present only when the query NAMED a brand the catalogue sells. See `applied` for whether it gated retrieval or was superseded by the empty-result retreat.\",\"type\":\"object\",\"properties\":{\"brandSlug\":{\"type\":\"string\",\"description\":\"The brand the query named, e.g. \\\"daltile\\\" — its stable slug.\"},\"brandName\":{\"type\":\"string\",\"description\":\"The brand's name as catalogued.\"},\"confidence\":{\"type\":\"number\",\"description\":\"Resolver confidence in [0,1]. 1 means the query IS the brand's name or slug; the gate's floor is 0.88.\"},\"margin\":{\"description\":\"Lead over the runner-up brand — the tie-break guard that stops an ambiguous query gating. Null when there was no runner-up.\",\"type\":[\"number\",\"null\"]},\"tier\":{\"type\":\"string\",\"enum\":[\"exact_id\",\"exact_alias\",\"lexical\",\"semantic\"],\"description\":\"Which rung of the resolver ladder won. Only the identity rungs (`exact_id`, `exact_alias`) may gate — a `lexical` or `semantic` win means the query RESEMBLES a brand name rather than naming one.\"},\"variantCount\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991,\"description\":\"Catalogued variants behind the brand — the gated universe's size.\"},\"applied\":{\"type\":\"boolean\",\"description\":\"Whether the anchor actually gated retrieval. False means the brand held nothing retrievable, so the eng",
53
+ "ine re-ran unanchored (and said so in `note`) rather than returning an empty page.\"}},\"required\":[\"brandSlug\",\"brandName\",\"confidence\",\"margin\",\"tier\",\"variantCount\",\"applied\"],\"additionalProperties\":false},\"brandSkipped\":{\"description\":\"Present only when the brand stage saw a real brand and REFUSED to gate on it. Mutually exclusive with `brandAnchor`.\",\"type\":\"object\",\"properties\":{\"reason\":{\"type\":\"string\",\"enum\":[\"ambiguous\",\"weak-tier\",\"too-broad\"],\"description\":\"Why the brand gate was declined. `ambiguous` = two brands inside the resolver's gap; `weak-tier` = the query resembles a brand name rather than naming one; `too-broad` = the brand exceeds the exact-scan ceiling.\"},\"candidates\":{\"description\":\"The brands that tied — populated for `ambiguous` only, so the response can name what it declined to choose between.\",\"type\":\"array\",\"items\":{\"type\":\"string\"}}},\"required\":[\"reason\"],\"additionalProperties\":false}},\"required\":[\"grain\",\"weights\",\"queryTransform\"],\"additionalProperties\":false},\"timings\":{\"description\":\"Wall-clock milliseconds per step of the search workflow, keyed by step id, plus `total` for the whole run. The key set is open and depends on the path a query took, so read it by iterating rather than by naming a key. Present only when `explain` is set — diagnostic, not part of the ranking contract.\",\"type\":\"object\",\"propertyNames\":{\"type\":\"string\"},\"additionalProperties\":{\"type\":\"number\"}},\"query\":{\"type\":\"object\",\"propertyNames\":{\"type\":\"string\"},\"additionalProperties\":{}},\"seed\":{\"type\":\"object\",\"propertyNames\":{\"type\":\"string\"},\"additionalProperties\":{}},\"explain\":{\"type\":\"object\",\"properties\":{\"ledger\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"propertyNames\":{\"type\":\"string\"},\"additionalProperties\":{}}}},\"required\":[\"ledger\"],\"additionalProperties\":false}},\"required\":[\"view\",\"results\",\"suggestions\",\"mode\",\"maxScore\",\"query\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"view\":{\"type\":\"string\",\"const\":\"card\",\"description\":\"Projection echo — always 'card' on this envelope variant.\"},\"results\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"resultId\":{\"type\":\"string\",\"description\":\"This exact hit's attribution key — `<responseId>:<rank>`, where rank is 1-based within THIS response. Unique across pages, because each page is its own response with its own id. Store it verbatim next to the outcome; it is opaque, and `attribution.responseId` is the field to group by.\"},\"variantId\":{\"type\":\"string\"},\"productId\":{\"type\":[\"string\",\"null\"]},\"brandSlug\":{\"type\":[\"string\",\"null\"]},\"brand\":{\"type\":\"string\"},\"name\":{\"type\":\"string\",\"description\":\"Product name (the full item's `product`).\"},\"variantName\":{\"type\":[\"string\",\"null\"]},\"imageUrl\":{\"type\":[\"string\",\"null\"]},\"manufacturerCode\":{\"description\":\"The governed manufacturer/source-native item code from `variants.code` (for example \\\"K-7265-BV\\\") — the same key and the same value the full item publishes. It is what separates two cards a reader cannot otherwise tell apart: brand, product name and variant name are shared by 280,334 of 371,515 cardable variants, because `name` is the PRODUCT name and a paint product is a whole collection. ABSENT means no code is recorded for the variant, never an invented one.\",\"type\":\"string\"},\"projectSaveCapability\":{\"type\":\"string\",\"const\":\"allowed\"},\"sampleable\":{\"description\":\"Whether a physical sample is explicitly known to be orderable. Absent means unknown, never unavailable.\",\"type\":\"boolean\"},\"sourceStatus\":{\"type\":\"string\",\"const\":\"provisional\"},\"score\":{\"type\":\"number\"},\"attributes\":{\"description\":\"Resolved attribute values for the ids sent in `returnAttributes`, keyed by attribute id. ABSENT entirely when `returnAttributes` was not sent. A key present with `null` means this variant carries no accepted value for that attribute — absent is not empty and neither is null: the three states are distinct on purpose. Values resolve with the normal variant-over-product precedence.\",\"type\":\"object\",\"propertyNames\":{\"type\":\"string\"},\"additionalProperties\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"array\",\"items\":{\"typ",
54
+ "e\":\"string\"}},{\"type\":\"null\"}]}}},\"required\":[\"variantId\",\"productId\",\"brandSlug\",\"brand\",\"name\",\"variantName\",\"imageUrl\",\"score\"],\"additionalProperties\":false}},\"attribution\":{\"type\":\"object\",\"properties\":{\"responseId\":{\"type\":\"string\",\"description\":\"This response's stable id — the same value as the `X-Request-Id` header. Store it next to the outcome (an order, a save, a click) to join that outcome back to the ranking that produced it. Opaque: round-trip it verbatim, never parse it.\"},\"basis\":{\"type\":\"string\",\"enum\":[\"image\",\"colour\",\"text\",\"lexical\",\"entity-browse\",\"match\"],\"description\":\"The ranking basis, groupable across both ranked routes. On /api/v1/search this is the envelope's `mode` verbatim; /api/v1/search/match reports 'match'. Group your stored outcomes by this field.\"},\"issuedAt\":{\"type\":\"string\",\"description\":\"ISO-8601 timestamp of when this response was assembled, so a stored outcome carries its own age without a second lookup.\"}},\"required\":[\"responseId\",\"basis\",\"issuedAt\"],\"additionalProperties\":false,\"description\":\"Result attribution receipt. Present on every successful ranked response. Nothing on our side retains it — what you store IS the record.\"},\"refinement\":{\"description\":\"Inspectable receipt for narrowing this exact finalized candidate pool. The token is opaque and never exposes variant ids.\",\"type\":\"object\",\"properties\":{\"token\":{\"type\":\"string\"},\"expiresAt\":{\"type\":\"string\",\"format\":\"date-time\",\"pattern\":\"^(?:(?:\\\\d\\\\d[2468][048]|\\\\d\\\\d[13579][26]|\\\\d\\\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\\\d|30)|(?:02)-(?:0[1-9]|1\\\\d|2[0-8])))T(?:(?:[01]\\\\d|2[0-3]):[0-5]\\\\d:[0-5]\\\\d(?:\\\\.\\\\d+)?(?:Z))$\"},\"poolSize\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":800},\"schemaVersion\":{\"type\":\"string\"},\"projectionVersion\":{\"type\":\"integer\",\"minimum\":-9007199254740991,\"maximum\":9007199254740991}},\"required\":[\"token\",\"expiresAt\",\"poolSize\",\"schemaVersion\",\"projectionVersion\"],\"additionalProperties\":false},\"understanding\":{\"description\":\"How the ordinary search path interpreted the raw material-domain query: recognized senses, applied/refused/ambiguous outcomes, evidence, and the semantic text left after exact entity consumption. New standard-search responses include it; optional in the schema for rolling-deploy compatibility.\",\"type\":\"object\",\"properties\":{\"version\":{\"type\":\"string\",\"const\":\"1\"},\"rawQuery\":{\"type\":\"string\"},\"normalisedQuery\":{\"type\":\"string\"},\"residualText\":{\"type\":\"string\"},\"status\":{\"type\":\"string\",\"enum\":[\"resolved\",\"ambiguous\",\"partial\",\"unresolved\"]},\"mode\":{\"type\":\"string\",\"enum\":[\"image\",\"colour\",\"text\",\"lexical\",\"entity-browse\"]},\"senses\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"id\":{\"type\":\"string\",\"minLength\":1},\"kind\":{\"type\":\"string\",\"enum\":[\"brand\",\"product\",\"paint_identity\",\"category\",\"material\",\"colour\",\"attribute\",\"attribute_value\",\"certification\"]},\"label\":{\"type\":\"string\",\"minLength\":1},\"outcome\":{\"type\":\"string\",\"enum\":[\"applied\",\"ambiguous\",\"recognized\",\"refused\"]},\"effect\":{\"type\":\"string\",\"enum\":[\"restrict\",\"prioritize\",\"rerank\",\"offer\",\"none\"]},\"confidence\":{\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"evidence\":{\"type\":\"object\",\"properties\":{\"source\":{\"type\":\"string\",\"minLength\":1},\"matchedText\":{\"type\":\"string\",\"minLength\":1},\"span\":{\"type\":\"object\",\"properties\":{\"start\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"end\":{\"type\":\"integer\",\"exclusiveMinimum\":0,\"maximum\":9007199254740991}},\"required\":[\"start\",\"end\"],\"additionalProperties\":false}},\"required\":[\"source\",\"matchedText\"],\"additionalProperties\":false},\"entity\":{\"type\":\"object\",\"properties\":{\"brandSlug\":{\"type\":\"string\"},\"productId\":{\"type\":\"string\"},\"variantId\":{\"type\":\"string\"},\"materialBankId\":{\"type\":\"string\"},\"productTypeId\":{\"type\":\"string\"},\"paintCode\":{\"type\":\"string\"},\"hex\":{\"type\":\"string\"}},\"additionalProperties\":false},\"reason\":{\"type\":\"string\"}},\"required\":[\"id\",\"kind\",\"label\",\"outcome\",\"effect\",\"confidence\",\"evidence\"],\"additionalProperties\":false}},\"canonicalLexical\":{\"type\":\"object\",\"properties\":{",
55
+ "\"original\":{\"type\":\"string\"},\"outcome\":{\"type\":\"string\",\"enum\":[\"resolved\",\"ambiguous\",\"declined\",\"partial\",\"unresolved\"]},\"matches\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"original\":{\"type\":\"string\"},\"canonical\":{\"type\":\"string\"},\"rule\":{\"type\":\"string\",\"enum\":[\"exact\",\"plural-to-singular\",\"typo-to-canonical\"]},\"span\":{\"type\":\"object\",\"properties\":{\"start\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"end\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991}},\"required\":[\"start\",\"end\"],\"additionalProperties\":false},\"coverage\":{\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"readings\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"matchTarget\":{\"oneOf\":[{\"type\":\"object\",\"properties\":{\"type\":{\"type\":\"string\",\"const\":\"none\"},\"hasSelection\":{\"type\":\"boolean\",\"const\":false}},\"required\":[\"type\",\"hasSelection\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"type\":{\"type\":\"string\",\"const\":\"oklab\"},\"hasSelection\":{\"type\":\"boolean\",\"const\":true},\"L\":{\"type\":\"number\"},\"a\":{\"type\":\"number\"},\"b\":{\"type\":\"number\"},\"label\":{\"type\":\"string\"},\"previewColors\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}}},\"required\":[\"type\",\"hasSelection\",\"L\",\"a\",\"b\",\"label\",\"previewColors\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"type\":{\"type\":\"string\",\"const\":\"range\"},\"hasSelection\":{\"type\":\"boolean\",\"const\":true},\"L\":{\"type\":\"number\"},\"a\":{\"type\":\"number\"},\"b\":{\"type\":\"number\"},\"label\":{\"type\":\"string\"},\"previewColors\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"range\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"chroma\":{\"type\":\"array\",\"prefixItems\":[{\"type\":\"number\",\"minimum\":0},{\"type\":\"number\",\"minimum\":0}],\"items\":false,\"minItems\":2,\"maxItems\":2},\"hue\":{\"anyOf\":[{\"type\":\"array\",\"prefixItems\":[{\"type\":\"number\",\"minimum\":0,\"maximum\":360},{\"type\":\"number\",\"minimum\":0,\"maximum\":360}],\"items\":false,\"minItems\":2,\"maxItems\":2},{\"type\":\"null\"}]},\"lightness\":{\"type\":\"array\",\"prefixItems\":[{\"type\":\"number\",\"minimum\":0,\"maximum\":1},{\"type\":\"number\",\"minimum\":0,\"maximum\":1}],\"items\":false,\"minItems\":2,\"maxItems\":2}},\"required\":[\"chroma\",\"hue\",\"lightness\"],\"additionalProperties\":false}},\"representative\":{\"type\":\"object\",\"properties\":{\"hex\":{\"type\":\"string\",\"pattern\":\"^#[0-9a-f]{6}$\"}},\"required\":[\"hex\"],\"additionalProperties\":false}},\"required\":[\"type\",\"hasSelection\",\"L\",\"a\",\"b\",\"label\",\"previewColors\",\"range\",\"representative\"],\"additionalProperties\":false}],\"type\":\"object\"},\"colorResolution\":{\"oneOf\":[{\"type\":\"object\",\"properties\":{\"confidence\":{\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"input\":{\"type\":\"string\"},\"kind\":{\"type\":\"string\",\"const\":\"resolved\"},\"others\":{\"maxItems\":4,\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"band\":{\"type\":\"string\",\"enum\":[\"inside\",\"near\",\"far\"]},\"family\":{\"type\":\"string\",\"enum\":[\"white\",\"cream\",\"gray\",\"black\",\"beige\",\"taupe\",\"brown\",\"red\",\"orange\",\"yellow\",\"green\",\"teal\",\"blue\",\"purple\",\"pink\"]},\"hex\":{\"type\":\"string\",\"pattern\":\"^#[0-9a-f]{6}$\"},\"id\":{\"type\":\"string\",\"minLength\":1},\"name\":{\"type\":\"string\",\"minLength\":1},\"range\":{\"type\":\"object\",\"properties\":{\"chroma\":{\"type\":\"array\",\"prefixItems\":[{\"type\":\"number\",\"minimum\":0},{\"type\":\"number\",\"minimum\":0}],\"items\":false,\"minItems\":2,\"maxItems\":2},\"hue\":{\"anyOf\":[{\"type\":\"array\",\"prefixItems\":[{\"type\":\"number\",\"minimum\":0,\"maximum\":360},{\"type\":\"number\",\"minimum\":0,\"maximum\":360}],\"items\":false,\"minItems\":2,\"maxItems\":2},{\"type\":\"null\"}]},\"lightness\":{\"type\":\"array\",\"prefixItems\":[{\"type\":\"number\",\"minimum\":0,\"maximum\":1},{\"type\":\"number\",\"minimum\":0,\"maximum\":1}],\"items\":false,\"minItems\":2,\"maxItems\":2}},\"required\":[\"chroma\",\"hue\",\"lightness\"],\"additionalProperties\":false}},\"required\":[\"band\",\"family\",\"hex\",\"id\",\"name\",\"range\"],\"additionalProperties\":false}},\"principal\":{\"type\":\"object\",\"properties\":{\"band\":{\"type\":\"string\",\"enum\":[\"inside\",\"near\",\"far\"]},\"family\":{\"type\":\"string\",\"enum\":[\"white\",\"cream\",\"gray\",\"black\",\"beige\",\"taupe\",\"brown\",\"red\",\"orange\",\"yellow\",\"green\",\"teal\",\"blue\",\"purple\",\"pink\"]},\"hex\":{\"type\":\"string\",\"pattern\":\"^#[0-9a-f]{6}$\"},\"id\":{\"type\":\"string\",\"minLength",
56
+ "\":1},\"name\":{\"type\":\"string\",\"minLength\":1},\"range\":{\"type\":\"object\",\"properties\":{\"chroma\":{\"type\":\"array\",\"prefixItems\":[{\"type\":\"number\",\"minimum\":0},{\"type\":\"number\",\"minimum\":0}],\"items\":false,\"minItems\":2,\"maxItems\":2},\"hue\":{\"anyOf\":[{\"type\":\"array\",\"prefixItems\":[{\"type\":\"number\",\"minimum\":0,\"maximum\":360},{\"type\":\"number\",\"minimum\":0,\"maximum\":360}],\"items\":false,\"minItems\":2,\"maxItems\":2},{\"type\":\"null\"}]},\"lightness\":{\"type\":\"array\",\"prefixItems\":[{\"type\":\"number\",\"minimum\":0,\"maximum\":1},{\"type\":\"number\",\"minimum\":0,\"maximum\":1}],\"items\":false,\"minItems\":2,\"maxItems\":2}},\"required\":[\"chroma\",\"hue\",\"lightness\"],\"additionalProperties\":false}},\"required\":[\"band\",\"family\",\"hex\",\"id\",\"name\",\"range\"],\"additionalProperties\":false},\"source\":{\"type\":\"string\",\"enum\":[\"name\",\"family\",\"composed\",\"hex\",\"reference\",\"semantic\"]}},\"required\":[\"confidence\",\"input\",\"kind\",\"others\",\"principal\",\"source\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"input\":{\"type\":\"string\"},\"kind\":{\"type\":\"string\",\"const\":\"unresolved\"},\"reason\":{\"type\":\"string\",\"enum\":[\"unknown-term\",\"ambiguous-modifier\",\"contradictory-modifiers\",\"unsupported-base\",\"empty-range\",\"unrepresentable-color\"]},\"terms\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}}},\"required\":[\"input\",\"kind\",\"reason\",\"terms\"],\"additionalProperties\":false}],\"type\":\"object\"},\"authority\":{\"type\":\"string\",\"enum\":[\"taxonomy\",\"colorscope\"]},\"identity\":{\"type\":\"string\",\"minLength\":1},\"outcome\":{\"type\":\"string\",\"enum\":[\"equivalent\",\"ambiguous\",\"declined\"]},\"executionStatus\":{\"type\":\"string\",\"enum\":[\"applied\",\"suppressed\",\"executable\",\"recognized-but-unexecutable\",\"declined\"],\"description\":\"MaterialGraph's execution verdict for this provider-owned reading. Taxonomy adapters report 'applied' only when their category or criterion contributed to this query; an adapter-known ambiguous or conflicting reading is 'suppressed'. 'recognized-but-unexecutable' means no current mapping can apply it.\"},\"role\":{\"anyOf\":[{\"type\":\"string\",\"enum\":[\"primary\",\"secondary\"]},{\"type\":\"null\"}]},\"kind\":{\"type\":\"string\",\"enum\":[\"domain\",\"core-colour\",\"paint\"]},\"label\":{\"type\":\"string\"},\"libraryId\":{\"type\":\"string\"},\"code\":{\"type\":\"string\"},\"aliasCount\":{\"type\":\"integer\",\"exclusiveMinimum\":0,\"maximum\":9007199254740991},\"matchForm\":{\"type\":\"string\",\"enum\":[\"name\",\"code\",\"authoritative-term\"]},\"collisionCount\":{\"type\":\"integer\",\"exclusiveMinimum\":0,\"maximum\":9007199254740991},\"familyId\":{\"type\":\"string\"},\"hex\":{\"type\":\"string\",\"pattern\":\"^#[0-9a-f]{6}$\"},\"level\":{\"type\":\"string\",\"enum\":[\"family\",\"tone-band\",\"branch\",\"concept\"]},\"lifecycle\":{\"type\":\"object\",\"properties\":{\"introducedAt\":{\"type\":\"string\",\"minLength\":1},\"reviewedAt\":{\"type\":\"string\",\"minLength\":1},\"status\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"introducedAt\",\"status\"],\"additionalProperties\":false},\"provenance\":{\"type\":\"object\",\"properties\":{\"basis\":{\"type\":\"string\",\"minLength\":1},\"snapshot\":{\"type\":\"string\",\"minLength\":1},\"source\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"basis\",\"source\"],\"additionalProperties\":false}},\"required\":[\"authority\",\"identity\",\"outcome\",\"executionStatus\",\"role\"],\"additionalProperties\":false}},\"receipt\":{\"type\":\"object\",\"properties\":{\"original\":{\"type\":\"string\"},\"canonical\":{\"type\":\"string\"},\"rule\":{\"type\":\"string\",\"enum\":[\"exact\",\"plural-to-singular\",\"typo-to-canonical\"]},\"authorities\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"authority\":{\"type\":\"string\",\"enum\":[\"taxonomy\",\"colorscope\"]},\"version\":{\"type\":\"string\",\"minLength\":1},\"revision\":{\"type\":\"string\",\"minLength\":1},\"digest\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"authority\",\"version\",\"revision\"],\"additionalProperties\":false}}},\"required\":[\"original\",\"canonical\",\"rule\",\"authorities\"],\"additionalProperties\":false}},\"required\":[\"original\",\"canonical\",\"rule\",\"span\",\"coverage\",\"readings\",\"receipt\"],\"additionalProperties\":false}},\"unresolved\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"original\":{\"type\":\"string\"},\"canonical\":{\"type\":\"string\"},\"rule\":{\"type\":\"string\",\"enum\":[\"exact\",\"plural-to-singular\",\"typo-to-canonical\"]},\"sp",
57
+ "an\":{\"type\":\"object\",\"properties\":{\"start\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"end\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991}},\"required\":[\"start\",\"end\"],\"additionalProperties\":false},\"coverage\":{\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"readings\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"matchTarget\":{\"oneOf\":[{\"type\":\"object\",\"properties\":{\"type\":{\"type\":\"string\",\"const\":\"none\"},\"hasSelection\":{\"type\":\"boolean\",\"const\":false}},\"required\":[\"type\",\"hasSelection\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"type\":{\"type\":\"string\",\"const\":\"oklab\"},\"hasSelection\":{\"type\":\"boolean\",\"const\":true},\"L\":{\"type\":\"number\"},\"a\":{\"type\":\"number\"},\"b\":{\"type\":\"number\"},\"label\":{\"type\":\"string\"},\"previewColors\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}}},\"required\":[\"type\",\"hasSelection\",\"L\",\"a\",\"b\",\"label\",\"previewColors\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"type\":{\"type\":\"string\",\"const\":\"range\"},\"hasSelection\":{\"type\":\"boolean\",\"const\":true},\"L\":{\"type\":\"number\"},\"a\":{\"type\":\"number\"},\"b\":{\"type\":\"number\"},\"label\":{\"type\":\"string\"},\"previewColors\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"range\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"chroma\":{\"type\":\"array\",\"prefixItems\":[{\"type\":\"number\",\"minimum\":0},{\"type\":\"number\",\"minimum\":0}],\"items\":false,\"minItems\":2,\"maxItems\":2},\"hue\":{\"anyOf\":[{\"type\":\"array\",\"prefixItems\":[{\"type\":\"number\",\"minimum\":0,\"maximum\":360},{\"type\":\"number\",\"minimum\":0,\"maximum\":360}],\"items\":false,\"minItems\":2,\"maxItems\":2},{\"type\":\"null\"}]},\"lightness\":{\"type\":\"array\",\"prefixItems\":[{\"type\":\"number\",\"minimum\":0,\"maximum\":1},{\"type\":\"number\",\"minimum\":0,\"maximum\":1}],\"items\":false,\"minItems\":2,\"maxItems\":2}},\"required\":[\"chroma\",\"hue\",\"lightness\"],\"additionalProperties\":false}},\"representative\":{\"type\":\"object\",\"properties\":{\"hex\":{\"type\":\"string\",\"pattern\":\"^#[0-9a-f]{6}$\"}},\"required\":[\"hex\"],\"additionalProperties\":false}},\"required\":[\"type\",\"hasSelection\",\"L\",\"a\",\"b\",\"label\",\"previewColors\",\"range\",\"representative\"],\"additionalProperties\":false}],\"type\":\"object\"},\"colorResolution\":{\"oneOf\":[{\"type\":\"object\",\"properties\":{\"confidence\":{\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"input\":{\"type\":\"string\"},\"kind\":{\"type\":\"string\",\"const\":\"resolved\"},\"others\":{\"maxItems\":4,\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"band\":{\"type\":\"string\",\"enum\":[\"inside\",\"near\",\"far\"]},\"family\":{\"type\":\"string\",\"enum\":[\"white\",\"cream\",\"gray\",\"black\",\"beige\",\"taupe\",\"brown\",\"red\",\"orange\",\"yellow\",\"green\",\"teal\",\"blue\",\"purple\",\"pink\"]},\"hex\":{\"type\":\"string\",\"pattern\":\"^#[0-9a-f]{6}$\"},\"id\":{\"type\":\"string\",\"minLength\":1},\"name\":{\"type\":\"string\",\"minLength\":1},\"range\":{\"type\":\"object\",\"properties\":{\"chroma\":{\"type\":\"array\",\"prefixItems\":[{\"type\":\"number\",\"minimum\":0},{\"type\":\"number\",\"minimum\":0}],\"items\":false,\"minItems\":2,\"maxItems\":2},\"hue\":{\"anyOf\":[{\"type\":\"array\",\"prefixItems\":[{\"type\":\"number\",\"minimum\":0,\"maximum\":360},{\"type\":\"number\",\"minimum\":0,\"maximum\":360}],\"items\":false,\"minItems\":2,\"maxItems\":2},{\"type\":\"null\"}]},\"lightness\":{\"type\":\"array\",\"prefixItems\":[{\"type\":\"number\",\"minimum\":0,\"maximum\":1},{\"type\":\"number\",\"minimum\":0,\"maximum\":1}],\"items\":false,\"minItems\":2,\"maxItems\":2}},\"required\":[\"chroma\",\"hue\",\"lightness\"],\"additionalProperties\":false}},\"required\":[\"band\",\"family\",\"hex\",\"id\",\"name\",\"range\"],\"additionalProperties\":false}},\"principal\":{\"type\":\"object\",\"properties\":{\"band\":{\"type\":\"string\",\"enum\":[\"inside\",\"near\",\"far\"]},\"family\":{\"type\":\"string\",\"enum\":[\"white\",\"cream\",\"gray\",\"black\",\"beige\",\"taupe\",\"brown\",\"red\",\"orange\",\"yellow\",\"green\",\"teal\",\"blue\",\"purple\",\"pink\"]},\"hex\":{\"type\":\"string\",\"pattern\":\"^#[0-9a-f]{6}$\"},\"id\":{\"type\":\"string\",\"minLength\":1},\"name\":{\"type\":\"string\",\"minLength\":1},\"range\":{\"type\":\"object\",\"properties\":{\"chroma\":{\"type\":\"array\",\"prefixItems\":[{\"type\":\"number\",\"minimum\":0},{\"type\":\"number\",\"minimum\":0}],\"items\":false,\"minItems\":2,\"maxItems\":2},\"hue\":{\"anyOf\":[{\"type\":\"array\",\"prefixItems\":[{\"type\":\"number\",\"minimum\":0,\"maximum\":360},{\"type\":\"number\",",
58
+ "\"minimum\":0,\"maximum\":360}],\"items\":false,\"minItems\":2,\"maxItems\":2},{\"type\":\"null\"}]},\"lightness\":{\"type\":\"array\",\"prefixItems\":[{\"type\":\"number\",\"minimum\":0,\"maximum\":1},{\"type\":\"number\",\"minimum\":0,\"maximum\":1}],\"items\":false,\"minItems\":2,\"maxItems\":2}},\"required\":[\"chroma\",\"hue\",\"lightness\"],\"additionalProperties\":false}},\"required\":[\"band\",\"family\",\"hex\",\"id\",\"name\",\"range\"],\"additionalProperties\":false},\"source\":{\"type\":\"string\",\"enum\":[\"name\",\"family\",\"composed\",\"hex\",\"reference\",\"semantic\"]}},\"required\":[\"confidence\",\"input\",\"kind\",\"others\",\"principal\",\"source\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"input\":{\"type\":\"string\"},\"kind\":{\"type\":\"string\",\"const\":\"unresolved\"},\"reason\":{\"type\":\"string\",\"enum\":[\"unknown-term\",\"ambiguous-modifier\",\"contradictory-modifiers\",\"unsupported-base\",\"empty-range\",\"unrepresentable-color\"]},\"terms\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}}},\"required\":[\"input\",\"kind\",\"reason\",\"terms\"],\"additionalProperties\":false}],\"type\":\"object\"},\"authority\":{\"type\":\"string\",\"enum\":[\"taxonomy\",\"colorscope\"]},\"identity\":{\"type\":\"string\",\"minLength\":1},\"outcome\":{\"type\":\"string\",\"enum\":[\"equivalent\",\"ambiguous\",\"declined\"]},\"executionStatus\":{\"type\":\"string\",\"enum\":[\"applied\",\"suppressed\",\"executable\",\"recognized-but-unexecutable\",\"declined\"],\"description\":\"MaterialGraph's execution verdict for this provider-owned reading. Taxonomy adapters report 'applied' only when their category or criterion contributed to this query; an adapter-known ambiguous or conflicting reading is 'suppressed'. 'recognized-but-unexecutable' means no current mapping can apply it.\"},\"role\":{\"anyOf\":[{\"type\":\"string\",\"enum\":[\"primary\",\"secondary\"]},{\"type\":\"null\"}]},\"kind\":{\"type\":\"string\",\"enum\":[\"domain\",\"core-colour\",\"paint\"]},\"label\":{\"type\":\"string\"},\"libraryId\":{\"type\":\"string\"},\"code\":{\"type\":\"string\"},\"aliasCount\":{\"type\":\"integer\",\"exclusiveMinimum\":0,\"maximum\":9007199254740991},\"matchForm\":{\"type\":\"string\",\"enum\":[\"name\",\"code\",\"authoritative-term\"]},\"collisionCount\":{\"type\":\"integer\",\"exclusiveMinimum\":0,\"maximum\":9007199254740991},\"familyId\":{\"type\":\"string\"},\"hex\":{\"type\":\"string\",\"pattern\":\"^#[0-9a-f]{6}$\"},\"level\":{\"type\":\"string\",\"enum\":[\"family\",\"tone-band\",\"branch\",\"concept\"]},\"lifecycle\":{\"type\":\"object\",\"properties\":{\"introducedAt\":{\"type\":\"string\",\"minLength\":1},\"reviewedAt\":{\"type\":\"string\",\"minLength\":1},\"status\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"introducedAt\",\"status\"],\"additionalProperties\":false},\"provenance\":{\"type\":\"object\",\"properties\":{\"basis\":{\"type\":\"string\",\"minLength\":1},\"snapshot\":{\"type\":\"string\",\"minLength\":1},\"source\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"basis\",\"source\"],\"additionalProperties\":false}},\"required\":[\"authority\",\"identity\",\"outcome\",\"executionStatus\",\"role\"],\"additionalProperties\":false}},\"receipt\":{\"type\":\"object\",\"properties\":{\"original\":{\"type\":\"string\"},\"canonical\":{\"type\":\"string\"},\"rule\":{\"type\":\"string\",\"enum\":[\"exact\",\"plural-to-singular\",\"typo-to-canonical\"]},\"authorities\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"authority\":{\"type\":\"string\",\"enum\":[\"taxonomy\",\"colorscope\"]},\"version\":{\"type\":\"string\",\"minLength\":1},\"revision\":{\"type\":\"string\",\"minLength\":1},\"digest\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"authority\",\"version\",\"revision\"],\"additionalProperties\":false}}},\"required\":[\"original\",\"canonical\",\"rule\",\"authorities\"],\"additionalProperties\":false}},\"required\":[\"original\",\"canonical\",\"rule\",\"span\",\"coverage\",\"readings\",\"receipt\"],\"additionalProperties\":false}}},\"required\":[\"original\",\"outcome\",\"matches\",\"unresolved\"],\"additionalProperties\":false},\"criteria\":{\"type\":\"object\",\"properties\":{\"applied\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"criterion\":{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, e.g. \\\"primary_material\\\" or \\\"fire_rating\\\" — or the special id \\\"brand\\\", which filters on the brand RELATION (a join, carried by every variant) rather than on any attribute.\"},\"term\":{\"type\":\"string\",\"description\":\"Canonical ",
59
+ "term the attribute must equal, e.g. \\\"textile\\\". For \\\"brand\\\", a brand slug (\\\"kvadrat\\\") or display name (\\\"Kvadrat\\\"), case-insensitive — exactly the `value` a `brand` facet reports. An unrecognised brand is a 400 naming it, never an empty page.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"term\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, or the special id \\\"brand\\\" for the brand relation.\"},\"terms\":{\"minItems\":1,\"type\":\"array\",\"items\":{\"type\":\"string\"},\"description\":\"Canonical terms, ANY ONE of which satisfies the filter — this is how OR is expressed. OR lives inside one entry; separate `filters[]` entries AND together. Used for ordinal scales, where a stricter class satisfies a looser one, and for a multi-select rail — `{attributeId:\\\"brand\\\", terms:[\\\"kvadrat\\\",\\\"rubelli\\\"]}` returns materials from either brand.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"terms\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\"},\"excludesTerm\":{\"type\":\"string\",\"minLength\":1},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"excludesTerm\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id that must have an accepted value, e.g. \\\"sample_image_mm_per_px\\\".\"},\"hasValue\":{\"type\":\"boolean\",\"const\":true,\"description\":\"Require any accepted value for this attribute.\"},\"asOf\":{\"description\":\"Reference date for EPD expiration, exclusive: the value must remain valid after this date.\",\"type\":\"string\",\"format\":\"date\",\"pattern\":\"^(?:(?:\\\\d\\\\d[2468][048]|\\\\d\\\\d[13579][26]|\\\\d\\\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\\\d|30)|(?:02)-(?:0[1-9]|1\\\\d|2[0-8])))$\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\"",
60
+ ",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"hasValue\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id holding a NUMBER, e.g. \\\"light_reflectance_value\\\" or \\\"lead_time_days\\\".\"},\"min\":{\"description\":\"Inclusive lower bound. Omit for a ceiling-only range.\",\"type\":\"number\"},\"max\":{\"description\":\"Inclusive upper bound. Omit for a floor-only range.\",\"type\":\"number\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\"],\"additionalProperties\":false}]},\"lane\":{\"type\":\"string\",\"enum\":[\"filter\",\"preference\"]},\"lexicalSignal\":{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"minLength\":1},\"canonical\":{\"type\":\"string\",\"minLength\":1},\"identity\":{\"type\":\"string\",\"minLength\":1},\"original\":{\"type\":\"string\",\"minLength\":1},\"relation\":{\"type\":\"string\",\"enum\":[\"equivalent\",\"broader-storage-term\"]},\"span\":{\"type\":\"object\",\"properties\":{\"start\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"end\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991}},\"required\":[\"start\",\"end\"],\"additionalProperties\":false},\"storedTerm\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"attributeId\",\"canonical\",\"identity\",\"original\",\"relation\",\"span\",\"storedTerm\"],\"additionalProperties\":false},\"source\":{\"type\":\"string\",\"enum\":[\"compiled\",\"taxonomy-adapter\"]},\"span\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"criterion\",\"lane\",\"source\",\"span\"],\"additionalProperties\":false}},\"conflicts\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"minLength\":1},\"caller\":{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, e.g. \\\"primary_material\\\" or \\\"fire_rating\\\" — or the special id \\\"brand\\\", which filters on the brand RELATION (a join, carried by every variant) rather than on any attribute.\"},\"term\":{\"type\":\"string\",\"description\":\"Canonical term the attribute must equal, e.g. \\\"textile\\\". For \\\"brand\\\", a brand slug (\\\"kvadrat\\\") or display name (\\\"Kvadrat\\\"), case-insensitive — exactly the `value` a `brand` facet reports. An unrecognised brand is a 400 naming it, never an empty page.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"term\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, or the special id \\\"brand\\\" for the brand relation.\"},\"terms\":{\"minItems\":1,\"type\":\"array\",\"items\":{\"type\":\"string\"},\"description\":\"Canonical terms, ANY ONE of which satisfies the filter — this is how OR is expressed. OR lives inside one entry; separate `filters[]` entries AND together. Used for ordinal scales, where a stricter class satisfies a looser one, and for a multi-select rail — `{attributeId:\\\"brand\\\", terms:[\\\"kvadrat\\\",\\\"rubelli\\\"]}` returns materials from either brand.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"descripti",
61
+ "on\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"terms\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\"},\"excludesTerm\":{\"type\":\"string\",\"minLength\":1},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"excludesTerm\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id that must have an accepted value, e.g. \\\"sample_image_mm_per_px\\\".\"},\"hasValue\":{\"type\":\"boolean\",\"const\":true,\"description\":\"Require any accepted value for this attribute.\"},\"asOf\":{\"description\":\"Reference date for EPD expiration, exclusive: the value must remain valid after this date.\",\"type\":\"string\",\"format\":\"date\",\"pattern\":\"^(?:(?:\\\\d\\\\d[2468][048]|\\\\d\\\\d[13579][26]|\\\\d\\\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\\\d|30)|(?:02)-(?:0[1-9]|1\\\\d|2[0-8])))$\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"hasValue\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id holding a NUMBER, e.g. \\\"light_reflectance_value\\\" or \\\"lead_time_days\\\".\"},\"min\":{\"description\":\"Inclusive lower bound. Omit for a ceiling-only range.\",\"type\":\"number\"},\"max\":{\"description\":\"Inclusive upper bound. Omit for a floor-only range.\",\"type\":\"number\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\"],\"additionalProperties\":false}]},\"compiled\":{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, e.g. \\\"primary_material\\\" or \\\"fire_rating\\\" — or the special id \\\"brand\\\", which filters on the brand RELATION (a join, carried by every variant) rather than on any attribute.\"},\"term\":{\"type\":\"string\",\"description\":\"Canonical term the attribute must equal, e.g. \\\"textile\\\". For \\\"brand\\\", a b",
62
+ "rand slug (\\\"kvadrat\\\") or display name (\\\"Kvadrat\\\"), case-insensitive — exactly the `value` a `brand` facet reports. An unrecognised brand is a 400 naming it, never an empty page.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"term\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, or the special id \\\"brand\\\" for the brand relation.\"},\"terms\":{\"minItems\":1,\"type\":\"array\",\"items\":{\"type\":\"string\"},\"description\":\"Canonical terms, ANY ONE of which satisfies the filter — this is how OR is expressed. OR lives inside one entry; separate `filters[]` entries AND together. Used for ordinal scales, where a stricter class satisfies a looser one, and for a multi-select rail — `{attributeId:\\\"brand\\\", terms:[\\\"kvadrat\\\",\\\"rubelli\\\"]}` returns materials from either brand.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"terms\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\"},\"excludesTerm\":{\"type\":\"string\",\"minLength\":1},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"excludesTerm\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id that must have an accepted value, e.g. \\\"sample_image_mm_per_px\\\".\"},\"hasValue\":{\"type\":\"boolean\",\"const\":true,\"description\":\"Require any accepted value for this attribute.\"},\"asOf\":{\"description\":\"Reference date for EPD expiration, exclusive: the value must remain valid after this date.\",\"type\":\"string\",\"format\":\"date\",\"pattern\":\"^(?:(?:\\\\d\\\\d[2468][048]|\\\\d\\\\d[13579][26]|\\\\d\\\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\\\d|30)|(?:02)-(?:0[1-9]|1\\\\d|2[0-8])))$\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"re",
63
+ "quired\":[\"attributeId\",\"hasValue\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id holding a NUMBER, e.g. \\\"light_reflectance_value\\\" or \\\"lead_time_days\\\".\"},\"min\":{\"description\":\"Inclusive lower bound. Omit for a ceiling-only range.\",\"type\":\"number\"},\"max\":{\"description\":\"Inclusive upper bound. Omit for a floor-only range.\",\"type\":\"number\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\"],\"additionalProperties\":false}]},\"reason\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"attributeId\",\"caller\",\"compiled\",\"reason\"],\"additionalProperties\":false}},\"exclusions\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"criterion\":{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, e.g. \\\"primary_material\\\" or \\\"fire_rating\\\" — or the special id \\\"brand\\\", which filters on the brand RELATION (a join, carried by every variant) rather than on any attribute.\"},\"term\":{\"type\":\"string\",\"description\":\"Canonical term the attribute must equal, e.g. \\\"textile\\\". For \\\"brand\\\", a brand slug (\\\"kvadrat\\\") or display name (\\\"Kvadrat\\\"), case-insensitive — exactly the `value` a `brand` facet reports. An unrecognised brand is a 400 naming it, never an empty page.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"term\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, or the special id \\\"brand\\\" for the brand relation.\"},\"terms\":{\"minItems\":1,\"type\":\"array\",\"items\":{\"type\":\"string\"},\"description\":\"Canonical terms, ANY ONE of which satisfies the filter — this is how OR is expressed. OR lives inside one entry; separate `filters[]` entries AND together. Used for ordinal scales, where a stricter class satisfies a looser one, and for a multi-select rail — `{attributeId:\\\"brand\\\", terms:[\\\"kvadrat\\\",\\\"rubelli\\\"]}` returns materials from either brand.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"terms\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\"},\"excludesTerm\":{\"type\":\"string\",\"minLength\":1},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXA",
64
+ "CT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"excludesTerm\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id that must have an accepted value, e.g. \\\"sample_image_mm_per_px\\\".\"},\"hasValue\":{\"type\":\"boolean\",\"const\":true,\"description\":\"Require any accepted value for this attribute.\"},\"asOf\":{\"description\":\"Reference date for EPD expiration, exclusive: the value must remain valid after this date.\",\"type\":\"string\",\"format\":\"date\",\"pattern\":\"^(?:(?:\\\\d\\\\d[2468][048]|\\\\d\\\\d[13579][26]|\\\\d\\\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\\\d|30)|(?:02)-(?:0[1-9]|1\\\\d|2[0-8])))$\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"hasValue\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id holding a NUMBER, e.g. \\\"light_reflectance_value\\\" or \\\"lead_time_days\\\".\"},\"min\":{\"description\":\"Inclusive lower bound. Omit for a ceiling-only range.\",\"type\":\"number\"},\"max\":{\"description\":\"Inclusive upper bound. Omit for a floor-only range.\",\"type\":\"number\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\"],\"additionalProperties\":false}]},\"execution\":{\"type\":\"string\",\"const\":\"blocked-positive-only-contract\"},\"lane\":{\"type\":\"string\",\"enum\":[\"hard\",\"soft\"]},\"missingValuePolicy\":{\"type\":\"string\",\"const\":\"retain-unknown\"},\"span\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"criterion\",\"execution\",\"lane\",\"missingValuePolicy\",\"span\"],\"additionalProperties\":false}},\"preferences\":{\"type\":\"array\",\"items\":{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, e.g. \\\"primary_material\\\" or \\\"fire_rating\\\" — or the special id \\\"brand\\\", which filters on the brand RELATION (a join, carried by every variant) rather than on any attribute.\"},\"term\":{\"type\":\"string\",\"description\":\"Canonical term the attribute must equal, e.g. \\\"textile\\\". For \\\"brand\\\", a brand slug (\\\"kvadrat\\\") or display name (\\\"Kvadrat\\\"), case-insensitive — exactly the `value` a `brand` facet reports. An unrecognised brand is a 400 naming it, never an empty page.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"",
65
+ "Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"term\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, or the special id \\\"brand\\\" for the brand relation.\"},\"terms\":{\"minItems\":1,\"type\":\"array\",\"items\":{\"type\":\"string\"},\"description\":\"Canonical terms, ANY ONE of which satisfies the filter — this is how OR is expressed. OR lives inside one entry; separate `filters[]` entries AND together. Used for ordinal scales, where a stricter class satisfies a looser one, and for a multi-select rail — `{attributeId:\\\"brand\\\", terms:[\\\"kvadrat\\\",\\\"rubelli\\\"]}` returns materials from either brand.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"terms\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\"},\"excludesTerm\":{\"type\":\"string\",\"minLength\":1},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"excludesTerm\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id that must have an accepted value, e.g. \\\"sample_image_mm_per_px\\\".\"},\"hasValue\":{\"type\":\"boolean\",\"const\":true,\"description\":\"Require any accepted value for this attribute.\"},\"asOf\":{\"description\":\"Reference date for EPD expiration, exclusive: the value must remain valid after this date.\",\"type\":\"string\",\"format\":\"date\",\"pattern\":\"^(?:(?:\\\\d\\\\d[2468][048]|\\\\d\\\\d[13579][26]|\\\\d\\\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\\\d|30)|(?:02)-(?:0[1-9]|1\\\\d|2[0-8])))$\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"hasValue\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id holding a NUMBER, e.g. \\\"light_reflectance_value\\\" or \\\"lead_time_days\\\".\"},\"min\":{\"description\":\"Inclusive lower bound. Omit for a ceiling-only range.\",\"type\":\"number\"},\"max\":{\"description\":\"Inclusive upper bound. Omit for a floor-only range.\",\"type\":\"number\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"typ",
66
+ "e\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\"],\"additionalProperties\":false}]}},\"residualText\":{\"type\":[\"string\",\"null\"]},\"unresolved\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"code\":{\"type\":\"string\",\"enum\":[\"missing-searchable-attribute\",\"missing-dimension-pair-contract\",\"missing-explicit-comparator\",\"negative-criterion-contract-blocked\",\"registry-contract-drift\",\"unsupported-numeric-format\",\"unsupported-unit\",\"unsupported-strict-comparator\"]},\"reason\":{\"type\":\"string\",\"minLength\":1},\"span\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"code\",\"reason\",\"span\"],\"additionalProperties\":false}}},\"required\":[\"applied\",\"conflicts\",\"exclusions\",\"preferences\",\"residualText\",\"unresolved\"],\"additionalProperties\":false},\"productType\":{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"enum\":[\"leaf\",\"group\",\"vector\",\"ambiguous\",\"none\"]},\"gate\":{\"type\":\"string\",\"enum\":[\"hard\",\"constrain\",\"none\"]},\"pass\":{\"type\":\"string\",\"enum\":[\"taxonomy\",\"synonym\",\"display-name\",\"group\",\"head-noun\",\"vector\",\"none\"]},\"reason\":{\"type\":\"string\",\"minLength\":1},\"displayName\":{\"type\":[\"string\",\"null\"]},\"productTypeId\":{\"type\":[\"string\",\"null\"]},\"groupKey\":{\"type\":[\"string\",\"null\"]},\"productTypeIds\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"similarity\":{\"type\":[\"number\",\"null\"]},\"margin\":{\"type\":[\"number\",\"null\"]},\"residualText\":{\"type\":\"string\"},\"vectorPassRan\":{\"type\":\"boolean\"},\"registryVersion\":{\"type\":\"string\",\"minLength\":1},\"durationMs\":{\"type\":\"number\",\"minimum\":0},\"candidates\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"productTypeId\":{\"type\":\"string\",\"minLength\":1},\"displayName\":{\"type\":\"string\",\"minLength\":1},\"similarity\":{\"type\":\"number\"}},\"required\":[\"productTypeId\",\"displayName\",\"similarity\"],\"additionalProperties\":false}}},\"required\":[\"kind\",\"gate\",\"pass\",\"reason\",\"displayName\",\"productTypeId\",\"groupKey\",\"productTypeIds\",\"similarity\",\"margin\",\"residualText\",\"vectorPassRan\",\"registryVersion\",\"durationMs\",\"candidates\"],\"additionalProperties\":false},\"arm\":{\"type\":\"object\",\"properties\":{\"arm\":{\"anyOf\":[{\"type\":\"string\",\"enum\":[\"dense\",\"lexical\",\"colour\"]},{\"type\":\"null\"}]},\"reason\":{\"type\":\"string\",\"minLength\":1},\"reasonDetail\":{\"type\":\"string\",\"enum\":[\"derived-colour-veto\",\"typo-recovery\",\"literal-compound-without-type\"]},\"outcome\":{\"type\":\"string\",\"enum\":[\"dense\",\"lexical\",\"colour\",\"browse\",\"criteria-only\",\"ambiguous\"]},\"decidedBy\":{\"type\":\"string\",\"enum\":[\"deterministic\",\"model\"]},\"productTypeGate\":{\"type\":\"string\",\"enum\":[\"hard\",\"constrain\",\"none\"]},\"productTypeIds\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"residual\":{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"enum\":[\"compound\",\"descriptive\",\"ambiguous\",\"empty\"]},\"tokens\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"resolvedTokens\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"unresolvedTokens\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"resolvedRatio\":{\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"comparisonFrame\":{\"type\":\"boolean\"}},\"required\":[\"kind\",\"tokens\",\"resolvedTokens\",\"unresolvedTokens\",\"resolvedRatio\",\"comparisonFrame\"],\"additionalProperties\":false},\"registryVersion\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"arm\",\"reason\",\"outcome\",\"decidedBy\",\"productTypeGate\",\"productTypeIds\",\"residual\",\"registryVersion\"],\"additionalProperties\":false},\"ambiguity\":{\"type\":\"object\",\"properties\":{\"outcome\":{\"type\":\"string\",\"enum\":[\"applied\",\"abstained\",\"refused\",\"timeout\"]},\"modelId\":{\"type\":[\"string\",\"null\"]},\"providerRan\":{\"type\":\"boolean\"},\"durationMs\":{\"type\":\"number\",\"minimum\":0},\"timeoutMs\":{\"type\":\"number\",\"exclusiveMinimum\":0},\"validation\":{\"type\":\"string\",\"enum\":[\"valid\",\"invalid\",\"not-run\"]},\"refusalCode\":{\"type\":[\"st",
67
+ "ring\",\"null\"]},\"confidence\":{\"type\":[\"number\",\"null\"]},\"routing\":{\"anyOf\":[{\"type\":\"string\",\"enum\":[\"compound\",\"descriptive\"]},{\"type\":\"null\"}]},\"productTypeIds\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"changedArm\":{\"type\":\"boolean\"}},\"required\":[\"outcome\",\"modelId\",\"providerRan\",\"durationMs\",\"timeoutMs\",\"validation\",\"refusalCode\",\"confidence\",\"routing\",\"productTypeIds\",\"changedArm\"],\"additionalProperties\":false},\"categoryScope\":{\"type\":\"object\",\"properties\":{\"source\":{\"type\":\"string\",\"enum\":[\"caller\",\"anchor\"]},\"mechanism\":{\"type\":\"string\",\"enum\":[\"sql-predicate\",\"provider-filter\",\"allowlist\",\"prune\"]},\"variantCount\":{\"anyOf\":[{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},{\"type\":\"null\"}]},\"ceiling\":{\"type\":\"integer\",\"exclusiveMinimum\":0,\"maximum\":9007199254740991},\"specScanBounded\":{\"type\":\"boolean\"}},\"required\":[\"source\",\"mechanism\",\"variantCount\",\"ceiling\"],\"additionalProperties\":{}},\"specAllowlist\":{\"type\":\"object\",\"properties\":{\"limit\":{\"type\":\"integer\",\"exclusiveMinimum\":0,\"maximum\":9007199254740991},\"returned\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"retrieved\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"truncated\":{\"type\":\"boolean\"},\"emptyMeansScarcity\":{\"type\":\"boolean\"},\"outcome\":{\"type\":\"string\",\"enum\":[\"corpus-scarcity\",\"retrieval-miss\",\"unknown\"]}},\"required\":[\"limit\",\"returned\",\"truncated\",\"emptyMeansScarcity\"],\"additionalProperties\":false},\"unsatisfiableNarrowing\":{\"type\":\"object\",\"properties\":{\"narrowing\":{\"type\":\"string\",\"enum\":[\"product-type\"]},\"gate\":{\"type\":\"string\",\"enum\":[\"hard\"]},\"productTypeId\":{\"type\":\"string\",\"minLength\":1},\"displayName\":{\"type\":\"string\",\"minLength\":1},\"productTypeIds\":{\"type\":\"array\",\"items\":{\"type\":\"string\",\"minLength\":1}},\"pass\":{\"type\":\"string\",\"minLength\":1},\"evidence\":{\"type\":\"string\",\"enum\":[\"zero-accepted-variants\"]},\"relaxedTo\":{\"type\":\"string\",\"enum\":[\"none\"]}},\"required\":[\"narrowing\",\"gate\",\"productTypeId\",\"displayName\",\"productTypeIds\",\"pass\",\"evidence\",\"relaxedTo\"],\"additionalProperties\":false}},\"required\":[\"version\",\"rawQuery\",\"normalisedQuery\",\"residualText\",\"status\",\"mode\",\"senses\"],\"additionalProperties\":false},\"suggestions\":{\"default\":[],\"description\":\"Bounded structured refinements. Each request is a complete executable replacement for the current search request.\",\"maxItems\":6,\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"label\":{\"type\":\"string\",\"minLength\":1,\"description\":\"Human-readable description of the refinement.\"},\"request\":{\"type\":\"object\",\"properties\":{\"text\":{\"description\":\"Free-text query, matched VISUALLY against material imagery (\\\"striped wallpaper\\\", \\\"warm terrazzo\\\"). A bare colour word (\\\"sage\\\") automatically runs as a colour-only search.\",\"type\":\"string\",\"maxLength\":500},\"intent\":{\"default\":\"search\",\"description\":\"Budget profile for the search primitive. Default \\\"search\\\" is the full engine. \\\"typeahead\\\" is the as-you-type profile: lexical-only with prefix match on the final token, forced `view:\\\"card\\\"`, `limit` capped at 24, and no mood/colour detection tiers, semantic fallback, category boost, secondary scoring, refinement suggestions, counts, or meanings. `textModes` is ignored under typeahead. Recommended wire shape for library typeahead UIs.\",\"type\":\"string\",\"enum\":[\"search\",\"typeahead\"]},\"textModes\":{\"description\":\"How to interpret `text`. Default [\\\"visual\\\"] — a text→image VISUAL search. \\\"lexical\\\" (opt-in) runs a BM25 ATTRIBUTE-lens search over material names + canonical attribute terms (find by what it's called / what it IS); detected colour words are stripped from the lexical terms and retained only as diagnostic or hard-constraint evidence. Requesting BOTH [\\\"visual\\\",\\\"lexical\\\"] runs the visual search and notes it — compose a separate lexical search if both result sets are required. Ignored when `intent` is \\\"typeahead\\\".\",\"type\":\"array\",\"items\":{\"type\":\"string\",\"enum\":[\"visual\",\"lexical\"]}},\"colours\":{\"description\":\"Explicit colour palette to match. Each entry is EITHER a `hex` OR a `name` (a descriptive or brand paint name resolved server-side — never invent a hex",
68
+ " for a name). PRECEDENCE: when set, these override any colour word detected in `text`. Unresolvable names fail with a typed error listing them, so you can correct and retry.\",\"maxItems\":8,\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"hex\":{\"description\":\"The colour to match, as 3- or 6-digit hex. Provide EITHER `hex` OR `name`, never both. Only pass `hex` when you already hold an EXACT hex — for a colour NAME use `name` and let the server resolve it.\",\"type\":\"string\",\"pattern\":\"^#?(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$\"},\"name\":{\"description\":\"A colour NAME to match — a descriptive name ('sage green', 'terracotta', 'warm sand') or a brand paint name ('Gauntlet Gray', 'Hale Navy', 'White Dove'). Resolved server-side to its exact library hex (misspellings are fuzzy-matched). NEVER invent a hex for a name — pass the name here. Provide EITHER `name` OR `hex`, never both.\",\"type\":\"string\",\"minLength\":1,\"maxLength\":120},\"weight\":{\"description\":\"Relative proportion of this colour in the desired palette (0–1). Weights are normalised across the palette; omit for equal parts.\",\"type\":\"number\",\"exclusiveMinimum\":0,\"maximum\":1},\"role\":{\"type\":\"string\",\"enum\":[\"any\",\"is\",\"accent\"],\"description\":\"Per-colour swatch eligibility (default 'any'). PRECEDENCE: `colourLogic` governs cross-colour combination (all/any); `role` governs which swatch of a product may satisfy THIS colour, and ANDs on top of the logic. 'is' = the colour must be the product's DOMINANT swatch; 'accent' = the colour must be a minor share (proportion 0.05–0.35; all shades in a named colour region count together) and is excluded from the proportion-match term; 'any' = today's nearest-swatch match.\"},\"sourceName\":{\"description\":\"Human name the colour came from (e.g. \\\"sage\\\", \\\"Elephant's Breath\\\") — echoed back for display; never affects matching. (For a name you want RESOLVED to a hex, use `name` instead.)\",\"type\":\"string\",\"maxLength\":120}},\"additionalProperties\":false}},\"colourLogic\":{\"description\":\"\\\"all\\\" (default): results should contain the whole palette, ranked by average distance + proportion match. \\\"any\\\": results matching ANY palette colour, ranked by their best single-colour distance.\",\"type\":\"string\",\"enum\":[\"all\",\"any\"]},\"colourTolerance\":{\"description\":\"How close a colour must be to count as a match. 'normal' (default) is today's behaviour; 'tight' narrows the OKLab match threshold and prunes far single-colour matches; 'loose' widens the threshold.\",\"type\":\"string\",\"enum\":[\"tight\",\"normal\",\"loose\"]},\"colourAppearance\":{\"type\":\"object\",\"properties\":{\"lightness\":{\"description\":\"OKLab lightness window (L ∈ [0,1]); dark ≈ 0, light ≈ 1.\",\"type\":\"object\",\"properties\":{\"min\":{\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"max\":{\"type\":\"number\",\"minimum\":0,\"maximum\":1}},\"required\":[\"min\",\"max\"],\"additionalProperties\":false},\"warmth\":{\"description\":\"OKLab warmth window (b axis); cool < 0 < warm, ≈ −0.1..0.1.\",\"type\":\"object\",\"properties\":{\"min\":{\"type\":\"number\",\"minimum\":-0.5,\"maximum\":0.5},\"max\":{\"type\":\"number\",\"minimum\":-0.5,\"maximum\":0.5}},\"required\":[\"min\",\"max\"],\"additionalProperties\":false}},\"additionalProperties\":false,\"description\":\"HARD appearance gate on the material's DOMINANT colour, independent of the colour palette — works on a text-only query too. Variants without colour extraction are EXCLUDED whenever any axis is set (NULL scalars never match).\"},\"mood\":{\"type\":\"string\",\"enum\":[\"warm\",\"cool\",\"earthy\",\"moody\",\"bold\",\"light-airy\",\"natural\",\"neutral\",\"romantic\",\"coastal\",\"industrial\",\"luxe\",\"vintage\",\"serene\",\"scandi\",\"cosy\",\"heritage\",\"mid-century\",\"pastel\"],\"description\":\"Mood-language colour entry — resolves to a curated checked-in colour palette. Use it for evocative mood queries ('coastal', 'moody', 'earthy') rather than a single colour. A mood ALONE runs as a colour search; with text it remains diagnostic or hard-constraint evidence and does not blend into the text mode's ordering score. One of: warm (Warm — Reds, oranges, yellows - inviting and cozy); cool (Cool — Blues, teals, cool grays - calm and professional); earthy (Earthy — Browns, tan",
69
+ "s, terracotta - natural and grounded); moody (Moody — Deep, saturated, dramatic - rich and atmospheric); bold (Bold — High chroma, vibrant - energetic and eye-catching); light-airy (Light + Airy — Pastels, pale tones - soft and spacious); natural (Natural — Greens, wood tones, stone - organic and balanced); neutral (Neutral — Whites, grays, blacks - versatile and timeless); romantic (Romantic — Soft pinks, mauves, blush - tender and elegant); coastal (Coastal — Sandy beiges, ocean blues, seafoam - relaxed seaside); industrial (Industrial — Cool grays, concrete, metallic - urban and modern); luxe (Luxe — Deep jewel tones, rich emeralds, sapphires - opulent); vintage (Vintage — Muted, dusty tones - nostalgic and timeworn); serene (Serene — Soft blues, sage, muted lavender - calm and peaceful); scandi (Scandi — Warm whites, pale woods, cream - Scandinavian minimalism); cosy (Cosy — Deep plaster, amber, tobacco - enveloping warmth); heritage (Heritage — Rich historical tones with saturated depth - classic and grounded); mid-century (Mid-Century — Olive, mustard, teal, terracotta - retro modern); pastel (Pastel — Sage, duck egg, blush - soft colors with body).\"},\"sort\":{\"type\":\"string\",\"enum\":[\"relevance\",\"light\",\"dark\",\"vivid\",\"muted\",\"colourful\",\"name\",\"name_desc\",\"recent\"],\"description\":\"Re-order the final scored results (default 'relevance' = the selected mode's primary score). Two families. COLOUR RAMPS: 'light'/'dark' by dominant lightness, 'vivid'/'muted' by chroma, 'colourful' by overall vibrancy — variants without colour extraction sink to the end. CATALOGUE ORDERS: 'name' (A→Z), 'name_desc' (Z→A) and 'recent' (newest first), which read the same `name` / `created_at` columns, in the same directions, that /api/v1/search/browse orders on — one word, one order, on both lanes. Every sort REORDERS the pool the search signals already retrieved; it never widens or substitutes for one. Retired materials sort below visible ones under every non-relevance sort. For an unranked walk of the whole corpus in any of these orders, use the browse surface instead.\"},\"orderBy\":{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"minLength\":1,\"description\":\"The attribute to sort by. Only attributes with a DECLARED value ladder can be ordered — ordering is never inferred from a value's spelling — and anything else is refused with the orderable set named. Orderable today: price_tier.\"},\"direction\":{\"default\":\"asc\",\"description\":\"'asc' (default) walks the ladder from its first rung.\",\"type\":\"string\",\"enum\":[\"asc\",\"desc\"]}},\"required\":[\"attributeId\",\"direction\"],\"additionalProperties\":false,\"description\":\"Sort results along ONE attribute's declared ladder — a separate axis from `sort`, which stays the closed relevance/colour/catalogue vocabulary. Sent together, `orderBy` wins and the response `note` says so. A variant whose value is off the ladder, or which carries no value at all, sorts LAST in both directions: it has no rung, and it is still a real row, so it is placed at the end rather than dropped or guessed at.\"},\"returnAttributes\":{\"maxItems\":8,\"type\":\"array\",\"items\":{\"type\":\"string\",\"minLength\":1},\"description\":\"Attribute ids whose RESOLVED VALUE should ride each result, under `attributes` keyed by attribute id. Values resolve with the normal variant-over-product precedence. THREE STATES, and they are different: an attribute you did not ask for is not a key at all; an attribute you asked for that this variant carries no accepted value for is `null`; anything else is the value (a string, or an array for a multi-valued attribute). An unknown attribute id is refused naming it, never silently dropped. Pair it with `filters` to show what you filtered on.\"},\"similarTo\":{\"description\":\"Find materials that LOOK like a seed image. Supply `variantId` to seed from an existing variant's primary image, `materialBankId` to seed from a Material Bank SKU — the ONE EXTERNAL identifier this API accepts, so a caller already holding a SKU needs no resolve round trip — or `vector`, a pre-computed halfvec literal (`[a,b,…]`) from an externally-em",
70
+ "bedded image, to drive the image arm directly. A `materialBankId` that names no variant is a 400 naming the offending value, never an empty result set and never an unseeded search. PRECEDENCE: wins over `text` (the response carries a note when both were given).\",\"anyOf\":[{\"type\":\"object\",\"properties\":{\"variantId\":{\"type\":\"string\"}},\"required\":[\"variantId\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"materialBankId\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"materialBankId\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"vector\":{\"type\":\"string\"}},\"required\":[\"vector\"],\"additionalProperties\":false}]},\"excludeVariantIds\":{\"description\":\"Canonical variant ids that must not appear in this result. Applied before the returned page is cut, so the page refills from the deeper ranked pool instead of being shortened by client-side filtering. IDs are opaque; duplicates are harmless and deduplicated by the engine.\",\"maxItems\":256,\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"image\":{\"description\":\"A raw incoming image to search by (URL or base64). The HTTP adapter embeds it and sets `similarTo: { vector }`; wins over `text`.\",\"oneOf\":[{\"type\":\"object\",\"properties\":{\"type\":{\"type\":\"string\",\"const\":\"url\"},\"url\":{\"type\":\"string\",\"format\":\"uri\",\"description\":\"HTTP(S) URL of the query image. MUST be an https URL.\"}},\"required\":[\"type\",\"url\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"type\":{\"type\":\"string\",\"const\":\"base64\"},\"data\":{\"type\":\"string\",\"minLength\":1,\"description\":\"Base64-encoded image bytes (or a data URI) the caller already holds — e.g. a form upload — so there is nothing to host.\"}},\"required\":[\"type\",\"data\"],\"additionalProperties\":false}],\"type\":\"object\"},\"category\":{\"type\":\"object\",\"properties\":{\"boost\":{\"description\":\"Soft category preference — results of this product type score higher but others still appear.\",\"type\":\"string\"},\"filter\":{\"description\":\"HARD gate: only these public taxonomy categories are returned. Values are category-group or product-type display names (case-insensitive), or canonical taxonomy keys. A group expands to every child product type; an unknown value errors listing every valid name.\",\"type\":\"array\",\"items\":{\"type\":\"string\"}}},\"additionalProperties\":false},\"weights\":{\"type\":\"object\",\"properties\":{\"image\":{\"type\":\"number\",\"minimum\":0},\"category\":{\"type\":\"number\",\"minimum\":0},\"colour\":{\"type\":\"number\",\"minimum\":0},\"lexical\":{\"type\":\"number\",\"minimum\":0},\"text\":{\"type\":\"number\",\"minimum\":0}},\"additionalProperties\":false,\"description\":\"Optional multiplier for the selected mode's one ordering signal. At most one applied weight is non-zero: colour→colour, lexical/entity-browse→lexical, variant-text→portrait, image-text/image→image, and filter-only→none. Overrides may scale only that live signal and cannot activate any other. Other similarities are diagnostic or hard-gate receipts and do not blend into score.\"},\"filters\":{\"description\":\"Typed filters applied as a hard restriction before ranking. Each entry is a term match (`attributeId` + `term`), a term SET (`attributeId` + `terms` — ANY one of them satisfies it), a presence check (`attributeId` + `hasValue: true`), or a numeric range (`attributeId` + `min`/`max`). AND/OR: entries AND together, and OR is expressed WITHIN one entry via `terms`. BRAND: `attributeId: \\\"brand\\\"` filters on the brand RELATION — a join every one of the catalogue's variants carries — taking a brand slug or display name, exactly the `value` a `brand` facet reports. Do NOT use the `brand_name` ATTRIBUTE for this: it is a sparse spec value present on about 1.5% of variants and absent entirely from every catalog-ingested brand, so filtering on it returns a near-empty page that reads as a corpus gap. Presence filters always route as hard spec gates. Aesthetic/meaning term filters (pattern, texture, directionality, and the other visual-meaning attributes) are applied as a pool-bounded post-filter over the visual candidate pool rather than a whole-catalog restriction. Each entry may carry a `trust` bar the matching value must clea",
71
+ "r.\",\"type\":\"array\",\"items\":{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, e.g. \\\"primary_material\\\" or \\\"fire_rating\\\" — or the special id \\\"brand\\\", which filters on the brand RELATION (a join, carried by every variant) rather than on any attribute.\"},\"term\":{\"type\":\"string\",\"description\":\"Canonical term the attribute must equal, e.g. \\\"textile\\\". For \\\"brand\\\", a brand slug (\\\"kvadrat\\\") or display name (\\\"Kvadrat\\\"), case-insensitive — exactly the `value` a `brand` facet reports. An unrecognised brand is a 400 naming it, never an empty page.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"term\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, or the special id \\\"brand\\\" for the brand relation.\"},\"terms\":{\"minItems\":1,\"type\":\"array\",\"items\":{\"type\":\"string\"},\"description\":\"Canonical terms, ANY ONE of which satisfies the filter — this is how OR is expressed. OR lives inside one entry; separate `filters[]` entries AND together. Used for ordinal scales, where a stricter class satisfies a looser one, and for a multi-select rail — `{attributeId:\\\"brand\\\", terms:[\\\"kvadrat\\\",\\\"rubelli\\\"]}` returns materials from either brand.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"terms\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\"},\"excludesTerm\":{\"type\":\"string\",\"minLength\":1},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"excludesTerm\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id that must have an accepted value, e.g. \\\"sample_image_mm_per_px\\\".\"},\"hasValue\":{\"type\":\"boolean\",\"const\":true,\"description\":\"Require any accepted value for this attribute.\"},\"asOf\":{\"description\":\"Reference date for EPD expiration, exclusive: the value must remain valid after this date.\",\"type\":\"string\",\"format\":\"date\",\"pattern\":\"^(?:(?:\\\\d\\\\d[2468][048]|\\\\d\\\\d[13579][26]|\\\\d\\\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\\\d|30)|(?:02)-(?:0[1-9]|1\\\\d|2[0-8])))$\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"max",
72
+ "imum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"hasValue\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id holding a NUMBER, e.g. \\\"light_reflectance_value\\\" or \\\"lead_time_days\\\".\"},\"min\":{\"description\":\"Inclusive lower bound. Omit for a ceiling-only range.\",\"type\":\"number\"},\"max\":{\"description\":\"Inclusive upper bound. Omit for a floor-only range.\",\"type\":\"number\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\"],\"additionalProperties\":false}]}},\"preferences\":{\"description\":\"SOFT spec criteria — rank-never-exclude. Same shape as `filters`, but a preference NEVER removes a result: each hit's `verdict` gains a `preferred` cell, and a failed/missing preferred cell only DEMOTES the hit WITHIN its page (the cursor is from the pre-demotion order).\",\"maxItems\":8,\"type\":\"array\",\"items\":{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, e.g. \\\"primary_material\\\" or \\\"fire_rating\\\" — or the special id \\\"brand\\\", which filters on the brand RELATION (a join, carried by every variant) rather than on any attribute.\"},\"term\":{\"type\":\"string\",\"description\":\"Canonical term the attribute must equal, e.g. \\\"textile\\\". For \\\"brand\\\", a brand slug (\\\"kvadrat\\\") or display name (\\\"Kvadrat\\\"), case-insensitive — exactly the `value` a `brand` facet reports. An unrecognised brand is a 400 naming it, never an empty page.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"term\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, or the special id \\\"brand\\\" for the brand relation.\"},\"terms\":{\"minItems\":1,\"type\":\"array\",\"items\":{\"type\":\"string\"},\"description\":\"Canonical terms, ANY ONE of which satisfies the filter — this is how OR is expressed. OR lives inside one entry; separate `filters[]` entries AND together. Used for ordinal scales, where a stricter class satisfies a looser one, and for a multi-select rail — `{attributeId:\\\"brand\\\", terms:[\\\"kvadrat\\\",\\\"rubelli\\\"]}` returns materials from either brand.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribut",
73
+ "e's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"terms\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\"},\"excludesTerm\":{\"type\":\"string\",\"minLength\":1},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"excludesTerm\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id that must have an accepted value, e.g. \\\"sample_image_mm_per_px\\\".\"},\"hasValue\":{\"type\":\"boolean\",\"const\":true,\"description\":\"Require any accepted value for this attribute.\"},\"asOf\":{\"description\":\"Reference date for EPD expiration, exclusive: the value must remain valid after this date.\",\"type\":\"string\",\"format\":\"date\",\"pattern\":\"^(?:(?:\\\\d\\\\d[2468][048]|\\\\d\\\\d[13579][26]|\\\\d\\\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\\\d|30)|(?:02)-(?:0[1-9]|1\\\\d|2[0-8])))$\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"hasValue\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id holding a NUMBER, e.g. \\\"light_reflectance_value\\\" or \\\"lead_time_days\\\".\"},\"min\":{\"description\":\"Inclusive lower bound. Omit for a ceiling-only range.\",\"type\":\"number\"},\"max\":{\"description\":\"Inclusive upper bound. Omit for a floor-only range.\",\"type\":\"number\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\"],\"additionalProperties\":false}]}},\"verdicts\":{\"description\":\"Opt-in seed-relative verdict cells. Only meaningful with `similarTo: { variantId }`: costs one seed fetch + one page read to diff each hit's accepted COMPARABLE attributes (numeric + a colour axis) against the seed's, emitting `preferred` `verdict` cells with directional deviations (`above`/`below`, e.g. 2 mm thicker, warmer). Ignored on the default path; a `similarTo: { vector }` / raw-image seed carries no seed variant, so the response notes that seed cells were skipped.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Results per page (default 60, max 60).\",\"type\":\"integer\",\"minimum\":1,\"maximum\":60},\"cursor\":{\"description\":\"Opaque pagination cursor from a previous response. NOTE: ANN search is approximate — tail items may reorder ",
74
+ "slightly between pages.\",\"type\":\"string\"},\"explain\":{\"description\":\"Include per-signal scores on each result (`signals`).\",\"type\":\"boolean\"},\"collapse\":{\"description\":\"Group results by product before pagination. \\\"product\\\" returns one representative (highest-ranked) variant per product, with hidden siblings counted in `siblingCount`. The engine and GET adapter default to \\\"none\\\"; public POST requests default to \\\"product\\\" and may request \\\"none\\\" explicitly for variant-grain results.\",\"type\":\"string\",\"enum\":[\"none\",\"product\"]},\"count\":{\"default\":false,\"description\":\"Opt-in feed total. When true the response carries `counts.variants` (and `counts.products` under `collapse:\\\"product\\\"`). The variant total is EXACT up to a cap of 1000 and reported as `basis:\\\"lowerBound\\\"` beyond it — and ALWAYS `lowerBound` for ANN-ranked pools (image / colour / visual-text / visual-meaning post-filters), where the finite candidate pool is the only honest figure. Counts are never silently approximated: an exact count is labelled `exact`, a capped or pool-bounded one `lowerBound`. Default false (skip the count query entirely).\",\"type\":\"boolean\"},\"facetCounts\":{\"description\":\"Opt-in per-query facet counts (MG-1010). Name up to 10 facet ids — `brand`, `category`, or spec attribute ids — and the response carries a `facets` block counting each facet's values WITHIN this query's match set. Absent means no `facets` in the response and no counting cost. An id the registry does not know is a 400 naming it (with `didYouMean` candidates); a known but uncountable id lands in `facets.absent` with a reason. Counts follow the same honesty discipline as `count`: ranked (ANN) queries report pool-derived floors labelled `lowerBound` with `poolSize` stated; only a filter-driven query earns `exact`.\",\"maxItems\":10,\"type\":\"array\",\"items\":{\"type\":\"string\",\"minLength\":1}},\"refinementToken\":{\"description\":\"Opaque five-minute receipt from a prior pool-derived faceted search. Resubmitting it narrows that exact finalized pool instead of running a new candidate source.\",\"type\":\"string\",\"minLength\":1,\"maxLength\":131072},\"hasImages\":{\"default\":false,\"description\":\"When true, only return results that have at least one catalog image (a servable card thumbnail). Off by default so search stays honest about the FULL catalog, including image-less records. Applied on EVERY retrieval arm as a post-filter over the candidate pool, so a short page means the pool ran out of image-backed matches rather than that some arm ignored the flag. The opt-in `counts.variants` feed total honours it too, and filter, count and display share one SQL definition of image presence. Default false.\",\"type\":\"boolean\"},\"sampleableOnly\":{\"description\":\"Require an accepted sampleable=true variant fact before pagination. False or absent leaves the corpus unchanged.\",\"type\":\"boolean\"},\"view\":{\"default\":\"full\",\"description\":\"Result projection. \\\"full\\\" (default) returns the complete `searchResultItemSchema` (palette, colourMeta, hasImage, siblings, explain signals/meanings). \\\"card\\\" returns the lean `searchCardItemSchema` (identity + name + thumbnail + score) for grid rendering, skipping the per-page palette / appearance / meaning reads. The response echoes the chosen `view`, which discriminates the `results` union.\",\"type\":\"string\",\"enum\":[\"full\",\"card\"]},\"corpusScope\":{\"description\":\"Which corpus to answer from. samplizeCatalogue admits published US Samplize paint colours regardless of stock. ABSENT means the WHOLE GRAPH — every material MaterialGraph knows, orderable or not; absence is never a narrower default. Three transaction-oriented scopes gate on governed CHANNEL membership: 'materialBankNaOrderable' (Material Bank North America, about 277,000 materials), 'materialBankEuOrderable' (Material Bank Europe, about 65,000) and 'designShopOrderable' (DesignShop, about 34,000, excluding what DesignShop hides from browse). 'materialBankNaAndCatalogue' is a governed discovery corpus over Material Bank NA or Catalogue editorial membership; it is not a buyability claim, and references alone do not confer ",
75
+ "membership. 'materialBankOrderable' instead restricts retrieval to materials carrying a Material Bank identity, which is a provenance signal rather than a channel one, and admits about 98% of the graph. Scope is distribution/visibility membership, never suitability; its receipt states whether that means transaction or editorial inclusion. Every scope is applied INSIDE the query: `limit` fills with eligible rows, `counts` describe the SCOPED corpus, and the response echoes the scope under `corpusScope`. This is what post-filtering a returned page cannot give you — a client-side filter shortens the page exactly when the query drifts toward the corpus MaterialGraph carries beyond the Material Bank feed, which is unpredictable from outside. An unknown value is a 400 naming it.\",\"type\":\"string\",\"enum\":[\"all\",\"samplizeCatalogue\",\"materialBankOrderable\",\"materialBankNaOrderable\",\"materialBankNaAndCatalogue\",\"materialBankEuOrderable\",\"designShopOrderable\"]},\"includeSamplingArtifacts\":{\"description\":\"OPS/AUDIT escape hatch — DO NOT use for normal search. Default behaviour (absent or false) EXCLUDES sampling-artifact products (multi-swatch sample cards — orderable samples, never specifiable materials) from every result, count, and candidate pool. Set true only to surface them for ops inspection or an audit.\",\"type\":\"boolean\"}},\"required\":[\"intent\",\"count\",\"hasImages\",\"view\"],\"additionalProperties\":false,\"description\":\"Complete replacement search request. It can be parsed and executed directly; it is not a delta.\"}},\"required\":[\"label\",\"request\"],\"additionalProperties\":false}},\"disambiguation\":{\"description\":\"Present when the query has multiple valid readings — a colour⇄material word ('walnut'), a certification phrase wearing colour words ('red list free'), or a pinned paint chip that is also a colour to look for across materials, or an exact paint name shared by several makers. The reading that ran carries `isDefault`; for a refused paint collision every reading is false. Every reading is a complete executable request. ABSENT means the query was unambiguous, never that the alternative was checked and found empty.\",\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"enum\":[\"compliance-phrase\",\"colour-material\",\"paint-colour\",\"paint-identity\"],\"description\":\"Governed ambiguity class.\"},\"term\":{\"type\":\"string\",\"minLength\":1,\"description\":\"The ambiguous term or phrase.\"},\"interpretations\":{\"minItems\":2,\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"id\":{\"type\":\"string\",\"minLength\":1,\"description\":\"Stable id of this reading within the clarification; exact paints include maker, identity, and swatch.\"},\"label\":{\"type\":\"string\",\"minLength\":1,\"description\":\"Human-facing name for the reading, e.g. 'the colour walnut'.\"},\"kind\":{\"type\":\"string\",\"minLength\":1,\"description\":\"Short noun for what this reading searches.\"},\"isDefault\":{\"type\":\"boolean\",\"description\":\"True for the reading THIS response ran. Every reading is false when the engine intentionally refused to choose, such as an exact cross-library paint collision.\"},\"hex\":{\"description\":\"Representative swatch, present when the reading is a colour.\",\"type\":\"string\"},\"request\":{\"type\":\"object\",\"properties\":{\"text\":{\"description\":\"Free-text query, matched VISUALLY against material imagery (\\\"striped wallpaper\\\", \\\"warm terrazzo\\\"). A bare colour word (\\\"sage\\\") automatically runs as a colour-only search.\",\"type\":\"string\",\"maxLength\":500},\"intent\":{\"default\":\"search\",\"description\":\"Budget profile for the search primitive. Default \\\"search\\\" is the full engine. \\\"typeahead\\\" is the as-you-type profile: lexical-only with prefix match on the final token, forced `view:\\\"card\\\"`, `limit` capped at 24, and no mood/colour detection tiers, semantic fallback, category boost, secondary scoring, refinement suggestions, counts, or meanings. `textModes` is ignored under typeahead. Recommended wire shape for library typeahead UIs.\",\"type\":\"string\",\"enum\":[\"search\",\"typeahead\"]},\"textModes\":{\"description\":\"How to interpret `text`. Default [\\\"visual\\\"] — a text→image VISUAL search. \\\"lexical\\\" (o",
76
+ "pt-in) runs a BM25 ATTRIBUTE-lens search over material names + canonical attribute terms (find by what it's called / what it IS); detected colour words are stripped from the lexical terms and retained only as diagnostic or hard-constraint evidence. Requesting BOTH [\\\"visual\\\",\\\"lexical\\\"] runs the visual search and notes it — compose a separate lexical search if both result sets are required. Ignored when `intent` is \\\"typeahead\\\".\",\"type\":\"array\",\"items\":{\"type\":\"string\",\"enum\":[\"visual\",\"lexical\"]}},\"colours\":{\"description\":\"Explicit colour palette to match. Each entry is EITHER a `hex` OR a `name` (a descriptive or brand paint name resolved server-side — never invent a hex for a name). PRECEDENCE: when set, these override any colour word detected in `text`. Unresolvable names fail with a typed error listing them, so you can correct and retry.\",\"maxItems\":8,\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"hex\":{\"description\":\"The colour to match, as 3- or 6-digit hex. Provide EITHER `hex` OR `name`, never both. Only pass `hex` when you already hold an EXACT hex — for a colour NAME use `name` and let the server resolve it.\",\"type\":\"string\",\"pattern\":\"^#?(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$\"},\"name\":{\"description\":\"A colour NAME to match — a descriptive name ('sage green', 'terracotta', 'warm sand') or a brand paint name ('Gauntlet Gray', 'Hale Navy', 'White Dove'). Resolved server-side to its exact library hex (misspellings are fuzzy-matched). NEVER invent a hex for a name — pass the name here. Provide EITHER `name` OR `hex`, never both.\",\"type\":\"string\",\"minLength\":1,\"maxLength\":120},\"weight\":{\"description\":\"Relative proportion of this colour in the desired palette (0–1). Weights are normalised across the palette; omit for equal parts.\",\"type\":\"number\",\"exclusiveMinimum\":0,\"maximum\":1},\"role\":{\"type\":\"string\",\"enum\":[\"any\",\"is\",\"accent\"],\"description\":\"Per-colour swatch eligibility (default 'any'). PRECEDENCE: `colourLogic` governs cross-colour combination (all/any); `role` governs which swatch of a product may satisfy THIS colour, and ANDs on top of the logic. 'is' = the colour must be the product's DOMINANT swatch; 'accent' = the colour must be a minor share (proportion 0.05–0.35; all shades in a named colour region count together) and is excluded from the proportion-match term; 'any' = today's nearest-swatch match.\"},\"sourceName\":{\"description\":\"Human name the colour came from (e.g. \\\"sage\\\", \\\"Elephant's Breath\\\") — echoed back for display; never affects matching. (For a name you want RESOLVED to a hex, use `name` instead.)\",\"type\":\"string\",\"maxLength\":120}},\"additionalProperties\":false}},\"colourLogic\":{\"description\":\"\\\"all\\\" (default): results should contain the whole palette, ranked by average distance + proportion match. \\\"any\\\": results matching ANY palette colour, ranked by their best single-colour distance.\",\"type\":\"string\",\"enum\":[\"all\",\"any\"]},\"colourTolerance\":{\"description\":\"How close a colour must be to count as a match. 'normal' (default) is today's behaviour; 'tight' narrows the OKLab match threshold and prunes far single-colour matches; 'loose' widens the threshold.\",\"type\":\"string\",\"enum\":[\"tight\",\"normal\",\"loose\"]},\"colourAppearance\":{\"type\":\"object\",\"properties\":{\"lightness\":{\"description\":\"OKLab lightness window (L ∈ [0,1]); dark ≈ 0, light ≈ 1.\",\"type\":\"object\",\"properties\":{\"min\":{\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"max\":{\"type\":\"number\",\"minimum\":0,\"maximum\":1}},\"required\":[\"min\",\"max\"],\"additionalProperties\":false},\"warmth\":{\"description\":\"OKLab warmth window (b axis); cool < 0 < warm, ≈ −0.1..0.1.\",\"type\":\"object\",\"properties\":{\"min\":{\"type\":\"number\",\"minimum\":-0.5,\"maximum\":0.5},\"max\":{\"type\":\"number\",\"minimum\":-0.5,\"maximum\":0.5}},\"required\":[\"min\",\"max\"],\"additionalProperties\":false}},\"additionalProperties\":false,\"description\":\"HARD appearance gate on the material's DOMINANT colour, independent of the colour palette — works on a text-only query too. Variants without colour extraction are EXCLUDED whenever any axis is set (NULL scalars never match).\"},\"mood\":{\"type\":\"string\",\"enum",
77
+ "\":[\"warm\",\"cool\",\"earthy\",\"moody\",\"bold\",\"light-airy\",\"natural\",\"neutral\",\"romantic\",\"coastal\",\"industrial\",\"luxe\",\"vintage\",\"serene\",\"scandi\",\"cosy\",\"heritage\",\"mid-century\",\"pastel\"],\"description\":\"Mood-language colour entry — resolves to a curated checked-in colour palette. Use it for evocative mood queries ('coastal', 'moody', 'earthy') rather than a single colour. A mood ALONE runs as a colour search; with text it remains diagnostic or hard-constraint evidence and does not blend into the text mode's ordering score. One of: warm (Warm — Reds, oranges, yellows - inviting and cozy); cool (Cool — Blues, teals, cool grays - calm and professional); earthy (Earthy — Browns, tans, terracotta - natural and grounded); moody (Moody — Deep, saturated, dramatic - rich and atmospheric); bold (Bold — High chroma, vibrant - energetic and eye-catching); light-airy (Light + Airy — Pastels, pale tones - soft and spacious); natural (Natural — Greens, wood tones, stone - organic and balanced); neutral (Neutral — Whites, grays, blacks - versatile and timeless); romantic (Romantic — Soft pinks, mauves, blush - tender and elegant); coastal (Coastal — Sandy beiges, ocean blues, seafoam - relaxed seaside); industrial (Industrial — Cool grays, concrete, metallic - urban and modern); luxe (Luxe — Deep jewel tones, rich emeralds, sapphires - opulent); vintage (Vintage — Muted, dusty tones - nostalgic and timeworn); serene (Serene — Soft blues, sage, muted lavender - calm and peaceful); scandi (Scandi — Warm whites, pale woods, cream - Scandinavian minimalism); cosy (Cosy — Deep plaster, amber, tobacco - enveloping warmth); heritage (Heritage — Rich historical tones with saturated depth - classic and grounded); mid-century (Mid-Century — Olive, mustard, teal, terracotta - retro modern); pastel (Pastel — Sage, duck egg, blush - soft colors with body).\"},\"sort\":{\"type\":\"string\",\"enum\":[\"relevance\",\"light\",\"dark\",\"vivid\",\"muted\",\"colourful\",\"name\",\"name_desc\",\"recent\"],\"description\":\"Re-order the final scored results (default 'relevance' = the selected mode's primary score). Two families. COLOUR RAMPS: 'light'/'dark' by dominant lightness, 'vivid'/'muted' by chroma, 'colourful' by overall vibrancy — variants without colour extraction sink to the end. CATALOGUE ORDERS: 'name' (A→Z), 'name_desc' (Z→A) and 'recent' (newest first), which read the same `name` / `created_at` columns, in the same directions, that /api/v1/search/browse orders on — one word, one order, on both lanes. Every sort REORDERS the pool the search signals already retrieved; it never widens or substitutes for one. Retired materials sort below visible ones under every non-relevance sort. For an unranked walk of the whole corpus in any of these orders, use the browse surface instead.\"},\"orderBy\":{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"minLength\":1,\"description\":\"The attribute to sort by. Only attributes with a DECLARED value ladder can be ordered — ordering is never inferred from a value's spelling — and anything else is refused with the orderable set named. Orderable today: price_tier.\"},\"direction\":{\"default\":\"asc\",\"description\":\"'asc' (default) walks the ladder from its first rung.\",\"type\":\"string\",\"enum\":[\"asc\",\"desc\"]}},\"required\":[\"attributeId\",\"direction\"],\"additionalProperties\":false,\"description\":\"Sort results along ONE attribute's declared ladder — a separate axis from `sort`, which stays the closed relevance/colour/catalogue vocabulary. Sent together, `orderBy` wins and the response `note` says so. A variant whose value is off the ladder, or which carries no value at all, sorts LAST in both directions: it has no rung, and it is still a real row, so it is placed at the end rather than dropped or guessed at.\"},\"returnAttributes\":{\"maxItems\":8,\"type\":\"array\",\"items\":{\"type\":\"string\",\"minLength\":1},\"description\":\"Attribute ids whose RESOLVED VALUE should ride each result, under `attributes` keyed by attribute id. Values resolve with the normal variant-over-product precedence. THREE STATES, and they are different: an attribute you did not ask for is not a ",
78
+ "key at all; an attribute you asked for that this variant carries no accepted value for is `null`; anything else is the value (a string, or an array for a multi-valued attribute). An unknown attribute id is refused naming it, never silently dropped. Pair it with `filters` to show what you filtered on.\"},\"similarTo\":{\"description\":\"Find materials that LOOK like a seed image. Supply `variantId` to seed from an existing variant's primary image, `materialBankId` to seed from a Material Bank SKU — the ONE EXTERNAL identifier this API accepts, so a caller already holding a SKU needs no resolve round trip — or `vector`, a pre-computed halfvec literal (`[a,b,…]`) from an externally-embedded image, to drive the image arm directly. A `materialBankId` that names no variant is a 400 naming the offending value, never an empty result set and never an unseeded search. PRECEDENCE: wins over `text` (the response carries a note when both were given).\",\"anyOf\":[{\"type\":\"object\",\"properties\":{\"variantId\":{\"type\":\"string\"}},\"required\":[\"variantId\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"materialBankId\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"materialBankId\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"vector\":{\"type\":\"string\"}},\"required\":[\"vector\"],\"additionalProperties\":false}]},\"excludeVariantIds\":{\"description\":\"Canonical variant ids that must not appear in this result. Applied before the returned page is cut, so the page refills from the deeper ranked pool instead of being shortened by client-side filtering. IDs are opaque; duplicates are harmless and deduplicated by the engine.\",\"maxItems\":256,\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"image\":{\"description\":\"A raw incoming image to search by (URL or base64). The HTTP adapter embeds it and sets `similarTo: { vector }`; wins over `text`.\",\"oneOf\":[{\"type\":\"object\",\"properties\":{\"type\":{\"type\":\"string\",\"const\":\"url\"},\"url\":{\"type\":\"string\",\"format\":\"uri\",\"description\":\"HTTP(S) URL of the query image. MUST be an https URL.\"}},\"required\":[\"type\",\"url\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"type\":{\"type\":\"string\",\"const\":\"base64\"},\"data\":{\"type\":\"string\",\"minLength\":1,\"description\":\"Base64-encoded image bytes (or a data URI) the caller already holds — e.g. a form upload — so there is nothing to host.\"}},\"required\":[\"type\",\"data\"],\"additionalProperties\":false}],\"type\":\"object\"},\"category\":{\"type\":\"object\",\"properties\":{\"boost\":{\"description\":\"Soft category preference — results of this product type score higher but others still appear.\",\"type\":\"string\"},\"filter\":{\"description\":\"HARD gate: only these public taxonomy categories are returned. Values are category-group or product-type display names (case-insensitive), or canonical taxonomy keys. A group expands to every child product type; an unknown value errors listing every valid name.\",\"type\":\"array\",\"items\":{\"type\":\"string\"}}},\"additionalProperties\":false},\"weights\":{\"type\":\"object\",\"properties\":{\"image\":{\"type\":\"number\",\"minimum\":0},\"category\":{\"type\":\"number\",\"minimum\":0},\"colour\":{\"type\":\"number\",\"minimum\":0},\"lexical\":{\"type\":\"number\",\"minimum\":0},\"text\":{\"type\":\"number\",\"minimum\":0}},\"additionalProperties\":false,\"description\":\"Optional multiplier for the selected mode's one ordering signal. At most one applied weight is non-zero: colour→colour, lexical/entity-browse→lexical, variant-text→portrait, image-text/image→image, and filter-only→none. Overrides may scale only that live signal and cannot activate any other. Other similarities are diagnostic or hard-gate receipts and do not blend into score.\"},\"filters\":{\"description\":\"Typed filters applied as a hard restriction before ranking. Each entry is a term match (`attributeId` + `term`), a term SET (`attributeId` + `terms` — ANY one of them satisfies it), a presence check (`attributeId` + `hasValue: true`), or a numeric range (`attributeId` + `min`/`max`). AND/OR: entries AND together, and OR is expressed WITHIN one entry via `terms`. BRAND: `attributeId: \\\"brand\\\"` filters on the brand RELATION — a join every one of the",
79
+ " catalogue's variants carries — taking a brand slug or display name, exactly the `value` a `brand` facet reports. Do NOT use the `brand_name` ATTRIBUTE for this: it is a sparse spec value present on about 1.5% of variants and absent entirely from every catalog-ingested brand, so filtering on it returns a near-empty page that reads as a corpus gap. Presence filters always route as hard spec gates. Aesthetic/meaning term filters (pattern, texture, directionality, and the other visual-meaning attributes) are applied as a pool-bounded post-filter over the visual candidate pool rather than a whole-catalog restriction. Each entry may carry a `trust` bar the matching value must clear.\",\"type\":\"array\",\"items\":{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, e.g. \\\"primary_material\\\" or \\\"fire_rating\\\" — or the special id \\\"brand\\\", which filters on the brand RELATION (a join, carried by every variant) rather than on any attribute.\"},\"term\":{\"type\":\"string\",\"description\":\"Canonical term the attribute must equal, e.g. \\\"textile\\\". For \\\"brand\\\", a brand slug (\\\"kvadrat\\\") or display name (\\\"Kvadrat\\\"), case-insensitive — exactly the `value` a `brand` facet reports. An unrecognised brand is a 400 naming it, never an empty page.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"term\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, or the special id \\\"brand\\\" for the brand relation.\"},\"terms\":{\"minItems\":1,\"type\":\"array\",\"items\":{\"type\":\"string\"},\"description\":\"Canonical terms, ANY ONE of which satisfies the filter — this is how OR is expressed. OR lives inside one entry; separate `filters[]` entries AND together. Used for ordinal scales, where a stricter class satisfies a looser one, and for a multi-select rail — `{attributeId:\\\"brand\\\", terms:[\\\"kvadrat\\\",\\\"rubelli\\\"]}` returns materials from either brand.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"terms\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\"},\"excludesTerm\":{\"type\":\"string\",\"minLength\":1},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"excludesTerm\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id that must have an accepte",
80
+ "d value, e.g. \\\"sample_image_mm_per_px\\\".\"},\"hasValue\":{\"type\":\"boolean\",\"const\":true,\"description\":\"Require any accepted value for this attribute.\"},\"asOf\":{\"description\":\"Reference date for EPD expiration, exclusive: the value must remain valid after this date.\",\"type\":\"string\",\"format\":\"date\",\"pattern\":\"^(?:(?:\\\\d\\\\d[2468][048]|\\\\d\\\\d[13579][26]|\\\\d\\\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\\\d|30)|(?:02)-(?:0[1-9]|1\\\\d|2[0-8])))$\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"hasValue\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id holding a NUMBER, e.g. \\\"light_reflectance_value\\\" or \\\"lead_time_days\\\".\"},\"min\":{\"description\":\"Inclusive lower bound. Omit for a ceiling-only range.\",\"type\":\"number\"},\"max\":{\"description\":\"Inclusive upper bound. Omit for a floor-only range.\",\"type\":\"number\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\"],\"additionalProperties\":false}]}},\"preferences\":{\"description\":\"SOFT spec criteria — rank-never-exclude. Same shape as `filters`, but a preference NEVER removes a result: each hit's `verdict` gains a `preferred` cell, and a failed/missing preferred cell only DEMOTES the hit WITHIN its page (the cursor is from the pre-demotion order).\",\"maxItems\":8,\"type\":\"array\",\"items\":{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, e.g. \\\"primary_material\\\" or \\\"fire_rating\\\" — or the special id \\\"brand\\\", which filters on the brand RELATION (a join, carried by every variant) rather than on any attribute.\"},\"term\":{\"type\":\"string\",\"description\":\"Canonical term the attribute must equal, e.g. \\\"textile\\\". For \\\"brand\\\", a brand slug (\\\"kvadrat\\\") or display name (\\\"Kvadrat\\\"), case-insensitive — exactly the `value` a `brand` facet reports. An unrecognised brand is a 400 naming it, never an empty page.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"term\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id, or the special id \\\"brand\\\" for the brand relation.\"},\"terms\":{\"minItems\":1,\"type\":\"array\",\"items\":{\"type\":\"string\"},\"description\":\"Canonical terms, ANY ONE of which satisfies the filter — this is how OR is expressed. ",
81
+ "OR lives inside one entry; separate `filters[]` entries AND together. Used for ordinal scales, where a stricter class satisfies a looser one, and for a multi-select rail — `{attributeId:\\\"brand\\\", terms:[\\\"kvadrat\\\",\\\"rubelli\\\"]}` returns materials from either brand.\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"terms\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\"},\"excludesTerm\":{\"type\":\"string\",\"minLength\":1},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"excludesTerm\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id that must have an accepted value, e.g. \\\"sample_image_mm_per_px\\\".\"},\"hasValue\":{\"type\":\"boolean\",\"const\":true,\"description\":\"Require any accepted value for this attribute.\"},\"asOf\":{\"description\":\"Reference date for EPD expiration, exclusive: the value must remain valid after this date.\",\"type\":\"string\",\"format\":\"date\",\"pattern\":\"^(?:(?:\\\\d\\\\d[2468][048]|\\\\d\\\\d[13579][26]|\\\\d\\\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\\\d|30)|(?:02)-(?:0[1-9]|1\\\\d|2[0-8])))$\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\",\"hasValue\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"description\":\"Canonical attribute id holding a NUMBER, e.g. \\\"light_reflectance_value\\\" or \\\"lead_time_days\\\".\"},\"min\":{\"description\":\"Inclusive lower bound. Omit for a ceiling-only range.\",\"type\":\"number\"},\"max\":{\"description\":\"Inclusive upper bound. Omit for a floor-only range.\",\"type\":\"number\"},\"trust\":{\"type\":\"object\",\"properties\":{\"minConfidence\":{\"description\":\"Explicit confidence floor ∈ [0,1] the value must clear.\",\"type\":\"number\",\"minimum\":0,\"maximum\":1},\"sourceBasis\":{\"description\":\"Require an EXACT source basis (closed enum: catalog_feed, declared, derived).\",\"type\":\"string\",\"enum\":[\"catalog_feed\",\"declared\",\"derived\"]},\"meetsRequirement\":{\"description\":\"Require the value to clear the attribute's REGISTRY confidence floor. `minConfidence` wins; does NOT constrain basis; no requirement is met vacuously.\",\"type\":\"boolean\",\"const\":true}},\"additionalProperties\":false}},\"required\":[\"attributeId\"],\"additionalProperties\":false}]}},\"verdicts\":{\"description\":\"Opt-in seed-relative verdict cells. Only meaningful w",
82
+ "ith `similarTo: { variantId }`: costs one seed fetch + one page read to diff each hit's accepted COMPARABLE attributes (numeric + a colour axis) against the seed's, emitting `preferred` `verdict` cells with directional deviations (`above`/`below`, e.g. 2 mm thicker, warmer). Ignored on the default path; a `similarTo: { vector }` / raw-image seed carries no seed variant, so the response notes that seed cells were skipped.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Results per page (default 60, max 60).\",\"type\":\"integer\",\"minimum\":1,\"maximum\":60},\"cursor\":{\"description\":\"Opaque pagination cursor from a previous response. NOTE: ANN search is approximate — tail items may reorder slightly between pages.\",\"type\":\"string\"},\"explain\":{\"description\":\"Include per-signal scores on each result (`signals`).\",\"type\":\"boolean\"},\"collapse\":{\"description\":\"Group results by product before pagination. \\\"product\\\" returns one representative (highest-ranked) variant per product, with hidden siblings counted in `siblingCount`. The engine and GET adapter default to \\\"none\\\"; public POST requests default to \\\"product\\\" and may request \\\"none\\\" explicitly for variant-grain results.\",\"type\":\"string\",\"enum\":[\"none\",\"product\"]},\"count\":{\"default\":false,\"description\":\"Opt-in feed total. When true the response carries `counts.variants` (and `counts.products` under `collapse:\\\"product\\\"`). The variant total is EXACT up to a cap of 1000 and reported as `basis:\\\"lowerBound\\\"` beyond it — and ALWAYS `lowerBound` for ANN-ranked pools (image / colour / visual-text / visual-meaning post-filters), where the finite candidate pool is the only honest figure. Counts are never silently approximated: an exact count is labelled `exact`, a capped or pool-bounded one `lowerBound`. Default false (skip the count query entirely).\",\"type\":\"boolean\"},\"facetCounts\":{\"description\":\"Opt-in per-query facet counts (MG-1010). Name up to 10 facet ids — `brand`, `category`, or spec attribute ids — and the response carries a `facets` block counting each facet's values WITHIN this query's match set. Absent means no `facets` in the response and no counting cost. An id the registry does not know is a 400 naming it (with `didYouMean` candidates); a known but uncountable id lands in `facets.absent` with a reason. Counts follow the same honesty discipline as `count`: ranked (ANN) queries report pool-derived floors labelled `lowerBound` with `poolSize` stated; only a filter-driven query earns `exact`.\",\"maxItems\":10,\"type\":\"array\",\"items\":{\"type\":\"string\",\"minLength\":1}},\"refinementToken\":{\"description\":\"Opaque five-minute receipt from a prior pool-derived faceted search. Resubmitting it narrows that exact finalized pool instead of running a new candidate source.\",\"type\":\"string\",\"minLength\":1,\"maxLength\":131072},\"hasImages\":{\"default\":false,\"description\":\"When true, only return results that have at least one catalog image (a servable card thumbnail). Off by default so search stays honest about the FULL catalog, including image-less records. Applied on EVERY retrieval arm as a post-filter over the candidate pool, so a short page means the pool ran out of image-backed matches rather than that some arm ignored the flag. The opt-in `counts.variants` feed total honours it too, and filter, count and display share one SQL definition of image presence. Default false.\",\"type\":\"boolean\"},\"sampleableOnly\":{\"description\":\"Require an accepted sampleable=true variant fact before pagination. False or absent leaves the corpus unchanged.\",\"type\":\"boolean\"},\"view\":{\"default\":\"full\",\"description\":\"Result projection. \\\"full\\\" (default) returns the complete `searchResultItemSchema` (palette, colourMeta, hasImage, siblings, explain signals/meanings). \\\"card\\\" returns the lean `searchCardItemSchema` (identity + name + thumbnail + score) for grid rendering, skipping the per-page palette / appearance / meaning reads. The response echoes the chosen `view`, which discriminates the `results` union.\",\"type\":\"string\",\"enum\":[\"full\",\"card\"]},\"corpusScope\":{\"description\":\"Which corpus to answer from. samplizeCatalogue",
83
+ " admits published US Samplize paint colours regardless of stock. ABSENT means the WHOLE GRAPH — every material MaterialGraph knows, orderable or not; absence is never a narrower default. Three transaction-oriented scopes gate on governed CHANNEL membership: 'materialBankNaOrderable' (Material Bank North America, about 277,000 materials), 'materialBankEuOrderable' (Material Bank Europe, about 65,000) and 'designShopOrderable' (DesignShop, about 34,000, excluding what DesignShop hides from browse). 'materialBankNaAndCatalogue' is a governed discovery corpus over Material Bank NA or Catalogue editorial membership; it is not a buyability claim, and references alone do not confer membership. 'materialBankOrderable' instead restricts retrieval to materials carrying a Material Bank identity, which is a provenance signal rather than a channel one, and admits about 98% of the graph. Scope is distribution/visibility membership, never suitability; its receipt states whether that means transaction or editorial inclusion. Every scope is applied INSIDE the query: `limit` fills with eligible rows, `counts` describe the SCOPED corpus, and the response echoes the scope under `corpusScope`. This is what post-filtering a returned page cannot give you — a client-side filter shortens the page exactly when the query drifts toward the corpus MaterialGraph carries beyond the Material Bank feed, which is unpredictable from outside. An unknown value is a 400 naming it.\",\"type\":\"string\",\"enum\":[\"all\",\"samplizeCatalogue\",\"materialBankOrderable\",\"materialBankNaOrderable\",\"materialBankNaAndCatalogue\",\"materialBankEuOrderable\",\"designShopOrderable\"]},\"includeSamplingArtifacts\":{\"description\":\"OPS/AUDIT escape hatch — DO NOT use for normal search. Default behaviour (absent or false) EXCLUDES sampling-artifact products (multi-swatch sample cards — orderable samples, never specifiable materials) from every result, count, and candidate pool. Set true only to surface them for ops inspection or an audit.\",\"type\":\"boolean\"}},\"required\":[\"intent\",\"count\",\"hasImages\",\"view\"],\"additionalProperties\":false,\"description\":\"Complete replacement search request that runs this reading. Executable directly; it is not a delta.\"}},\"required\":[\"id\",\"label\",\"kind\",\"isDefault\",\"request\"],\"additionalProperties\":false},\"description\":\"Every valid reading. The one that ran is flagged `isDefault`; all are false when the engine intentionally chose none. Each is a complete executable request.\"}},\"required\":[\"kind\",\"term\",\"interpretations\"],\"additionalProperties\":false},\"supersededMoodId\":{\"description\":\"A mood term detected in the query but overridden by a stronger explicit colour reading. Absent means no mood was superseded, not that mood detection ran and found none.\",\"type\":\"string\",\"enum\":[\"warm\",\"cool\",\"earthy\",\"moody\",\"bold\",\"light-airy\",\"natural\",\"neutral\",\"romantic\",\"coastal\",\"industrial\",\"luxe\",\"vintage\",\"serene\",\"scandi\",\"cosy\",\"heritage\",\"mid-century\",\"pastel\"]},\"moodInterpretation\":{\"type\":\"object\",\"properties\":{\"query\":{\"description\":\"The original query text, or null without text.\",\"type\":[\"string\",\"null\"]},\"moodId\":{\"anyOf\":[{\"type\":\"string\",\"enum\":[\"warm\",\"cool\",\"earthy\",\"moody\",\"bold\",\"light-airy\",\"natural\",\"neutral\",\"romantic\",\"coastal\",\"industrial\",\"luxe\",\"vintage\",\"serene\",\"scandi\",\"cosy\",\"heritage\",\"mid-century\",\"pastel\"],\"description\":\"Mood-language colour entry — resolves to a curated checked-in colour palette. Use it for evocative mood queries ('coastal', 'moody', 'earthy') rather than a single colour. A mood ALONE runs as a colour search; with text it remains diagnostic or hard-constraint evidence and does not blend into the text mode's ordering score. One of: warm (Warm — Reds, oranges, yellows - inviting and cozy); cool (Cool — Blues, teals, cool grays - calm and professional); earthy (Earthy — Browns, tans, terracotta - natural and grounded); moody (Moody — Deep, saturated, dramatic - rich and atmospheric); bold (Bold — High chroma, vibrant - energetic and eye-catching); light-airy (Light + Airy — Pastels, pale tones - soft and spacious); natural (Natural",
84
+ " — Greens, wood tones, stone - organic and balanced); neutral (Neutral — Whites, grays, blacks - versatile and timeless); romantic (Romantic — Soft pinks, mauves, blush - tender and elegant); coastal (Coastal — Sandy beiges, ocean blues, seafoam - relaxed seaside); industrial (Industrial — Cool grays, concrete, metallic - urban and modern); luxe (Luxe — Deep jewel tones, rich emeralds, sapphires - opulent); vintage (Vintage — Muted, dusty tones - nostalgic and timeworn); serene (Serene — Soft blues, sage, muted lavender - calm and peaceful); scandi (Scandi — Warm whites, pale woods, cream - Scandinavian minimalism); cosy (Cosy — Deep plaster, amber, tobacco - enveloping warmth); heritage (Heritage — Rich historical tones with saturated depth - classic and grounded); mid-century (Mid-Century — Olive, mustard, teal, terracotta - retro modern); pastel (Pastel — Sage, duck egg, blush - soft colors with body).\"},{\"type\":\"null\"}],\"description\":\"The recognized mood id, or null when none was selected.\"},\"supersededMoodId\":{\"anyOf\":[{\"type\":\"string\",\"enum\":[\"warm\",\"cool\",\"earthy\",\"moody\",\"bold\",\"light-airy\",\"natural\",\"neutral\",\"romantic\",\"coastal\",\"industrial\",\"luxe\",\"vintage\",\"serene\",\"scandi\",\"cosy\",\"heritage\",\"mid-century\",\"pastel\"],\"description\":\"Mood-language colour entry — resolves to a curated checked-in colour palette. Use it for evocative mood queries ('coastal', 'moody', 'earthy') rather than a single colour. A mood ALONE runs as a colour search; with text it remains diagnostic or hard-constraint evidence and does not blend into the text mode's ordering score. One of: warm (Warm — Reds, oranges, yellows - inviting and cozy); cool (Cool — Blues, teals, cool grays - calm and professional); earthy (Earthy — Browns, tans, terracotta - natural and grounded); moody (Moody — Deep, saturated, dramatic - rich and atmospheric); bold (Bold — High chroma, vibrant - energetic and eye-catching); light-airy (Light + Airy — Pastels, pale tones - soft and spacious); natural (Natural — Greens, wood tones, stone - organic and balanced); neutral (Neutral — Whites, grays, blacks - versatile and timeless); romantic (Romantic — Soft pinks, mauves, blush - tender and elegant); coastal (Coastal — Sandy beiges, ocean blues, seafoam - relaxed seaside); industrial (Industrial — Cool grays, concrete, metallic - urban and modern); luxe (Luxe — Deep jewel tones, rich emeralds, sapphires - opulent); vintage (Vintage — Muted, dusty tones - nostalgic and timeworn); serene (Serene — Soft blues, sage, muted lavender - calm and peaceful); scandi (Scandi — Warm whites, pale woods, cream - Scandinavian minimalism); cosy (Cosy — Deep plaster, amber, tobacco - enveloping warmth); heritage (Heritage — Rich historical tones with saturated depth - classic and grounded); mid-century (Mid-Century — Olive, mustard, teal, terracotta - retro modern); pastel (Pastel — Sage, duck egg, blush - soft colors with body).\"},{\"type\":\"null\"}],\"description\":\"A recognized mood displaced by a stronger colour reading.\"}},\"required\":[\"query\",\"moodId\",\"supersededMoodId\"],\"additionalProperties\":false,\"description\":\"Mood interpretation receipt. Present when the standard search evaluated mood; null ids explicitly mean no mood was selected. Absent means mood was not evaluated or the response came from an older server.\"},\"mode\":{\"type\":\"string\",\"enum\":[\"image\",\"colour\",\"text\",\"lexical\",\"entity-browse\"],\"description\":\"The primary ranking source the query resolved to: 'image' (look-alike), 'colour' (the colour arm — pure), 'text' (visual text→image, the default), 'lexical' (BM25 over the attribute/name projection), or 'entity-browse' (the query names a brand and nothing else, so the brand's own catalogue in browse order is returned instead of a similarity ranking). A fully governed hard form + colour query filters the primary source's one candidate pool; that source still owns order and remains the reported mode.\"},\"textLane\":{\"type\":\"string\",\"enum\":[\"image-text\",\"variant-text\"],\"description\":\"Which dense text lane answered: 'image-text' (the query carries evidence it describes appearance, ",
85
+ "so it is ranked against image vectors) or 'variant-text' (ranked against each variant's written portrait). Absent when the answer did not come from the dense text arm — absence is unknown, not a default.\"},\"rerank\":{\"type\":\"object\",\"properties\":{\"colour\":{\"type\":\"string\",\"enum\":[\"ok\",\"failed\",\"skipped\"]},\"colourWeight\":{\"type\":\"number\"},\"colourLabel\":{\"type\":\"string\"}},\"additionalProperties\":false},\"colourConstraint\":{\"type\":\"object\",\"properties\":{\"basis\":{\"type\":\"string\",\"enum\":[\"explicit\",\"detected-strong\"]},\"family\":{\"type\":\"string\",\"minLength\":1},\"queryHexes\":{\"minItems\":1,\"type\":\"array\",\"items\":{\"type\":\"string\",\"pattern\":\"^#[0-9a-f]{6}$\"}},\"toneBandIds\":{\"minItems\":1,\"type\":\"array\",\"items\":{\"type\":\"string\",\"minLength\":1}},\"toneBandDropped\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"considered\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"kept\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"dropped\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"unknown\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"adjacent\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"abundance\":{\"type\":\"object\",\"properties\":{\"grain\":{\"type\":\"string\",\"enum\":[\"variant\",\"product\"]},\"requested\":{\"type\":\"integer\",\"exclusiveMinimum\":0,\"maximum\":9007199254740991},\"eligible\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"candidateWindow\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"sourceExhausted\":{\"type\":\"boolean\"},\"state\":{\"type\":\"string\",\"enum\":[\"filled\",\"corpus_scarcity\",\"evidence_unavailable\",\"retrieval_window_exhausted\"]}},\"required\":[\"grain\",\"requested\",\"eligible\",\"candidateWindow\",\"sourceExhausted\",\"state\"],\"additionalProperties\":false}},\"required\":[\"basis\",\"family\",\"queryHexes\",\"considered\",\"kept\",\"dropped\",\"unknown\",\"adjacent\"],\"additionalProperties\":false,\"description\":\"Eligibility for a compound text colour: counts cover the bounded candidate pool, `adjacent` passed ColorScope hue compatibility, and missing colour evidence failed closed. Absent means colour was not a hard constraint.\"},\"formConstraint\":{\"type\":\"object\",\"properties\":{\"instruction\":{\"type\":\"string\",\"enum\":[\"subway\",\"plank\",\"square\",\"hexagon\",\"penny-round\"]},\"considered\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"kept\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"dropped\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"contradicted\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"unknown\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"abundance\":{\"type\":\"object\",\"properties\":{\"grain\":{\"type\":\"string\",\"enum\":[\"variant\",\"product\"]},\"requested\":{\"type\":\"integer\",\"exclusiveMinimum\":0,\"maximum\":9007199254740991},\"eligible\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"candidateWindow\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"sourceExhausted\":{\"type\":\"boolean\"},\"state\":{\"type\":\"string\",\"enum\":[\"filled\",\"corpus_scarcity\",\"evidence_unavailable\",\"retrieval_window_exhausted\"]}},\"required\":[\"grain\",\"requested\",\"eligible\",\"candidateWindow\",\"sourceExhausted\",\"state\"],\"additionalProperties\":false}},\"required\":[\"instruction\",\"considered\",\"kept\",\"dropped\",\"contradicted\",\"unknown\",\"abundance\"],\"additionalProperties\":false,\"description\":\"Hard form eligibility for a compound text query. A candidate is kept only when governed size or body-type evidence supports the requested form; product and variant names are never proof.\"},\"maxScore\":{\"type\":\"number\"},\"counts\":{\"description\":\"Feed totals, present when `count:true` was requested OR when `collapse:\\\"product\\\"` grouped the results. `variants` is the total matching variants; `products` (only under `collapse`) the distinct product groups. Each total carries a `basis`: `exact` (true count, up to the cap of 1000) or `lowerBound` (a floor — the cap was hit, an ANN-ranked pool bounds it, or the count degraded). The lexical arm counts exactly via the tsvector predicate; a single accepted spec term reads the `mv_attribute_facet_counts` materialized view (refr",
86
+ "eshed post-ingest, so a hair stale by design); ANN pools (image / colour / visual-text) and `products` are always `lowerBound`. The two totals describe the same result set, so `products` never exceeds `variants`: both are measured against the pool this page was drawn from, and a `variants` total that came out below that pool is reported as the pool-bounded floor instead — it counted a narrower universe than the results beside it.\",\"type\":\"object\",\"properties\":{\"variants\":{\"type\":\"object\",\"properties\":{\"value\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"basis\":{\"type\":\"string\",\"enum\":[\"exact\",\"lowerBound\"]}},\"required\":[\"value\",\"basis\"],\"additionalProperties\":false},\"products\":{\"type\":\"object\",\"properties\":{\"value\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"basis\":{\"type\":\"string\",\"enum\":[\"exact\",\"lowerBound\"]}},\"required\":[\"value\",\"basis\"],\"additionalProperties\":false}},\"required\":[\"variants\"],\"additionalProperties\":false},\"facets\":{\"description\":\"Per-query facet counts (MG-1010), present ONLY when the request sent `facetCounts`. Each requested facet's values are counted WITHIN this query's match set — a ranked query reports pool-derived floors (`basis:\\\"lowerBound\\\"`, `poolSize` stated), a filter-driven query true counts (`basis:\\\"exact\\\"`). See the `facetCounts` input field.\",\"type\":\"object\",\"properties\":{\"poolSize\":{\"description\":\"Present when the counts are POOL-DERIVED (every ranked query, and any filter-only query whose exact aggregation degraded): the size of the finalised candidate pool the per-value figures were counted over. Absent when the counts are exact under the query's whole predicate.\",\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"requested\":{\"type\":\"array\",\"items\":{\"type\":\"string\",\"minLength\":1},\"description\":\"The facet ids the request asked for, echoed verbatim.\"},\"absent\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"facetId\":{\"type\":\"string\",\"minLength\":1,\"description\":\"The requested facet id, verbatim.\"},\"reason\":{\"type\":\"string\",\"enum\":[\"unsupported\",\"timeout\"],\"description\":\"Why no counts were computed. `unsupported` — the id is KNOWN (a real registry attribute or virtual facet) but facet counting does not cover it: numeric attributes, non-facetable attributes, and (v1) the virtual certification programme. `timeout` — counting did not complete inside the facet stage's own time budget; the RESULTS are unaffected (facet counting can never take the page down), only the counts are missing. An id the registry has never heard of is a 400 naming it, never an `absent` entry.\"}},\"required\":[\"facetId\",\"reason\"],\"additionalProperties\":false},\"description\":\"Requested facets that were not counted, each with its reason — `unsupported` (the id is known but not countable) or `timeout` (the facet stage's own budget expired; the results are unaffected). Empty when every requested facet was counted.\"},\"counts\":{\"type\":\"object\",\"propertyNames\":{\"type\":\"string\"},\"additionalProperties\":{\"type\":\"object\",\"properties\":{\"values\":{\"maxItems\":25,\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"value\":{\"type\":\"string\",\"minLength\":1,\"description\":\"The CANONICAL token — a brand slug, a taxonomy product-type id, or a spec attribute's canonical term. Send it back verbatim as a filter term; never display-derive it.\"},\"label\":{\"type\":\"string\",\"minLength\":1,\"description\":\"Display label for the value — the brand's name, the product type's display name, or the registry dictionary label for a spec term. Falls back to the canonical token when the registry carries no label.\"},\"variants\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991,\"description\":\"Variants carrying this value within the counted set.\"}},\"required\":[\"value\",\"label\",\"variants\"],\"additionalProperties\":false},\"description\":\"Top values by variant count, at most 25. An empty list is a facet with no values in the counted set, not an error.\"},\"truncated\":{\"type\":\"boolean\",\"description\":\"ALWAYS present. True when more distinct values existed beyond the reported list; false when the list is complete. Never absent-",
87
+ "or-false.\"},\"basis\":{\"type\":\"string\",\"enum\":[\"exact\",\"lowerBound\"],\"description\":\"Exactly \\\"exact\\\" or \\\"lowerBound\\\" — nothing else, ever. `exact`: each `variants` figure is the true count under the query's whole predicate (filter-driven requests only). `lowerBound`: the figures were counted over the ranked candidate pool (`facets.poolSize`), so each is a floor of the true per-value total. Pool-counted values are NEVER `exact`.\"}},\"required\":[\"values\",\"truncated\",\"basis\"],\"additionalProperties\":false},\"description\":\"Per-facet value counts, keyed by the requested facet id. Every requested facet that is not in `absent` has an entry, including facets whose counted set held no values (`values: []`).\"}},\"required\":[\"requested\",\"absent\",\"counts\"],\"additionalProperties\":false},\"corpusScope\":{\"description\":\"The corpus this page AND its `counts` were drawn from — present only when the request narrowed it. ABSENT means the whole graph. Read it with `counts`: under a scope a short page is a small SCOPED corpus, not a truncated one.\",\"type\":\"object\",\"properties\":{\"scope\":{\"type\":\"string\",\"enum\":[\"all\",\"samplizeCatalogue\",\"materialBankOrderable\",\"materialBankNaOrderable\",\"materialBankNaAndCatalogue\",\"materialBankEuOrderable\",\"designShopOrderable\"],\"description\":\"Which corpus to answer from. samplizeCatalogue admits published US paint colours regardless of stock. ABSENT (or 'all') means the WHOLE GRAPH — every material MaterialGraph knows, orderable or not; absence is never a narrower default. Three transaction-oriented scopes gate on governed CHANNEL membership: 'materialBankNaOrderable' (Material Bank North America, ~277,000 materials), 'materialBankEuOrderable' (Material Bank Europe, ~65,000) and 'designShopOrderable' (DesignShop, ~34,000, with the products DesignShop hides from browse excluded). 'materialBankNaAndCatalogue' is the one-query union of Material Bank North America and the governed Catalogue editorial discovery corpus; that union does not by itself claim every member is transactable. 'materialBankOrderable' is NOT one of them: it selects materials carrying a Material Bank identity, which is a provenance signal rather than a channel one, and it admits 98.4% of the graph — use it to exclude crawl-sourced brands, not to mean orderable. A scope answers membership in its named governed corpus, never suitability: scope on channel, then refine on sector. Every scope is applied INSIDE the query: `limit` fills with eligible rows and `counts` describe the scoped corpus, which post-filtering a page cannot give you. An unknown value is a 400 naming it, never a silent fall back to the whole graph.\"},\"description\":{\"type\":\"string\",\"description\":\"What the scope admits, in one sentence, for a human reader.\"}},\"required\":[\"scope\",\"description\"],\"additionalProperties\":false},\"effectiveColours\":{\"description\":\"The `#rrggbb` colours the colour signal ACTUALLY ran on, after minor-proportion accents were dropped (MG-1549). Present whenever the request carried explicit `colours`. Compare against your request: a difference means the search that ran is not the search that was asked for.\",\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"droppedColours\":{\"description\":\"Colours removed from the query for carrying a `weight` below the 5% accent threshold. EMPTY is the normal case. Non-empty and the dropped colours are absent from colour retrieval and ranking entirely — they did not merely rank low. To keep a small accent in the query, give it `role: \\\"accent\\\"`.\",\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"note\":{\"type\":\"string\"},\"degraded\":{\"description\":\"Which parts of the search did not answer, on EVERY response rather than only under `explain`. A named part says that check failed and the search carried on without it, so the page may be missing results or a warning it would otherwise have carried. An empty `parts` says no part reported a failure; it does NOT promise every check ran, because a check the request never needed is skipped rather than failed. ABSENT means the server does not report this, never that nothing was degraded. The vocabulary is o",
88
+ "pen: read it with a default branch.\",\"type\":\"object\",\"properties\":{\"parts\":{\"type\":\"array\",\"items\":{\"type\":\"string\",\"enum\":[\"filter-coverage\",\"term-coverage\"]}}},\"required\":[\"parts\"],\"additionalProperties\":false},\"emptyOutcome\":{\"description\":\"Why this page carries no results, on EVERY empty response rather than only under `explain`. `proven-corpus-empty` means the catalogue was checked and holds nothing matching — safe to tell someone there is nothing. `unproven-empty` means the search ended with nothing and could not prove the catalogue is empty, so it is never grounds for a claim about the catalogue. `search-degraded` means part of the search did not answer; retry before reading anything into the empty page. ABSENT means the page carries results.\",\"oneOf\":[{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"const\":\"proven-corpus-empty\"},\"basis\":{\"type\":\"string\",\"enum\":[\"spec-allowlist\",\"brand-scope\",\"brand-product-type\"]},\"exhaustive\":{\"type\":\"boolean\",\"const\":true},\"sourceEmpty\":{\"type\":[\"boolean\",\"null\"]},\"counts\":{\"type\":\"object\",\"properties\":{\"retrieved\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"afterPrune\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"final\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991}},\"required\":[\"retrieved\",\"afterPrune\",\"final\"],\"additionalProperties\":false}},\"required\":[\"kind\",\"basis\",\"exhaustive\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"const\":\"unproven-empty\"},\"reason\":{\"type\":\"string\",\"enum\":[\"allowlist-truncated\",\"retrieval-window-not-exhaustive\",\"source-empty\",\"post-retrieval-rejected\",\"pagination-empty\",\"no-authoritative-empty-proof\"]},\"sourceEmpty\":{\"type\":[\"boolean\",\"null\"]},\"counts\":{\"type\":\"object\",\"properties\":{\"retrieved\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"afterPrune\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"final\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991}},\"required\":[\"retrieved\",\"afterPrune\",\"final\"],\"additionalProperties\":false}},\"required\":[\"kind\",\"reason\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"const\":\"search-degraded\"},\"parts\":{\"minItems\":1,\"type\":\"array\",\"items\":{\"type\":\"string\",\"enum\":[\"filter-coverage\",\"term-coverage\"]}},\"sourceEmpty\":{\"type\":[\"boolean\",\"null\"]},\"counts\":{\"type\":\"object\",\"properties\":{\"retrieved\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"afterPrune\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"final\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991}},\"required\":[\"retrieved\",\"afterPrune\",\"final\"],\"additionalProperties\":false}},\"required\":[\"kind\",\"parts\"],\"additionalProperties\":false}],\"type\":\"object\"},\"termCoverage\":{\"description\":\"Lexical-projection coverage of substantive terms. Present only under explain.\",\"type\":\"object\",\"properties\":{\"status\":{\"type\":\"string\",\"enum\":[\"covered\",\"uncovered\",\"unavailable\",\"skipped\"]},\"terms\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"note\":{\"type\":\"string\"}},\"required\":[\"status\",\"terms\"],\"additionalProperties\":false},\"cursor\":{\"type\":\"string\"},\"window\":{\"type\":\"object\",\"properties\":{\"profile\":{\"type\":\"string\",\"const\":\"interactive-r1\"},\"candidateTier\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"retrievalRounds\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"retrievalMs\":{\"type\":\"number\",\"minimum\":0},\"exhaustive\":{\"type\":\"boolean\"},\"candidateCounts\":{\"type\":\"object\",\"properties\":{\"rawProviderMatches\":{\"anyOf\":[{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},{\"type\":\"null\"}]},\"selectedMatches\":{\"anyOf\":[{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},{\"type\":\"null\"}]},\"hydratedCanonicalCandidates\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991}},\"required\":[\"rawProviderMatches\",\"selectedMatches\",\"hydratedCanonicalCandidates\"],\"additionalProperties\":false}},\"required\":[\"profile\",\"candidateTier\",\"retrievalRounds\",\"retrievalMs\",\"exhaustive\"],\"additionalProperties\":false,\"description\":\"The single bounded candidate window that produced thi",
89
+ "s page: versioned profile, requested depth, retrieval cost, and whether the selected source proved it was exhausted.\"},\"plan\":{\"description\":\"The executed query shape — result grain and the per-arm weight profile. Present only when `explain` is set. Diagnostic, not part of the ranking contract.\",\"type\":\"object\",\"properties\":{\"textLane\":{\"type\":\"string\",\"enum\":[\"image-text\",\"variant-text\"]},\"grain\":{\"type\":\"string\",\"enum\":[\"variant\",\"product\"],\"description\":\"What one result stands for: \\\"variant\\\" (the default — every matching variant is its own result) or \\\"product\\\" (`collapse: \\\"product\\\"` grouped variants, so each result fronts a product and hides siblings).\"},\"weights\":{\"type\":\"object\",\"properties\":{\"portrait\":{\"type\":\"number\"},\"image\":{\"type\":\"number\"},\"category\":{\"type\":\"number\"},\"colour\":{\"type\":\"number\"},\"lexical\":{\"type\":\"number\"},\"text\":{\"type\":\"number\"}},\"required\":[\"portrait\",\"image\",\"category\",\"colour\",\"lexical\",\"text\"],\"additionalProperties\":false,\"description\":\"The applied ordering-signal profile. At most one weight is non-zero: the selected mode's primary signal. Multiplying that signal by its weight reproduces `score`, and the weight is `maxScore`. Other signals are diagnostic or hard-gate receipts and never blend.\"},\"queryTransform\":{\"type\":\"array\",\"items\":{\"oneOf\":[{\"type\":\"object\",\"properties\":{\"stage\":{\"type\":\"string\",\"enum\":[\"normalise\",\"resolve-spec\",\"resolve-paint\",\"resolve-brand\",\"resolve-category\"]},\"applied\":{\"type\":\"boolean\",\"const\":true}},\"required\":[\"stage\",\"applied\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"stage\":{\"type\":\"string\",\"enum\":[\"normalise\",\"resolve-spec\",\"resolve-paint\",\"resolve-brand\",\"resolve-category\"]},\"applied\":{\"type\":\"boolean\",\"const\":false},\"reason\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"stage\",\"applied\",\"reason\"],\"additionalProperties\":false}],\"type\":\"object\"},\"description\":\"The query-transformation stages that actually executed, in execution order. An applied stage changed execution; a skipped stage carries its reason. An empty array means this path ran no query transform.\"},\"steps\":{\"description\":\"Ordered request-understanding steps. Every entry says whether the step executed or skipped, why, and its measured wall-clock duration.\",\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"key\":{\"type\":\"string\",\"minLength\":1},\"status\":{\"type\":\"string\",\"enum\":[\"executed\",\"skipped\"]},\"reason\":{\"type\":\"string\",\"minLength\":1},\"durationMs\":{\"type\":\"number\",\"minimum\":0}},\"required\":[\"key\",\"status\",\"reason\",\"durationMs\"],\"additionalProperties\":false}},\"categoryAnchor\":{\"description\":\"Present only when the query NAMED a canonical product type. See `applied` for whether it gated the candidate pool.\",\"type\":\"object\",\"properties\":{\"productTypeId\":{\"type\":\"string\",\"description\":\"The leaf product-type id the query anchored to, e.g. \\\"modular_carpet\\\" — or, for a \\\"group\\\" anchor, the GROUP key, e.g. \\\"flooring\\\". A group key is a taxonomy node no variant is typed as; the ids actually gated on are in `productTypeIds`.\"},\"productTypeIds\":{\"description\":\"The leaf type ids a \\\"group\\\" anchor expanded to. Absent for the leaf sources, where `productTypeId` is already the single type gated on.\",\"readOnly\":true,\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"displayName\":{\"type\":\"string\",\"description\":\"The registry display name of that type.\"},\"similarity\":{\"type\":\"number\",\"description\":\"Cosine of the RAW (unanchored) query vector against the type vector for a \\\"vector\\\" anchor; 1 for the exact synonym / display-name passes.\"},\"margin\":{\"description\":\"Lead over the runner-up type for a \\\"vector\\\" anchor — the tie-break guard that stops an ambiguous query gating. Null for the exact passes.\",\"type\":[\"number\",\"null\"]},\"source\":{\"type\":\"string\",\"enum\":[\"taxonomy\",\"synonym\",\"display-name\",\"group\",\"head-noun\",\"vector\"],\"description\":\"How the anchor was established: a curated customer-language synonym, a registry display name appearing verbatim in the query, a public product-type group named by the query's trailing head noun, a bare canonical category noun whose leaf types cut a",
90
+ "cross groups (\\\"tile\\\"), or the measured vector match.\"},\"candidateSource\":{\"description\":\"Provider-owned retrieval policy. When present, the hard category is applied inside lexical candidate selection before the result limit.\",\"type\":\"string\",\"const\":\"lexical\"},\"applied\":{\"type\":\"boolean\",\"description\":\"Whether the anchor actually gated the pool. False means the anchored category held nothing in the candidate pool, so the engine fell back to unanchored ranking (and said so in `note`) rather than returning an empty page.\"}},\"required\":[\"productTypeId\",\"displayName\",\"similarity\",\"margin\",\"source\",\"applied\"],\"additionalProperties\":false},\"brandAnchor\":{\"description\":\"Present only when the query NAMED a brand the catalogue sells. See `applied` for whether it gated retrieval or was superseded by the empty-result retreat.\",\"type\":\"object\",\"properties\":{\"brandSlug\":{\"type\":\"string\",\"description\":\"The brand the query named, e.g. \\\"daltile\\\" — its stable slug.\"},\"brandName\":{\"type\":\"string\",\"description\":\"The brand's name as catalogued.\"},\"confidence\":{\"type\":\"number\",\"description\":\"Resolver confidence in [0,1]. 1 means the query IS the brand's name or slug; the gate's floor is 0.88.\"},\"margin\":{\"description\":\"Lead over the runner-up brand — the tie-break guard that stops an ambiguous query gating. Null when there was no runner-up.\",\"type\":[\"number\",\"null\"]},\"tier\":{\"type\":\"string\",\"enum\":[\"exact_id\",\"exact_alias\",\"lexical\",\"semantic\"],\"description\":\"Which rung of the resolver ladder won. Only the identity rungs (`exact_id`, `exact_alias`) may gate — a `lexical` or `semantic` win means the query RESEMBLES a brand name rather than naming one.\"},\"variantCount\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991,\"description\":\"Catalogued variants behind the brand — the gated universe's size.\"},\"applied\":{\"type\":\"boolean\",\"description\":\"Whether the anchor actually gated retrieval. False means the brand held nothing retrievable, so the engine re-ran unanchored (and said so in `note`) rather than returning an empty page.\"}},\"required\":[\"brandSlug\",\"brandName\",\"confidence\",\"margin\",\"tier\",\"variantCount\",\"applied\"],\"additionalProperties\":false},\"brandSkipped\":{\"description\":\"Present only when the brand stage saw a real brand and REFUSED to gate on it. Mutually exclusive with `brandAnchor`.\",\"type\":\"object\",\"properties\":{\"reason\":{\"type\":\"string\",\"enum\":[\"ambiguous\",\"weak-tier\",\"too-broad\"],\"description\":\"Why the brand gate was declined. `ambiguous` = two brands inside the resolver's gap; `weak-tier` = the query resembles a brand name rather than naming one; `too-broad` = the brand exceeds the exact-scan ceiling.\"},\"candidates\":{\"description\":\"The brands that tied — populated for `ambiguous` only, so the response can name what it declined to choose between.\",\"type\":\"array\",\"items\":{\"type\":\"string\"}}},\"required\":[\"reason\"],\"additionalProperties\":false}},\"required\":[\"grain\",\"weights\",\"queryTransform\"],\"additionalProperties\":false},\"timings\":{\"description\":\"Wall-clock milliseconds per step of the search workflow, keyed by step id, plus `total` for the whole run. The key set is open and depends on the path a query took, so read it by iterating rather than by naming a key. Present only when `explain` is set — diagnostic, not part of the ranking contract.\",\"type\":\"object\",\"propertyNames\":{\"type\":\"string\"},\"additionalProperties\":{\"type\":\"number\"}},\"query\":{\"type\":\"object\",\"propertyNames\":{\"type\":\"string\"},\"additionalProperties\":{}},\"seed\":{\"type\":\"object\",\"propertyNames\":{\"type\":\"string\"},\"additionalProperties\":{}},\"explain\":{\"type\":\"object\",\"properties\":{\"ledger\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"propertyNames\":{\"type\":\"string\"},\"additionalProperties\":{}}}},\"required\":[\"ledger\"],\"additionalProperties\":false}},\"required\":[\"view\",\"results\",\"suggestions\",\"mode\",\"maxScore\",\"query\"],\"additionalProperties\":false}],\"type\":\"object\"},\"meta\":{\"type\":\"object\",\"properties\":{\"operationId\":{\"type\":\"string\",\"enum\":[\"search_materials_post\",\"search_materials_get\"]},\"requestId\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"operationId\",\"requestId\"]",
91
+ ",\"additionalProperties\":false},\"links\":{\"type\":\"object\",\"properties\":{},\"additionalProperties\":false},\"warnings\":{\"type\":\"array\",\"items\":{\"type\":\"string\",\"minLength\":1}}},\"required\":[\"data\",\"meta\",\"links\",\"warnings\"],\"additionalProperties\":false}}},\"annotations\":{\"readOnlyHint\":true,\"untrustedContentHint\":true},\"requiresVisibleConfirmation\":false,\"requiredHeaders\":[]},{\"name\":\"compare\",\"operationId\":\"retrieval.compare\",\"path\":\"/api/v1/compare\",\"method\":\"POST\",\"description\":\"Reads 2–10 available canonical Variants and compares their stated values across the selected blocks or attributes. Unknown and conflicting cells remain explicit; no canonical data is changed. Requires the `mg:read` bearer scope.\",\"inputSchema\":{\"$ref\":\"#/$defs/schema0\",\"$defs\":{\"schema0\":{\"type\":\"object\",\"properties\":{\"variantIds\":{\"minItems\":2,\"maxItems\":10,\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"selection\":{\"oneOf\":[{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"const\":\"blocks\"},\"blockIds\":{\"minItems\":1,\"maxItems\":31,\"type\":\"array\",\"items\":{\"type\":\"string\"}}},\"required\":[\"kind\",\"blockIds\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"const\":\"attributes\"},\"attributeIds\":{\"minItems\":1,\"maxItems\":278,\"type\":\"array\",\"items\":{\"type\":\"string\"}}},\"required\":[\"kind\",\"attributeIds\"],\"additionalProperties\":false}],\"type\":\"object\"}},\"required\":[\"variantIds\"],\"additionalProperties\":false}}},\"outputSchema\":{\"$ref\":\"#/$defs/schema0\",\"$defs\":{\"schema0\":{\"type\":\"object\",\"properties\":{\"data\":{\"type\":\"object\",\"properties\":{\"targets\":{\"minItems\":2,\"maxItems\":10,\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"variantId\":{\"type\":\"string\"},\"version\":{\"type\":\"string\"},\"freshness\":{\"type\":\"object\",\"properties\":{\"oldestAt\":{\"type\":[\"string\",\"null\"]},\"newestAt\":{\"type\":[\"string\",\"null\"]}},\"required\":[\"oldestAt\",\"newestAt\"],\"additionalProperties\":false},\"identity\":{\"type\":\"object\",\"properties\":{\"variantId\":{\"type\":\"string\"},\"variantName\":{\"type\":[\"string\",\"null\"]},\"brand\":{\"type\":\"string\"},\"brandSlug\":{\"type\":\"string\"},\"product\":{\"type\":\"string\"},\"productId\":{\"type\":\"string\"},\"colourName\":{\"type\":[\"string\",\"null\"]},\"category\":{\"type\":[\"string\",\"null\"]},\"categoryPath\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"imageUrl\":{\"type\":[\"string\",\"null\"]},\"installationImageUrl\":{\"type\":[\"string\",\"null\"]},\"dominantHex\":{\"type\":[\"string\",\"null\"]},\"siblingCount\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991}},\"required\":[\"variantId\",\"variantName\",\"brand\",\"brandSlug\",\"product\",\"productId\",\"colourName\",\"category\",\"categoryPath\",\"imageUrl\",\"installationImageUrl\",\"dominantHex\",\"siblingCount\"],\"additionalProperties\":false},\"availability\":{\"type\":\"object\",\"properties\":{\"state\":{\"type\":\"string\",\"enum\":[\"available\",\"discontinued\",\"superseded\",\"unknown\"],\"description\":\"Orderability: 'available' (listed in the catalog of record), 'discontinued' (withdrawn from that catalog — not a reviewed manufacturer-discontinuation claim), 'superseded' (a reviewed successor relationship exists and the material is no longer listed), 'unknown' (never checked).\"},\"label\":{\"type\":[\"string\",\"null\"]},\"supersededBy\":{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"variantId\":{\"type\":\"string\"},\"brandSlug\":{\"type\":\"string\"},\"productId\":{\"type\":\"string\"},\"brand\":{\"type\":\"string\"},\"product\":{\"type\":\"string\"},\"variant\":{\"type\":\"string\"},\"color\":{\"type\":[\"string\",\"null\"]},\"availability\":{\"type\":\"string\",\"enum\":[\"available\",\"discontinued\",\"superseded\",\"unknown\"],\"description\":\"Orderability: 'available' (listed in the catalog of record), 'discontinued' (withdrawn from that catalog — not a reviewed manufacturer-discontinuation claim), 'superseded' (a reviewed successor relationship exists and the material is no longer listed), 'unknown' (never checked).\"}},\"required\":[\"variantId\",\"brandSlug\",\"productId\",\"brand\",\"product\",\"variant\",\"color\",\"availability\"],\"additionalProperties\":false},{\"type\":\"null\"}]}},\"required\":[\"state\",\"label\",\"supersededBy\"],\"additionalProperties\":false},\"sampleability\":{\"type\":[\"boolean\",\"null\"]}},\"required\":[\"variantId\",\"version\",\"freshness\"",
92
+ ",\"identity\",\"availability\",\"sampleability\"],\"additionalProperties\":false}},\"rows\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"blockId\":{\"type\":\"string\"},\"blockName\":{\"type\":\"string\"},\"attributeId\":{\"type\":\"string\"},\"label\":{\"type\":\"string\"},\"outcome\":{\"type\":\"string\",\"enum\":[\"same\",\"different\",\"incomplete\",\"indeterminate\",\"not_applicable\"]},\"cells\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"variantId\":{\"type\":\"string\"},\"state\":{\"type\":\"string\",\"enum\":[\"known\",\"unknown\",\"conflicting\",\"inapplicable\"]},\"recordState\":{\"type\":\"string\",\"enum\":[\"stated\",\"conflicting\",\"stale\",\"unknown\",\"missing\",\"not_applicable\",\"not_evaluated\"]},\"value\":{\"anyOf\":[{},{\"type\":\"null\"}]},\"unit\":{\"type\":[\"string\",\"null\"]},\"verification\":{\"type\":\"string\",\"enum\":[\"verified\",\"unverified\",\"unbacked\"]},\"confidence\":{\"anyOf\":[{\"type\":\"number\",\"minimum\":0,\"maximum\":1},{\"type\":\"null\"}]},\"sourceBasis\":{\"type\":[\"string\",\"null\"]},\"sourceRef\":{\"type\":[\"string\",\"null\"]},\"freshnessAt\":{\"type\":[\"string\",\"null\"]},\"claimScope\":{\"anyOf\":[{\"type\":\"string\",\"enum\":[\"variant\",\"product\",\"brand\"]},{\"type\":\"null\"}]},\"competingClaims\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"scope\":{\"type\":\"string\",\"enum\":[\"variant\",\"product\",\"brand\"]},\"value\":{\"anyOf\":[{},{\"type\":\"null\"}]},\"sourceRef\":{\"type\":[\"string\",\"null\"]}},\"required\":[\"scope\",\"value\",\"sourceRef\"],\"additionalProperties\":false}}},\"required\":[\"variantId\",\"state\",\"recordState\",\"value\",\"unit\",\"verification\",\"confidence\",\"sourceBasis\",\"sourceRef\",\"freshnessAt\",\"claimScope\",\"competingClaims\"],\"additionalProperties\":false}}},\"required\":[\"blockId\",\"blockName\",\"attributeId\",\"label\",\"outcome\",\"cells\"],\"additionalProperties\":false}}},\"required\":[\"targets\",\"rows\"],\"additionalProperties\":false},\"meta\":{\"type\":\"object\",\"properties\":{\"operationId\":{\"type\":\"string\",\"const\":\"retrieval.compare\"},\"requestId\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"operationId\",\"requestId\"],\"additionalProperties\":false},\"links\":{\"type\":\"object\",\"properties\":{},\"additionalProperties\":false},\"warnings\":{\"maxItems\":0,\"type\":\"array\",\"items\":{\"not\":{}}}},\"required\":[\"data\",\"meta\",\"links\",\"warnings\"],\"additionalProperties\":false}}},\"annotations\":{\"readOnlyHint\":true,\"untrustedContentHint\":true},\"requiresVisibleConfirmation\":false,\"requiredHeaders\":[]},{\"name\":\"create_correction\",\"operationId\":\"corrections.create\",\"path\":\"/api/v1/corrections\",\"method\":\"POST\",\"description\":\"Records page and graph evidence as a caller-owned review proposal. The submission does not change accepted catalog truth. Requires `mg:read` and `mg:corrections`, plus a distinct caller principal.\",\"inputSchema\":{\"$ref\":\"#/$defs/schema0\",\"$defs\":{\"schema1\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"number\"},{\"type\":\"boolean\"},{\"type\":\"null\"},{\"type\":\"array\",\"items\":{\"$ref\":\"#/$defs/schema1\"}},{\"type\":\"object\",\"propertyNames\":{\"type\":\"string\"},\"additionalProperties\":{\"$ref\":\"#/$defs/schema1\"}}]},\"schema2\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"number\"},{\"type\":\"boolean\"},{\"type\":\"null\"},{\"type\":\"array\",\"items\":{\"$ref\":\"#/$defs/schema2\"}},{\"type\":\"object\",\"propertyNames\":{\"type\":\"string\"},\"additionalProperties\":{\"$ref\":\"#/$defs/schema2\"}}]},\"schema0\":{\"type\":\"object\",\"properties\":{\"target\":{\"oneOf\":[{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"const\":\"identity\"},\"subject\":{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"enum\":[\"brand\",\"product\",\"variant\"]},\"id\":{\"type\":\"string\",\"minLength\":1,\"maxLength\":300}},\"required\":[\"kind\",\"id\"],\"additionalProperties\":false}},\"required\":[\"kind\",\"subject\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"const\":\"attribute\"},\"subject\":{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"enum\":[\"brand\",\"product\",\"variant\"]},\"id\":{\"type\":\"string\",\"minLength\":1,\"maxLength\":300}},\"required\":[\"kind\",\"id\"],\"additionalProperties\":false},\"blockId\":{\"type\":\"string\",\"minLength\":1,\"maxLength\":300},\"attributeId\":{\"type\":\"string\",\"minLength\":1,\"maxLength\":300}},\"required\":[\"kind\",\"subject\",\"blockId\",\"attributeId\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"ki",
93
+ "nd\":{\"type\":\"string\",\"const\":\"relationship\"},\"subject\":{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"enum\":[\"brand\",\"product\",\"variant\"]},\"id\":{\"type\":\"string\",\"minLength\":1,\"maxLength\":300}},\"required\":[\"kind\",\"id\"],\"additionalProperties\":false},\"relationship\":{\"type\":\"string\",\"minLength\":1,\"maxLength\":300},\"relatedSubject\":{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"enum\":[\"brand\",\"product\",\"variant\"]},\"id\":{\"type\":\"string\",\"minLength\":1,\"maxLength\":300}},\"required\":[\"kind\",\"id\"],\"additionalProperties\":false}},\"required\":[\"kind\",\"subject\",\"relationship\",\"relatedSubject\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"const\":\"source_interpretation\"},\"sourceMaterialId\":{\"type\":\"string\",\"minLength\":1,\"maxLength\":300},\"subject\":{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"enum\":[\"brand\",\"product\",\"variant\"]},\"id\":{\"type\":\"string\",\"minLength\":1,\"maxLength\":300}},\"required\":[\"kind\",\"id\"],\"additionalProperties\":false}},\"required\":[\"kind\",\"sourceMaterialId\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"const\":\"missing_record\"},\"url\":{\"type\":\"string\",\"maxLength\":2048,\"format\":\"uri\"}},\"required\":[\"kind\",\"url\"],\"additionalProperties\":false}],\"type\":\"object\"},\"pageEvidence\":{\"type\":\"object\",\"properties\":{\"state\":{\"type\":\"string\",\"enum\":[\"known\",\"unknown\",\"conflicting\",\"not_applicable\"]},\"value\":{\"$ref\":\"#/$defs/schema1\"},\"sourceUrl\":{\"type\":\"string\",\"maxLength\":2048,\"format\":\"uri\"},\"note\":{\"type\":\"string\",\"maxLength\":2000}},\"required\":[\"state\"],\"additionalProperties\":false},\"graphEvidence\":{\"type\":\"object\",\"properties\":{\"state\":{\"type\":\"string\",\"enum\":[\"known\",\"unknown\",\"conflicting\",\"not_applicable\"]},\"value\":{\"$ref\":\"#/$defs/schema1\"},\"sourceUrl\":{\"type\":\"string\",\"maxLength\":2048,\"format\":\"uri\"},\"note\":{\"type\":\"string\",\"maxLength\":2000}},\"required\":[\"state\"],\"additionalProperties\":false},\"proposedChange\":{\"$ref\":\"#/$defs/schema2\"},\"rationale\":{\"type\":\"string\",\"minLength\":1,\"maxLength\":2000}},\"required\":[\"target\",\"pageEvidence\",\"graphEvidence\",\"proposedChange\",\"rationale\"],\"additionalProperties\":false}}},\"outputSchema\":{\"$ref\":\"#/$defs/schema0\",\"$defs\":{\"schema1\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"number\"},{\"type\":\"boolean\"},{\"type\":\"null\"},{\"type\":\"array\",\"items\":{\"$ref\":\"#/$defs/schema1\"}},{\"type\":\"object\",\"propertyNames\":{\"type\":\"string\"},\"additionalProperties\":{\"$ref\":\"#/$defs/schema1\"}}]},\"schema2\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"number\"},{\"type\":\"boolean\"},{\"type\":\"null\"},{\"type\":\"array\",\"items\":{\"$ref\":\"#/$defs/schema2\"}},{\"type\":\"object\",\"propertyNames\":{\"type\":\"string\"},\"additionalProperties\":{\"$ref\":\"#/$defs/schema2\"}}]},\"schema0\":{\"type\":\"object\",\"properties\":{\"data\":{\"type\":\"object\",\"properties\":{\"id\":{\"type\":\"string\",\"minLength\":1,\"maxLength\":300},\"submission\":{\"type\":\"object\",\"properties\":{\"target\":{\"oneOf\":[{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"const\":\"identity\"},\"subject\":{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"enum\":[\"brand\",\"product\",\"variant\"]},\"id\":{\"type\":\"string\",\"minLength\":1,\"maxLength\":300}},\"required\":[\"kind\",\"id\"],\"additionalProperties\":false}},\"required\":[\"kind\",\"subject\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"const\":\"attribute\"},\"subject\":{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"enum\":[\"brand\",\"product\",\"variant\"]},\"id\":{\"type\":\"string\",\"minLength\":1,\"maxLength\":300}},\"required\":[\"kind\",\"id\"],\"additionalProperties\":false},\"blockId\":{\"type\":\"string\",\"minLength\":1,\"maxLength\":300},\"attributeId\":{\"type\":\"string\",\"minLength\":1,\"maxLength\":300}},\"required\":[\"kind\",\"subject\",\"blockId\",\"attributeId\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"const\":\"relationship\"},\"subject\":{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"enum\":[\"brand\",\"product\",\"variant\"]},\"id\":{\"type\":\"string\",\"minLength\":1,\"maxLength\":300}},\"required\":[\"kind\",\"id\"],\"additionalProperties\":false},\"relationship\":{\"type\":\"string\",\"minLength\":1,\"maxLength\":300},\"relatedSubject\":{",
94
+ "\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"enum\":[\"brand\",\"product\",\"variant\"]},\"id\":{\"type\":\"string\",\"minLength\":1,\"maxLength\":300}},\"required\":[\"kind\",\"id\"],\"additionalProperties\":false}},\"required\":[\"kind\",\"subject\",\"relationship\",\"relatedSubject\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"const\":\"source_interpretation\"},\"sourceMaterialId\":{\"type\":\"string\",\"minLength\":1,\"maxLength\":300},\"subject\":{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"enum\":[\"brand\",\"product\",\"variant\"]},\"id\":{\"type\":\"string\",\"minLength\":1,\"maxLength\":300}},\"required\":[\"kind\",\"id\"],\"additionalProperties\":false}},\"required\":[\"kind\",\"sourceMaterialId\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"const\":\"missing_record\"},\"url\":{\"type\":\"string\",\"maxLength\":2048,\"format\":\"uri\"}},\"required\":[\"kind\",\"url\"],\"additionalProperties\":false}],\"type\":\"object\"},\"pageEvidence\":{\"type\":\"object\",\"properties\":{\"state\":{\"type\":\"string\",\"enum\":[\"known\",\"unknown\",\"conflicting\",\"not_applicable\"]},\"value\":{\"$ref\":\"#/$defs/schema1\"},\"sourceUrl\":{\"type\":\"string\",\"maxLength\":2048,\"format\":\"uri\"},\"note\":{\"type\":\"string\",\"maxLength\":2000}},\"required\":[\"state\"],\"additionalProperties\":false},\"graphEvidence\":{\"type\":\"object\",\"properties\":{\"state\":{\"type\":\"string\",\"enum\":[\"known\",\"unknown\",\"conflicting\",\"not_applicable\"]},\"value\":{\"$ref\":\"#/$defs/schema1\"},\"sourceUrl\":{\"type\":\"string\",\"maxLength\":2048,\"format\":\"uri\"},\"note\":{\"type\":\"string\",\"maxLength\":2000}},\"required\":[\"state\"],\"additionalProperties\":false},\"proposedChange\":{\"$ref\":\"#/$defs/schema2\"},\"rationale\":{\"type\":\"string\",\"minLength\":1,\"maxLength\":2000}},\"required\":[\"target\",\"pageEvidence\",\"graphEvidence\",\"proposedChange\",\"rationale\"],\"additionalProperties\":false},\"status\":{\"type\":\"string\",\"enum\":[\"submitted\",\"in_review\",\"resolved\",\"withdrawn\"]},\"resolution\":{\"anyOf\":[{\"type\":\"string\",\"enum\":[\"substantiated\",\"not_substantiated\",\"duplicate\",\"superseded\",\"cannot_determine\"]},{\"type\":\"null\"}]},\"createdAt\":{\"type\":\"string\",\"format\":\"date-time\",\"pattern\":\"^(?:(?:\\\\d\\\\d[2468][048]|\\\\d\\\\d[13579][26]|\\\\d\\\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\\\d|30)|(?:02)-(?:0[1-9]|1\\\\d|2[0-8])))T(?:(?:[01]\\\\d|2[0-3]):[0-5]\\\\d:[0-5]\\\\d(?:\\\\.\\\\d+)?(?:Z))$\"},\"updatedAt\":{\"type\":\"string\",\"format\":\"date-time\",\"pattern\":\"^(?:(?:\\\\d\\\\d[2468][048]|\\\\d\\\\d[13579][26]|\\\\d\\\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\\\d|30)|(?:02)-(?:0[1-9]|1\\\\d|2[0-8])))T(?:(?:[01]\\\\d|2[0-3]):[0-5]\\\\d:[0-5]\\\\d(?:\\\\.\\\\d+)?(?:Z))$\"},\"acceptedTruthChanged\":{\"type\":\"boolean\",\"const\":false}},\"required\":[\"id\",\"submission\",\"status\",\"resolution\",\"createdAt\",\"updatedAt\",\"acceptedTruthChanged\"],\"additionalProperties\":false},\"meta\":{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"operationId\":{\"type\":\"string\",\"const\":\"corrections.create\"},\"requestId\":{\"type\":\"string\",\"minLength\":1},\"idempotency\":{\"type\":\"object\",\"properties\":{\"replayed\":{\"type\":\"boolean\"}},\"required\":[\"replayed\"],\"additionalProperties\":false}},\"required\":[\"operationId\",\"requestId\",\"idempotency\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"operationId\":{\"type\":\"string\",\"const\":\"corrections.get\"},\"requestId\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"operationId\",\"requestId\"],\"additionalProperties\":false}]},\"links\":{\"type\":\"object\",\"properties\":{\"self\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"self\"],\"additionalProperties\":false},\"warnings\":{\"maxItems\":0,\"type\":\"array\",\"items\":{\"not\":{}}}},\"required\":[\"data\",\"meta\",\"links\",\"warnings\"],\"additionalProperties\":false}}},\"annotations\":{\"readOnlyHint\":false,\"untrustedContentHint\":true},\"requiresVisibleConfirmation\":true,\"requiredHeaders\":[\"Idempotency-Key\"]}]",
89
95
  ].join("");
90
96
  export const WEBMCP_TOOLS = JSON.parse(json);