geospec 0.0.0 → 0.1.0-beta.3

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 (100) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +244 -0
  3. package/agent/doctrine.md +55 -0
  4. package/agent/geospec-authoring/SKILL.md +495 -0
  5. package/agent/geospec-authoring/api-classes.md +13 -0
  6. package/agent/geospec-authoring/api-constants.md +16 -0
  7. package/agent/geospec-authoring/api-functions.md +57 -0
  8. package/agent/geospec-authoring/api-index.md +377 -0
  9. package/agent/geospec-authoring/api-types.md +1788 -0
  10. package/agent/geospec-authoring/public-api-classes.md +22 -0
  11. package/agent/geospec-authoring/public-api-constants.md +51 -0
  12. package/agent/geospec-authoring/public-api-functions.md +215 -0
  13. package/agent/geospec-authoring/public-api-index.md +893 -0
  14. package/agent/geospec-authoring/public-api-types-2.md +1560 -0
  15. package/agent/geospec-authoring/public-api-types-3.md +217 -0
  16. package/agent/geospec-authoring/public-api-types.md +1948 -0
  17. package/agent/resources.d.ts +19 -0
  18. package/agent/resources.js +27 -0
  19. package/agent/skills.d.cts +13 -0
  20. package/agent/skills.json +28 -0
  21. package/dist/assertion-client/client.d.mts +134 -0
  22. package/dist/assertion-client/client.mjs +2 -0
  23. package/dist/assertion-client/index.d.mts +5 -0
  24. package/dist/assertion-client/index.mjs +1 -0
  25. package/dist/config/index.d.mts +3 -0
  26. package/dist/config/index.mjs +1 -0
  27. package/dist/config/node/index.d.mts +3 -0
  28. package/dist/config/node/index.mjs +1 -0
  29. package/dist/config/node/load.d.mts +19 -0
  30. package/dist/config/node/load.mjs +1 -0
  31. package/dist/config/tau-project-artifact.d.mts +54 -0
  32. package/dist/config/tau-project-artifact.mjs +3 -0
  33. package/dist/config/types.d.mts +57 -0
  34. package/dist/config/validate.mjs +1 -0
  35. package/dist/create-geospec.d.mts +50 -0
  36. package/dist/create-geospec.mjs +1 -0
  37. package/dist/engine/client.d.mts +113 -0
  38. package/dist/engine/client.mjs +1 -0
  39. package/dist/engine/index.d.mts +3 -0
  40. package/dist/engine/index.mjs +1 -0
  41. package/dist/engine/matchers.d.mts +70 -0
  42. package/dist/engine/matchers.mjs +1 -0
  43. package/dist/engine/pmi.types.d.mts +78 -0
  44. package/dist/engine/protocol.d.mts +20 -0
  45. package/dist/engine/protocol.mjs +1 -0
  46. package/dist/geometry-unit.d.mts +12 -0
  47. package/dist/index.d.mts +6 -0
  48. package/dist/index.mjs +1 -0
  49. package/dist/mesh/index.d.mts +4 -0
  50. package/dist/mesh/index.mjs +1 -0
  51. package/dist/mesh/types.d.mts +575 -0
  52. package/dist/model/errors.d.mts +20 -0
  53. package/dist/model/errors.mjs +2 -0
  54. package/dist/model/export-intent.d.mts +79 -0
  55. package/dist/model/export-intent.mjs +1 -0
  56. package/dist/model/index.d.mts +7 -0
  57. package/dist/model/index.mjs +1 -0
  58. package/dist/model/load-model.d.mts +25 -0
  59. package/dist/model/load-model.mjs +1 -0
  60. package/dist/model/native-model-loader.d.mts +142 -0
  61. package/dist/model/native-model-loader.mjs +1 -0
  62. package/dist/model/region-scope.d.mts +23 -0
  63. package/dist/model/region-scope.mjs +1 -0
  64. package/dist/model/subject-assertions.mjs +1 -0
  65. package/dist/model/subject.d.mts +11 -0
  66. package/dist/model/subject.mjs +1 -0
  67. package/dist/model/types.d.mts +254 -0
  68. package/dist/runner/collector.d.mts +64 -0
  69. package/dist/runner/collector.mjs +2 -0
  70. package/dist/runner/discovery.d.mts +93 -0
  71. package/dist/runner/discovery.mjs +1 -0
  72. package/dist/runner/filter.d.mts +50 -0
  73. package/dist/runner/filter.mjs +1 -0
  74. package/dist/runner/index.d.mts +7 -0
  75. package/dist/runner/index.mjs +1 -0
  76. package/dist/runner/native/index.d.mts +4 -0
  77. package/dist/runner/native/index.mjs +1 -0
  78. package/dist/runner/native/native-pool-grants.d.mts +21 -0
  79. package/dist/runner/native/native-pool-grants.mjs +1 -0
  80. package/dist/runner/native/native-serial-runner.d.mts +29 -0
  81. package/dist/runner/native/native-serial-runner.mjs +1 -0
  82. package/dist/runner/pool/pool-messages.d.mts +94 -0
  83. package/dist/runner/run-geospec-module.d.mts +13 -0
  84. package/dist/runner/run-geospec-module.mjs +47 -0
  85. package/dist/runner/timeout-codes.d.mts +45 -0
  86. package/dist/runner/timeout-codes.mjs +1 -0
  87. package/dist/runner/types.d.mts +776 -0
  88. package/dist/runner/worker/index.d.mts +4 -0
  89. package/dist/runner/worker/index.mjs +1 -0
  90. package/dist/runner/worker/no-matching-tests-issue.d.mts +12 -0
  91. package/dist/runner/worker/no-matching-tests-issue.mjs +1 -0
  92. package/dist/runner/worker/runner-types.d.mts +167 -0
  93. package/dist/runner/worker/serial-runner.mjs +1 -0
  94. package/dist/selector/types.d.mts +271 -0
  95. package/dist/step/types.d.mts +134 -0
  96. package/dist/vitest/adapter.d.mts +56 -0
  97. package/dist/vitest/adapter.mjs +3 -0
  98. package/dist/vitest/index.d.mts +4 -0
  99. package/dist/vitest/index.mjs +1 -0
  100. package/package.json +114 -5
@@ -0,0 +1,1948 @@
1
+ # geospec — Types
2
+
3
+ 90 top-level symbols. Signatures are verbatim typescript.
4
+
5
+ // A model admitted by one live GeoSpec host scope
6
+ GeoSpecSubject: {
7
+ readonly [subjectBrand]: true;
8
+ }
9
+
10
+ readonly [subjectBrand]: true
11
+
12
+ // Geometry units accepted at GeoSpec evidence-loading boundaries
13
+ // Remarks: Loaded subjects normalize coordinates into canonical millimetres; this type describes source and provenance units, not a project-wide configuration.
14
+ GeoSpecUnit: 'mm' | 'cm' | 'm' | 'in' | 'ft' | (string & {})
15
+
16
+ // Geometry assertion collected from a GeoSpec test module
17
+ GeoSpecAssertion: {
18
+ /** Assertion kind. */
19
+ kind: 'boundingBox' | 'connectedComponents' | 'watertight' | 'componentInterference' | 'assemblyOccurrences' | 'spatialRelationships' | 'meshIntegrity' | 'noDiagnostics' | 'surfaceArea' | 'volume' | 'mass' | 'centerOfMass' | 'validBrep' | 'topologyCounts' | 'stepUnits' | 'productStructure' | 'planarFace' | 'cylindricalFace' | 'circularHole' | 'circularHolePattern' | 'chamferFeature' | 'filletFeature' | 'minimumWallThickness' | 'voidContinuity' | 'toSatisfyRationalPlate' | 'toSatisfyParallelPlaneDistance';
20
+ /** User-authored value passed to expectGeo(). */
21
+ subject: unknown;
22
+ /** Expected geometry condition. */
23
+ expected: unknown;
24
+ /** True when the assertion evaluated successfully. */
25
+ passed?: boolean;
26
+ /** Structured diagnostics from matcher evaluation. */
27
+ diagnostics?: GeometryDiagnostic[];
28
+ /** Exact compiled assertion report, including core-owned bytes and polarity. */
29
+ report?: GeoSpecCanonicalClaimReport;
30
+ /** The host load which admitted this assertion's subject; independent of equal geometry hashes. */
31
+ loadId?: string;
32
+ /**
33
+ * The crop this assertion's subject was loaded with. Present only for a region load: the verdict
34
+ * holds for this millimetre box, never for the whole model.
35
+ */
36
+ region?: GeoSpecLoadRegion;
37
+ /** Wall-clock cost of matcher evaluation in milliseconds (R1: budgeted matchers only). */
38
+ durationMs?: number;
39
+ }
40
+
41
+ // Assertion kind
42
+ kind: 'boundingBox' | 'connectedComponents' | 'watertight' | 'componentInterference' | 'assemblyOccurrences' | 'spatialRelationships' | 'meshIntegrity' | 'noDiagnostics' | 'surfaceArea' | 'volume' | 'mass' | 'centerOfMass' | 'validBrep' | 'topologyCounts' | 'stepUnits' | 'productStructure' | 'planarFace' | 'cylindricalFace' | 'circularHole' | 'circularHolePattern' | 'chamferFeature' | 'filletFeature' | 'minimumWallThickness' | 'voidContinuity' | 'toSatisfyRationalPlate' | 'toSatisfyParallelPlaneDistance'
43
+
44
+ // User-authored value passed to expectGeo()
45
+ subject: unknown
46
+
47
+ // Expected geometry condition
48
+ expected: unknown
49
+
50
+ // True when the assertion evaluated successfully
51
+ passed?: boolean
52
+
53
+ // Structured diagnostics from matcher evaluation
54
+ diagnostics?: GeometryDiagnostic[]
55
+
56
+ // Exact compiled assertion report, including core-owned bytes and polarity
57
+ report?: GeoSpecCanonicalClaimReport
58
+
59
+ // The host load which admitted this assertion's subject
60
+ loadId?: string
61
+
62
+ // The crop this assertion's subject was loaded with
63
+ region?: GeoSpecLoadRegion
64
+
65
+ // Wall-clock cost of matcher evaluation in milliseconds (R1
66
+ durationMs?: number
67
+
68
+ // Assembly occurrence rule accepted by `expectGeo(...).toHaveAssemblyOccurrences(...)`
69
+ GeoSpecAssemblyOccurrenceExpectation: {
70
+ name: GeoSpecComponentSelector;
71
+ count?: GeoSpecNumericExpectation;
72
+ bounds?: {
73
+ within?: GeoSpecComponentSelector;
74
+ min?: Vec3 | GeoSpecAxisExpectation;
75
+ max?: Vec3 | GeoSpecAxisExpectation;
76
+ center?: GeoSpecPointExpectation;
77
+ tolerance?: number;
78
+ };
79
+ }
80
+
81
+ name: GeoSpecComponentSelector
82
+
83
+ count?: GeoSpecNumericExpectation
84
+
85
+ bounds?: {
86
+ within?: GeoSpecComponentSelector;
87
+ min?: Vec3 | GeoSpecAxisExpectation;
88
+ max?: Vec3 | GeoSpecAxisExpectation;
89
+ center?: GeoSpecPointExpectation;
90
+ tolerance?: number;
91
+ }
92
+
93
+ // Assembly occurrence expectation accepted by `expectGeo(...).toHaveAssemblyOccurrences(...)`
94
+ GeoSpecAssemblyOccurrencesExpectation: {
95
+ occurrences: GeoSpecAssemblyOccurrenceExpectation[];
96
+ uniqueNames?: boolean;
97
+ }
98
+
99
+ occurrences: GeoSpecAssemblyOccurrenceExpectation[]
100
+
101
+ uniqueNames?: boolean
102
+
103
+ // Axis-keyed numeric expectation used by high-level geometry matchers
104
+ GeoSpecAxisExpectation: {
105
+ x?: number;
106
+ y?: number;
107
+ z?: number;
108
+ }
109
+
110
+ x?: number
111
+
112
+ y?: number
113
+
114
+ z?: number
115
+
116
+ // Bounding-box expectation accepted by `expectGeo(...).toHaveBoundingBox(...)`
117
+ GeoSpecBoundingBoxExpectation: {
118
+ min?: Vec3 | Partial<Record<keyof GeoSpecAxisExpectation, GeoSpecNumericExpectation>>;
119
+ max?: Vec3 | Partial<Record<keyof GeoSpecAxisExpectation, GeoSpecNumericExpectation>>;
120
+ size?: Vec3 | Partial<Record<keyof GeoSpecAxisExpectation, GeoSpecNumericExpectation>>;
121
+ center?: Vec3 | Partial<Record<keyof GeoSpecAxisExpectation, GeoSpecNumericExpectation>>;
122
+ tolerance?: number;
123
+ }
124
+
125
+ min?: Vec3 | Partial<Record<keyof GeoSpecAxisExpectation, GeoSpecNumericExpectation>>
126
+
127
+ max?: Vec3 | Partial<Record<keyof GeoSpecAxisExpectation, GeoSpecNumericExpectation>>
128
+
129
+ size?: Vec3 | Partial<Record<keyof GeoSpecAxisExpectation, GeoSpecNumericExpectation>>
130
+
131
+ center?: Vec3 | Partial<Record<keyof GeoSpecAxisExpectation, GeoSpecNumericExpectation>>
132
+
133
+ tolerance?: number
134
+
135
+ // Center-of-mass expectation accepted by `expectGeo(...).toHaveCenterOfMass(...)`
136
+ GeoSpecCenterOfMassExpectation: {
137
+ /** Exact expected point in millimetres; per-axis ranges such as `{ greaterThan }` are not supported. */
138
+ point: GeoSpecPointExpectation;
139
+ tolerance?: number;
140
+ }
141
+
142
+ // Exact expected point in millimetres
143
+ point: GeoSpecPointExpectation
144
+
145
+ tolerance?: number
146
+
147
+ // Chamfer-feature expectation accepted by `expectGeo(...).toHaveChamferFeature(...)`
148
+ GeoSpecChamferFeatureExpectation: {
149
+ distance: number;
150
+ selection?: string;
151
+ tolerance?: number;
152
+ }
153
+
154
+ distance: number
155
+
156
+ selection?: string
157
+
158
+ tolerance?: number
159
+
160
+ // Circular-hole expectation accepted by `expectGeo(...).toHaveCircularHole(...)`
161
+ GeoSpecCircularHoleExpectation: {
162
+ diameter: number;
163
+ through?: boolean;
164
+ axis?: 'x' | 'y' | 'z';
165
+ center?: GeoSpecPointExpectation;
166
+ tolerance?: number;
167
+ }
168
+
169
+ diameter: number
170
+
171
+ through?: boolean
172
+
173
+ axis?: 'x' | 'y' | 'z'
174
+
175
+ center?: GeoSpecPointExpectation
176
+
177
+ tolerance?: number
178
+
179
+ // Cylindrical-face expectation accepted by `expectGeo(...).toHaveCylindricalFace(...)`
180
+ GeoSpecCylindricalFaceExpectation: {
181
+ radius: number;
182
+ axis: 'x' | 'y' | 'z';
183
+ tolerance?: number;
184
+ }
185
+
186
+ radius: number
187
+
188
+ axis: 'x' | 'y' | 'z'
189
+
190
+ tolerance?: number
191
+
192
+ // Per-chain options accepted by `expectGeo(subject, options)`
193
+ GeoSpecExpectOptions: {
194
+ /**
195
+ * Deterministic work-unit budget for every claim in this chain: a positive integer no
196
+ * greater than the host's maximum (default `geoSpecMaximumWorkUnitLimit`, 512 000 000). Omitted uses the subject's
197
+ * `loadModel` budget, then the host's, then the engine default. Raise it to retry a
198
+ * `MATCHER_TIMEOUT` refusal whose `details.used` exceeded `details.limit`.
199
+ */
200
+ readonly workUnitLimit?: number;
201
+ }
202
+
203
+ // Deterministic work-unit budget for every claim in this chain
204
+ readonly workUnitLimit?: number
205
+
206
+ // Intentional component interference allowance accepted by `expectGeo(...).toHaveNoComponentInterference(...)`
207
+ GeoSpecComponentInterferenceAllowance: {
208
+ kind?: 'intentionalInterference';
209
+ left: GeoSpecComponentSelector;
210
+ right: GeoSpecComponentSelector;
211
+ maxVolume?: number;
212
+ reason: string;
213
+ }
214
+
215
+ kind?: 'intentionalInterference'
216
+
217
+ left: GeoSpecComponentSelector
218
+
219
+ right: GeoSpecComponentSelector
220
+
221
+ maxVolume?: number
222
+
223
+ reason: string
224
+
225
+ // Component-interference expectation accepted by `expectGeo(...).toHaveNoComponentInterference(...)`
226
+ // Remarks: GeoSpec checks for positive solid intersection volume between assembly components. Tangent contact and correctly meshed gears are allowed. `pairs` narrows the check to specific component-label pairs while preserving exact positive-volume evidence for every selected pair. `allowances` documents explicitly intentional positive-volume interference such as gasket compression, press fits, or simplified thread engagement.
227
+ GeoSpecComponentInterferenceExpectation: {
228
+ tolerance?: number;
229
+ pairs?: GeoSpecComponentInterferencePairExpectation[];
230
+ allowances?: GeoSpecComponentInterferenceAllowance[];
231
+ }
232
+
233
+ tolerance?: number
234
+
235
+ pairs?: GeoSpecComponentInterferencePairExpectation[]
236
+
237
+ allowances?: GeoSpecComponentInterferenceAllowance[]
238
+
239
+ // A pair-specific component-interference check accepted by `expectGeo(...).toHaveNoComponentInterference(...)`
240
+ GeoSpecComponentInterferencePairExpectation: {
241
+ left: GeoSpecComponentSelector;
242
+ right: GeoSpecComponentSelector;
243
+ }
244
+
245
+ left: GeoSpecComponentSelector
246
+
247
+ right: GeoSpecComponentSelector
248
+
249
+ // Connected-components expectation accepted by `expectGeo(...).toHaveConnectedComponents(...)`
250
+ GeoSpecConnectedComponentsExpectation: {
251
+ count: number;
252
+ tolerance?: number;
253
+ toleranceMm?: number;
254
+ }
255
+
256
+ count: number
257
+
258
+ tolerance?: number
259
+
260
+ toleranceMm?: number
261
+
262
+ // Geometry selector used by inspection and spatial relationship matchers
263
+ GeoSpecGeometrySelector: GeoSpecComponentSelector | {
264
+ kind: 'occurrence';
265
+ /** Product or instance name to match (also matches the PRODUCT name shared by instanced parts). */
266
+ name?: GeoSpecComponentSelector;
267
+ /** Exact occurrence path — the per-instance identity when instances share one product. */
268
+ path?: GeoSpecComponentSelector;
269
+ } | {
270
+ kind: 'axis';
271
+ name?: GeoSpecComponentSelector;
272
+ axis?: 'x' | 'y' | 'z';
273
+ center?: Vec3;
274
+ direction?: Vec3;
275
+ radius?: number;
276
+ tolerance?: number;
277
+ } | {
278
+ kind: 'plane';
279
+ name?: GeoSpecComponentSelector;
280
+ normal?: Vec3;
281
+ offset?: number;
282
+ tolerance?: number;
283
+ }
284
+ /**
285
+ * SB3 V1 selector catalog kinds (body/face/datum/interface/group plus the
286
+ * string shorthand), resolved by the native engine. SB4 routes
287
+ * relationship endpoints through that engine: the legacy explicit
288
+ * axis/plane members above resolve as `stability: 'explicit'` fixtures
289
+ * (rejected by the production evidence policy), while named legacy forms
290
+ * become engine queries. The catalog's occurrence/axis/plane query forms
291
+ * stay out of this union because their `kind` discriminants collide with
292
+ * the legacy explicit members.
293
+ */
294
+ | Exclude<GeometrySelector, string | {
295
+ kind: 'occurrence' | 'axis' | 'plane';
296
+ }>
297
+
298
+ // Assertion chain returned by `expectGeo(subject)`
299
+ GeoSpecMatcher: {
300
+ /** Core-owned negation; missing or refused evidence still fails. */
301
+ readonly not: Omit<GeoSpecMatcher, 'not'>;
302
+ /** Assert the fixed rational plate contract. */
303
+ toSatisfyRationalPlate(): GeoSpecAssertion;
304
+ /** Assert the fixed parallel-plane distance contract. */
305
+ toSatisfyParallelPlaneDistance(): GeoSpecAssertion;
306
+ /**
307
+ * Assert axis-aligned bounds, size, or center for a loaded geometry subject.
308
+ */
309
+ toHaveBoundingBox(first: Vec3 | GeoSpecBoundingBoxExpectation, second?: Vec3): GeoSpecAssertion;
310
+ /**
311
+ * Assert how many spatially disjoint chunks the mesh contains.
312
+ */
313
+ toHaveConnectedComponents(expected: GeoSpecConnectedComponentsExpectation): GeoSpecAssertion;
314
+ /**
315
+ * Assert closed mesh edge incidence; this does not prove vertex-manifold
316
+ * validity, embeddedness, a Boolean fuse, or material connectivity.
317
+ */
318
+ toBeWatertight(): GeoSpecAssertion;
319
+ /**
320
+ * Assert that separate assembly components do not occupy the same solid volume.
321
+ */
322
+ toHaveNoComponentInterference(expected?: GeoSpecComponentInterferenceExpectation): GeoSpecAssertion;
323
+ /**
324
+ * Assert that named assembly occurrences exist with expected counts and bounds.
325
+ */
326
+ toHaveAssemblyOccurrences(expected: GeoSpecAssemblyOccurrencesExpectation): GeoSpecAssertion;
327
+ /**
328
+ * Assert that selected entities satisfy declared spatial relationships.
329
+ */
330
+ toHaveSpatialRelationships(expected: GeoSpecSpatialRelationshipsExpectation): GeoSpecAssertion;
331
+ /**
332
+ * Assert rendered mesh evidence is internally trustworthy for downstream checks.
333
+ */
334
+ toHaveMeshIntegrity(expected: GeoSpecMeshIntegrityExpectation): GeoSpecAssertion;
335
+ /**
336
+ * Assert that the subject carries no diagnostics at the rejected severities.
337
+ */
338
+ toHaveNoDiagnostics(expected?: GeoSpecNoDiagnosticsExpectation): GeoSpecAssertion;
339
+ /**
340
+ * Assert total surface area, preferring exact BRep mass properties when available.
341
+ */
342
+ toHaveSurfaceArea(expected: GeoSpecSurfaceAreaExpectation): GeoSpecAssertion;
343
+ /**
344
+ * Assert enclosed volume, preferring exact BRep mass properties when available.
345
+ */
346
+ toHaveVolume(expected: GeoSpecVolumeExpectation): GeoSpecAssertion;
347
+ /**
348
+ * Assert mass derived from exact mass properties or volume times density.
349
+ */
350
+ toHaveMass(expected: GeoSpecMassExpectation): GeoSpecAssertion;
351
+ /**
352
+ * Assert the center of mass or mesh-derived centroid for a closed subject.
353
+ */
354
+ toHaveCenterOfMass(expected: GeoSpecCenterOfMassExpectation): GeoSpecAssertion;
355
+ /**
356
+ * Assert that exact BRep evidence reports a valid shape.
357
+ */
358
+ toBeValidBrep(expected?: GeoSpecValidBrepExpectation): GeoSpecAssertion;
359
+ /**
360
+ * Assert exact BRep topology counts.
361
+ */
362
+ toHaveTopologyCounts(expected: GeoSpecTopologyCountsExpectation): GeoSpecAssertion;
363
+ /**
364
+ * Assert the STEP unit evidence.
365
+ */
366
+ toHaveStepUnits(expected: GeoSpecStepUnitsExpectation): GeoSpecAssertion;
367
+ /**
368
+ * Assert STEP product-structure evidence.
369
+ */
370
+ toHaveProductStructure(expected: GeoSpecProductStructureExpectation): GeoSpecAssertion;
371
+ /**
372
+ * Assert that BRep evidence contains a planar face matching the requested constraints.
373
+ */
374
+ toHavePlanarFace(expected: GeoSpecPlanarFaceExpectation): GeoSpecAssertion;
375
+ /**
376
+ * Assert that BRep evidence contains a cylindrical face with the requested radius and axis.
377
+ */
378
+ toHaveCylindricalFace(expected: GeoSpecCylindricalFaceExpectation): GeoSpecAssertion;
379
+ /**
380
+ * Assert that BRep evidence contains a circular hole matching diameter, center, and axis.
381
+ */
382
+ toHaveCircularHole(expected: GeoSpecCircularHoleExpectation): GeoSpecAssertion;
383
+ /**
384
+ * Assert that BRep evidence contains a repeated circular-hole pattern.
385
+ */
386
+ toHaveCircularHolePattern(expected: GeoSpecCircularHolePatternExpectation): GeoSpecAssertion;
387
+ /**
388
+ * Assert that BRep evidence contains a chamfer feature with the requested distance.
389
+ */
390
+ toHaveChamferFeature(expected: GeoSpecChamferFeatureExpectation): GeoSpecAssertion;
391
+ /**
392
+ * Assert that BRep evidence contains a fillet feature with the requested radius.
393
+ */
394
+ toHaveFilletFeature(expected: GeoSpecFilletFeatureExpectation): GeoSpecAssertion;
395
+ /**
396
+ * Assert that BRep evidence reports a minimum wall thickness satisfying the expectation.
397
+ */
398
+ toHaveMinimumWallThickness(expected: GeoSpecMinimumWallThicknessExpectation): GeoSpecAssertion;
399
+ /**
400
+ * Assert that the declared waypoints share one connected void, stay isolated
401
+ * from the declared spaces, and hold the minimum cross-section.
402
+ */
403
+ toHaveVoidContinuity(expected: GeoSpecVoidContinuityExpectation): GeoSpecAssertion;
404
+ }
405
+
406
+ // Core-owned negation
407
+ readonly not: Omit<GeoSpecMatcher, 'not'>
408
+
409
+ // Assert the fixed rational plate contract
410
+ // GeoSpecMatcher.toSatisfyRationalPlate (method)
411
+ toSatisfyRationalPlate(): GeoSpecAssertion;
412
+
413
+ // Assert the fixed parallel-plane distance contract
414
+ // GeoSpecMatcher.toSatisfyParallelPlaneDistance (method)
415
+ toSatisfyParallelPlaneDistance(): GeoSpecAssertion;
416
+
417
+ // Assert axis-aligned bounds, size, or center for a loaded geometry subject
418
+ // GeoSpecMatcher.toHaveBoundingBox (method)
419
+ toHaveBoundingBox(first: Vec3 | GeoSpecBoundingBoxExpectation, second?: Vec3): GeoSpecAssertion;
420
+
421
+ // Assert how many spatially disjoint chunks the mesh contains
422
+ // GeoSpecMatcher.toHaveConnectedComponents (method)
423
+ toHaveConnectedComponents(expected: GeoSpecConnectedComponentsExpectation): GeoSpecAssertion;
424
+
425
+ // Assert closed mesh edge incidence
426
+ // GeoSpecMatcher.toBeWatertight (method)
427
+ toBeWatertight(): GeoSpecAssertion;
428
+
429
+ // Assert that separate assembly components do not occupy the same solid volume
430
+ // GeoSpecMatcher.toHaveNoComponentInterference (method)
431
+ toHaveNoComponentInterference(expected?: GeoSpecComponentInterferenceExpectation): GeoSpecAssertion;
432
+
433
+ // Assert that named assembly occurrences exist with expected counts and bounds
434
+ // GeoSpecMatcher.toHaveAssemblyOccurrences (method)
435
+ toHaveAssemblyOccurrences(expected: GeoSpecAssemblyOccurrencesExpectation): GeoSpecAssertion;
436
+
437
+ // Assert that selected entities satisfy declared spatial relationships
438
+ // GeoSpecMatcher.toHaveSpatialRelationships (method)
439
+ toHaveSpatialRelationships(expected: GeoSpecSpatialRelationshipsExpectation): GeoSpecAssertion;
440
+
441
+ // Assert rendered mesh evidence is internally trustworthy for downstream checks
442
+ // GeoSpecMatcher.toHaveMeshIntegrity (method)
443
+ toHaveMeshIntegrity(expected: GeoSpecMeshIntegrityExpectation): GeoSpecAssertion;
444
+
445
+ // Assert that the subject carries no diagnostics at the rejected severities
446
+ // GeoSpecMatcher.toHaveNoDiagnostics (method)
447
+ toHaveNoDiagnostics(expected?: GeoSpecNoDiagnosticsExpectation): GeoSpecAssertion;
448
+
449
+ // Assert total surface area, preferring exact BRep mass properties when available
450
+ // GeoSpecMatcher.toHaveSurfaceArea (method)
451
+ toHaveSurfaceArea(expected: GeoSpecSurfaceAreaExpectation): GeoSpecAssertion;
452
+
453
+ // Assert enclosed volume, preferring exact BRep mass properties when available
454
+ // GeoSpecMatcher.toHaveVolume (method)
455
+ toHaveVolume(expected: GeoSpecVolumeExpectation): GeoSpecAssertion;
456
+
457
+ // Assert mass derived from exact mass properties or volume times density
458
+ // GeoSpecMatcher.toHaveMass (method)
459
+ toHaveMass(expected: GeoSpecMassExpectation): GeoSpecAssertion;
460
+
461
+ // Assert the center of mass or mesh-derived centroid for a closed subject
462
+ // GeoSpecMatcher.toHaveCenterOfMass (method)
463
+ toHaveCenterOfMass(expected: GeoSpecCenterOfMassExpectation): GeoSpecAssertion;
464
+
465
+ // Assert that exact BRep evidence reports a valid shape
466
+ // GeoSpecMatcher.toBeValidBrep (method)
467
+ toBeValidBrep(expected?: GeoSpecValidBrepExpectation): GeoSpecAssertion;
468
+
469
+ // Assert exact BRep topology counts
470
+ // GeoSpecMatcher.toHaveTopologyCounts (method)
471
+ toHaveTopologyCounts(expected: GeoSpecTopologyCountsExpectation): GeoSpecAssertion;
472
+
473
+ // Assert the STEP unit evidence
474
+ // GeoSpecMatcher.toHaveStepUnits (method)
475
+ toHaveStepUnits(expected: GeoSpecStepUnitsExpectation): GeoSpecAssertion;
476
+
477
+ // Assert STEP product-structure evidence
478
+ // GeoSpecMatcher.toHaveProductStructure (method)
479
+ toHaveProductStructure(expected: GeoSpecProductStructureExpectation): GeoSpecAssertion;
480
+
481
+ // Assert that BRep evidence contains a planar face matching the requested constraints
482
+ // GeoSpecMatcher.toHavePlanarFace (method)
483
+ toHavePlanarFace(expected: GeoSpecPlanarFaceExpectation): GeoSpecAssertion;
484
+
485
+ // Assert that BRep evidence contains a cylindrical face with the requested radius and axis
486
+ // GeoSpecMatcher.toHaveCylindricalFace (method)
487
+ toHaveCylindricalFace(expected: GeoSpecCylindricalFaceExpectation): GeoSpecAssertion;
488
+
489
+ // Assert that BRep evidence contains a circular hole matching diameter, center, and axis
490
+ // GeoSpecMatcher.toHaveCircularHole (method)
491
+ toHaveCircularHole(expected: GeoSpecCircularHoleExpectation): GeoSpecAssertion;
492
+
493
+ // Assert that BRep evidence contains a repeated circular-hole pattern
494
+ // GeoSpecMatcher.toHaveCircularHolePattern (method)
495
+ toHaveCircularHolePattern(expected: GeoSpecCircularHolePatternExpectation): GeoSpecAssertion;
496
+
497
+ // Assert that BRep evidence contains a chamfer feature with the requested distance
498
+ // GeoSpecMatcher.toHaveChamferFeature (method)
499
+ toHaveChamferFeature(expected: GeoSpecChamferFeatureExpectation): GeoSpecAssertion;
500
+
501
+ // Assert that BRep evidence contains a fillet feature with the requested radius
502
+ // GeoSpecMatcher.toHaveFilletFeature (method)
503
+ toHaveFilletFeature(expected: GeoSpecFilletFeatureExpectation): GeoSpecAssertion;
504
+
505
+ // Assert that BRep evidence reports a minimum wall thickness satisfying the expectation
506
+ // GeoSpecMatcher.toHaveMinimumWallThickness (method)
507
+ toHaveMinimumWallThickness(expected: GeoSpecMinimumWallThicknessExpectation): GeoSpecAssertion;
508
+
509
+ // Assert that the declared waypoints share one connected void, stay isolated from the declared spaces, and hold the minimum cross-section
510
+ // GeoSpecMatcher.toHaveVoidContinuity (method)
511
+ toHaveVoidContinuity(expected: GeoSpecVoidContinuityExpectation): GeoSpecAssertion;
512
+
513
+ // Mass expectation accepted by `expectGeo(...).toHaveMass(...)`
514
+ GeoSpecMassExpectation: {
515
+ value: number | GeoSpecNumericExpectation;
516
+ density?: number;
517
+ tolerance?: number;
518
+ }
519
+
520
+ value: number | GeoSpecNumericExpectation
521
+
522
+ density?: number
523
+
524
+ tolerance?: number
525
+
526
+ // Mesh integrity expectation accepted by `expectGeo(...).toHaveMeshIntegrity(...)`
527
+ GeoSpecMeshIntegrityExpectation: {
528
+ finitePositions?: boolean;
529
+ degenerateTriangles?: {
530
+ count?: number;
531
+ maxCount?: number;
532
+ areaTolerance?: number;
533
+ };
534
+ duplicateFaces?: {
535
+ count?: number;
536
+ maxCount?: number;
537
+ };
538
+ watertight?: boolean;
539
+ triangleCount?: GeoSpecNumericExpectation;
540
+ }
541
+
542
+ finitePositions?: boolean
543
+
544
+ degenerateTriangles?: {
545
+ count?: number;
546
+ maxCount?: number;
547
+ areaTolerance?: number;
548
+ }
549
+
550
+ duplicateFaces?: {
551
+ count?: number;
552
+ maxCount?: number;
553
+ }
554
+
555
+ watertight?: boolean
556
+
557
+ triangleCount?: GeoSpecNumericExpectation
558
+
559
+ // Diagnostic severities rejected by `expectGeo(...).toHaveNoDiagnostics(...)`
560
+ // Remarks: Defaults to `error` and `warning` when omitted.
561
+ GeoSpecNoDiagnosticsExpectation: {
562
+ severities?: Array<GeometryDiagnostic['severity']>;
563
+ }
564
+
565
+ severities?: Array<GeometryDiagnostic['severity']>
566
+
567
+ // Minimum-wall-thickness expectation accepted by `expectGeo(...).toHaveMinimumWallThickness(...)`
568
+ GeoSpecMinimumWallThicknessExpectation: {
569
+ value: GeoSpecNumericExpectation;
570
+ tolerance?: number;
571
+ }
572
+
573
+ value: GeoSpecNumericExpectation
574
+
575
+ tolerance?: number
576
+
577
+ // Circular-hole-pattern expectation accepted by `expectGeo(...).toHaveCircularHolePattern(...)`
578
+ GeoSpecCircularHolePatternExpectation: {
579
+ count: number;
580
+ holeDiameter: number;
581
+ boltCircleDiameter?: number;
582
+ axis?: 'x' | 'y' | 'z';
583
+ center?: GeoSpecPointExpectation;
584
+ tolerance?: number;
585
+ }
586
+
587
+ count: number
588
+
589
+ holeDiameter: number
590
+
591
+ boltCircleDiameter?: number
592
+
593
+ axis?: 'x' | 'y' | 'z'
594
+
595
+ center?: GeoSpecPointExpectation
596
+
597
+ tolerance?: number
598
+
599
+ // Fillet-feature expectation accepted by `expectGeo(...).toHaveFilletFeature(...)`
600
+ GeoSpecFilletFeatureExpectation: {
601
+ radius: number;
602
+ selection?: string;
603
+ tolerance?: number;
604
+ }
605
+
606
+ radius: number
607
+
608
+ selection?: string
609
+
610
+ tolerance?: number
611
+
612
+ // Shared scalar expectation used by geometry measurements
613
+ GeoSpecNumericExpectation: number | {
614
+ value?: number;
615
+ greaterThan?: number;
616
+ greaterThanOrEqual?: number;
617
+ lessThan?: number;
618
+ lessThanOrEqual?: number;
619
+ }
620
+
621
+ // Planar-face expectation accepted by `expectGeo(...).toHavePlanarFace(...)`
622
+ GeoSpecPlanarFaceExpectation: {
623
+ normal: GeoSpecPointExpectation;
624
+ offset: number;
625
+ area?: GeoSpecNumericExpectation;
626
+ tolerance?: number;
627
+ }
628
+
629
+ normal: GeoSpecPointExpectation
630
+
631
+ offset: number
632
+
633
+ area?: GeoSpecNumericExpectation
634
+
635
+ tolerance?: number
636
+
637
+ // Point expectation accepted by center and feature matchers
638
+ GeoSpecPointExpectation: Vec3 | GeoSpecAxisExpectation
639
+
640
+ // Product-structure expectation accepted by `expectGeo(...).toHaveProductStructure(...)`
641
+ GeoSpecProductStructureExpectation: {
642
+ names?: string[];
643
+ count?: GeoSpecNumericExpectation;
644
+ }
645
+
646
+ names?: string[]
647
+
648
+ count?: GeoSpecNumericExpectation
649
+
650
+ // One spatial relationship accepted by `expectGeo(...).toHaveSpatialRelationships(...)`
651
+ // Remarks: Verdicts are decided by exact BRep evidence only (D3): extrema for `contact`/`clearance`, analytic fact comparison for `coaxial`/`concentric`/`coplanar`/`parallel`/`perpendicular`/`angle`, exact solid classification for `containment`/`insertion`, and exact boolean common volume for `interference` (positive volume outside the `minVolume`/`maxVolume` allowance band fails).
652
+ GeoSpecSpatialRelationshipExpectation: {
653
+ id?: string;
654
+ kind: 'contact' | 'clearance' | 'coaxial' | 'concentric' | 'coplanar' | 'parallel' | 'perpendicular' | 'angle' | 'containment' | 'insertion' | 'interference';
655
+ subject: GeoSpecGeometrySelector;
656
+ target: GeoSpecGeometrySelector;
657
+ tolerance?: number;
658
+ angularToleranceDegrees?: number;
659
+ /** Expected angle in degrees for `kind: 'angle'` (orientation-insensitive). */
660
+ angleDegrees?: number;
661
+ /** Declared insertion axis (subject-frame direction) for `kind: 'insertion'`. */
662
+ axis?: Vec3;
663
+ min?: number;
664
+ max?: number;
665
+ minVolume?: number;
666
+ maxVolume?: number;
667
+ reason?: string;
668
+ }
669
+
670
+ id?: string
671
+
672
+ kind: 'contact' | 'clearance' | 'coaxial' | 'concentric' | 'coplanar' | 'parallel' | 'perpendicular' | 'angle' | 'containment' | 'insertion' | 'interference'
673
+
674
+ subject: GeoSpecGeometrySelector
675
+
676
+ target: GeoSpecGeometrySelector
677
+
678
+ tolerance?: number
679
+
680
+ angularToleranceDegrees?: number
681
+
682
+ // Expected angle in degrees for `kind
683
+ angleDegrees?: number
684
+
685
+ // Declared insertion axis (subject-frame direction) for `kind
686
+ axis?: Vec3
687
+
688
+ min?: number
689
+
690
+ max?: number
691
+
692
+ minVolume?: number
693
+
694
+ maxVolume?: number
695
+
696
+ reason?: string
697
+
698
+ // Spatial relationship expectation accepted by `expectGeo(...).toHaveSpatialRelationships(...)`
699
+ GeoSpecSpatialRelationshipsExpectation: {
700
+ relationships: GeoSpecSpatialRelationshipExpectation[];
701
+ }
702
+
703
+ relationships: GeoSpecSpatialRelationshipExpectation[]
704
+
705
+ // STEP unit expectation accepted by `expectGeo(...).toHaveStepUnits(...)`
706
+ GeoSpecStepUnitsExpectation: {
707
+ unit: string;
708
+ }
709
+
710
+ unit: string
711
+
712
+ // Surface-area expectation accepted by `expectGeo(...).toHaveSurfaceArea(...)`
713
+ GeoSpecSurfaceAreaExpectation: {
714
+ value: number | GeoSpecNumericExpectation;
715
+ tolerance?: number;
716
+ }
717
+
718
+ value: number | GeoSpecNumericExpectation
719
+
720
+ tolerance?: number
721
+
722
+ // Topology-count expectation accepted by `expectGeo(...).toHaveTopologyCounts(...)`
723
+ GeoSpecTopologyCountsExpectation: {
724
+ vertices?: GeoSpecNumericExpectation;
725
+ edges?: GeoSpecNumericExpectation;
726
+ wires?: GeoSpecNumericExpectation;
727
+ faces?: GeoSpecNumericExpectation;
728
+ shells?: GeoSpecNumericExpectation;
729
+ solids?: GeoSpecNumericExpectation;
730
+ compounds?: GeoSpecNumericExpectation;
731
+ tolerance?: number;
732
+ }
733
+
734
+ vertices?: GeoSpecNumericExpectation
735
+
736
+ edges?: GeoSpecNumericExpectation
737
+
738
+ wires?: GeoSpecNumericExpectation
739
+
740
+ faces?: GeoSpecNumericExpectation
741
+
742
+ shells?: GeoSpecNumericExpectation
743
+
744
+ solids?: GeoSpecNumericExpectation
745
+
746
+ compounds?: GeoSpecNumericExpectation
747
+
748
+ tolerance?: number
749
+
750
+ // Exact BRep validity expectation accepted by `expectGeo(...).toBeValidBrep(...)`
751
+ GeoSpecValidBrepExpectation: {
752
+ maxTolerance?: number;
753
+ freeBounds?: {
754
+ count?: GeoSpecNumericExpectation;
755
+ };
756
+ minEdgeLength?: number;
757
+ sameParameter?: boolean;
758
+ closedShells?: boolean;
759
+ closedWires?: boolean;
760
+ }
761
+
762
+ maxTolerance?: number
763
+
764
+ freeBounds?: {
765
+ count?: GeoSpecNumericExpectation;
766
+ }
767
+
768
+ minEdgeLength?: number
769
+
770
+ sameParameter?: boolean
771
+
772
+ closedShells?: boolean
773
+
774
+ closedWires?: boolean
775
+
776
+ // Void-continuity expectation accepted by `expectGeo(...).toHaveVoidContinuity(...)`
777
+ // Remarks: A whole-assembly negative-space claim: the ordered `path` waypoints must all lie in ONE connected open-void component (void = outside every `material` solid), that component must not reach any `isolatedFrom` point, and its tightest sampled cross-section must meet `minCrossSection`. Connectivity and isolation are proven from Boolean shell topology, generalized winding-number body identity, and deterministic cross-sections.
778
+ GeoSpecVoidContinuityExpectation: {
779
+ /** Ordered waypoints (>= 1) known to lie in the void being proven. */
780
+ path: GeoSpecVoidWaypoint[];
781
+ /**
782
+ * Occurrence names whose solids bound the void. Defaults to every occurrence
783
+ * in the subject (the whole-assembly negative space).
784
+ */
785
+ material?: string[];
786
+ /** Minimum required bottleneck cross-section (mm²), sampled. */
787
+ minCrossSection?: number;
788
+ /** Points that must NOT be reachable from the path void (isolation claim). */
789
+ isolatedFrom?: Vec3[];
790
+ /**
791
+ * Region bounded for the proof (subject frame). Defaults to the union
792
+ * of the `material` occurrence bounds, which encloses every interior void.
793
+ */
794
+ bounds?: {
795
+ min: Vec3;
796
+ max: Vec3;
797
+ };
798
+ }
799
+
800
+ // Ordered waypoints (>= 1) known to lie in the void being proven
801
+ path: GeoSpecVoidWaypoint[]
802
+
803
+ // Occurrence names whose solids bound the void
804
+ material?: string[]
805
+
806
+ // Minimum required bottleneck cross-section (mm²), sampled
807
+ minCrossSection?: number
808
+
809
+ // Points that must NOT be reachable from the path void (isolation claim)
810
+ isolatedFrom?: Vec3[]
811
+
812
+ // Region bounded for the proof (subject frame)
813
+ bounds?: {
814
+ min: Vec3;
815
+ max: Vec3;
816
+ }
817
+
818
+ // One void-continuity waypoint
819
+ GeoSpecVoidWaypoint: Vec3 | {
820
+ occurrence: string;
821
+ }
822
+
823
+ // Volume expectation accepted by `expectGeo(...).toHaveVolume(...)`
824
+ GeoSpecVolumeExpectation: {
825
+ value: number | GeoSpecNumericExpectation;
826
+ tolerance?: number;
827
+ }
828
+
829
+ value: number | GeoSpecNumericExpectation
830
+
831
+ tolerance?: number
832
+
833
+ // Basic exact or topology-derived BRep evidence consumed by early feature matchers
834
+ BrepEvidence: {
835
+ validity?: {
836
+ valid: boolean;
837
+ checks?: Array<{
838
+ shape: string;
839
+ status: string;
840
+ }>;
841
+ maxTolerance?: number;
842
+ freeBounds?: {
843
+ count: number;
844
+ };
845
+ smallEdges?: Array<{
846
+ length: number;
847
+ shape?: string;
848
+ location?: Vec3;
849
+ }>;
850
+ sameParameter?: boolean;
851
+ closedShells?: boolean;
852
+ closedSolids?: boolean;
853
+ solidCount?: number;
854
+ invalidSolidCount?: number;
855
+ openEdgeCount?: number;
856
+ reason?: string;
857
+ closedWires?: boolean;
858
+ };
859
+ topologyCounts?: {
860
+ vertices?: number;
861
+ edges?: number;
862
+ wires?: number;
863
+ faces?: number;
864
+ shells?: number;
865
+ solids?: number;
866
+ compounds?: number;
867
+ };
868
+ boundingBox?: {
869
+ min: Vec3;
870
+ max: Vec3;
871
+ size: Vec3;
872
+ center: Vec3;
873
+ };
874
+ massProperties?: {
875
+ surfaceArea?: number;
876
+ volume?: number;
877
+ centerOfMass?: Vec3;
878
+ mass?: number;
879
+ };
880
+ planarFaces?: Array<{
881
+ normal: Vec3;
882
+ offset: number;
883
+ area?: number;
884
+ center?: Vec3;
885
+ }>;
886
+ cylindricalFaces?: Array<{
887
+ radius: number;
888
+ axis: 'x' | 'y' | 'z';
889
+ center?: Vec3;
890
+ axisRange?: {
891
+ min: number;
892
+ max: number;
893
+ };
894
+ }>;
895
+ circularHoles?: Array<{
896
+ diameter: number;
897
+ through: boolean;
898
+ axis: 'x' | 'y' | 'z';
899
+ center?: Vec3;
900
+ axisRange?: {
901
+ min: number;
902
+ max: number;
903
+ };
904
+ }>;
905
+ circularHolePatterns?: Array<{
906
+ count: number;
907
+ holeDiameter: number;
908
+ boltCircleDiameter: number;
909
+ axis: 'x' | 'y' | 'z';
910
+ center?: Vec3;
911
+ }>;
912
+ chamferFeatures?: Array<{
913
+ distance: number;
914
+ selection?: string;
915
+ }>;
916
+ filletFeatures?: Array<{
917
+ radius: number;
918
+ selection?: string;
919
+ }>;
920
+ minimumWallThickness?: {
921
+ value: number;
922
+ location?: Vec3;
923
+ pointA?: Vec3;
924
+ pointB?: Vec3;
925
+ solidIndex?: number;
926
+ tieCount?: number;
927
+ algorithm?: string;
928
+ tolerance?: number;
929
+ supportA?: {
930
+ faceIndex?: number;
931
+ surfaceType?: string;
932
+ supportType?: string;
933
+ };
934
+ supportB?: {
935
+ faceIndex?: number;
936
+ surfaceType?: string;
937
+ supportType?: string;
938
+ };
939
+ rejections?: Record<string, number>;
940
+ };
941
+ }
942
+
943
+ validity?: {
944
+ valid: boolean;
945
+ checks?: Array<{
946
+ shape: string;
947
+ status: string;
948
+ }>;
949
+ maxTolerance?: number;
950
+ freeBounds?: {
951
+ count: number;
952
+ };
953
+ smallEdges?: Array<{
954
+ length: number;
955
+ shape?: string;
956
+ location?: Vec3;
957
+ }>;
958
+ sameParameter?: boolean;
959
+ closedShells?: boolean;
960
+ closedSolids?: boolean;
961
+ solidCount?: number;
962
+ invalidSolidCount?: number;
963
+ openEdgeCount?: number;
964
+ reason?: string;
965
+ closedWires?: boolean;
966
+ }
967
+
968
+ topologyCounts?: {
969
+ vertices?: number;
970
+ edges?: number;
971
+ wires?: number;
972
+ faces?: number;
973
+ shells?: number;
974
+ solids?: number;
975
+ compounds?: number;
976
+ }
977
+
978
+ boundingBox?: {
979
+ min: Vec3;
980
+ max: Vec3;
981
+ size: Vec3;
982
+ center: Vec3;
983
+ }
984
+
985
+ massProperties?: {
986
+ surfaceArea?: number;
987
+ volume?: number;
988
+ centerOfMass?: Vec3;
989
+ mass?: number;
990
+ }
991
+
992
+ planarFaces?: Array<{
993
+ normal: Vec3;
994
+ offset: number;
995
+ area?: number;
996
+ center?: Vec3;
997
+ }>
998
+
999
+ cylindricalFaces?: Array<{
1000
+ radius: number;
1001
+ axis: 'x' | 'y' | 'z';
1002
+ center?: Vec3;
1003
+ axisRange?: {
1004
+ min: number;
1005
+ max: number;
1006
+ };
1007
+ }>
1008
+
1009
+ circularHoles?: Array<{
1010
+ diameter: number;
1011
+ through: boolean;
1012
+ axis: 'x' | 'y' | 'z';
1013
+ center?: Vec3;
1014
+ axisRange?: {
1015
+ min: number;
1016
+ max: number;
1017
+ };
1018
+ }>
1019
+
1020
+ circularHolePatterns?: Array<{
1021
+ count: number;
1022
+ holeDiameter: number;
1023
+ boltCircleDiameter: number;
1024
+ axis: 'x' | 'y' | 'z';
1025
+ center?: Vec3;
1026
+ }>
1027
+
1028
+ chamferFeatures?: Array<{
1029
+ distance: number;
1030
+ selection?: string;
1031
+ }>
1032
+
1033
+ filletFeatures?: Array<{
1034
+ radius: number;
1035
+ selection?: string;
1036
+ }>
1037
+
1038
+ minimumWallThickness?: {
1039
+ value: number;
1040
+ location?: Vec3;
1041
+ pointA?: Vec3;
1042
+ pointB?: Vec3;
1043
+ solidIndex?: number;
1044
+ tieCount?: number;
1045
+ algorithm?: string;
1046
+ tolerance?: number;
1047
+ supportA?: {
1048
+ faceIndex?: number;
1049
+ surfaceType?: string;
1050
+ supportType?: string;
1051
+ };
1052
+ supportB?: {
1053
+ faceIndex?: number;
1054
+ surfaceType?: string;
1055
+ supportType?: string;
1056
+ };
1057
+ rejections?: Record<string, number>;
1058
+ }
1059
+
1060
+ // Geometry file formats understood by GeoSpec provenance
1061
+ GeometryFileFormat: MeshFileFormat | 'step' | 'stp'
1062
+
1063
+ // Capability exposed by a loaded subject
1064
+ GeometryCapability: {
1065
+ kind: 'mesh';
1066
+ feature: 'triangles' | 'bounding-box' | 'connected-components' | 'watertightness' | 'surface-area' | 'volume' | 'center-of-mass' | 'distance' | 'component-overlap';
1067
+ } | {
1068
+ kind: 'brep';
1069
+ feature: 'validity' | 'topology-counts' | 'bounding-box' | 'mass-properties' | 'planar-faces' | 'cylindrical-faces' | 'circular-holes' | 'circular-hole-patterns' | 'chamfer-features' | 'fillet-features' | 'wall-thickness';
1070
+ } | {
1071
+ kind: 'step';
1072
+ feature: 'schema' | 'units' | 'product-structure' | 'reader-provenance';
1073
+ } | {
1074
+ kind: 'unsupported';
1075
+ feature: string;
1076
+ reason: string;
1077
+ }
1078
+
1079
+ kind: 'mesh'
1080
+
1081
+ feature: 'triangles' | 'bounding-box' | 'connected-components' | 'watertightness' | 'surface-area' | 'volume' | 'center-of-mass' | 'distance' | 'component-overlap'
1082
+
1083
+ // Diagnostic emitted by GeoSpec loaders, analyzers, and matchers
1084
+ GeometryDiagnostic: {
1085
+ code: KernelIssueCode | (string & {});
1086
+ severity: 'error' | 'warning' | 'info';
1087
+ message: string;
1088
+ suggestion?: string;
1089
+ spatial?: {
1090
+ min?: Vec3;
1091
+ max?: Vec3;
1092
+ center?: Vec3;
1093
+ };
1094
+ details?: unknown;
1095
+ }
1096
+
1097
+ code: KernelIssueCode | (string & {})
1098
+
1099
+ severity: 'error' | 'warning' | 'info'
1100
+
1101
+ message: string
1102
+
1103
+ suggestion?: string
1104
+
1105
+ spatial?: {
1106
+ min?: Vec3;
1107
+ max?: Vec3;
1108
+ center?: Vec3;
1109
+ }
1110
+
1111
+ details?: unknown
1112
+
1113
+ // Provenance recorded by GeoSpec loaders
1114
+ GeometryProvenance: {
1115
+ source: GeometrySource;
1116
+ unit: GeoSpecUnit;
1117
+ loader: 'gltf-transform' | 'in-memory' | 'opencascade-step';
1118
+ contentHash?: string;
1119
+ parameters?: Record<string, JSONValue>;
1120
+ exportIntent?: GeometryExportIntent;
1121
+ }
1122
+
1123
+ source: GeometrySource
1124
+
1125
+ unit: GeoSpecUnit
1126
+
1127
+ loader: 'gltf-transform' | 'in-memory' | 'opencascade-step'
1128
+
1129
+ contentHash?: string
1130
+
1131
+ parameters?: Record<string, JSONValue>
1132
+
1133
+ exportIntent?: GeometryExportIntent
1134
+
1135
+ // Source metadata for a loaded geometry subject
1136
+ GeometrySource: {
1137
+ kind: 'bytes' | 'array-buffer' | 'blob' | 'file' | 'path' | 'url' | 'mesh-buffer' | 'readable-stream' | 'async-iterable';
1138
+ format: GeometryFileFormat;
1139
+ path?: string;
1140
+ name?: string;
1141
+ byteLength?: number;
1142
+ }
1143
+
1144
+ kind: 'bytes' | 'array-buffer' | 'blob' | 'file' | 'path' | 'url' | 'mesh-buffer' | 'readable-stream' | 'async-iterable'
1145
+
1146
+ format: GeometryFileFormat
1147
+
1148
+ path?: string
1149
+
1150
+ name?: string
1151
+
1152
+ byteLength?: number
1153
+
1154
+ // Canonical P0 object under test for GeoSpec
1155
+ // Remarks: This is intentionally a GeoSpec-loaded subject rather than a Tau runtime contract. Runtime integrations pass GLB/glTF bytes or files into loaders.
1156
+ GeometrySubject: {
1157
+ kind: 'geometry-subject';
1158
+ /** Opaque engine-owned identifier used by every protocol claim. */
1159
+ subjectId: string;
1160
+ mesh: GeometrySubjectMeshEvidence;
1161
+ brep?: BrepEvidence;
1162
+ step?: StepEvidence;
1163
+ provenance: GeometryProvenance;
1164
+ capabilities: GeometryCapability[];
1165
+ diagnostics: GeometryEvidenceDiagnostic[];
1166
+ }
1167
+
1168
+ kind: 'geometry-subject'
1169
+
1170
+ // Opaque engine-owned identifier used by every protocol claim
1171
+ subjectId: string
1172
+
1173
+ mesh: GeometrySubjectMeshEvidence
1174
+
1175
+ brep?: BrepEvidence
1176
+
1177
+ step?: StepEvidence
1178
+
1179
+ provenance: GeometryProvenance
1180
+
1181
+ capabilities: GeometryCapability[]
1182
+
1183
+ diagnostics: GeometryEvidenceDiagnostic[]
1184
+
1185
+ // Wire-safe mesh summary carried by an opaque geometry subject
1186
+ GeometrySubjectMeshEvidence: {
1187
+ format: MeshFileFormat;
1188
+ stats: Pick<GeometryStats, 'vertexCount' | 'meshCount' | 'triangleCount'>;
1189
+ }
1190
+
1191
+ format: MeshFileFormat
1192
+
1193
+ stats: Pick<GeometryStats, 'vertexCount' | 'meshCount' | 'triangleCount'>
1194
+
1195
+ // Mesh evidence loaded from geometry bytes or buffers
1196
+ MeshEvidence: {
1197
+ format: MeshFileFormat;
1198
+ stats: GeometryStats;
1199
+ }
1200
+
1201
+ format: MeshFileFormat
1202
+
1203
+ stats: GeometryStats
1204
+
1205
+ // Geometry file formats supported by the P0 mesh loader
1206
+ MeshFileFormat: 'glb' | 'gltf' | 'mesh-buffer'
1207
+
1208
+ // Triangle quality and scalar mesh metrics used by P0 GeoSpec matchers
1209
+ // Remarks: Values are reported in the glTF document coordinate units.
1210
+ MeshQualityStats: {
1211
+ triangleCount: number;
1212
+ nonFiniteVertices: Array<{
1213
+ primitive: string;
1214
+ vertexIndex: number;
1215
+ position: [number, number, number];
1216
+ }>;
1217
+ degenerateTriangles: Array<{
1218
+ primitive: string;
1219
+ triangleIndex: number;
1220
+ area: number;
1221
+ center: [number, number, number];
1222
+ }>;
1223
+ duplicateFaces: Array<{
1224
+ primitive: string;
1225
+ triangleIndex: number;
1226
+ firstTriangleIndex: number;
1227
+ }>;
1228
+ triangles: MeshTriangle[];
1229
+ surfaceArea: number;
1230
+ signedVolume: number;
1231
+ centerOfMass?: Vec3;
1232
+ }
1233
+
1234
+ triangleCount: number
1235
+
1236
+ nonFiniteVertices: Array<{
1237
+ primitive: string;
1238
+ vertexIndex: number;
1239
+ position: [number, number, number];
1240
+ }>
1241
+
1242
+ degenerateTriangles: Array<{
1243
+ primitive: string;
1244
+ triangleIndex: number;
1245
+ area: number;
1246
+ center: [number, number, number];
1247
+ }>
1248
+
1249
+ duplicateFaces: Array<{
1250
+ primitive: string;
1251
+ triangleIndex: number;
1252
+ firstTriangleIndex: number;
1253
+ }>
1254
+
1255
+ triangles: MeshTriangle[]
1256
+
1257
+ surfaceArea: number
1258
+
1259
+ signedVolume: number
1260
+
1261
+ centerOfMass?: Vec3
1262
+
1263
+ // One triangle from mesh evidence, in geometry document coordinates
1264
+ MeshTriangle: {
1265
+ primitive: string;
1266
+ triangleIndex: number;
1267
+ a: [number, number, number];
1268
+ b: [number, number, number];
1269
+ c: [number, number, number];
1270
+ center: [number, number, number];
1271
+ area: number;
1272
+ }
1273
+
1274
+ primitive: string
1275
+
1276
+ triangleIndex: number
1277
+
1278
+ a: [number, number, number]
1279
+
1280
+ b: [number, number, number]
1281
+
1282
+ c: [number, number, number]
1283
+
1284
+ center: [number, number, number]
1285
+
1286
+ area: number
1287
+
1288
+ // STEP/XDE evidence extracted while loading a STEP subject
1289
+ StepEvidence: {
1290
+ schema?: string;
1291
+ unit?: GeoSpecUnit;
1292
+ productStructure?: Array<{
1293
+ name: string;
1294
+ path: string;
1295
+ transform?: number[];
1296
+ }>;
1297
+ readStrategy: {
1298
+ strategy: 'native-stream' | 'filesystem';
1299
+ inputKind: GeometrySource['kind'];
1300
+ bytesRead: number;
1301
+ nativeReadStream: boolean;
1302
+ copiedToEmscriptenFs: boolean;
1303
+ };
1304
+ capabilities: Array<{
1305
+ feature: string;
1306
+ supported: boolean;
1307
+ reason?: string;
1308
+ }>;
1309
+ /** Structured AP242 XDE read result (occurrences, subshape names, datum placements). */
1310
+ xde?: XdeReadResult;
1311
+ }
1312
+
1313
+ schema?: string
1314
+
1315
+ unit?: GeoSpecUnit
1316
+
1317
+ productStructure?: Array<{
1318
+ name: string;
1319
+ path: string;
1320
+ transform?: number[];
1321
+ }>
1322
+
1323
+ readStrategy: {
1324
+ strategy: 'native-stream' | 'filesystem';
1325
+ inputKind: GeometrySource['kind'];
1326
+ bytesRead: number;
1327
+ nativeReadStream: boolean;
1328
+ copiedToEmscriptenFs: boolean;
1329
+ }
1330
+
1331
+ capabilities: Array<{
1332
+ feature: string;
1333
+ supported: boolean;
1334
+ reason?: string;
1335
+ }>
1336
+
1337
+ // Structured AP242 XDE read result (occurrences, subshape names, datum placements)
1338
+ xde?: XdeReadResult
1339
+
1340
+ // Numeric 3D vector
1341
+ Vec3: readonly [number, number, number]
1342
+
1343
+ // Numeric 3D vector
1344
+ GeoSpecVec3: readonly [number, number, number]
1345
+
1346
+ // One matcher's contract entry
1347
+ GeoSpecMatcherDescriptor: {
1348
+ readonly kind: GeoSpecAssertion['kind'];
1349
+ readonly expected: GeoSpecMatcherExpectedShape;
1350
+ readonly mode: GeoSpecMatcherMode;
1351
+ }
1352
+
1353
+ readonly kind: GeoSpecAssertion['kind']
1354
+
1355
+ readonly expected: GeoSpecMatcherExpectedShape
1356
+
1357
+ readonly mode: GeoSpecMatcherMode
1358
+
1359
+ // How the substrate derives an assertion's recorded `expected` value from the arguments the spec author passed
1360
+ // Remarks: - `first` — the first argument verbatim. - `first-or-empty` — the first argument, defaulting to `{}`. - `bounds` — `(min, max)` pairs collapse to `{ min, max }`; a lone object argument passes through. - `true` — nullary matchers record the literal `true`.
1361
+ GeoSpecMatcherExpectedShape: 'first' | 'first-or-empty' | 'bounds' | 'true'
1362
+
1363
+ // Whether a matcher settles synchronously (throwing its `GeoSpecAssertionError` inside the `it()` body) or asynchronously (settled before the test completes)
1364
+ GeoSpecMatcherMode: 'sync' | 'async'
1365
+
1366
+ // Every matcher name exposed by `expectGeo(...)`
1367
+ GeoSpecMatcherName: Exclude<keyof GeoSpecMatcher, 'not'>
1368
+
1369
+ // In-memory triangle mesh source
1370
+ MeshBufferSource: {
1371
+ format: 'mesh-buffer';
1372
+ positions: Float32Array<ArrayBuffer> | number[];
1373
+ indices?: Uint32Array<ArrayBuffer> | Uint16Array<ArrayBuffer> | number[];
1374
+ name?: string;
1375
+ }
1376
+
1377
+ format: 'mesh-buffer'
1378
+
1379
+ positions: Float32Array<ArrayBuffer> | number[]
1380
+
1381
+ indices?: Uint32Array<ArrayBuffer> | Uint16Array<ArrayBuffer> | number[]
1382
+
1383
+ name?: string
1384
+
1385
+ // Mesh source forms accepted by {@link import ('./load-model.js').loadModel}
1386
+ MeshSource: Uint8Array<ArrayBuffer> | ArrayBuffer | Blob | File | URL | string | MeshBufferSource
1387
+
1388
+ // Axis-aligned bounding box in glTF document units (meters)
1389
+ AabbMeters: {
1390
+ min: [number, number, number];
1391
+ max: [number, number, number];
1392
+ }
1393
+
1394
+ min: [number, number, number]
1395
+
1396
+ max: [number, number, number]
1397
+
1398
+ // Dominant primitive on an axis extremum for `boundingBox` failures
1399
+ BoundingBoxAxisExtremum: {
1400
+ name: string;
1401
+ aabb: AabbMeters;
1402
+ value: number;
1403
+ }
1404
+
1405
+ name: string
1406
+
1407
+ aabb: AabbMeters
1408
+
1409
+ value: number
1410
+
1411
+ // One axis failure for `boundingBox` checks
1412
+ BoundingBoxAxisFailure: {
1413
+ axis: 'x' | 'y' | 'z';
1414
+ field: 'size' | 'center';
1415
+ expected: number;
1416
+ actual: number;
1417
+ tolerance: number;
1418
+ minExtremum?: BoundingBoxAxisExtremum;
1419
+ maxExtremum?: BoundingBoxAxisExtremum;
1420
+ }
1421
+
1422
+ axis: 'x' | 'y' | 'z'
1423
+
1424
+ field: 'size' | 'center'
1425
+
1426
+ expected: number
1427
+
1428
+ actual: number
1429
+
1430
+ tolerance: number
1431
+
1432
+ minExtremum?: BoundingBoxAxisExtremum
1433
+
1434
+ maxExtremum?: BoundingBoxAxisExtremum
1435
+
1436
+ // Structured payload when `boundingBox` fails
1437
+ BoundingBoxFailure: {
1438
+ axisFailures: BoundingBoxAxisFailure[];
1439
+ }
1440
+
1441
+ axisFailures: BoundingBoxAxisFailure[]
1442
+
1443
+ // Scene bounding box with per-primitive contributors in the subject's unit and frame
1444
+ BoundingBoxStats: {
1445
+ size: [number, number, number];
1446
+ center: [number, number, number];
1447
+ primitives: PrimitiveRecord[];
1448
+ }
1449
+
1450
+ size: [number, number, number]
1451
+
1452
+ center: [number, number, number]
1453
+
1454
+ primitives: PrimitiveRecord[]
1455
+
1456
+ // Result of evaluating a single test requirement against geometry stats
1457
+ CheckResult: {
1458
+ passed: true;
1459
+ } | {
1460
+ passed: false;
1461
+ check: 'boundingBox';
1462
+ reason: string;
1463
+ suggestion: string;
1464
+ failure: BoundingBoxFailure;
1465
+ } | {
1466
+ passed: false;
1467
+ check: 'connectedComponents';
1468
+ reason: string;
1469
+ suggestion: string;
1470
+ failure: ConnectedComponentsFailure;
1471
+ } | {
1472
+ passed: false;
1473
+ check: 'watertight';
1474
+ reason: string;
1475
+ suggestion: string;
1476
+ failure: WatertightFailure;
1477
+ } | {
1478
+ passed: false;
1479
+ check: 'invalid';
1480
+ reason: string;
1481
+ suggestion: string;
1482
+ }
1483
+
1484
+ passed: true
1485
+
1486
+ // Smallest clearance between two clusters along the dominant separation axis
1487
+ ClusterGap: {
1488
+ fromLabel: string;
1489
+ toLabel: string;
1490
+ axis: 'x' | 'y' | 'z';
1491
+ /** Millimetres — clearance between the two named primitives' AABBs. */
1492
+ gapMm: number;
1493
+ fromPrimitive: string;
1494
+ toPrimitive: string;
1495
+ }
1496
+
1497
+ fromLabel: string
1498
+
1499
+ toLabel: string
1500
+
1501
+ axis: 'x' | 'y' | 'z'
1502
+
1503
+ // Millimetres — clearance between the two named primitives' AABBs
1504
+ gapMm: number
1505
+
1506
+ fromPrimitive: string
1507
+
1508
+ toPrimitive: string
1509
+
1510
+ // One spatial cluster from AABB overlap grouping
1511
+ ClusterReport: {
1512
+ label: string;
1513
+ primitives: PrimitiveRecord[];
1514
+ aabb: AabbMeters;
1515
+ centroid: [number, number, number];
1516
+ totalVertices: number;
1517
+ }
1518
+
1519
+ label: string
1520
+
1521
+ primitives: PrimitiveRecord[]
1522
+
1523
+ aabb: AabbMeters
1524
+
1525
+ centroid: [number, number, number]
1526
+
1527
+ totalVertices: number
1528
+
1529
+ // Structured payload when `connectedComponents` fails
1530
+ ConnectedComponentsFailure: {
1531
+ expected: number;
1532
+ got: number;
1533
+ toleranceMm: number;
1534
+ clusters: ClusterReport[];
1535
+ gaps: ClusterGap[];
1536
+ }
1537
+
1538
+ expected: number
1539
+
1540
+ got: number
1541
+
1542
+ toleranceMm: number
1543
+
1544
+ clusters: ClusterReport[]
1545
+
1546
+ gaps: ClusterGap[]
1547
+
1548
+ // Full connected-components analysis at one tolerance
1549
+ ConnectedComponentsResult: {
1550
+ count: number;
1551
+ clusters: ClusterReport[];
1552
+ gaps: ClusterGap[];
1553
+ }
1554
+
1555
+ count: number
1556
+
1557
+ clusters: ClusterReport[]
1558
+
1559
+ gaps: ClusterGap[]
1560
+
1561
+ // Diagnostic form permitted inside a wire-safe subject snapshot
1562
+ GeometryEvidenceDiagnostic: Omit<GeometryDiagnostic, 'details'> & {
1563
+ details?: JSONValue;
1564
+ }
1565
+
1566
+ code: KernelIssueCode | (string & {})
1567
+
1568
+ severity: 'error' | 'warning' | 'info'
1569
+
1570
+ message: string
1571
+
1572
+ suggestion?: string
1573
+
1574
+ spatial?: {
1575
+ min?: Vec3;
1576
+ max?: Vec3;
1577
+ center?: Vec3;
1578
+ }
1579
+
1580
+ details?: JSONValue
1581
+
1582
+ // Statistics about a parsed GLB geometry
1583
+ // Remarks: `vertexCount` and `meshCount` are kept on the type for internal diagnostic use (and for the kernel-author Vitest harness in `kernel-geometry-testing.utils.ts`); they are no longer exposed via the agent-facing requirement schema.
1584
+ GeometryStats: {
1585
+ vertexCount: number;
1586
+ meshCount: number;
1587
+ triangleCount: number;
1588
+ meshQuality: MeshQualityStats;
1589
+ watertight: boolean;
1590
+ boundingBox?: BoundingBoxStats;
1591
+ }
1592
+
1593
+ vertexCount: number
1594
+
1595
+ meshCount: number
1596
+
1597
+ triangleCount: number
1598
+
1599
+ meshQuality: MeshQualityStats
1600
+
1601
+ watertight: boolean
1602
+
1603
+ boundingBox?: BoundingBoxStats
1604
+
1605
+ // One TRIANGLES primitive with identity for spatial-test feedback
1606
+ PrimitiveRecord: {
1607
+ /** The glTF node / mesh name (from kernel ShapeConfig.name when present). */
1608
+ name: string;
1609
+ color?: string;
1610
+ vertices: number;
1611
+ aabb: AabbMeters;
1612
+ }
1613
+
1614
+ // The glTF node / mesh name (from kernel ShapeConfig.name when present)
1615
+ name: string
1616
+
1617
+ color?: string
1618
+
1619
+ vertices: number
1620
+
1621
+ aabb: AabbMeters
1622
+
1623
+ // Structured payload when `watertight` fails
1624
+ WatertightFailure: {
1625
+ /** Edges with incidence ≠ 2 (open or non-manifold). */
1626
+ irregularEdges: number;
1627
+ /** Edges shared by exactly one triangle (open boundary). */
1628
+ openBoundaryEdges: number;
1629
+ /** Edges shared by more than two triangles (over-adjacent/non-manifold). */
1630
+ nonManifoldEdges: number;
1631
+ irregularEdgeKindCounts: {
1632
+ openBoundary: number;
1633
+ nonManifold: number;
1634
+ };
1635
+ irregularEdgeClusters: WatertightIrregularEdgeCluster[];
1636
+ irregularEdgeFraction: number;
1637
+ perPrimitive: WatertightPrimitiveBreakdown[];
1638
+ }
1639
+
1640
+ // Edges with incidence ≠ 2 (open or non-manifold)
1641
+ irregularEdges: number
1642
+
1643
+ // Edges shared by exactly one triangle (open boundary)
1644
+ openBoundaryEdges: number
1645
+
1646
+ // Edges shared by more than two triangles (over-adjacent/non-manifold)
1647
+ nonManifoldEdges: number
1648
+
1649
+ irregularEdgeKindCounts: {
1650
+ openBoundary: number;
1651
+ nonManifold: number;
1652
+ }
1653
+
1654
+ irregularEdgeClusters: WatertightIrregularEdgeCluster[]
1655
+
1656
+ irregularEdgeFraction: number
1657
+
1658
+ perPrimitive: WatertightPrimitiveBreakdown[]
1659
+
1660
+ // Spatial cluster of related irregular edges
1661
+ WatertightIrregularEdgeCluster: {
1662
+ kind: WatertightIrregularEdgeKind;
1663
+ edgeCount: number;
1664
+ aabb: {
1665
+ min: Vec3;
1666
+ max: Vec3;
1667
+ center: Vec3;
1668
+ };
1669
+ samples: WatertightIrregularEdgeSample[];
1670
+ }
1671
+
1672
+ kind: WatertightIrregularEdgeKind
1673
+
1674
+ edgeCount: number
1675
+
1676
+ aabb: {
1677
+ min: Vec3;
1678
+ max: Vec3;
1679
+ center: Vec3;
1680
+ }
1681
+
1682
+ samples: WatertightIrregularEdgeSample[]
1683
+
1684
+ // Class of irregular mesh edge found during watertight analysis
1685
+ WatertightIrregularEdgeKind: 'open-boundary' | 'non-manifold'
1686
+
1687
+ // Representative irregular edge, in glTF document coordinates
1688
+ WatertightIrregularEdgeSample: {
1689
+ start: Vec3;
1690
+ end: Vec3;
1691
+ center: Vec3;
1692
+ incidentTriangleCount: number;
1693
+ primitives: string[];
1694
+ color?: string;
1695
+ }
1696
+
1697
+ start: Vec3
1698
+
1699
+ end: Vec3
1700
+
1701
+ center: Vec3
1702
+
1703
+ incidentTriangleCount: number
1704
+
1705
+ primitives: string[]
1706
+
1707
+ color?: string
1708
+
1709
+ // Per-primitive watertight diagnostic (local tessellation only)
1710
+ WatertightPrimitiveBreakdown: {
1711
+ name: string;
1712
+ boundaryEdges: number;
1713
+ loopCentroid: [number, number, number];
1714
+ }
1715
+
1716
+ name: string
1717
+
1718
+ boundaryEdges: number
1719
+
1720
+ loopCentroid: [number, number, number]
1721
+
1722
+ // Full watertight analysis (global + per-primitive breakdown)
1723
+ WatertightResult: {
1724
+ watertight: boolean;
1725
+ irregularEdges: number;
1726
+ openBoundaryEdges: number;
1727
+ nonManifoldEdges: number;
1728
+ irregularEdgeKindCounts: {
1729
+ openBoundary: number;
1730
+ nonManifold: number;
1731
+ };
1732
+ irregularEdgeClusters: WatertightIrregularEdgeCluster[];
1733
+ totalEdges: number;
1734
+ irregularEdgeFraction: number;
1735
+ perPrimitive: WatertightPrimitiveBreakdown[];
1736
+ }
1737
+
1738
+ watertight: boolean
1739
+
1740
+ irregularEdges: number
1741
+
1742
+ openBoundaryEdges: number
1743
+
1744
+ nonManifoldEdges: number
1745
+
1746
+ irregularEdgeKindCounts: {
1747
+ openBoundary: number;
1748
+ nonManifold: number;
1749
+ }
1750
+
1751
+ irregularEdgeClusters: WatertightIrregularEdgeCluster[]
1752
+
1753
+ totalEdges: number
1754
+
1755
+ irregularEdgeFraction: number
1756
+
1757
+ perPrimitive: WatertightPrimitiveBreakdown[]
1758
+
1759
+ // Runtime route metadata used to decide whether a runtime export can honor GeoSpec evidence requirements
1760
+ GeoSpecExportRoute: Partial<ExportRoute> & {
1761
+ kernelId?: string;
1762
+ sourceFormat?: string;
1763
+ targetFormat?: string;
1764
+ transcoderId?: string;
1765
+ fidelity?: string;
1766
+ exportOptions?: {
1767
+ schema?: {
1768
+ properties?: Record<string, unknown>;
1769
+ };
1770
+ defaults?: Record<string, unknown>;
1771
+ };
1772
+ }
1773
+
1774
+ RuntimeBackedModelFormat: Exclude<GeoSpecModelFormat, 'mesh-buffer'>
1775
+
1776
+ // Runtime client shape for route-aware Tau runtimes
1777
+ RuntimeClientWithRoutes: GeoSpecRuntimeClient & {
1778
+ bestRouteFor(format: string, options?: {
1779
+ readonly kernelId?: string;
1780
+ }): GeoSpecExportRoute | undefined;
1781
+ }
1782
+
1783
+ open: (input: Pick<Parameters<RuntimeClient['open']>[0], 'source' | 'parameters' | 'stage' | 'watch' | 'signal'>) => Pick<ReturnType<RuntimeClient['open']>, 'export' | 'close'>
1784
+
1785
+ // RuntimeClientWithRoutes.on (method)
1786
+ on?(event: 'telemetry', handler: (batch: {
1787
+ readonly entries: ReadonlyArray<{
1788
+ name: string;
1789
+ duration: number;
1790
+ startTime: number;
1791
+ workerTimeOrigin: number;
1792
+ }>;
1793
+ }) => void): () => void;
1794
+
1795
+ // RuntimeClientWithRoutes.bestRouteFor (method)
1796
+ bestRouteFor(format: string, options?: {
1797
+ readonly kernelId?: string;
1798
+ }): GeoSpecExportRoute | undefined;
1799
+
1800
+ // Resolved runtime export request and provenance for a GeoSpec model load
1801
+ RuntimeExportIntent: {
1802
+ options: Record<string, unknown>;
1803
+ provenance: GeometryExportIntent;
1804
+ sourceUnit: GeoSpecUnit;
1805
+ }
1806
+
1807
+ options: Record<string, unknown>
1808
+
1809
+ provenance: GeometryExportIntent
1810
+
1811
+ sourceUnit: GeoSpecUnit
1812
+
1813
+ // Structured failure returned when a runtime cannot provide the requested GeoSpec evidence
1814
+ RuntimeExportIntentFailure: {
1815
+ success: false;
1816
+ diagnostics: GeometryDiagnostic[];
1817
+ }
1818
+
1819
+ success: false
1820
+
1821
+ diagnostics: GeometryDiagnostic[]
1822
+
1823
+ // Defaults accepted by {@link import ('./load-model.js').createModelLoader}
1824
+ CreateModelLoaderOptions: {
1825
+ /** Initialized compiled engine supplied by the host, never selected by an authored spec. */
1826
+ engine: GeoSpecNativeModelEngine;
1827
+ /** Rooted host reader for direct filesystem or URL sources. */
1828
+ readSource?: GeoSpecNativeSourceReader;
1829
+ /** Geometry format to export when an individual call does not specify one. */
1830
+ format?: GeoSpecModelFormat;
1831
+ /** Runtime client or lazy runtime factory. */
1832
+ runtime?: GeoSpecRuntimeClient | GeoSpecRuntimeClientFactory;
1833
+ /** Source-specific runtime adapters, e.g. host-provided GPL-isolated kernels. */
1834
+ sourceAdapters?: readonly GeoSpecRuntimeSourceAdapter[];
1835
+ /** Project root used by runtime integrations. */
1836
+ projectPath?: string;
1837
+ /** STEP reader strategy used for STEP sources or exports. */
1838
+ stepStreaming?: StepStreamingMode;
1839
+ /** Whether STEP loading should also produce mesh evidence. Defaults to true. */
1840
+ mesh?: boolean;
1841
+ /** Linear tolerance used while meshing exact BRep evidence. */
1842
+ meshLinearTolerance?: number;
1843
+ /** Angular tolerance in degrees used while meshing exact BRep evidence. */
1844
+ meshAngularToleranceDegrees?: number;
1845
+ }
1846
+
1847
+ // Initialized compiled engine supplied by the host, never selected by an authored spec
1848
+ engine: GeoSpecNativeModelEngine
1849
+
1850
+ // Rooted host reader for direct filesystem or URL sources
1851
+ readSource?: GeoSpecNativeSourceReader
1852
+
1853
+ // Geometry format to export when an individual call does not specify one
1854
+ format?: GeoSpecModelFormat
1855
+
1856
+ // Runtime client or lazy runtime factory
1857
+ runtime?: GeoSpecRuntimeClient | GeoSpecRuntimeClientFactory
1858
+
1859
+ // Source-specific runtime adapters, e.g
1860
+ sourceAdapters?: readonly GeoSpecRuntimeSourceAdapter[]
1861
+
1862
+ // Project root used by runtime integrations
1863
+ projectPath?: string
1864
+
1865
+ // STEP reader strategy used for STEP sources or exports
1866
+ stepStreaming?: StepStreamingMode
1867
+
1868
+ // Whether STEP loading should also produce mesh evidence
1869
+ mesh?: boolean
1870
+
1871
+ // Linear tolerance used while meshing exact BRep evidence
1872
+ meshLinearTolerance?: number
1873
+
1874
+ // Angular tolerance in degrees used while meshing exact BRep evidence
1875
+ meshAngularToleranceDegrees?: number
1876
+
1877
+ // Axis-aligned crop box in canonical Z-up millimetres
1878
+ GeoSpecLoadRegion: {
1879
+ readonly min: readonly [number, number, number];
1880
+ readonly max: readonly [number, number, number];
1881
+ }
1882
+
1883
+ readonly min: readonly [number, number, number]
1884
+
1885
+ readonly max: readonly [number, number, number]
1886
+
1887
+ // Geometry formats accepted by {@link import ('./load-model.js').loadModel}
1888
+ GeoSpecModelFormat: MeshFileFormat | 'step' | 'stp'
1889
+
1890
+ // Function shape used by GeoSpec runners to provide model loading inside VM executed test files
1891
+ GeoSpecModelLoader: <Code extends Record<string, string> = Record<string, string>>(options: LoadModelOptions<Code>) => Promise<GeoSpecSubject>
1892
+
1893
+ // A configured loader whose shared runtime can be released with its owner
1894
+ ManagedGeoSpecModelLoader: GeoSpecModelLoader & {
1895
+ dispose(): Promise<void>;
1896
+ }
1897
+
1898
+ // ManagedGeoSpecModelLoader.dispose (method)
1899
+ dispose(): Promise<void>;
1900
+
1901
+ // Runtime client surface consumed by `geospec/model`
1902
+ // Remarks: GeoSpec accepts concrete Tau runtime clients from multiple call sites but only needs connection lifecycle and request-scoped documents. Keep this shape small so typed runtime clients do not have to widen their full generic method surface to GeoSpec's testing DSL. A client with `shutdown` is awaited through it when GeoSpec releases an owned runtime, so its host resources are closed before the caller (for example a worker thread) exits. A client with `snapshotSource` lets one run reuse an identical file load after confirming that the source closure it exported is unchanged.
1903
+ GeoSpecRuntimeClient: Pick<RuntimeClient, 'connect' | 'terminate'> & Partial<Pick<RuntimeClient, 'shutdown' | 'snapshotSource' | 'describe'>> & {
1904
+ open: (input: Pick<Parameters<RuntimeClient['open']>[0], 'source' | 'parameters' | 'stage' | 'watch' | 'signal'>) => Pick<ReturnType<RuntimeClient['open']>, 'export' | 'close'>;
1905
+ on?(event: 'telemetry', handler: (batch: {
1906
+ readonly entries: ReadonlyArray<{
1907
+ name: string;
1908
+ duration: number;
1909
+ startTime: number;
1910
+ workerTimeOrigin: number;
1911
+ }>;
1912
+ }) => void): () => void;
1913
+ }
1914
+
1915
+ open: (input: Pick<Parameters<RuntimeClient['open']>[0], 'source' | 'parameters' | 'stage' | 'watch' | 'signal'>) => Pick<ReturnType<RuntimeClient['open']>, 'export' | 'close'>
1916
+
1917
+ // GeoSpecRuntimeClient.on (method)
1918
+ on?(event: 'telemetry', handler: (batch: {
1919
+ readonly entries: ReadonlyArray<{
1920
+ name: string;
1921
+ duration: number;
1922
+ startTime: number;
1923
+ workerTimeOrigin: number;
1924
+ }>;
1925
+ }) => void): () => void;
1926
+
1927
+ // Lazy runtime factory consumed by `geospec/model`
1928
+ GeoSpecRuntimeClientFactory: () => Promise<GeoSpecRuntimeClient>
1929
+
1930
+ // Explicit source adapter for formats whose runtime setup is not part of the generic Tau runtime preset
1931
+ GeoSpecRuntimeSourceAdapter: {
1932
+ id: string;
1933
+ extensions: readonly string[];
1934
+ createRuntime(options: {
1935
+ projectPath?: string;
1936
+ file?: string;
1937
+ }): Promise<GeoSpecRuntimeClient>;
1938
+ }
1939
+
1940
+ id: string
1941
+
1942
+ extensions: readonly string[]
1943
+
1944
+ // GeoSpecRuntimeSourceAdapter.createRuntime (method)
1945
+ createRuntime(options: {
1946
+ projectPath?: string;
1947
+ file?: string;
1948
+ }): Promise<GeoSpecRuntimeClient>;