@instruments/taxonomy 0.3.0 → 1.1.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.
package/dist/index.d.ts CHANGED
@@ -1,300 +1,342 @@
1
- import { R as RelationPredicate, T as Term, M as MatchStrength } from './match-CWzTEuL-.js';
2
- export { A as AlternativeReason, b as AlternativesMatch, c as BundleMatch, d as BundleMember, a as BundleMemberInput, B as BundleRole, e as BundleSearch, f as CandidateAnnotations, C as CanonicalAlternatives, g as CanonicalBundle, D as DEFAULT_BUNDLE_ROLE, E as ExternalMapping, h as ExternalScheme, i as MATCH_TEST_VECTORS, j as MATCH_WEIGHTS, k as MatchTestCandidate, l as MatchTestVector, m as MatchTier, P as PeriodSpan, n as RankedCandidate, o as RelationRole, S as SearchOptions, p as SearchedWithout, q as SourceBasis, r as StyleKind, s as TermProvenance, t as TermRelation, u as TermStatus, v as TermUsage, U as UsageBand, w as UsageWithheld, x as defineAlternatives, y as defineBundle, z as matchAlternatives, F as matchBundle, G as searchBundle } from './match-CWzTEuL-.js';
3
- export { C as CrosswalkMapping, a as ambiguousMapping, b as approximateMapping, c as constitutiveMapping, d as declinedMapping, r as resolvedMapping } from './mapping-pvHk5VjC.js';
4
- export { AmbiguousAssertionMeaning, AmbiguousCanonicalisation, AssertionAuthority, AssertionMeaning, AssertionPolarity, AssertionProvenance, AssertionRole, AssertionScope, CanonicalisationInput, CanonicalisationVerdict, DeclinedCanonicalisation, PORTABLE_ASSERTION_TEST_VECTORS, PortableAssertionTestVector, PortableTasteAssertion, ProjectContextAssertion, RequirementAssertion, RequirementConstraint, RequirementOperator, ResolvedAssertionMeaning, ResolvedCanonicalisation, TASTE_ASSERTION_CONTRACT_VERSION, TAXONOMY_SNAPSHOT_VERSION, TasteAssertion, UnresolvedAssertionMeaning, UnresolvedCanonicalisation, assertionMeaningFrom } from './taste.js';
5
-
6
- /** The relation predicates, at runtime. TypeScript cannot enumerate a union, so this is by hand. */
7
- declare const RELATION_PREDICATES: readonly ["blend-of", "evokes", "influenced-by", "part-of", "reads-as", "related-to", "serves", "suitable-for", "typically-in"];
8
- /** Where a canonical term lives, and what it is called in the one spelling that counts. */
9
- interface TermLocation {
10
- axisId: string;
11
- facetId: string;
12
- termId: string;
13
- /** `<axis>.<term>`, or `<axis>.<facet>.<term>` on a faceted axis. ADR 0001's target form. */
14
- qualifiedId: string;
15
- term: Term;
16
- }
17
- /** A parsed assertion: an optional relation predicate wrapping a term reference. */
18
- interface ParsedAssertion {
19
- predicate?: RelationPredicate;
20
- /** The term reference, verbatim and unfolded. */
21
- target: string;
22
- }
23
- interface ResolvedAssertion extends ParsedAssertion {
24
- location: TermLocation;
25
- /** The spelling the matcher compares on: `predicate(qualifiedId)` or a bare `qualifiedId`. */
26
- canonical: string;
27
- /** True when the named term is deprecated. Resolves anyway; a published id must keep resolving. */
28
- deprecated: boolean;
29
- /** The term that replaced it, when deprecated. */
30
- supersededBy?: string;
31
- }
1
+ /** An external vocabulary this package crosswalks out to. */
2
+ type ExternalScheme =
3
+ /** Getty Art & Architecture Thesaurus. ODC-By 1.0 - redistributable with attribution. */
4
+ "aat"
5
+ /** Uniclass 2015 (NBS). CC BY-ND 4.0 - referenced by code, never adapted. */
6
+ | "uniclass"
7
+ /** Wikidata Q-items. CC0. */
8
+ | "wikidata";
32
9
  /**
33
- * Split `evokes(place.region.nordic)` into its predicate and target. A bare term reference parses
34
- * to a target and no predicate. An unrecognised predicate does NOT parse: `foo(bar)` is not an
35
- * assertion with an exotic predicate, it is a string nobody has defined.
10
+ * SKOS mapping strength, using SKOS's own vocabulary so the published RDF is a direct rendering
11
+ * rather than a translation. `exact` asserts the two terms are interchangeable in retrieval;
12
+ * `close` asserts they are near enough to substitute in most contexts but not all; `broad` and
13
+ * `narrow` assert containment in the named direction; `related` asserts association only.
36
14
  */
37
- declare const parseAssertion: (assertion: string) => ParsedAssertion | undefined;
38
- /** Every canonical term, with the qualified id that names it. */
39
- declare const TERM_LOCATIONS: readonly TermLocation[];
40
- /** Every accepted spelling of every term, folded. Exported so a consumer can size the vocabulary. */
41
- declare const ASSERTION_KEYS: ReadonlySet<string>;
42
- /** Resolve any published spelling of a term to the one location it names. */
43
- declare const resolveTerm: (reference: string) => TermLocation | undefined;
15
+ type MatchStrength = "exact" | "close" | "broad" | "narrow" | "related";
16
+ interface ExternalMapping {
17
+ scheme: ExternalScheme;
18
+ /** The external identifier, verbatim: an AAT subject id, a Uniclass code, a Wikidata Q-number. */
19
+ identifier: string;
20
+ /** The external vocabulary's own label for that identifier, verbatim and unedited. */
21
+ label: string;
22
+ match: MatchStrength;
23
+ }
44
24
  /**
45
- * Resolve a whole assertion, predicate included. Returns undefined when the predicate is not one
46
- * `term.ts` defines, or when the target names no canonical term.
25
+ * Where a term's content came from, and when. Ranks the SOURCE, never the accuracy: a term lifted
26
+ * verbatim from an upstream dictionary is `vendored` whether or not the upstream got it right.
27
+ * Using this field as a proxy for correctness is how confident wrong answers get made.
47
28
  */
48
- declare const resolveAssertion: (assertion: string) => ResolvedAssertion | undefined;
29
+ type SourceBasis =
30
+ /** Copied unedited from a named upstream dictionary at a stated snapshot. */
31
+ "vendored"
32
+ /** Written here, from scratch, by a named editor. */
33
+ | "authored"
34
+ /** Written here, but derived from measured usage in a named corpus. */
35
+ | "evidenced"
36
+ /** Written here from one or more named authoritative references, without adopting their term. */
37
+ | "referenced"
38
+ /** Adopted from a published external standard, with its identifier carried. */
39
+ | "standard";
40
+ interface TermProvenance {
41
+ basis: SourceBasis;
42
+ /** The upstream file, corpus or standard. Free text, but always specific enough to re-read. */
43
+ source: string;
44
+ /** ISO date the source was read. Absent only for `authored`. */
45
+ snapshot?: string;
46
+ }
49
47
  /**
50
- * The spelling the matcher compares on.
48
+ * A DATED NEGATIVE RESULT: a scheme that was searched for this term and did not have it.
51
49
  *
52
- * An assertion this package cannot resolve folds to ITSELF rather than to nothing. That is the
53
- * difference between a matcher and a gate: a consumer's private tag must still match its own
54
- * counterpart on the other side, and only the canon's own artefacts are held to resolving.
55
- */
56
- declare const canonicalAssertion: (assertion: string) => string;
57
- /** True when this package can resolve the assertion to a canonical term. */
58
- declare const isCanonicalAssertion: (assertion: string) => boolean;
59
- /** The assertions this package cannot resolve, in input order. The failure message IS the work item. */
60
- declare const unknownAssertions: (assertions: Iterable<string>) => readonly string[];
61
- /** Assertions naming a term that still resolves but has been superseded, with its successor. */
62
- declare const deprecatedAssertions: (assertions: Iterable<string>) => readonly {
63
- assertion: string;
64
- supersededBy: string;
65
- }[];
66
-
67
- /**
68
- * The SKOS property each strength renders to.
50
+ * This is the field that makes the gap publishable. ADR 0001 section 8 softens the planned
51
+ * invariant "every STYLE and PERIOD term carries an AAT mapping" to "carries a mapping OR carries
52
+ * one of these", because AAT has no consumer interiors coverage at all - a live search for
53
+ * *japandi* returns nothing, and *farmhouse*, *transitional* and *coastal* are absent as interior
54
+ * idioms. Without this field an unmapped term is indistinguishable from an unchecked one, and the
55
+ * two are opposites: one is evidence and the other is a to-do.
69
56
  *
70
- * `skos:relatedMatch`, not `skos:related`: the target sits in another scheme, and SKOS reserves the
71
- * unqualified property for associations inside one scheme.
57
+ * A dated negative against the best art-and-architecture thesaurus in the world is the clearest
58
+ * available statement of why this vocabulary needs to exist.
72
59
  */
73
- declare const SKOS_MATCH_PREDICATE: Readonly<Record<MatchStrength, string>>;
74
- /** The strengths, at runtime. TypeScript cannot enumerate a union, so this is by hand. */
75
- declare const MATCH_STRENGTHS: readonly ["broad", "close", "exact", "narrow", "related"];
60
+ interface SearchedWithout {
61
+ scheme: ExternalScheme;
62
+ /** ISO date the search was run. */
63
+ date: string;
64
+ /** What was searched, so a reader can repeat it. */
65
+ query: string;
66
+ /** Where the search was run, e.g. the SPARQL endpoint. */
67
+ via: string;
68
+ }
76
69
  /**
77
- * True when the row asserts the two concepts are interchangeable. The only strength a consumer may
78
- * treat as an identity; everything else is an approximation it has been told about.
70
+ * A band, never a count.
71
+ *
72
+ * THE SCALE, stated once so every axis uses the same one. Measured against the corpus named on the
73
+ * term: `dominant` is the top decile of values by frequency, `common` the next, `occasional` a
74
+ * value that appears regularly, `rare` one that barely does, and `unattested` a value the corpus
75
+ * never uses. Set `usage` only where the named corpus can actually speak to the term: omitting it
76
+ * says "not measured here", which is honest, whereas `unattested` against an unrelated corpus
77
+ * would read as a claim about the term rather than about the corpus. The usage figures behind these derive from Material Bank's warehouse and
78
+ * from the DesignShop capture; publishing a raw number would publish a figure from someone else's
79
+ * business. The band carries the signal a reader actually needs - is this term common or rare -
80
+ * and nothing more.
79
81
  */
80
- declare const isInterchangeable: (strength: MatchStrength) => boolean;
81
-
82
- declare const ELEMENT_TERMS: readonly Term[];
83
-
84
- /** How a product or material is made; never a claim about its style or visible motif. */
85
- declare const CONSTRUCTION_TERMS: readonly Term[];
86
-
87
- /** Kept for consumers importing the axis as one list; prefer the facets. */
88
- declare const FINISH_TERMS: readonly Term[];
89
-
90
- /** The supplied physical unit or delivery shape, independent of material and installation pattern. */
91
- declare const FORMAT_TERMS: readonly Term[];
92
-
93
- /** Specific substance or material system; narrower than the retrieval-oriented material family. */
94
- declare const MATERIAL_TERMS: readonly Term[];
95
-
96
- declare const MOOD_TERMS: readonly Term[];
97
-
98
- /** Kept for consumers importing the axis as one list; prefer the facets. */
99
- declare const PATTERN_TERMS: readonly Term[];
100
-
101
- /** What kind of purchasable or specifiable thing the record describes, independent of substance. */
102
- declare const PRODUCT_CATEGORY_TERMS: readonly Term[];
103
-
104
- /** A dimension on which a project can impose a constraint; the assertion carries its operator/value. */
105
- declare const REQUIREMENT_TERMS: readonly Term[];
106
-
107
- declare const PALETTE_HUE_TERMS: readonly Term[];
108
- declare const PALETTE_TEMPERATURE_TERMS: readonly Term[];
109
- declare const PALETTE_CHARACTER_TERMS: readonly Term[];
110
- /** The three palette facets, each a closed dictionary in its own right. */
111
- declare const PALETTE_FACETS: readonly ["hue", "temperature", "character"];
112
- type PaletteFacet = (typeof PALETTE_FACETS)[number];
113
-
114
- declare const PERIOD_TERMS: readonly Term[];
82
+ type UsageBand = "dominant" | "common" | "occasional" | "rare" | "unattested";
83
+ interface TermUsage {
84
+ band: UsageBand;
85
+ /** The corpus the band was measured against, and when. */
86
+ corpus: string;
87
+ snapshot: string;
88
+ }
115
89
  /**
116
- * The periods a year could belong to, best match first.
117
- *
118
- * Sibling spans overlap on purpose - Georgian and Regency genuinely do - so "what period is 1800?"
119
- * has no single answer in the data. Resolving it lives here rather than in each consumer, because
120
- * a tie-break reinvented five times is five different answers to one question.
121
- *
122
- * THE RANKING, and it is not quite the one TAX-10 proposed. That ticket said prefer the narrowest
123
- * match, then the un-hedged one. Narrowest-first alone gets 1800 wrong: it returns Regency, whose
124
- * core is 1811-1820, over Georgian, whose core contains 1800 outright. So the first key is whether
125
- * the year falls in the period's UNCONTROVERSIAL CORE at all:
90
+ * Why a term carries no usage band when the corpus plainly has something to say about it.
126
91
  *
127
- * 1. a core match beats an edge match 1800 is Georgian, only arguably Regency
128
- * 2. then narrowest core 1815 is Regency, not merely Georgian
129
- * 3. then `circa: false` dated boundaries beat conventional ones
130
- * 4. among edge matches, least far outside 1832 is late Georgian, not late Regency
92
+ * The distinguishing case is `style.modernism`. DesignShop tags a dominant share of its room
93
+ * schemes `Modern`, but that is the crosswalk row that DECLINES: `Modern` reads as either
94
+ * `style.modernism` or `style.contemporary` and the source never recorded which. Attributing the
95
+ * usage would smuggle in the guess the crosswalk refused to make.
131
96
  *
132
- * `strict` drops the edge matches entirely, which is the difference between "give me a canonical
133
- * period for this year" and "could this year defensibly be called that".
134
- */
135
- declare const periodsAt: (year: number, { strict }?: {
136
- strict?: boolean | undefined;
137
- }) => readonly Term[];
138
- /** The single best period for a year, or undefined when none covers it. */
139
- declare const periodAt: (year: number, options?: {
140
- strict?: boolean;
141
- }) => Term | undefined;
142
-
143
- declare const PLACE_REGION_TERMS: readonly Term[];
144
- declare const PLACE_LANDSCAPE_TERMS: readonly Term[];
145
- declare const PLACE_FACETS: readonly ["region", "landscape"];
146
- type PlaceFacet = (typeof PLACE_FACETS)[number];
147
-
148
- declare const STYLE_TERMS: readonly Term[];
149
-
150
- type StyleId = (typeof STYLE_TERMS)[number]["id"];
151
- /** At least one anchor, so a key cannot be added and left empty. */
152
- type Anchors$1 = readonly [string, ...string[]];
153
- declare const STYLE_ANCHORS: Readonly<Record<string, Anchors$1>>;
154
- /** The anchors for a style term, or undefined when the id names no style. */
155
- declare const anchorsFor: (styleId: string) => Anchors$1 | undefined;
156
-
157
- declare const MOOD_ANCHORS: Readonly<Record<string, Anchors$1>>;
158
- /** The anchors for a mood term, or undefined when the id names no mood. */
159
- declare const moodAnchorsFor: (moodId: string) => Anchors$1 | undefined;
160
-
161
- declare const PERIOD_ANCHORS: Readonly<Record<string, Anchors$1>>;
162
- /** The anchors for a period term, or undefined when the id names no period. */
163
- declare const periodAnchorsFor: (periodId: string) => Anchors$1 | undefined;
164
-
165
- declare const PLACE_ANCHORS: Readonly<Record<string, Anchors$1>>;
166
- /** The anchors for a place term, or undefined when the id names no place. */
167
- declare const placeAnchorsFor: (placeId: string) => Anchors$1 | undefined;
168
-
169
- type Anchors = readonly [string, ...string[]];
170
- /**
171
- * Every space anchor, one file per sector so each stays inside the line budget and reads
172
- * alphabetically within its own group.
97
+ * Without this field, a reader sees "not measured" and cannot tell that apart from "nobody looked",
98
+ * and those are opposite claims. Same reasoning as `searchedWithout`: an absence somebody reasoned
99
+ * their way to is evidence, and an absence nobody has examined is a to-do.
173
100
  */
174
- declare const SPACE_ANCHORS: Readonly<Record<string, Anchors>>;
175
- /** The anchors for a space term, or undefined when the id names no space. */
176
- declare const spaceAnchorsFor: (spaceId: string) => Anchors | undefined;
177
-
101
+ interface UsageWithheld {
102
+ reason: string;
103
+ /** The corpus that would otherwise have supplied a band. */
104
+ corpus: string;
105
+ }
178
106
  /**
179
- * A sector of the SPACE axis: a group of rooms that answer the same programme question.
107
+ * A period's extent, as four numbers rather than two.
180
108
  *
181
- * NOT THE SECTOR AXIS, despite the shared word, and the collision is old rather than chosen:
182
- * these are PROGRAMME groups (where you sleep, wash, eat), while `sector.*` is the market a
183
- * project serves. The market claim lives on individual terms as `serves` edges - see
184
- * `marketsServed` below and the register in `space/market-neutral.ts` - and this grouping makes
185
- * no market claim at all: the "healthcare" file holds `clean_room`, which serves no single
186
- * market, and the "domestic" file is a programme word, not `sector.residential`.
109
+ * Two dates cannot answer the questions consumers actually ask. "Is 1832 Georgian?" wants the
110
+ * outer edge; "give me a canonical date for Georgian" wants the core. So each period carries a
111
+ * fuzzy interval: `earliest` and `latest` bound what could defensibly be called this period,
112
+ * `start` and `end` bound what uncontroversially is.
187
113
  *
188
- * NOT A FACET, and not part of a term's identifier. `space.scullery` is the id whatever sector the
189
- * scullery is filed under, and re-filing a room must never move its URI. This is a browse
190
- * structure, and it exists because 197 rooms sorted alphabetically put Operating Room next to
191
- * Orangery - which is a list you can search and not a list you can read.
114
+ * `end` and `latest` are NULLABLE, and that is load-bearing rather than tidy. An open-ended period
115
+ * closed with the current year needs an arbitrary edit at every release, and the edit is a lie in
116
+ * between: `contemporary` does not end in 2026, it has not ended.
192
117
  *
193
- * NOR IS IT `broaderTermId`. A scullery is not a kind of "Dining", so an ISA edge would be false;
194
- * ADR 0001's relation rule is explicit that subsumption means every instance of the narrower term
195
- * is an instance of the broader one. Sector is a filing decision about the vocabulary, which is a
196
- * different kind of claim from a fact about the world, and conflating the two is how hierarchies
197
- * fill up with things that are not parents.
118
+ * SIBLING SPANS ARE ALLOWED TO OVERLAP, deliberately. Georgian and Regency genuinely do. Resolving
119
+ * "what period is 1800?" is therefore an accessor's job, not the data's - see `periodAt`, which
120
+ * prefers the narrowest match and then the un-hedged one. Leaving that to each consumer would get
121
+ * it reinvented differently every time.
198
122
  */
199
- interface SpaceSector {
200
- id: string;
201
- /** The reader's word for the sector. */
202
- label: string;
203
- terms: readonly Term[];
123
+ interface PeriodSpan {
124
+ /** The earliest year anyone would defensibly call this period. */
125
+ earliest: number;
126
+ /** The first year it uncontroversially is this period. */
127
+ start: number;
128
+ /** The last year it uncontroversially is. Null when the period has not ended. */
129
+ end: number | null;
130
+ /** The last year anyone would defensibly call it this. Null when the period has not ended. */
131
+ latest: number | null;
132
+ /** True when the boundaries are conventional rather than evidenced by an event. */
133
+ circa: boolean;
134
+ /**
135
+ * Where these dates apply, as ISO 3166-1 alpha-2, or `global`. Regional variance beyond this
136
+ * belongs in NARROWER TERMS with their own spans, never in a region-to-span map on one term: a
137
+ * map invites partial coverage, and a narrower term is something a relation can point at.
138
+ */
139
+ regions: readonly string[];
204
140
  }
205
141
  /**
206
- * The seventeen sectors, in the order a browse page should show them.
142
+ * What KIND of thing a style term is, because the three kinds have different truth conditions and
143
+ * conflating them is most of why style vocabularies rot.
207
144
  *
208
- * Ordered by programme rather than alphabetically or by size: where you arrive, where you move,
209
- * where you sleep, wash, eat, gather, and so on out to the specialised sectors and the unclassified
210
- * tail. A reader looking for their own corner of the built environment finds it by walking through
211
- * a building, which is the order this list is in.
145
+ * A `movement` has an art-historical literature, dates and adherents, and its boundaries are
146
+ * arguable in the way a scholarly claim is arguable. A `tradition` is a body of regional or
147
+ * vernacular practice, defined by what is done rather than by what was declared. A `market-idiom`
148
+ * is a prototype category the trade converged on, defined by resemblance to central cases and by
149
+ * nothing else - it has no manifesto to appeal to, and asking for necessary and sufficient
150
+ * conditions misunderstands it.
212
151
  *
213
- * `SPACE_TERMS` is DERIVED from this, so the flat list and the grouped one cannot disagree. It was
214
- * previously the other way round - seventeen spreads into one array, with the grouping expressed
215
- * only by which file a term happened to sit in, where nothing outside the package could read it.
152
+ * Publishing the kind does two things. It tells a reader what sort of disagreement to expect: a
153
+ * dispute about Brutalism is a dispute about the record, a dispute about Farmhouse is a dispute
154
+ * about a prototype, and neither settles like the other. And it is the grouping a browse page needs
155
+ * once STYLE passes about fifty terms, when a flat alphabetical list stops being navigable.
156
+ */
157
+ type StyleKind = "movement" | "tradition" | "market-idiom";
158
+ /**
159
+ * The named predicates a term may carry to another canonical term. Everything that is not ISA
160
+ * lives here, so `broaderSlug` never silently becomes a bag of loose associations.
216
161
  */
217
- declare const SPACE_SECTORS: readonly SpaceSector[];
218
- declare const SPACE_TERMS: readonly Term[];
162
+ type RelationPredicate =
163
+ /** This element is physically a component of that element. */
164
+ "part-of"
165
+ /** This application or element is typically found in that space. */
166
+ | "typically-in"
167
+ /** This term is a material-suitability claim about that space. */
168
+ | "suitable-for"
219
169
  /**
220
- * The markets a space exists to serve, resolved through its ISA chain, or null where no market is
221
- * part of the room's identity.
170
+ * The subject exists to serve that sector's market, wherever the building that contains it
171
+ * stands: a hotel room in a mixed-use tower still serves hospitality, an operating room serves
172
+ * healthcare whoever the landlord is. An IDENTITY claim, never an occurrence claim - "occurs
173
+ * only in that sector" is false for almost any room (a multifamily amenity floor holds
174
+ * restrooms, a commercial kitchen and a gym inside `sector.residential`), which is why most
175
+ * rooms rightly carry no edge.
222
176
  *
223
- * THE ACCESSOR EXISTS SO THE INHERITANCE IS NOT REINVENTED. A `serves` edge restricts every
224
- * descendant - each suite is a hotel room, so each suite serves hospitality - and a consumer
225
- * reading raw edges would see `suite` carrying none and read it as unrestricted. The walk stops at
226
- * the NEAREST edge-bearing ancestor, because a child's own edges are a narrower claim that
227
- * replaces the inherited one rather than adding to it (`training_room` serves workplace inside a
228
- * neutral `classroom`).
177
+ * ABSENCE IS A CLAIM, NOT A GAP. A SPACE term carrying no serves edge and inheriting none
178
+ * through `broaderSlug` asserts that no market is part of its identity: it serves whatever
179
+ * market its project serves, and a consumer must read it as unrestricted - fail-open - never as
180
+ * a restriction nobody wrote down. What makes that reading safe is `MARKET_NEUTRAL_SPACES`
181
+ * (`axes/space/market-neutral.ts`, with the authoring criterion): every SPACE term either
182
+ * carries an edge or is registered there as reviewed-neutral, and the build fails naming any
183
+ * term that is neither. A reviewed absence and an unreviewed one cannot be confused - the same
184
+ * move `searchedWithout` and `usageWithheld` make.
229
185
  *
230
- * Null is a reviewed claim, not a gap: the term serves whatever market its project serves. The
231
- * register in `space/market-neutral.ts` is what makes that true, and the disposition test in
232
- * `axes.test.ts` is what keeps it true. Answering "what belongs in a residential scheme" is
233
- * therefore: every term whose result is null or includes "residential" - the neutral majority
234
- * spans all sectors, and excluding it would fail closed.
186
+ * Authored on SPACE only, and pinned there by test. The claim's shape would generalise, but
187
+ * TYPOLOGY must never carry it: sector moves when the tenant moves and typology does not, so a
188
+ * typology-to-sector edge dies on conversion - ADR 0001's converted warehouse full of flats is
189
+ * the standing counterexample.
235
190
  */
236
- declare const marketsServed: (termId: string) => readonly Sector[] | null;
237
-
238
- declare const MARKET_NEUTRAL_SPACES: ReadonlySet<string>;
191
+ | "serves"
192
+ /** This style or idiom draws on that place idiom or period. */
193
+ | "evokes"
194
+ /** This style descends from that style. */
195
+ | "influenced-by"
196
+ /** This style is a deliberate blend of two or more named styles, and is defined by being so. */
197
+ | "blend-of"
198
+ /** This surface reads as a material it is not made of. */
199
+ | "reads-as"
200
+ /** Association with no stronger claim available. */
201
+ | "related-to";
239
202
 
240
- declare const TYPOLOGY_TERMS: readonly Term[];
241
-
242
- declare const AXIS_IDS: readonly ["material_family", "product_category", "material", "pattern", "finish", "format", "construction", "requirement", "sector", "application", "space", "element", "typology", "style", "period", "mood", "palette", "place", "tier", "procurement", "strategy", "form", "density"];
243
- type AxisId = (typeof AXIS_IDS)[number];
244
- interface AxisFacet {
245
- id: string;
246
- label: string;
247
- terms: readonly Term[];
203
+ declare const TAXONOMY_BASE_URI: "https://taxonomy.materialinstruments.com";
204
+ declare const TAXONOMY_SNAPSHOT: "2026-08-30";
205
+ declare const TAXONOMY_VERSION: "1.1.0";
206
+ type CanonicalConceptUri = `${typeof TAXONOMY_BASE_URI}/id/${string}`;
207
+ type LifecycleStatus = "active" | "deprecated";
208
+ interface Lifecycle {
209
+ introducedAt: string;
210
+ reviewedAt: string;
211
+ status: LifecycleStatus;
212
+ supersededBy?: CanonicalConceptUri;
248
213
  }
249
- interface Axis {
250
- id: AxisId;
214
+ interface Governance {
215
+ explanation: string;
216
+ lifecycle: Lifecycle;
217
+ provenance: TermProvenance;
218
+ steward: "@instruments/taxonomy";
219
+ }
220
+ interface AxisNode {
221
+ boundary: string;
222
+ gap?: string;
223
+ governance: Governance;
224
+ kind: "axis";
251
225
  label: string;
252
- /** The question the axis answers, in a reader's words. One line. */
226
+ lifecycle: Lifecycle;
227
+ order: number;
228
+ provenance: TermProvenance;
253
229
  question: string;
254
- /**
255
- * Where this axis ends and its nearest neighbour begins, with the criterion that decides it.
256
- * Rendered verbatim on the axis page, because a boundary nobody can read is a boundary that gets
257
- * re-litigated.
258
- */
259
- boundary: string;
260
- /** `authored` ships terms. `declared` is a real question with no terms yet, and says so. */
230
+ slug: string;
261
231
  status: "authored" | "declared";
262
- /** Single-dictionary axes carry one facet named for the axis. */
263
- facets: readonly AxisFacet[];
264
- /** Why the axis has no terms yet. Present only when status is "declared". */
265
- gap?: string;
232
+ uri: string;
266
233
  }
267
- declare const AXES: readonly Axis[];
268
- declare const axisById: (id: AxisId) => Axis | undefined;
269
- /** Every term on an axis, across all its facets. */
270
- declare const axisTerms: (axis: Axis) => readonly Term[];
271
-
272
- declare const MATERIAL_FAMILY_TERMS: readonly Term[];
273
- declare const SECTOR_TERMS: readonly Term[];
274
- declare const APPLICATION_TERMS: readonly Term[];
275
-
276
- /**
277
- * The published term shape. Widened in 0.2.0 from the original three fields to the full `Term` in
278
- * `./term`, which adds external mappings, provenance, usage bands, supersession and typed
279
- * relations. Every added field is optional, so this is a backward-compatible minor exactly as the
280
- * banner above planned: "adding them later is a backward-compatible minor".
281
- */
282
- type TaxonomyTerm = Term;
283
-
284
- declare const PROVENANCE: {
285
- readonly snapshot: "2026-07-08";
286
- readonly source: "materialgraph packages/schema/src/registry/value-dictionaries.ts";
234
+ interface FacetNode {
235
+ axis: string;
236
+ governance: Governance;
237
+ kind: "facet";
238
+ label: string;
239
+ lifecycle: Lifecycle;
240
+ order: number;
241
+ provenance: TermProvenance;
242
+ slug: string;
243
+ uri: string;
244
+ }
245
+ interface ExternalReference {
246
+ authority: "aat" | "uniclass" | "wikidata";
247
+ identifier: string;
248
+ label: string;
249
+ match: ExternalMapping["match"];
250
+ }
251
+ interface ConceptNode {
252
+ altLabels?: readonly string[];
253
+ axis: string;
254
+ definition?: string;
255
+ externalReferences?: readonly ExternalReference[];
256
+ facet: string;
257
+ governance: Governance;
258
+ governedBy: "taxonomy";
259
+ kind: "concept";
260
+ label: string;
261
+ lifecycle: Lifecycle;
262
+ note?: string;
263
+ order: number;
264
+ provenance: TermProvenance;
265
+ rank?: number;
266
+ searchedWithout?: readonly SearchedWithout[];
267
+ seenAs?: readonly string[];
268
+ slug: string;
269
+ span?: PeriodSpan;
270
+ styleKind?: StyleKind;
271
+ uri: CanonicalConceptUri;
272
+ usage?: TermUsage;
273
+ usageWithheld?: UsageWithheld;
274
+ }
275
+ type KnowledgeGraphNode = AxisNode | FacetNode | ConceptNode;
276
+ type GraphPredicate = RelationPredicate | "belongs-to-axis" | "belongs-to-facet" | "broader" | "narrower" | "has-concept" | "has-facet" | "has-part" | "typically-contains" | "supported-by" | "served-by" | "evoked-by" | "influences" | "blended-into" | "reading-of";
277
+ interface KnowledgeGraphEdge {
278
+ assertion: "asserted" | "inverse";
279
+ from: string;
280
+ governance: Governance;
281
+ lifecycle: Lifecycle;
282
+ predicate: GraphPredicate;
283
+ provenance: TermProvenance;
284
+ role?: "constitutive" | "accent";
285
+ to: string;
286
+ }
287
+ interface KnowledgeGraph {
288
+ edges: readonly KnowledgeGraphEdge[];
289
+ nodes: readonly KnowledgeGraphNode[];
290
+ }
291
+ declare const KNOWLEDGE_GRAPH: KnowledgeGraph;
292
+ interface TaxonomyTarget {
293
+ authority: "taxonomy";
294
+ role?: "primary" | "secondary";
295
+ uri: CanonicalConceptUri;
296
+ }
297
+ declare const COLOR_SCOPE_REFERENCE_KINDS: readonly ["chroma-level", "color-family", "lightness-level", "named-color", "palette-mood", "temperature-level"];
298
+ type ColorScopeReferenceKind = (typeof COLOR_SCOPE_REFERENCE_KINDS)[number];
299
+ interface ColorScopeTarget {
300
+ authority: "colorscope";
301
+ authorityUri: "https://colorscope.materialinstruments.com";
302
+ identifier: string;
303
+ referenceKind: ColorScopeReferenceKind;
304
+ role?: "primary" | "secondary";
305
+ }
306
+ type LexicalTarget = TaxonomyTarget | ColorScopeTarget;
307
+ type LexicalContext = "colour" | "material";
308
+ interface LexicalBase {
309
+ explanation: string;
310
+ governance: Governance;
311
+ phrase: string;
312
+ }
313
+ type LexicalEntry = (LexicalBase & {
314
+ outcome: "equivalent";
315
+ target: LexicalTarget;
316
+ }) | (LexicalBase & {
317
+ contextualPreferences?: readonly {
318
+ context: LexicalContext;
319
+ target: LexicalTarget;
320
+ }[];
321
+ /** An explicitly authored default reading; never inferred from array order. */
322
+ defaultTarget?: LexicalTarget;
323
+ outcome: "ambiguous";
324
+ readings: readonly [LexicalTarget, LexicalTarget, ...LexicalTarget[]];
325
+ }) | (LexicalBase & {
326
+ outcome: "declined";
327
+ reason: string;
328
+ });
329
+ /** Declarative readings only. Consumers choose indexing and application behaviour. */
330
+ declare const LEXICAL_ENTRIES: readonly LexicalEntry[];
331
+ /** Content address for CI drift checks; changes whenever the public corpus representation changes. */
332
+ declare const TAXONOMY_CORPUS_REVISION: string;
333
+ declare const TAXONOMY_CORPUS_METADATA: {
334
+ readonly edgeCount: number;
335
+ readonly lexicalEntryCount: number;
336
+ readonly nodeCount: number;
337
+ readonly revision: string;
338
+ readonly snapshot: "2026-08-30";
339
+ readonly version: "1.1.0";
287
340
  };
288
- declare const MATERIAL_FAMILIES: readonly ["paint", "wall_finish", "wallcovering", "textile", "upholstery", "carpet", "wood", "stone", "tile", "terrazzo", "metal", "glass", "resilient_flooring", "composite", "concrete", "leather", "other"];
289
- type MaterialFamily = (typeof MATERIAL_FAMILIES)[number];
290
- declare const isMaterialFamily: (value: string) => value is MaterialFamily;
291
- declare const SECTORS: readonly ["hospitality", "healthcare", "workplace", "residential", "retail", "education", "civic", "mixed_use", "transportation", "other"];
292
- type Sector = (typeof SECTORS)[number];
293
- declare const isSector: (value: string) => value is Sector;
294
- declare const APPLICATIONS: readonly ["wall", "flooring", "ceiling", "bath", "kitchen", "furniture", "lighting", "door", "window", "facade", "countertop", "fireplace", "stair_elevator", "bedding", "decor", "vertically_hanging", "transportation", "pool_fountain", "paving_deck", "partition", "millwork", "cabinetry", "masonry", "roof", "garage", "bar", "awning_umbrella", "recreation_sport", "ornamentation", "aquatic_environments", "upholstery", "trim", "wall_backsplash", "wall_upholstered", "wall_wet_shower", "flooring_wet_area", "flooring_entrance", "flooring_radiant_heat", "flooring_or_lab", "flooring_anti_fatigue", "flooring_esd", "flooring_safety_slip", "flooring_raised_access", "flooring_rigid_core", "flooring_dining_area", "flooring_subject_oil", "flooring_walk_in_freezer", "flooring_ramps_inclines", "furniture_seating", "furniture_headboard", "furniture_slipcover", "furniture_systems", "furniture_tackboard", "furniture_throw_pillow", "fireplace_firebox", "fireplace_hearth", "fireplace_mantel", "fireplace_surround", "bedding_bed_scarf", "bedding_sheets", "bedding_skirt", "bedding_bedspread", "bedding_box_spring_cover", "stair_riser", "stair_tread", "stair_railing", "stair_elevator_cladding", "vh_drapery", "vh_privacy_curtain", "vh_shower_curtain", "vh_theatrical_curtain", "vh_window_shade", "transportation_automotive", "transportation_aviation", "transportation_marine", "pool_coping", "pool_decking", "pool_lining", "paving_covered_areas", "paving_patio", "partition_toilet", "masonry_structural", "masonry_veneer", "recreation_gymnasium", "recreation_playground", "recreation_weightlifting", "awning", "tensile_structure", "umbrella", "countertop_chemical_resistant"];
295
- type Application = (typeof APPLICATIONS)[number];
296
- declare const isApplication: (value: string) => value is Application;
297
- declare const APPLICATION_PARENTS: Readonly<Partial<Record<Application, Application>>>;
298
- declare const applicationTopLevelOf: (application: Application) => Application;
299
341
 
300
- export { APPLICATIONS, APPLICATION_PARENTS, APPLICATION_TERMS, ASSERTION_KEYS, AXES, AXIS_IDS, type Anchors$1 as Anchors, type Application, type Axis, type AxisFacet, type AxisId, CONSTRUCTION_TERMS, ELEMENT_TERMS, FINISH_TERMS, FORMAT_TERMS, MARKET_NEUTRAL_SPACES, MATCH_STRENGTHS, MATERIAL_FAMILIES, MATERIAL_FAMILY_TERMS, MATERIAL_TERMS, MOOD_ANCHORS, MOOD_TERMS, MatchStrength, type MaterialFamily, PALETTE_CHARACTER_TERMS, PALETTE_FACETS, PALETTE_HUE_TERMS, PALETTE_TEMPERATURE_TERMS, PATTERN_TERMS, PERIOD_ANCHORS, PERIOD_TERMS, PLACE_ANCHORS, PLACE_FACETS, PLACE_LANDSCAPE_TERMS, PLACE_REGION_TERMS, PRODUCT_CATEGORY_TERMS, PROVENANCE, type PaletteFacet, type ParsedAssertion, type PlaceFacet, RELATION_PREDICATES, REQUIREMENT_TERMS, RelationPredicate, type ResolvedAssertion, SECTORS, SECTOR_TERMS, SKOS_MATCH_PREDICATE, SPACE_ANCHORS, SPACE_SECTORS, SPACE_TERMS, STYLE_ANCHORS, STYLE_TERMS, type Sector, type SpaceSector, type StyleId, TERM_LOCATIONS, TYPOLOGY_TERMS, type TaxonomyTerm, Term, type TermLocation, anchorsFor, applicationTopLevelOf, axisById, axisTerms, canonicalAssertion, deprecatedAssertions, isApplication, isCanonicalAssertion, isInterchangeable, isMaterialFamily, isSector, marketsServed, moodAnchorsFor, parseAssertion, periodAnchorsFor, periodAt, periodsAt, placeAnchorsFor, resolveAssertion, resolveTerm, spaceAnchorsFor, unknownAssertions };
342
+ export { type AxisNode, COLOR_SCOPE_REFERENCE_KINDS, type CanonicalConceptUri, type ColorScopeReferenceKind, type ColorScopeTarget, type ConceptNode, type ExternalReference, type FacetNode, type Governance, type GraphPredicate, KNOWLEDGE_GRAPH, type KnowledgeGraph, type KnowledgeGraphEdge, type KnowledgeGraphNode, LEXICAL_ENTRIES, type LexicalContext, type LexicalEntry, type LexicalTarget, type Lifecycle, type LifecycleStatus, type PeriodSpan, type SearchedWithout, type StyleKind, TAXONOMY_BASE_URI, TAXONOMY_CORPUS_METADATA, TAXONOMY_CORPUS_REVISION, TAXONOMY_SNAPSHOT, TAXONOMY_VERSION, type TaxonomyTarget, type TermProvenance, type TermUsage, type UsageWithheld };