# Queries

Positive-only queries that report what the engine measured, without asserting an expected value.

Canonical page: https://www.geospec.xyz/docs/reference/queries

A query reports a measurement instead of comparing it with an expectation; a negated query is an `invalid-claim` error. `passed` carries the measurement in `evidence`; `refused` means the evidence the query reads is missing or cannot decide, under the common refusals and the codes below. A query asserts nothing, so it never fails.

`client.query({ capability, subject, payload })` resolves to a `GeoSpecCanonicalClaimReport`; `query_geo(subject, capability, payload)` returns a `GeoSpecAssertionReport` whose `passed` means status `passed`. Neither throws for the three outcomes. `client.query` admits the subjects of `createGeoSpecNativeModelLoader` and rejects those of `createModelLoader`, so this page uses the native loader throughout. Payload keys keep the camelCase field names in both languages (`maxRecords`).

`plate` and `gear` are the site's demo models; the rest are repository fixtures: `tessellated`, a STEP box whose faces carry no surface; `housingShaft`, a `housing` and a `shaft` that share material; `nist`, a NIST AP242 test case with semantic PMI. Python adds `y_up_plate`, the demo plate admitted y-up through `engine.ingest_subject`; no loader in `geospec@beta` admits one. Refusal codes add to the [common refusals](https://www.geospec.xyz/docs/reference/overview.mdx#common-refusals).

**TypeScript**

```ts title="check.ts"
import { readFile } from 'node:fs/promises';
import { createGeoSpecAssertionClient } from 'geospec/assertion-client';
import { createGeoSpecNativeModelLoader } from 'geospec/runner/native';
import { Engine } from 'geospec-engine/node';

const engine = new Engine();
const loadModel = createGeoSpecNativeModelLoader({
  engine,
  readSource: async (source) => new Uint8Array(await readFile(source as string)),
});
const client = createGeoSpecAssertionClient({ engine });
const plate = await loadModel({ source: 'plate-with-through-bore.step', format: 'step' });
const gear = await loadModel({ source: 'involute-gear.glb', format: 'glb', sourceUnit: 'mm' });
const tessellated = await loadModel({
  source: 'packages/geospec-engine/native/occt/rust/tests/fixtures/read-profile/box-tessellated-only.step',
  format: 'step',
});
const housingShaft = await loadModel({
  source: 'packages/geospec-engine/native/occt/rust/tests/fixtures/component-interference/original.step',
  format: 'step',
});
const nist = await loadModel({
  source: 'packages/geospec-engine/fixtures/interop/nist-pmi/nist_ctc_05_asme1_ap242-e1.stp',
  format: 'stp',
});
```

**Python**

```python title="check.py"
from pathlib import Path

from geospec import GeoSpecEngine, GeoSpecRegex, load_model, query_geo

with GeoSpecEngine(model_root=".") as engine:
    plate = load_model(file="plate-with-through-bore.step", format="step")
    gear = load_model(file="involute-gear.glb", format="glb", source_unit="mm")
    tessellated = load_model(
        file="packages/geospec-engine/native/occt/rust/tests/fixtures/read-profile/box-tessellated-only.step",
        format="step",
    )
    housing_shaft = load_model(
        file="packages/geospec-engine/native/occt/rust/tests/fixtures/component-interference/original.step",
        format="step",
    )
    nist = load_model(
        file="packages/geospec-engine/fixtures/interop/nist-pmi/nist_ctc_05_asme1_ap242-e1.stp",
        format="stp",
    )
    y_up_plate = engine.ingest_subject(
        Path("plate-with-through-bore.step").read_bytes(),
        format="step", coordinate_system="y-up", source_unit="auto", output_unit="mm",
    )
```

## analyzeBrep [#analyzebrep]

Reports a STEP subject's exact BRep facts: bounds, validity, topology counts, mass properties and the face, hole, chamfer and fillet inventories.

**Signature**

**TypeScript**

```ts
client.query({ capability: 'analyzeBrep', subject: part }): Promise<GeoSpecCanonicalClaimReport>
```

**Python**

```python
query_geo(part, "analyzeBrep") -> GeoSpecAssertionReport
```

**Fields**

None.

**Admitted geometry**

* STEP with exact BRep faces.
* An incomplete hole or edge-treatment inventory is a warning in the evidence.

**Refusal codes**

* `GEOSPEC_BREP_EVIDENCE_UNAVAILABLE`: the subject has no BRep, such as a mesh or glTF subject.

**Examples**

**TypeScript**

```ts
// passed: plate, evidence.brep lists 7 faces and a 10 mm through hole along z
await client.query({ capability: 'analyzeBrep', subject: plate });
// refused (GEOSPEC_UNSUPPORTED_EVIDENCE): tessellated, its faces carry no surface
await client.query({ capability: 'analyzeBrep', subject: tessellated });
```

**Python**

```python
# passed: plate, evidence["brep"] lists 7 faces and a 10 mm through hole along z
query_geo(plate, "analyzeBrep")
# refused (GEOSPEC_UNSUPPORTED_EVIDENCE): tessellated, its faces carry no surface
query_geo(tessellated, "analyzeBrep")
```

## analyzeMesh [#analyzemesh]

Reports the mesh the engine measures: counts, per-triangle rows, quality findings, watertightness and bounds per primitive.

**Signature**

**TypeScript**

```ts
client.query({ capability: 'analyzeMesh', subject: part }): Promise<GeoSpecCanonicalClaimReport>
```

**Python**

```python
query_geo(part, "analyzeMesh") -> GeoSpecAssertionReport
```

**Fields**

None.

**Admitted geometry**

* Mesh and glTF/GLB: the admitted triangles.
* STEP: the engine's own tessellation, so a STEP subject whose faces carry no surface is admitted.

**Refusal codes**

* `GEOSPEC_UNSUPPORTED_EVIDENCE`: the subject has no mesh record, such as a rational plate, or was admitted in a y-up frame.
* `GEOSPEC_ANALYSIS_LIMIT`: the watertight analysis inside the report needs more working memory than `maxMeshBytes` allows.

**Examples**

**TypeScript**

```ts
// passed: gear, 1872 vertices, 624 triangles, watertight, signedVolume 911.53 mm³
await client.query({ capability: 'analyzeMesh', subject: gear });
```

The refused case is Python only: no loader in geospec\@beta admits `y_up_plate`.

**Python**

```python
# passed: gear, 1872 vertices, 624 triangles, watertight, signedVolume 911.53 mm³
query_geo(gear, "analyzeMesh")
# refused (GEOSPEC_UNSUPPORTED_EVIDENCE): y_up_plate, a y-up subject answers minimumDistance only
query_geo(y_up_plate, "analyzeMesh")
```

## inspectGeometry [#inspectgeometry]

Resolves selectors and reports each match: occurrences and bodies with bounds and centre, planes with normal and offset, axes with direction and radius.

**Signature**

**TypeScript**

```ts
client.query({ capability: 'inspectGeometry', subject: part, payload: { selectors, evidence? } }): Promise<GeoSpecCanonicalClaimReport>
```

**Python**

```python
query_geo(part, "inspectGeometry", {"selectors": [...], "evidence": [...]}) -> GeoSpecAssertionReport
```

**Fields**

| Field       | Type                                       | Default  | Meaning                                                       |
| ----------- | ------------------------------------------ | -------- | ------------------------------------------------------------- |
| `selectors` | array of selectors                         | required | Names, `RegExp` (`GeoSpecRegex` in Python) or catalog objects |
| `evidence`  | array of `'bounds'`, `'facts'`, `'frames'` | none     | Recorded in the claim; does not narrow the report             |

The selectors are those of [`toHaveSpatialRelationships`](https://www.geospec.xyz/docs/reference/relationships.mdx#tohavespatialrelationships); `payload` is typed `unknown`, so every catalog form compiles here.

**Admitted geometry**

* STEP: occurrences, faces, axes and planes, from the exact BRep.
* glTF/GLB: bodies, by `{ kind: 'body', of: 'cubeA#0' }`.
* One selector that cannot resolve refuses the whole query, and the report lists no matches.

**Refusal codes**

* `GEOSPEC_SELECTOR_UNSUPPORTED_EVIDENCE`: a selector reads evidence the subject does not carry, such as a face, axis or plane selector on a glTF subject.
* `GEOSPEC_SELECTOR_UNMATCHED`, `GEOSPEC_SELECTOR_AMBIGUOUS`: a selector resolves to no entity, or to more than its cardinality allows.

**Examples**

**TypeScript**

```ts
// passed: housingShaft, housing and shaft, each with bounds and centre
await client.query({
  capability: 'inspectGeometry',
  subject: housingShaft,
  payload: { selectors: [{ kind: 'occurrence', name: /.*/, expect: 'many' }] },
});
// refused (GEOSPEC_UNSUPPORTED_EVIDENCE): tessellated, its faces carry no surface
await client.query({ capability: 'inspectGeometry', subject: tessellated, payload: { selectors: ['box'] } });
```

**Python**

```python
# passed: housing_shaft, housing and shaft, each with bounds and centre
query_geo(housing_shaft, "inspectGeometry", {"selectors": [dict(kind="occurrence", name=GeoSpecRegex(".*"), expect="many")]})
# refused (GEOSPEC_UNSUPPORTED_EVIDENCE): tessellated, its faces carry no surface
query_geo(tessellated, "inspectGeometry", {"selectors": ["box"]})
```

## analyzeMeshOverlap [#analyzemeshoverlap]

Reports each component pair's shared volume, without comparing it with an allowance.

**Signature**

**TypeScript**

```ts
client.query({ capability: 'analyzeMeshOverlap', subject: part, payload: { pairs?, tolerance? } }): Promise<GeoSpecCanonicalClaimReport>
```

**Python**

```python
query_geo(part, "analyzeMeshOverlap", {"pairs": [...], "tolerance": ...}) -> GeoSpecAssertionReport
```

**Fields**

| Field       | Type                       | Default    | Meaning                                                             |
| ----------- | -------------------------- | ---------- | ------------------------------------------------------------------- |
| `pairs`     | array of `{ left, right }` | every pair | Pairs to measure                                                    |
| `tolerance` | number, 0 or more, mm      | `0.001`    | Widens the candidate bounds check; a pair below its cube is omitted |

The payload object is required: `{}` measures every pair. Components and selectors follow `toHaveNoComponentInterference`.

**Admitted geometry**

* STEP: an exact Boolean intersection of each candidate pair's leaf solids; the evidence says it is not an allowance check.
* glTF/GLB: a polyhedral Boolean observation with no error bound; an omitted pair is not shown to be zero.

**Refusal codes**

* `GEOSPEC_COMPONENT_PARTITION_INCONCLUSIVE`: fewer than two components.
* `GEOSPEC_EXACT_COMPONENT_INVALID`, `GEOSPEC_MANIFOLD_COMPONENT_INVALID`: a candidate component is not a valid closed solid, so its overlap cannot be measured.
* `GEOSPEC_SELECTOR_UNMATCHED`: a `pairs` entry matches no pair of distinct components.
* `MATCHER_TIMEOUT`: one pair's Boolean work exceeds the budget; the diagnostic names the pair and its cost, 210 units for `housing` and `shaft`.
* `GEOSPEC_ANALYSIS_LIMIT`: the component partition or the overlap observation needs more than `maxMeshBytes` or `maxMeshEntries` allows.

**Examples**

**TypeScript**

```ts
// passed: housingShaft, intersectionVolume 380.81 mm³ for housing and shaft
await client.query({ capability: 'analyzeMeshOverlap', subject: housingShaft, payload: {} });
// refused (GEOSPEC_SELECTOR_UNMATCHED): housingShaft, no component is named nut
await client.query({
  capability: 'analyzeMeshOverlap',
  subject: housingShaft,
  payload: { pairs: [{ left: 'housing', right: 'nut' }] },
});
```

**Python**

```python
# passed: housing_shaft, intersectionVolume 380.81 mm³ for housing and shaft
query_geo(housing_shaft, "analyzeMeshOverlap", {})
# refused (GEOSPEC_SELECTOR_UNMATCHED): housing_shaft, no component is named nut
query_geo(housing_shaft, "analyzeMeshOverlap", {"pairs": [dict(left="housing", right="nut")]})
```

## queryPmi [#querypmi]

Reports a STEP file's product manufacturing information (PMI) records and the faces they refer to, as an inventory; it does not interpret or check tolerances.

**Signature**

**TypeScript**

```ts
client.query({ capability: 'queryPmi', subject: part, payload: { maxRecords?, maxOutputBytes? } }): Promise<GeoSpecCanonicalClaimReport>
```

**Python**

```python
query_geo(part, "queryPmi", {"maxRecords": ..., "maxOutputBytes": ...}) -> GeoSpecAssertionReport
```

**Fields**

| Field            | Type                    | Default   | Meaning      |
| ---------------- | ----------------------- | --------- | ------------ |
| `maxRecords`     | integer, 1 to 1,024     | `1024`    | Record limit |
| `maxOutputBytes` | integer, 1 to 1,048,576 | `1048576` | Byte limit   |

**Admitted geometry**

* STEP whose source is within 8 MiB. A file with no PMI, such as the plate, answers `passed` with an empty inventory.
* The query reads no face geometry, so a STEP subject whose faces carry no surface is admitted.

**Refusal codes**

* `GEOSPEC_UNSUPPORTED_EVIDENCE`: the subject is not STEP; `details.applicability.condition` is `retained-step-source`.
* `GEOSPEC_ANALYSIS_LIMIT`: the inventory exceeds `maxRecords` or `maxOutputBytes`, as the NIST case does with `maxRecords: 1`, and nothing truncated is returned; or the source exceeds 8 MiB, a capacity the inventory profile fixes, or the engine's `maxMeshBytes`. The message names the limit and what the query needed, and `details` carry `limit` and `required`.

**Examples**

**TypeScript**

```ts
// passed: nist, 94 records with inventory status 'semantic'
await client.query({ capability: 'queryPmi', subject: nist });
// refused (GEOSPEC_UNSUPPORTED_EVIDENCE): gear, a GLB mesh carries no STEP source
await client.query({ capability: 'queryPmi', subject: gear });
// refused (GEOSPEC_ANALYSIS_LIMIT): nist with maxRecords 1, the inventory needs 2 records
await client.query({ capability: 'queryPmi', subject: nist, payload: { maxRecords: 1 } });
```

**Python**

```python
# passed: nist, 94 records with inventory status "semantic"
query_geo(nist, "queryPmi")
# refused (GEOSPEC_UNSUPPORTED_EVIDENCE): gear, a GLB mesh carries no STEP source
query_geo(gear, "queryPmi")
# refused (GEOSPEC_ANALYSIS_LIMIT): nist with maxRecords 1, the inventory needs 2 records
query_geo(nist, "queryPmi", {"maxRecords": 1})
```

## minimumDistance [#minimumdistance]

Reports the exact minimum distance between two occurrences of a STEP assembly, with a closest point on each.

**Signature**

**TypeScript**

```ts
client.query({ capability: 'minimumDistance', subject: part, payload: { pair: [{ occurrencePath }, { occurrencePath }] } }): Promise<MinimumDistanceResult>
```

**Python**

```python
query_geo(part, "minimumDistance", {"pair": [{"occurrencePath": ...}, {"occurrencePath": ...}]}) -> GeoSpecAssertionReport
```

**Fields**

| Field  | Type                                   | Default  | Meaning                       |
| ------ | -------------------------------------- | -------- | ----------------------------- |
| `pair` | two `{ occurrencePath: string }` items | required | Two distinct occurrence paths |

**Returns**

**TypeScript**

```ts
type MinimumDistanceResult =
  | { status: 'complete'; fact: MinimumDistanceFact }
  | { status: 'refused'; code: 'unsupported-evidence' | 'invalid-selection' | 'work-limit'; message: string }
  | { status: 'interrupted'; code: 'cancelled' | 'executor-exited' | 'deadline' | 'engine-error'; message: string };
type MinimumDistanceFact = {
  distance: number;
  points: [[number, number, number], [number, number, number]];
  occurrences: [string, string];
  unit: 'mm';
  coordinateSystem: 'z-up';
  source: 'ap242';
  assurance: 'exact-brep';
  subjectHash: string;
  algorithmProfile: 'geospec-minimum-distance-v1';
};
```

**Python**

The report: `passed` carries the same fact in `result["evidence"]["fact"]`, and `refused` keeps the engine's code in `diagnostics`.

`refused` maps the engine's `GEOSPEC_UNSUPPORTED_EVIDENCE` to `unsupported-evidence`, `GEOSPEC_INVALID_SELECTION` to `invalid-selection` and `MATCHER_TIMEOUT` to `work-limit`; `interrupted` is a stop, not a verdict.

**Admitted geometry**

* An AP242 STEP subject with exact BRep faces, every length unit in millimetres.
* The one capability a y-up STEP subject answers; its points are reported z-up.

**Refusal codes**

* `GEOSPEC_UNSUPPORTED_EVIDENCE`: the subject is not STEP, the file is not AP242, or a length unit is not millimetres.
* `GEOSPEC_INVALID_SELECTION`: a path names no occurrence, or more than one.

**Examples**

**TypeScript**

```ts
// passed: housingShaft, fact.distance 0 with coincident points; housing and shaft share material
await client.query({
  capability: 'minimumDistance',
  subject: housingShaft,
  payload: { pair: [{ occurrencePath: 'housing' }, { occurrencePath: 'shaft' }] },
});
// refused (GEOSPEC_INVALID_SELECTION): housingShaft, no occurrence is named nut
await client.query({
  capability: 'minimumDistance',
  subject: housingShaft,
  payload: { pair: [{ occurrencePath: 'housing' }, { occurrencePath: 'nut' }] },
});
```

**Python**

```python
# passed: housing_shaft, fact distance 0 with coincident points; housing and shaft share material
query_geo(housing_shaft, "minimumDistance", {"pair": [{"occurrencePath": "housing"}, {"occurrencePath": "shaft"}]})
# refused (GEOSPEC_INVALID_SELECTION): housing_shaft, no occurrence is named nut
query_geo(housing_shaft, "minimumDistance", {"pair": [{"occurrencePath": "housing"}, {"occurrencePath": "nut"}]})
```