@instruments/taxonomy 0.1.0 → 0.3.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.
@@ -0,0 +1,23 @@
1
+ // src/canonicalisation.ts
2
+ var assertionMeaningFrom = (verdict) => {
3
+ const source = {
4
+ crosswalkVersion: verdict.crosswalkVersion,
5
+ sourceField: verdict.sourceField,
6
+ sourceValue: verdict.sourceValue,
7
+ taxonomySnapshotVersion: verdict.taxonomySnapshotVersion
8
+ };
9
+ if (verdict.status === "resolved") {
10
+ return { ...source, canonical: verdict.canonical, kind: "resolved" };
11
+ }
12
+ if (verdict.status === "ambiguous") {
13
+ return { ...source, alternatives: verdict.alternatives, kind: "ambiguous" };
14
+ }
15
+ if (verdict.status === "unresolved") {
16
+ return { ...source, kind: "unresolved" };
17
+ }
18
+ return null;
19
+ };
20
+
21
+ export {
22
+ assertionMeaningFrom
23
+ };
@@ -0,0 +1,103 @@
1
+ // src/taste-assertion.ts
2
+ var TASTE_ASSERTION_CONTRACT_VERSION = "1.0.0";
3
+ var TAXONOMY_SNAPSHOT_VERSION = "2026-08-25";
4
+ var FIXTURE_ACTOR_ID = "person:designer-1";
5
+ var FIXTURE_PROJECT_ID = "project:hotel-1";
6
+ var FIXTURE_SUBJECT_ID = "client:hotel-operator-1";
7
+ var RESOLVED = "resolved";
8
+ var baseFixture = {
9
+ actorId: FIXTURE_ACTOR_ID,
10
+ confirmedAt: "2026-08-25T09:00:10.000Z",
11
+ contractVersion: TASTE_ASSERTION_CONTRACT_VERSION,
12
+ observedAt: "2026-08-25T09:00:00.000Z",
13
+ projectId: FIXTURE_PROJECT_ID,
14
+ provenance: {
15
+ observationId: "observation:1",
16
+ sourceSnapshotVersion: "demo-estate-1",
17
+ sourceSystem: "designshop"
18
+ },
19
+ subjectId: FIXTURE_SUBJECT_ID
20
+ };
21
+ var PORTABLE_ASSERTION_TEST_VECTORS = [
22
+ {
23
+ assertion: {
24
+ ...baseFixture,
25
+ authority: "explicit",
26
+ id: "assertion:taste-plain-porcelain",
27
+ meaning: {
28
+ canonical: {
29
+ id: "bundle:constitutive%3Amaterial.porcelain%7Caccent%3Apattern.plain",
30
+ kind: "all-of",
31
+ members: [
32
+ { assertion: "material.porcelain", role: "constitutive" },
33
+ { assertion: "pattern.plain", role: "accent" }
34
+ ],
35
+ serialization: "constitutive:material.porcelain|accent:pattern.plain"
36
+ },
37
+ crosswalkVersion: "designshop-2026-08-25.1",
38
+ kind: RESOLVED,
39
+ sourceField: "confirmed_correction",
40
+ sourceValue: "porcelain stayed; pattern did not",
41
+ taxonomySnapshotVersion: TAXONOMY_SNAPSHOT_VERSION
42
+ },
43
+ polarity: "positive",
44
+ role: "taste",
45
+ scope: { kind: "room", roomId: "room:kitchen" }
46
+ },
47
+ expected: {
48
+ actorId: FIXTURE_ACTOR_ID,
49
+ meaningKind: RESOLVED,
50
+ projectId: FIXTURE_PROJECT_ID,
51
+ role: "taste",
52
+ subjectId: FIXTURE_SUBJECT_ID
53
+ },
54
+ id: "client-taste-does-not-belong-to-the-designer"
55
+ },
56
+ {
57
+ assertion: {
58
+ ...baseFixture,
59
+ authority: "explicit",
60
+ constraint: { operator: "at_least", unit: "PTV", value: 36 },
61
+ id: "assertion:requirement-slip",
62
+ meaning: {
63
+ canonical: {
64
+ id: "bundle:constitutive%3Arequirement.slip-resistance",
65
+ kind: "all-of",
66
+ members: [
67
+ {
68
+ assertion: "requirement.slip-resistance",
69
+ role: "constitutive"
70
+ }
71
+ ],
72
+ serialization: "constitutive:requirement.slip-resistance"
73
+ },
74
+ crosswalkVersion: "designshop-2026-08-25.1",
75
+ kind: RESOLVED,
76
+ sourceField: "requirement",
77
+ sourceValue: "Slip resistance",
78
+ taxonomySnapshotVersion: TAXONOMY_SNAPSHOT_VERSION
79
+ },
80
+ polarity: "positive",
81
+ role: "requirement",
82
+ scope: {
83
+ kind: "surface",
84
+ roomId: "room:lobby",
85
+ surfaceId: "surface:floor"
86
+ }
87
+ },
88
+ expected: {
89
+ actorId: FIXTURE_ACTOR_ID,
90
+ meaningKind: RESOLVED,
91
+ projectId: FIXTURE_PROJECT_ID,
92
+ role: "requirement",
93
+ subjectId: FIXTURE_SUBJECT_ID
94
+ },
95
+ id: "commercial-requirement-uses-the-same-envelope"
96
+ }
97
+ ];
98
+
99
+ export {
100
+ TASTE_ASSERTION_CONTRACT_VERSION,
101
+ TAXONOMY_SNAPSHOT_VERSION,
102
+ PORTABLE_ASSERTION_TEST_VECTORS
103
+ };
@@ -0,0 +1,42 @@
1
+ // src/crosswalks/mapping.ts
2
+ var resolved = (...assertions) => ({
3
+ assertions: assertions.map((assertion) => ({
4
+ assertion,
5
+ role: "constitutive"
6
+ })),
7
+ match: "exact",
8
+ status: "resolved"
9
+ });
10
+ var constitutive = (assertion) => ({
11
+ assertions: [{ assertion, role: "constitutive" }],
12
+ match: "exact",
13
+ status: "resolved"
14
+ });
15
+ var approximates = (match, note, ...assertions) => ({
16
+ assertions: assertions.map((assertion) => ({
17
+ assertion,
18
+ role: "constitutive"
19
+ })),
20
+ match,
21
+ note,
22
+ status: "resolved"
23
+ });
24
+ var ambiguous = (match, ...assertions) => ({
25
+ alternatives: assertions.map((assertion) => [
26
+ { assertion, role: "constitutive" }
27
+ ]),
28
+ match,
29
+ status: "ambiguous"
30
+ });
31
+ var declined = (reason) => ({
32
+ reason,
33
+ status: "declined"
34
+ });
35
+
36
+ export {
37
+ resolved,
38
+ constitutive,
39
+ approximates,
40
+ ambiguous,
41
+ declined
42
+ };
@@ -0,0 +1,33 @@
1
+ import { CanonicalisationInput, CanonicalisationVerdict } from '../taste.js';
2
+ import { C as CrosswalkMapping } from '../mapping-pvHk5VjC.js';
3
+ import '../match-CWzTEuL-.js';
4
+
5
+ declare const DESIGNSHOP_CROSSWALK_VERSION: "designshop-2026-08-25.1";
6
+ declare const DESIGNSHOP_SOURCE_SYSTEM: "designshop";
7
+ declare const DESIGNSHOP_SOURCE_FIELDS: readonly ["product_category", "material", "pattern", "finish", "format", "construction", "scheme_style", "product_style", "mood", "space", "requirement"];
8
+ type DesignShopSourceField = (typeof DESIGNSHOP_SOURCE_FIELDS)[number];
9
+ type Mapping = CrosswalkMapping;
10
+ /**
11
+ * Source-specific rows. Source fields are deliberately retained: DesignShop's `material:Mosaic`
12
+ * resolves to a format, and pretending the source field was already canonical would preserve its
13
+ * category error instead of its evidence.
14
+ */
15
+ declare const DESIGNSHOP_CROSSWALK: Readonly<Record<DesignShopSourceField, Readonly<Record<string, Mapping>>>>;
16
+ interface DesignShopCanonicalisationInput extends Omit<CanonicalisationInput, "sourceSystem" | "sourceField"> {
17
+ sourceField: DesignShopSourceField;
18
+ }
19
+ /** Unknown values are unresolved, never declined. Declines require an explicit curated row. */
20
+ declare const canonicaliseDesignShop: (input: DesignShopCanonicalisationInput) => CanonicalisationVerdict;
21
+ interface DesignShopAppearanceTestVector {
22
+ expectedAssertions: readonly string[];
23
+ facets: readonly {
24
+ sourceField: DesignShopSourceField;
25
+ sourceValue: string;
26
+ }[];
27
+ id: string;
28
+ market: "residential" | "commercial";
29
+ }
30
+ /** Shared fixtures prove axes compose instead of welding material identity to appearance. */
31
+ declare const DESIGNSHOP_APPEARANCE_TEST_VECTORS: readonly DesignShopAppearanceTestVector[];
32
+
33
+ export { DESIGNSHOP_APPEARANCE_TEST_VECTORS, DESIGNSHOP_CROSSWALK, DESIGNSHOP_CROSSWALK_VERSION, DESIGNSHOP_SOURCE_FIELDS, DESIGNSHOP_SOURCE_SYSTEM, type DesignShopAppearanceTestVector, type DesignShopCanonicalisationInput, type DesignShopSourceField, canonicaliseDesignShop };
@@ -0,0 +1,363 @@
1
+ import {
2
+ ambiguous,
3
+ approximates,
4
+ constitutive,
5
+ declined,
6
+ resolved
7
+ } from "../chunk-WIRA24HM.js";
8
+ import {
9
+ TAXONOMY_SNAPSHOT_VERSION
10
+ } from "../chunk-FCMCKNQF.js";
11
+ import {
12
+ defineAlternatives,
13
+ defineBundle
14
+ } from "../chunk-4ECRPD2I.js";
15
+
16
+ // src/crosswalks/designshop.ts
17
+ var DESIGNSHOP_CROSSWALK_VERSION = "designshop-2026-08-25.1";
18
+ var DESIGNSHOP_SOURCE_SYSTEM = "designshop";
19
+ var DESIGNSHOP_SOURCE_FIELDS = [
20
+ "product_category",
21
+ "material",
22
+ "pattern",
23
+ "finish",
24
+ "format",
25
+ "construction",
26
+ "scheme_style",
27
+ "product_style",
28
+ "mood",
29
+ "space",
30
+ "requirement"
31
+ ];
32
+ var DESIGNSHOP_CROSSWALK = {
33
+ /* oxlint-disable sonarjs/no-duplicate-string -- canonical ids repeat across deliberately
34
+ independent source rows; hiding them behind aliases would make the crosswalk harder to audit. */
35
+ construction: {
36
+ Encaustic: constitutive("construction.encaustic"),
37
+ "Engineered Hardwood": constitutive("construction.engineered-hardwood"),
38
+ Handcrafted: constitutive("construction.handcrafted"),
39
+ Handmade: constitutive("construction.handmade"),
40
+ Handwoven: constitutive("construction.handwoven"),
41
+ "High Pressure Laminate": constitutive(
42
+ "construction.high-pressure-laminate"
43
+ ),
44
+ Laminated: constitutive("construction.laminated"),
45
+ "Loop Pile": constitutive("construction.loop-pile"),
46
+ "Luxury Vinyl": constitutive("construction.luxury-vinyl"),
47
+ "Non-Woven": constitutive("construction.non-woven"),
48
+ "Pre-Cast / Molded": constitutive("construction.precast-moulded"),
49
+ Printed: constitutive("construction.printed"),
50
+ "Red Clay Body": constitutive("construction.red-clay-body"),
51
+ "Rigid Core Luxury Vinyl": constitutive(
52
+ "construction.rigid-core-luxury-vinyl"
53
+ ),
54
+ Sintered: constitutive("construction.sintered"),
55
+ "Through Body": constitutive("construction.through-body"),
56
+ Woven: constitutive("construction.woven")
57
+ },
58
+ finish: {
59
+ Glazed: constitutive("finish.glazed"),
60
+ // `finish.gloss` became the top rung of the sheen ladder when FINISH split into
61
+ // treatment/sheen/texture. In trade use "Gloss" IS the top rung — matte, eggshell,
62
+ // satin, semi-gloss, gloss — so this resolves rather than approximates.
63
+ Gloss: constitutive("finish.high-gloss"),
64
+ Honed: constitutive("finish.honed"),
65
+ Matte: constitutive("finish.matte"),
66
+ Natural: constitutive("finish.natural"),
67
+ Polished: constitutive("finish.polished")
68
+ },
69
+ format: {
70
+ "Area Rug": constitutive("format.area-rug"),
71
+ Hide: constitutive("format.hide"),
72
+ Liquid: constitutive("format.liquid"),
73
+ Modular: constitutive("format.modular"),
74
+ Mural: constitutive("format.mural"),
75
+ Panel: constitutive("format.panel"),
76
+ Plank: constitutive("format.plank"),
77
+ Roll: constitutive("format.roll"),
78
+ Slab: constitutive("format.slab"),
79
+ Tile: constitutive("format.tile"),
80
+ "Wide Plank": constitutive("format.wide-plank")
81
+ },
82
+ material: {
83
+ "Carpet Tiles": resolved(
84
+ "product-category.carpet-and-rug",
85
+ "format.modular"
86
+ ),
87
+ Ceramic: constitutive("material.ceramic"),
88
+ "Engineered & Composite": approximates(
89
+ "close",
90
+ "A two-word source value onto one canonical term. `material.composite` defines itself as an engineered material of two or more bound constituents, so it covers both halves - but the source names them separately and the canon does not, which is a substitution that holds in most contexts and not all.",
91
+ "material.composite"
92
+ ),
93
+ Interior: declined(
94
+ "A catalogue location masquerading as a material value."
95
+ ),
96
+ Laminate: constitutive("material.laminate"),
97
+ Mosaic: approximates(
98
+ "broad",
99
+ "Every mosaic is supplied as repeatable units; most modular things are not mosaics. The canon is broader than the source, and the tesserae themselves have no canonical term.",
100
+ "format.modular"
101
+ ),
102
+ Natural: approximates(
103
+ "narrow",
104
+ "The source says natural and the canon answers natural FIBRE. Stone and clay are natural materials this row cannot reach, so the canonical term is narrower than the source value.",
105
+ "material.natural-fibre"
106
+ ),
107
+ Paper: constitutive("material.paper"),
108
+ Porcelain: constitutive("material.porcelain"),
109
+ "Solid Surface": constitutive("material.solid-surface"),
110
+ Stone: constitutive("material.stone"),
111
+ Vinyl: constitutive("material.vinyl")
112
+ },
113
+ mood: {
114
+ Airy: constitutive("mood.airy"),
115
+ Calm: constitutive("mood.calm"),
116
+ Dramatic: constitutive("mood.dramatic"),
117
+ Sheltering: constitutive("mood.sheltering")
118
+ },
119
+ pattern: {
120
+ "Abstract / Organic": constitutive("pattern.abstract-organic"),
121
+ "Animal Print": constitutive("pattern.animal-print"),
122
+ Botanical: constitutive("pattern.botanical"),
123
+ "Brocade / Damask / Moire": approximates(
124
+ "narrow",
125
+ "Three named motifs onto one. Moire is a watered interference figure and not a damask at all; the canon carries one member of the source's set.",
126
+ "pattern.damask"
127
+ ),
128
+ "Concrete Effect": constitutive("pattern.concrete-effect"),
129
+ "Cultural Heritage": constitutive("pattern.cultural-heritage"),
130
+ Geometric: constitutive("pattern.geometric"),
131
+ "Herringbone / Chevron": constitutive("pattern.herringbone-chevron"),
132
+ "Leather Effect": constitutive("pattern.leather-effect"),
133
+ Marbleized: constitutive("pattern.marbleised"),
134
+ "Metal Effect": constitutive("pattern.metal-effect"),
135
+ Natural: approximates(
136
+ "close",
137
+ "`natural_motif` is the canon reading of a bare `Natural` in a pattern vocabulary, and the two substitute in most retrieval - but the source word is broad enough to have meant an undecorated natural material, which is `pattern.plain` plus a material claim.",
138
+ "pattern.natural-motif"
139
+ ),
140
+ Novelty: constitutive("pattern.novelty"),
141
+ "Plaid / Check / Houndstooth": approximates(
142
+ "narrow",
143
+ "Houndstooth is a broken twill figure, not the orthogonal crossing bands `plaid_check` defines. Two of the source's three members land; the third does not.",
144
+ "pattern.plaid-check"
145
+ ),
146
+ Scenic: constitutive("pattern.scenic"),
147
+ Solid: constitutive("pattern.plain"),
148
+ "Stone Effect": constitutive("pattern.stone-effect"),
149
+ "Strie / Ombre / Gradient": approximates(
150
+ "narrow",
151
+ "Strie is fine irregular striation - a stripe reading, not a transition - so the canon carries the ombre and gradient members of this set and not the first.",
152
+ "pattern.gradient"
153
+ ),
154
+ Stripe: constitutive("pattern.stripe"),
155
+ Texture: constitutive("pattern.texture"),
156
+ Toile: constitutive("pattern.toile"),
157
+ "Whimsy & Character": constitutive("pattern.character"),
158
+ "Woodgrain Effect": constitutive("pattern.woodgrain-effect"),
159
+ "Woven Effect": constitutive("pattern.woven-effect")
160
+ },
161
+ product_category: {
162
+ "Carpets & Rugs": constitutive("product-category.carpet-and-rug"),
163
+ Countertops: constitutive("product-category.countertop"),
164
+ Flooring: constitutive("product-category.flooring"),
165
+ Tile: constitutive("product-category.tile"),
166
+ Wallcovering: constitutive("product-category.wallcovering")
167
+ },
168
+ product_style: {
169
+ "Art Deco": constitutive("style.art-deco"),
170
+ "Classic/ Traditional": approximates(
171
+ "close",
172
+ "Classic and Traditional are one prototype category in trade use and two words in the source. `style.traditional` substitutes for both without carrying the pair.",
173
+ "style.traditional"
174
+ ),
175
+ Coastal: constitutive("evokes(place.landscape.coastal)"),
176
+ Contemporary: constitutive("style.contemporary"),
177
+ "Country/ Cottage": approximates(
178
+ "close",
179
+ "A compound reading: the cottage reference plus the traditional idiom. It carries most of what the source meant and not the rural setting, which is a place claim the source implies and never states.",
180
+ "evokes(typology.cottage)",
181
+ "style.traditional"
182
+ ),
183
+ Eclectic: constitutive("style.eclectic"),
184
+ Farmhouse: approximates(
185
+ "close",
186
+ "The farmhouse reference plus the rustic idiom. Together they read as the source does in most retrieval; neither half nor the pair carries the agricultural building itself.",
187
+ "evokes(typology.farmhouse)",
188
+ "style.rustic"
189
+ ),
190
+ Industrial: constitutive("style.industrial"),
191
+ "Mid-Century Modern": constitutive("style.mid-century-modern"),
192
+ Minimalist: constitutive("style.minimalism"),
193
+ Modern: ambiguous("close", "style.modernism", "style.contemporary"),
194
+ Rustic: constitutive("style.rustic"),
195
+ Scandinavian: approximates(
196
+ "broad",
197
+ "Nordic includes Finland and Iceland; Scandinavian does not. The canon covers more than the source claimed, and the estate carries no narrower region term.",
198
+ "evokes(place.region.nordic)"
199
+ ),
200
+ Transitional: constitutive("style.transitional")
201
+ },
202
+ requirement: {
203
+ "Acoustic performance": constitutive("requirement.acoustic-performance"),
204
+ Availability: constitutive("requirement.availability"),
205
+ Budget: constitutive("requirement.budget"),
206
+ "Fire performance": constitutive("requirement.fire-performance"),
207
+ "Indoor air quality": constitutive("requirement.indoor-air-quality"),
208
+ "Installation method": constitutive("requirement.installation-method"),
209
+ "Lead time": constitutive("requirement.lead-time"),
210
+ Maintenance: constitutive("requirement.maintenance"),
211
+ "Slip resistance": constitutive("requirement.slip-resistance"),
212
+ "Wet-area suitability": constitutive("requirement.wet-area-suitability")
213
+ },
214
+ scheme_style: {
215
+ Coastal: constitutive("evokes(place.landscape.coastal)"),
216
+ Contemporary: constitutive("style.contemporary"),
217
+ Country: ambiguous(
218
+ "close",
219
+ "evokes(place.region.english-country)",
220
+ "evokes(typology.farmhouse)"
221
+ ),
222
+ Eclectic: constitutive("style.eclectic"),
223
+ Farmhouse: approximates(
224
+ "close",
225
+ "The farmhouse reference plus the rustic idiom. Together they read as the source does in most retrieval; neither half nor the pair carries the agricultural building itself.",
226
+ "evokes(typology.farmhouse)",
227
+ "style.rustic"
228
+ ),
229
+ Minimalist: constitutive("style.minimalism"),
230
+ Modern: ambiguous("close", "style.modernism", "style.contemporary"),
231
+ Traditional: constitutive("style.traditional"),
232
+ Transitional: constitutive("style.transitional")
233
+ },
234
+ space: {
235
+ Bathroom: constitutive("room.bathroom"),
236
+ Kitchen: constitutive("room.kitchen"),
237
+ Lobby: constitutive("space.lobby"),
238
+ Restaurant: constitutive("room.restaurant")
239
+ }
240
+ /* oxlint-enable sonarjs/no-duplicate-string */
241
+ };
242
+ var canonicaliseDesignShop = (input) => {
243
+ const base = {
244
+ crosswalkVersion: DESIGNSHOP_CROSSWALK_VERSION,
245
+ provenance: input.provenance,
246
+ sourceField: input.sourceField,
247
+ sourceSystem: DESIGNSHOP_SOURCE_SYSTEM,
248
+ sourceValue: input.sourceValue,
249
+ taxonomySnapshotVersion: TAXONOMY_SNAPSHOT_VERSION
250
+ };
251
+ const mapping = DESIGNSHOP_CROSSWALK[input.sourceField][input.sourceValue];
252
+ if (mapping === void 0) {
253
+ return { ...base, status: "unresolved" };
254
+ }
255
+ if (mapping.status === "declined") {
256
+ return { ...base, reason: mapping.reason, status: "declined" };
257
+ }
258
+ if (mapping.status === "ambiguous") {
259
+ return {
260
+ ...base,
261
+ alternatives: defineAlternatives(
262
+ mapping.alternatives,
263
+ "source-ambiguous"
264
+ ),
265
+ match: mapping.match,
266
+ status: "ambiguous",
267
+ ...mapping.note === void 0 ? {} : { note: mapping.note }
268
+ };
269
+ }
270
+ return {
271
+ ...base,
272
+ canonical: defineBundle(mapping.assertions),
273
+ match: mapping.match,
274
+ status: "resolved",
275
+ ...mapping.note === void 0 ? {} : { note: mapping.note }
276
+ };
277
+ };
278
+ var DESIGNSHOP_APPEARANCE_TEST_VECTORS = [
279
+ {
280
+ expectedAssertions: [
281
+ "material.porcelain",
282
+ "pattern.plain",
283
+ "product-category.tile"
284
+ ],
285
+ facets: [
286
+ { sourceField: "product_category", sourceValue: "Tile" },
287
+ { sourceField: "material", sourceValue: "Porcelain" },
288
+ { sourceField: "pattern", sourceValue: "Solid" }
289
+ ],
290
+ id: "residential-plain-porcelain",
291
+ market: "residential"
292
+ },
293
+ {
294
+ expectedAssertions: [
295
+ "material.porcelain",
296
+ "pattern.geometric",
297
+ "product-category.tile"
298
+ ],
299
+ facets: [
300
+ { sourceField: "product_category", sourceValue: "Tile" },
301
+ { sourceField: "material", sourceValue: "Porcelain" },
302
+ { sourceField: "pattern", sourceValue: "Geometric" }
303
+ ],
304
+ id: "residential-patterned-porcelain",
305
+ market: "residential"
306
+ },
307
+ {
308
+ expectedAssertions: [
309
+ "material.porcelain",
310
+ "pattern.stone-effect",
311
+ "product-category.tile"
312
+ ],
313
+ facets: [
314
+ { sourceField: "product_category", sourceValue: "Tile" },
315
+ { sourceField: "material", sourceValue: "Porcelain" },
316
+ { sourceField: "pattern", sourceValue: "Stone Effect" }
317
+ ],
318
+ id: "residential-stone-effect-porcelain",
319
+ market: "residential"
320
+ },
321
+ {
322
+ expectedAssertions: [
323
+ "construction.loop-pile",
324
+ "format.modular",
325
+ "pattern.geometric",
326
+ "product-category.carpet-and-rug"
327
+ ],
328
+ facets: [
329
+ { sourceField: "product_category", sourceValue: "Carpets & Rugs" },
330
+ { sourceField: "format", sourceValue: "Modular" },
331
+ { sourceField: "construction", sourceValue: "Loop Pile" },
332
+ { sourceField: "pattern", sourceValue: "Geometric" }
333
+ ],
334
+ id: "commercial-patterned-carpet",
335
+ market: "commercial"
336
+ },
337
+ {
338
+ expectedAssertions: [
339
+ "construction.non-woven",
340
+ "format.roll",
341
+ "material.paper",
342
+ "pattern.botanical",
343
+ "product-category.wallcovering"
344
+ ],
345
+ facets: [
346
+ { sourceField: "product_category", sourceValue: "Wallcovering" },
347
+ { sourceField: "material", sourceValue: "Paper" },
348
+ { sourceField: "format", sourceValue: "Roll" },
349
+ { sourceField: "construction", sourceValue: "Non-Woven" },
350
+ { sourceField: "pattern", sourceValue: "Botanical" }
351
+ ],
352
+ id: "residential-patterned-wallcovering",
353
+ market: "residential"
354
+ }
355
+ ];
356
+ export {
357
+ DESIGNSHOP_APPEARANCE_TEST_VECTORS,
358
+ DESIGNSHOP_CROSSWALK,
359
+ DESIGNSHOP_CROSSWALK_VERSION,
360
+ DESIGNSHOP_SOURCE_FIELDS,
361
+ DESIGNSHOP_SOURCE_SYSTEM,
362
+ canonicaliseDesignShop
363
+ };
@@ -0,0 +1,41 @@
1
+ import { CanonicalisationInput, CanonicalisationVerdict } from '../taste.js';
2
+ import { B as BundleRole } from '../match-CWzTEuL-.js';
3
+ import { C as CrosswalkMapping } from '../mapping-pvHk5VjC.js';
4
+
5
+ declare const MATERIALGRAPH_CROSSWALK_VERSION: "materialgraph-2026-08-27.1";
6
+ declare const MATERIALGRAPH_SOURCE_SYSTEM: "materialgraph";
7
+ declare const MATERIALGRAPH_SNAPSHOT: "2026-08-27";
8
+ /**
9
+ * The two dictionaries this crosswalk covers. Both were `ms_`-prefixed until MaterialGraph renamed
10
+ * them on 2026-08-27; the bare names are the current ones.
11
+ */
12
+ declare const MATERIALGRAPH_SOURCE_FIELDS: readonly ["role", "character"];
13
+ type MaterialGraphSourceField = (typeof MATERIALGRAPH_SOURCE_FIELDS)[number];
14
+ /**
15
+ * Two `role` values that are not product properties at all, and where they actually go.
16
+ *
17
+ * A role IN A SCHEME is what `match.ts` already calls a `BundleRole`: `accent` means present and
18
+ * deliberate but the scheme survives without it, `constitutive` means remove it and the thing stops
19
+ * being what it is. A feature surface is the second by definition. Publishing these as a mapping to
20
+ * the bundle role rather than as terms is what stops the same slab being permanently labelled a
21
+ * feature surface because one scheme once used it as one.
22
+ */
23
+ declare const MATERIALGRAPH_COMPOSITIONAL_ROLES: Readonly<Record<string, BundleRole>>;
24
+ declare const MATERIALGRAPH_CROSSWALK: Readonly<Record<MaterialGraphSourceField, Readonly<Record<string, CrosswalkMapping>>>>;
25
+ interface MaterialGraphCanonicalisationInput extends Omit<CanonicalisationInput, "sourceSystem" | "sourceField"> {
26
+ sourceField: MaterialGraphSourceField;
27
+ }
28
+ /** Unknown values are unresolved, never declined. Declines require an explicit curated row. */
29
+ declare const canonicaliseMaterialGraph: (input: MaterialGraphCanonicalisationInput) => CanonicalisationVerdict;
30
+ /**
31
+ * The upstream dictionaries, vendored as id tuples so this file can be checked against them.
32
+ *
33
+ * Verbatim from materialgraph packages/schema/src/registry/value-dictionaries — `role` in
34
+ * `material-semantics-core.ts`, `character` in `material-semantics-aesthetics.ts`, both at the
35
+ * 2026-08-27 snapshot, after the rename that dropped their `ms_` prefixes.
36
+ */
37
+ declare const MATERIALGRAPH_ROLE_VALUES: readonly ["wall_finish", "flooring", "ceiling", "joinery", "upholstery", "drapery", "accent", "worksurface", "casegoods", "feature_surface", "trim", "other"];
38
+ declare const MATERIALGRAPH_CHARACTER_VALUES: readonly ["quiet", "expressive", "restrained", "bold", "rustic", "crafted", "refined", "formal", "casual", "natural_reading", "synthetic_reading", "soft_reading", "hard_reading"];
39
+ declare const MATERIALGRAPH_SOURCE_VALUES: Readonly<Record<MaterialGraphSourceField, readonly string[]>>;
40
+
41
+ export { MATERIALGRAPH_CHARACTER_VALUES, MATERIALGRAPH_COMPOSITIONAL_ROLES, MATERIALGRAPH_CROSSWALK, MATERIALGRAPH_CROSSWALK_VERSION, MATERIALGRAPH_ROLE_VALUES, MATERIALGRAPH_SNAPSHOT, MATERIALGRAPH_SOURCE_FIELDS, MATERIALGRAPH_SOURCE_SYSTEM, MATERIALGRAPH_SOURCE_VALUES, type MaterialGraphCanonicalisationInput, type MaterialGraphSourceField, canonicaliseMaterialGraph };