@instruments/materialgraph 0.29.0 → 0.31.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.
Files changed (89) hide show
  1. package/brands-client.d.ts +1 -1
  2. package/by-colour-types.d.ts +4 -4
  3. package/catalogue-client.d.ts +3 -3
  4. package/catalogue-client.js +3 -3
  5. package/catalogue-types.d.ts +1 -1
  6. package/client-vocabulary.d.ts +3 -5
  7. package/client-vocabulary.js +9 -6
  8. package/client.d.ts +85 -174
  9. package/client.js +98 -211
  10. package/colour-region-types.d.ts +18 -0
  11. package/colours-client.d.ts +10 -83
  12. package/colours-client.js +1 -85
  13. package/errors.d.ts +1 -1
  14. package/errors.js +1 -0
  15. package/find-companions-types.d.ts +70 -0
  16. package/find-companions-types.js +12 -0
  17. package/find-variants-by-colour-types.d.ts +197 -0
  18. package/find-variants-by-vector-types.d.ts +67 -0
  19. package/find-variants-by-vector-types.js +5 -0
  20. package/get-variant-types.d.ts +50 -0
  21. package/get-variant-types.js +10 -0
  22. package/http-transport.d.ts +1 -1
  23. package/identifiers.d.ts +1 -1
  24. package/identifiers.js +1 -1
  25. package/image-reference-types.d.ts +18 -0
  26. package/index.d.ts +9 -7
  27. package/index.js +1 -1
  28. package/list-enum-values-types.d.ts +58 -0
  29. package/list-variants-types.d.ts +6 -2
  30. package/material-asset-types.d.ts +2 -2
  31. package/material-types.d.ts +13 -151
  32. package/operations.d.ts +1 -1
  33. package/operations.js +27 -88
  34. package/package.json +1 -1
  35. package/palette-search-types.d.ts +3 -49
  36. package/resolve-colour-types.d.ts +5 -5
  37. package/resolve-variant-types.d.ts +75 -0
  38. package/{identify-types.js → resolve-variant-types.js} +2 -2
  39. package/response-schemas.generated.d.ts +1 -1
  40. package/response-schemas.generated.js +46 -84
  41. package/run-types.d.ts +23 -216
  42. package/runs.d.ts +10 -58
  43. package/runs.js +19 -106
  44. package/schema-resource-types.d.ts +2 -139
  45. package/schema-resource-types.js +0 -8
  46. package/search-card-types.d.ts +1 -1
  47. package/search-index-plan-types.d.ts +6 -2
  48. package/search-request-types.d.ts +10 -2
  49. package/spec-types.d.ts +4 -4
  50. package/spec-types.js +2 -2
  51. package/specifications.d.ts +1 -9
  52. package/specifications.js +0 -12
  53. package/sse.d.ts +2 -2
  54. package/sse.js +2 -2
  55. package/types.d.ts +4 -38
  56. package/validation.d.ts +0 -74
  57. package/validation.js +2 -116
  58. package/webmcp.generated.js +9 -9
  59. package/workflows-client.d.ts +54 -0
  60. package/workflows-client.js +41 -0
  61. package/client-source-material-comments.d.ts +0 -51
  62. package/client-source-material-comments.js +0 -6
  63. package/codes-client.d.ts +0 -118
  64. package/codes-client.js +0 -99
  65. package/codes-types.d.ts +0 -325
  66. package/colour-aggregate-types.d.ts +0 -163
  67. package/coordinate-client.d.ts +0 -23
  68. package/coordinate-client.js +0 -13
  69. package/demand-batch-types.d.ts +0 -118
  70. package/demand-types.d.ts +0 -154
  71. package/identify-types.d.ts +0 -82
  72. package/material-composition-client.d.ts +0 -21
  73. package/material-composition-client.js +0 -13
  74. package/material-composition-types.d.ts +0 -403
  75. package/material-composition-types.js +0 -1
  76. package/source-material-validation.d.ts +0 -7
  77. package/source-material-validation.js +0 -214
  78. package/spec-facet-types.d.ts +0 -48
  79. package/spec-facet-types.js +0 -7
  80. package/substitute-types.d.ts +0 -102
  81. package/substitute-types.js +0 -1
  82. package/suggest-types.d.ts +0 -67
  83. package/suggest-types.js +0 -28
  84. package/taxonomy-coverage-types.d.ts +0 -34
  85. package/taxonomy-coverage-types.js +0 -1
  86. /package/{codes-types.js → colour-region-types.js} +0 -0
  87. /package/{colour-aggregate-types.js → find-variants-by-colour-types.js} +0 -0
  88. /package/{demand-batch-types.js → image-reference-types.js} +0 -0
  89. /package/{demand-types.js → list-enum-values-types.js} +0 -0
package/codes-client.d.ts DELETED
@@ -1,118 +0,0 @@
1
- /**
2
- * The advisory building-code surface, namespaced as `mg.codes`.
3
- *
4
- * Three calls that are one workflow — discover the vocabulary, state the space,
5
- * then badge candidates against it — so they live together rather than as three
6
- * flat methods on an already-large client facade. `mg.browse` set the precedent
7
- * for a namespace when a capability is a small family rather than a single verb.
8
- *
9
- * The namespace holds no transport of its own: the client hands it bound
10
- * `get`/`post` callbacks, so auth, rate-limit accounting and error shaping stay
11
- * in exactly one place.
12
- *
13
- * ADVISORY ONLY: every verdict and every envelope carries the AHJ caveat — this
14
- * is model code as adopted, never a legal determination.
15
- *
16
- * ROUTING. Codes answers "is this material allowed in THIS SPACE?" — a question
17
- * about a jurisdiction, an occupancy group and a finish location, not about a
18
- * material alone.
19
- * - "which materials carry Class A?" → `mg.bySpec("fire_classifications:...")`.
20
- * That is a property question; this namespace is what tells you the space
21
- * demands Class A in the first place.
22
- * - "what does this one material hold?" → `mg.materialBlocks(variantId)`.
23
- * - "find me something like this, but compliant" → `mg.search({ filters })`
24
- * once `requirements()` has told you which filter to apply.
25
- */
26
- import type { CodeAdvisoryInput, CodeAdvisoryResponse, CodeContextRequest, CodeContextResponse, CodeRequirementsResponse } from "./codes-types.js";
27
- import type { QueryParams } from "./http-transport.js";
28
- import type { SdkOperationId } from "./operations.js";
29
- /** The client's own request primitives, handed down so this file owns no transport. */
30
- export type CodesTransport = {
31
- get<T>(operationId: SdkOperationId, query?: QueryParams, signal?: AbortSignal): Promise<T>;
32
- post<T>(operationId: SdkOperationId, body: unknown, signal?: AbortSignal): Promise<T>;
33
- };
34
- export declare class CodesClient {
35
- #private;
36
- constructor(transport: CodesTransport);
37
- /**
38
- * THE QUESTION: "what may I even say about a space?" — the vocabulary for
39
- * stating a code context, and the first call of the three.
40
- *
41
- * Everything {@link CodesClient.requirements} and {@link CodesClient.advisory}
42
- * accept is listed here, so nothing need be hardcoded and a 400 for an unknown
43
- * jurisdiction / occupancy / space is always avoidable.
44
- *
45
- * The vocabulary for stating a code context (GET `/api/v1/codes/context`):
46
- * seeded jurisdictions and their coverage tier, the occupancy groups the
47
- * registry keys on, finish locations, dimensions, classification scales with
48
- * classes ordered best-first, and which `room` term correlates to which
49
- * regulatory axes.
50
- *
51
- * Start here — every value the other two calls accept is listed in this one
52
- * response, so nothing need be hardcoded. A correlation the registry refuses to
53
- * make comes back `null`, never a guess.
54
- *
55
- * @example
56
- * const vocab = await mg.codes.context();
57
- * const texas = vocab.jurisdictions.find((j) => j.id === "us-tx");
58
- * // `model_baseline` means unamended model IBC stood in for this AHJ's own table.
59
- * console.log(texas?.coverage, texas?.adoptionNote);
60
- */
61
- context(signal?: AbortSignal): Promise<CodeContextResponse>;
62
- /**
63
- * THE QUESTION: "what does this space DEMAND?" — before any candidate exists.
64
- *
65
- * Run it before shortlisting, not after: it tells you which specification to
66
- * filter on, which you then spend on `mg.bySpec()` or `mg.search({ filters })`.
67
- * Use {@link CodesClient.advisory} once you hold candidates to badge.
68
- *
69
- * What a space demands, before any candidate exists (GET
70
- * `/api/v1/codes/requirements`). State the space by explicit axes, or by a
71
- * `roomType` the axes are correlated from.
72
- *
73
- * An empty `requirements` array means nothing in the registry applies to this
74
- * context. It is NOT a pass.
75
- *
76
- * @example
77
- * const { requirements } = await mg.codes.requirements({
78
- * jurisdictionId: "us-tx",
79
- * occupancyGroup: "A-2", // or: roomType: "hotel_room" to correlate the axes
80
- * spaceType: "corridors",
81
- * sprinklered: true,
82
- * });
83
- * requirements.map((r) => `${r.dimension}: ${r.label} (${r.section})`);
84
- */
85
- requirements(input: CodeContextRequest, signal?: AbortSignal): Promise<CodeRequirementsResponse>;
86
- /**
87
- * THE QUESTION: "do THESE candidates satisfy what this space demands?" — the
88
- * third call, once you hold materials.
89
- *
90
- * Context is CONDITIONAL: supply `occupancyGroup` + `spaceType`, or a
91
- * `roomType` for them to correlate from, or a mix — an explicit axis always
92
- * wins, and an axis that resolves to nothing is a 400 naming it. The full rule,
93
- * with the validator's own messages, is on {@link CodeContextRequest}.
94
- *
95
- * Read `summary.status` honestly: `unverified` means no comparable rating is on
96
- * file. It is not a pass, and it is common. Use {@link CodesClient.requirements}
97
- * when you have no candidates yet, and `mg.materialBlocks()` to see whether a
98
- * material's fire/slip facts are missing or merely unverified.
99
- *
100
- * Badge materials against the advisory building-code registry for one context
101
- * (POST `/api/v1/codes/advisory`, ≤ 100 ids). Each applicable requirement is
102
- * badged `meets` / `shortfall` / `unknown` from the material's accepted fire
103
- * and slip facts, in input order; each material also carries a rolled-up
104
- * `summary`, and `resolvedContext` reports the axes actually used.
105
- *
106
- * @example
107
- * const advisory = await mg.codes.advisory({
108
- * variantIds: scheme.picks.map((p) => p.variantId),
109
- * jurisdictionId: "us-tx",
110
- * occupancyGroup: "A-2", // or: roomType: "hotel_room" to correlate the axes
111
- * spaceType: "corridors", // corridors | exit_stairs_passageways | rooms_enclosed_spaces
112
- * sprinklered: true,
113
- * });
114
- * // `unverified` means no comparable rating is on file — common, never a pass.
115
- * const blocked = advisory.results.filter((r) => r.summary?.status === "shortfall");
116
- */
117
- advisory(input: CodeAdvisoryInput, signal?: AbortSignal): Promise<CodeAdvisoryResponse>;
118
- }
package/codes-client.js DELETED
@@ -1,99 +0,0 @@
1
- export class CodesClient {
2
- #transport;
3
- constructor(transport) {
4
- this.#transport = transport;
5
- }
6
- /**
7
- * THE QUESTION: "what may I even say about a space?" — the vocabulary for
8
- * stating a code context, and the first call of the three.
9
- *
10
- * Everything {@link CodesClient.requirements} and {@link CodesClient.advisory}
11
- * accept is listed here, so nothing need be hardcoded and a 400 for an unknown
12
- * jurisdiction / occupancy / space is always avoidable.
13
- *
14
- * The vocabulary for stating a code context (GET `/api/v1/codes/context`):
15
- * seeded jurisdictions and their coverage tier, the occupancy groups the
16
- * registry keys on, finish locations, dimensions, classification scales with
17
- * classes ordered best-first, and which `room` term correlates to which
18
- * regulatory axes.
19
- *
20
- * Start here — every value the other two calls accept is listed in this one
21
- * response, so nothing need be hardcoded. A correlation the registry refuses to
22
- * make comes back `null`, never a guess.
23
- *
24
- * @example
25
- * const vocab = await mg.codes.context();
26
- * const texas = vocab.jurisdictions.find((j) => j.id === "us-tx");
27
- * // `model_baseline` means unamended model IBC stood in for this AHJ's own table.
28
- * console.log(texas?.coverage, texas?.adoptionNote);
29
- */
30
- async context(signal) {
31
- return await this.#transport.get("list_code_context", undefined, signal);
32
- }
33
- /**
34
- * THE QUESTION: "what does this space DEMAND?" — before any candidate exists.
35
- *
36
- * Run it before shortlisting, not after: it tells you which specification to
37
- * filter on, which you then spend on `mg.bySpec()` or `mg.search({ filters })`.
38
- * Use {@link CodesClient.advisory} once you hold candidates to badge.
39
- *
40
- * What a space demands, before any candidate exists (GET
41
- * `/api/v1/codes/requirements`). State the space by explicit axes, or by a
42
- * `roomType` the axes are correlated from.
43
- *
44
- * An empty `requirements` array means nothing in the registry applies to this
45
- * context. It is NOT a pass.
46
- *
47
- * @example
48
- * const { requirements } = await mg.codes.requirements({
49
- * jurisdictionId: "us-tx",
50
- * occupancyGroup: "A-2", // or: roomType: "hotel_room" to correlate the axes
51
- * spaceType: "corridors",
52
- * sprinklered: true,
53
- * });
54
- * requirements.map((r) => `${r.dimension}: ${r.label} (${r.section})`);
55
- */
56
- async requirements(input, signal) {
57
- return await this.#transport.get("list_code_requirements", {
58
- jurisdiction: input.jurisdictionId,
59
- occupancy: input.occupancyGroup,
60
- room: input.roomType,
61
- space: input.spaceType,
62
- sprinklered: input.sprinklered,
63
- }, signal);
64
- }
65
- /**
66
- * THE QUESTION: "do THESE candidates satisfy what this space demands?" — the
67
- * third call, once you hold materials.
68
- *
69
- * Context is CONDITIONAL: supply `occupancyGroup` + `spaceType`, or a
70
- * `roomType` for them to correlate from, or a mix — an explicit axis always
71
- * wins, and an axis that resolves to nothing is a 400 naming it. The full rule,
72
- * with the validator's own messages, is on {@link CodeContextRequest}.
73
- *
74
- * Read `summary.status` honestly: `unverified` means no comparable rating is on
75
- * file. It is not a pass, and it is common. Use {@link CodesClient.requirements}
76
- * when you have no candidates yet, and `mg.materialBlocks()` to see whether a
77
- * material's fire/slip facts are missing or merely unverified.
78
- *
79
- * Badge materials against the advisory building-code registry for one context
80
- * (POST `/api/v1/codes/advisory`, ≤ 100 ids). Each applicable requirement is
81
- * badged `meets` / `shortfall` / `unknown` from the material's accepted fire
82
- * and slip facts, in input order; each material also carries a rolled-up
83
- * `summary`, and `resolvedContext` reports the axes actually used.
84
- *
85
- * @example
86
- * const advisory = await mg.codes.advisory({
87
- * variantIds: scheme.picks.map((p) => p.variantId),
88
- * jurisdictionId: "us-tx",
89
- * occupancyGroup: "A-2", // or: roomType: "hotel_room" to correlate the axes
90
- * spaceType: "corridors", // corridors | exit_stairs_passageways | rooms_enclosed_spaces
91
- * sprinklered: true,
92
- * });
93
- * // `unverified` means no comparable rating is on file — common, never a pass.
94
- * const blocked = advisory.results.filter((r) => r.summary?.status === "shortfall");
95
- */
96
- async advisory(input, signal) {
97
- return await this.#transport.post("resolveCodeAdvisory", input, signal);
98
- }
99
- }
package/codes-types.d.ts DELETED
@@ -1,325 +0,0 @@
1
- /**
2
- * Hand-mirrored wire types for the advisory building-code surface
3
- * (`GET /api/v1/codes/context`, `GET /api/v1/codes/requirements`,
4
- * `POST /api/v1/codes/advisory` — MG-433, MG-674).
5
- *
6
- * Like the rest of the SDK these interfaces have NO runtime dependency on any
7
- * `@materialgraph/*` package — they mirror the `codeAdvisoryInputSchema` /
8
- * `codeAdvisoryResultSchema` Zod contracts the route is built from. Response
9
- * enums follow the SDK's TOLERANT-PARSING contract (widened with `OpenEnum` so a
10
- * new server value still type-checks); `requirement.scale` is an open IBC-scale
11
- * id and `caveat` is the AHJ advisory sentence, both read as plain strings.
12
- *
13
- * ADVISORY ONLY: every verdict and the envelope carry the AHJ caveat — this is
14
- * model code as adopted, never a legal determination.
15
- */
16
- import type { VariantId } from "./identifiers.js";
17
- import type { FilterGroups } from "./list-variants-types.js";
18
- import type { OpenEnum } from "./types.js";
19
- /**
20
- * A code context: the space the registry is queried for.
21
- *
22
- * CONDITIONAL REQUIREMENT — two axes must END UP resolved, and each may be
23
- * stated OR correlated. Every field below is optional in the type because the
24
- * validator resolves them per axis independently; the rule it enforces at
25
- * runtime is:
26
- *
27
- * > occupancyGroup, resolved = `input.occupancyGroup` ?? correlate(roomType)
28
- * > spaceType, resolved = `input.spaceType` ?? correlate(roomType)
29
- *
30
- * and either resolving to nothing is a 400, verbatim:
31
- *
32
- * - `"An occupancy group is required — state occupancyGroup, or a roomType that
33
- * correlates to one."`
34
- * - `"A space type is required — state spaceType, or a roomType that correlates
35
- * to one."`
36
- * - `"Room type \"{roomType}\" does not correlate to an IBC occupancy group —
37
- * state occupancyGroup explicitly."`
38
- * - `"Room type \"{roomType}\" does not correlate to a Table 803.13 finish
39
- * location — state spaceType explicitly."`
40
- *
41
- * So: give `occupancyGroup` + `spaceType`, or give `roomType` and let both
42
- * correlate, or mix them — an explicit axis always wins over a correlated one,
43
- * and a room is only consulted for an axis you did not state. Correlation never
44
- * guesses: a room that maps to neither is an error naming the axis, and the
45
- * response's `resolvedContext` is where you audit what was inferred for you.
46
- *
47
- * Discover every accepted value — jurisdictions, occupancy groups, finish
48
- * locations, and the room correlations themselves — from
49
- * {@link CodesClient.context}. Nothing here should be hardcoded.
50
- */
51
- export type CodeContextRequest = {
52
- /** A seeded jurisdiction id (e.g. `"us-tx"`); an unknown id is a 400 listing the seeded set. */
53
- jurisdictionId: string;
54
- /**
55
- * The IBC occupancy group for the space (e.g. `"I-2"` hospital, `"A-2"`
56
- * restaurant). Required unless `roomType` correlates to one. An uncovered
57
- * group is a 400 listing the covered ones.
58
- */
59
- occupancyGroup?: string;
60
- /**
61
- * The finish location: `"corridors"`, `"exit_stairs_passageways"`, or
62
- * `"rooms_enclosed_spaces"`. Required unless `roomType` correlates to one.
63
- */
64
- spaceType?: string;
65
- /**
66
- * A `room` term id (e.g. `"hotel_room"`) to correlate the regulatory axes
67
- * from. Explicit `occupancyGroup` / `spaceType` always win. A room that
68
- * correlates to neither axis is a 400 naming the axis — never a guess.
69
- */
70
- roomType?: string;
71
- /** Whether the space is sprinklered — relaxes several fire-finish floors. No default. */
72
- sprinklered: boolean;
73
- };
74
- export type CodeAdvisoryInput = {
75
- /**
76
- * 1–100 opaque variant ids to badge, echoed back in `results` in this exact
77
- * order and cardinality (duplicates are echoed, never collapsed). Preserve
78
- * them verbatim — never parse or construct one.
79
- */
80
- variantIds: VariantId[];
81
- } & CodeContextRequest;
82
- /** The resolved jurisdiction the verdicts were evaluated against. */
83
- export type CodeAdvisoryJurisdiction = {
84
- id: string;
85
- name: string;
86
- codeFamily: string;
87
- edition: string;
88
- adoptionNote: string;
89
- };
90
- /** The echoed evaluation context (the space the badges apply to). */
91
- export type CodeAdvisoryContext = {
92
- occupancyGroup: string;
93
- spaceType: string;
94
- sprinklered: boolean;
95
- };
96
- /**
97
- * What one material is, and therefore what it was held to. `dimensions` are the
98
- * regulatory dimensions the material can be scored on; `basis` names which
99
- * source(s) produced them: a mapped `productTypeId`, `applicationIds`, both, or
100
- * `unresolved` when neither yielded anything. `unresolved` does NOT mean the
101
- * material is answerable for nothing — it means we could not tell what the
102
- * material is, so every requirement the space imposes was scored against it and
103
- * `notApplicable` on the result is empty. That is a coverage gap in the
104
- * product-type/application map, not a narrow material. A short `requirements`
105
- * list under `product_type` / `application` / `both` is a genuine intersection;
106
- * a full list under `unresolved` is the map falling short.
107
- */
108
- export type MaterialCodeProfile = {
109
- /** The variant's accepted primary product type, or null if it has none. */
110
- productTypeId: string | null;
111
- /** The variant's accepted `applications` terms, as stored. */
112
- applicationIds: string[];
113
- /** Regulatory dimensions this material can be held to. Empty ONLY when `basis` is `unresolved`. */
114
- dimensions: OpenEnum<"fire" | "floor" | "slip" | "decorative">[];
115
- basis: OpenEnum<"product_type" | "application" | "both" | "unresolved">;
116
- };
117
- /** Why one requirement the space imposes was excluded from this material's `requirements`. */
118
- export type CodeRequirementExclusion = {
119
- requirementId: string;
120
- dimension: OpenEnum<"fire" | "floor" | "slip" | "decorative">;
121
- reason: OpenEnum<"not_a_material_dimension">;
122
- /** One readable line, e.g. `"Slip resistance is not a dimension wallcovering answers for."`. */
123
- detail: string;
124
- };
125
- /** The threshold one requirement imposes: a class floor and/or a numeric floor. */
126
- export type CodeRequirementThreshold = {
127
- /** The IBC classification scale this requirement is measured on (open id). */
128
- scale: string;
129
- /** The minimum acceptable class on `scale`, or null when only a numeric floor applies. */
130
- minClass: string | null;
131
- /** A numeric floor (e.g. a DCOF minimum), or null when only a class floor applies. */
132
- numericFloor: {
133
- metric: string;
134
- value: number;
135
- unit: string;
136
- } | null;
137
- };
138
- /**
139
- * The accepted fact a verdict was decided from — null when the material carried
140
- * none, which is exactly the case that produces `verdict: "unknown"`.
141
- *
142
- * `confidence` and `sourceBasis` are the strength of the fact, not of the code
143
- * reading. Quote them whenever you quote the badge: a `meets` resting on a
144
- * `derived` value at low confidence is a materially different statement from one
145
- * resting on a `tested` figure.
146
- */
147
- export type CodeRequirementEvidence = {
148
- attributeId: OpenEnum<"fire_classifications" | "slip_resistance_dcof">;
149
- value: string;
150
- confidence: number | null;
151
- sourceBasis: string | null;
152
- };
153
- /** One requirement badged against one material. */
154
- export type CodeRequirementVerdict = {
155
- requirementId: string;
156
- dimension: OpenEnum<"fire" | "floor" | "slip" | "decorative">;
157
- section: string;
158
- requirement: CodeRequirementThreshold;
159
- /**
160
- * What may be said about this material against this requirement:
161
- * - `meets` — a defensible fact clears the threshold. Advisory, still not a
162
- * determination.
163
- * - `shortfall` — a defensible fact FAILS to clear it. A real negative.
164
- * - `unknown` — no defensible fact maps onto `scale`. NOT a failure and NOT a
165
- * pass: "no comparable rating on file". Never present it as either.
166
- */
167
- verdict: OpenEnum<"meets" | "shortfall" | "unknown">;
168
- evidence: CodeRequirementEvidence | null;
169
- /** The AHJ advisory caveat — model code as adopted, never a legal determination. */
170
- caveat: string;
171
- };
172
- /**
173
- * One candidate's requirement verdicts folded into a single status and sentence.
174
- * A shortfall outranks an unknown, and an unknown NEVER rolls up to a pass:
175
- * `not_applicable` (nothing applied) is deliberately distinct from `meets`.
176
- */
177
- export type CodeAdvisorySummary = {
178
- /**
179
- * The rolled-up card, and what each token licenses:
180
- * - `meets` — every applicable requirement was `meets`. Advisory pass.
181
- * - `shortfall` — at least one requirement failed on real evidence. Blocking.
182
- * - `unverified` — nothing failed, but something is `unknown`. COMMON, and
183
- * never a pass: it means "no comparable rating is on file". Show it as an
184
- * open question, not a green tick.
185
- * - `not_applicable` — the space imposes nothing this material answers for.
186
- * Deliberately distinct from `meets`: it is silence, not approval.
187
- */
188
- status: OpenEnum<"meets" | "shortfall" | "unverified" | "not_applicable">;
189
- /** One deterministic sentence naming what failed, e.g. `"Falls short on fire for A-2 corridors."`. */
190
- headline: string;
191
- shortfallDimensions: OpenEnum<"fire" | "floor" | "slip" | "decorative">[];
192
- unverifiedDimensions: OpenEnum<"fire" | "floor" | "slip" | "decorative">[];
193
- };
194
- /** Per-variant advisory outcome, in input order. */
195
- export type CodeAdvisoryVariantResult = {
196
- /** Opaque — echoed verbatim from `variantIds`, in that position. */
197
- variantId: VariantId;
198
- /** `evaluated` when the id resolved; `unknown_variant` (with `requirements: []`) when it did not. */
199
- state: OpenEnum<"evaluated" | "unknown_variant">;
200
- /** What this material is and what it was scored against. Null only for `state: "unknown_variant"`. */
201
- material: MaterialCodeProfile | null;
202
- /** Verdicts for the requirements this material is answerable for — the answerable subset only. */
203
- requirements: CodeRequirementVerdict[];
204
- /** Requirements the space imposes that this material cannot answer. Empty when nothing was excluded. */
205
- notApplicable: CodeRequirementExclusion[];
206
- /** The rolled-up card, or null for an id that resolved to nothing. */
207
- summary: CodeAdvisorySummary | null;
208
- };
209
- /** The `POST /api/v1/codes/advisory` envelope — per-variant, per-requirement verdicts. */
210
- export type CodeAdvisoryResponse = {
211
- jurisdiction: CodeAdvisoryJurisdiction;
212
- /** The context AS SUPPLIED, echoed verbatim. */
213
- context: CodeAdvisoryContext;
214
- /** The canonical axes the registry was queried with, and where they came from. */
215
- resolvedContext: ResolvedCodeContext;
216
- /** The AHJ advisory caveat, at the envelope level too. */
217
- caveat: string;
218
- results: CodeAdvisoryVariantResult[];
219
- };
220
- /** Where a resolved axis came from: stated outright, or correlated from a room type. */
221
- export type ResolvedCodeContext = {
222
- jurisdictionId: string;
223
- occupancyGroup: string;
224
- /** The canonical finish-location term id the registry was queried with. */
225
- spaceType: string;
226
- sprinklered: boolean;
227
- basis: OpenEnum<"explicit" | "derived_from_room">;
228
- /** The `room` term the axes were correlated from, or null. */
229
- roomType: string | null;
230
- };
231
- /**
232
- * How far a jurisdiction's rows have been reviewed against that AHJ's own tables.
233
- *
234
- * - `thorough` — reviewed against this jurisdiction's own adopted tables.
235
- * - `model_baseline` — UNAMENDED model IBC stood in, because that AHJ's own
236
- * amendments have not been reviewed. Local amendments may be stricter, so
237
- * say "per model IBC" rather than naming the jurisdiction as the authority.
238
- */
239
- export type CodeCoverageTier = OpenEnum<"thorough" | "model_baseline">;
240
- export type CodeJurisdictionSummary = {
241
- id: string;
242
- name: string;
243
- codeFamily: string;
244
- edition: string;
245
- coverage: CodeCoverageTier;
246
- /** One honest line on how the model code actually applies in this jurisdiction. */
247
- adoptionNote: string;
248
- };
249
- /** The `GET /api/v1/codes/context` envelope — the vocabulary for stating a context. */
250
- export type CodeContextResponse = {
251
- jurisdictions: CodeJurisdictionSummary[];
252
- occupancyGroups: {
253
- id: string;
254
- label: string;
255
- }[];
256
- finishLocations: {
257
- id: string;
258
- label: string;
259
- shortLabel: string;
260
- }[];
261
- dimensions: {
262
- id: OpenEnum<"fire" | "floor" | "slip" | "decorative">;
263
- label: string;
264
- description: string;
265
- /** The MaterialGraph attribute this dimension is scored against. */
266
- attributeId: string;
267
- }[];
268
- scales: {
269
- id: string;
270
- label: string;
271
- detail: string;
272
- /** Ordered BEST FIRST: index 0 outranks index 1. */
273
- classes: string[];
274
- }[];
275
- /** `room` → regulatory axes. `null` where the registry refuses to correlate. */
276
- roomCorrelations: {
277
- roomType: string;
278
- label: string;
279
- occupancyGroup: string | null;
280
- finishLocation: string | null;
281
- }[];
282
- /** The AHJ advisory caveat. */
283
- caveat: string;
284
- };
285
- /** One requirement a context imposes, independent of any candidate material. */
286
- export type CodeRequirementListing = {
287
- requirementId: string;
288
- dimension: OpenEnum<"fire" | "floor" | "slip" | "decorative">;
289
- /** The code section this demand is cited from. */
290
- section: string;
291
- attributeId: string;
292
- requirement: CodeRequirementThreshold;
293
- /** One readable line, e.g. `"Class A or better · ASTM E84 / UL 723"`. */
294
- label: string;
295
- };
296
- /** Why one requirement the context imposes could not become a `list_variants` filter predicate. */
297
- export type CodeSpecFilterUnexpressible = {
298
- requirementId: string;
299
- dimension: OpenEnum<"fire" | "floor" | "slip" | "decorative">;
300
- reason: OpenEnum<"no_matchable_tokens" | "unsupported_scale" | "no_floor">;
301
- /** One readable line naming why, e.g. `"Scale ... has no ordinal definition..."`. */
302
- detail: string;
303
- };
304
- /**
305
- * The `GET /api/v1/codes/requirements` envelope. An empty `requirements` array
306
- * means the registry holds nothing for this context — it is not a pass.
307
- */
308
- export type CodeRequirementsResponse = {
309
- jurisdiction: CodeJurisdictionSummary;
310
- context: ResolvedCodeContext;
311
- requirements: CodeRequirementListing[];
312
- /**
313
- * The same requirements as a ready-to-use `list_variants` `filters` group
314
- * (MG-2222, `{terms, numbers, presence}`) — pass it straight through as
315
- * `listVariants({ filters })`.
316
- * `null` when nothing here is filterable; read `specFilterUnexpressible`
317
- * for why, not an empty `requirements` list.
318
- */
319
- specFilter: FilterGroups | null;
320
- /** The dimensions `specFilter` actually narrows on — a subset of `requirements`' dimensions. */
321
- specFilterApplied: OpenEnum<"fire" | "floor" | "slip" | "decorative">[];
322
- /** Requirements that could not be turned into a filter predicate, and why. */
323
- specFilterUnexpressible: CodeSpecFilterUnexpressible[];
324
- caveat: string;
325
- };