API reference
Every export of geospec and the geospec-engine wheel, with options, defaults, results, errors and constants in TypeScript and Python.
npm install geospec@beta geospec-engine gives TypeScript the assertions and the engine; pip install geospec-engine gives Python the geospec package with the engine inside. Install covers the engine artifact each host loads.
The examples run beside the demo models plate-, a 40 × 40 × 20 mm STEP plate with a 10 mm bore, and involute-gear.glb, a gear mesh in millimetres.
Every option and its default
import { readFile } from 'node:fs/promises';
import { expectGeo } from 'geospec';
import { createModelLoader } from 'geospec/model';
import { Engine } from 'geospec-engine/node';
const loadModel = createModelLoader({
// required; Engine takes the
// cache, CPU permits and limits
engine: new Engine(),
// required for a path
readSource: async (path) => new Uint8Array(await readFile(path)),
// default for every load
format: 'glb',
});
const plate = await loadModel({
// a path, or the bytes
source: 'plate-with-through-bore.step',
// default 'glb'; also 'stp'
format: 'step',
// engine default: 8,000,000
workUnitLimit: 8_000_000,
});
const gear = await loadModel({
source: 'involute-gear.glb',
// default 'm' for a mesh file
sourceUnit: 'mm',
});
const size = expectGeo(plate, {
// default: the subject's,
// then the engine's
workUnitLimit: 8_000_000,
}).toHaveBoundingBox({
size: { x: 40, y: 40, z: 20 },
// default 0.02 mm
tolerance: 0.02,
});
console.log(size.report.claimId, size.report.status, size.report.claim.workUnitBudget); // geospec-claim-1 passed 8000000
await loadModel.dispose();from geospec import GeoSpecEngine, expect_geo, load_model
engine = GeoSpecEngine(
# default None, and file=
# refuses until it is set
model_root=".",
# engine default: 8_000_000
work_unit_budget=None,
# up to the host's CPU count
execution_permits=None,
# default: the entry profile
limits=None,
# set both to cache on disk
cache_root=None,
project_root=None,
# required for
# load_model(code=...)
model_loader=None,
)
with engine:
plate = load_model(
# under model_root; or
# source=bytes | path
file="plate-with-through-bore.step",
# default "glb"; also
# "gltf" and "stp"
format="step",
# default "auto" for STEP
# and "m" for a mesh
source_unit="auto",
# glTF buffers: dicts with
# "name" and "source"
resources=(),
# for STEP: {"name": ...},
# which changes the hash
ingest_options=None,
# provenance name;
# default: file
name="plate.step",
)
report = expect_geo(plate, claim_id="size").to_have_bounding_box(size={"x": 40, "y": 40, "z": 20})
print(report.passed, report.result["status"], report.claim["workUnitBudget"]) # True passed 8000000Entry points
geospec:expectGeo,geoSpecMatcherNamesand the types;describe,itandtestfor spec files.geospec/model:loadModel,createModelLoader,Geo.Spec Model Load Error geospec/:assertion- client create,Geo Spec Assertion Client Geo,Spec Assertion Error geo.Spec Maximum Work Unit Limit geospec/:runner/ native create.Geo Spec Native Model Loader geospec/vitest:setupGeoSpecVitest,installGeoSpecVitest,create.Geo Spec Vitest Adapter geospec-engine:Engine,initialize,canonicalize,ProtocolError(WASM).geospec-engine/node:Engine,canonicalize,ProtocolError(native addon).
geospec exports 52 names: the nine documented here, GeoSpecMatchers, Geo and the typed dicts behind them; geospec_ is the engine over bytes, and the wheel registers the pytest plugin geospec.
The engine's byte protocol is for embedders and lives in the engine package README.
Load a model
load Model
Read a model, admit it to the engine and return the subject claims measure.
loadModel(options: LoadModelSourceOptions): Promise<GeoSpecSubject>- Parameters: as annotated in the options block, plus
resources, named glTF buffers, andname,pathandparametersfor the load record. - Returns: an opaque
GeoSpecSubject, alive untilloadModel.dispose(). - Throws:
Geo; its first diagnostic'sSpec Model Load Error codenames the cause:GEOSPEC_outsideNATIVE_ MODEL_ FORMAT_ UNSUPPORTED glb,stepandstp,GEOSPEC_forMODEL_ OPTION_ UNSUPPORTED mesh,stepStreaming, the mesh tolerances andsourceUniton STEP,GEOSPEC_forNATIVE_ RUNTIME_ UNAVAILABLE { file }without a CAD runtime.
const plate = await loadModel({ source: 'plate-with-through-bore.step', format: 'step' });
const gear = await loadModel({ source: 'involute-gear.glb', sourceUnit: 'mm' });
const size = expectGeo(plate).toHaveBoundingBox({ size: { x: 40, y: 40, z: 20 } });
console.log(
size.report.status,
size.report.evidence.measured.size,
size.report.evidence.subjectContentHash.slice(0, 16),
);
// passed [ 40, 40, 20 ] fb622c854152ce81load_model(*, file=..., source=..., format="glb", source_unit=..., resources=(), ingest_options=None, name=...) -> GeoSpecSubject- Parameters: as annotated in the options block: exactly one of
fileandsource;format;source_unit;resources, each with exactlynameandsource;ingest_options;name. - Returns: a frozen
GeoSpecSubject(engine,slot,identity_field,identity,provenance), alive untilclose()on it or on the engine. - Raises:
GeowithSpec Assertion Error GEOSPEC_,MODEL_ HOST_ UNAVAILABLE GEOSPEC_,MODEL_ PATH_ OUTSIDE_ ROOT GEOSPEC_,MODEL_ FORMAT_ UNSUPPORTED GEOSPEC_orMODEL_ OPTION_ UNSUPPORTED invalid-field;FileNotFoundErrorfor a missing file;TypeErrorforfilewithsource.
plate = load_model(file="plate-with-through-bore.step", format="step")
gear = load_model(file="involute-gear.glb", source_unit="mm")
print(plate.provenance["primary"]["sha256"][:16], plate.identity_field, gear.identity[:16]) # fb622c854152ce81 subjectHash a9913a341848cb6bcreate Model Loader
createModelLoader(defaults: CreateModelLoaderOptions): ManagedGeoSpecModelLoader- Parameters:
engine;readSource, required for a path source;formatand the mesh options as defaults;projectPath,runtimeandsourceAdaptersfor code models. - Returns:
loadModelwithdispose(), which releases every subject it admitted. - Throws: nothing on creation; a path load without
readSourcethrowsGEOSPEC_.NATIVE_ SOURCE_ READER_ UNAVAILABLE
const plate = await loadModel({ source: 'plate-with-through-bore.step', format: 'step' });
await loadModel.dispose();
expectGeo(plate).toHaveVolume({ value: 30429.2 }); // TypeError GEOSPEC_SUBJECT_INVALID: reload it within its live scopeGeoSpecEngine is the loader: it owns its subjects and runs on the thread that created it.
GeoSpecEngine(*, model_root=None, work_unit_budget=None, execution_permits=None, cache_root=None, project_root=None, model_loader=None)- Parameters: as in the options block above;
cache_rootandproject_rootgo together. - Returns: the engine; as a context manager it is
load_model's engine and closes on exit, releasing every subject. - Raises:
ValueErrorfor a budget that is not a positive safe integer, permits outside 1 to the CPU count, or a cache root alone;RuntimeErroroff the owner thread or afterclose().
with GeoSpecEngine(model_root=".") as engine:
print(engine.protocol_version, engine.registry_version, len(engine.capabilities), engine.configuration["defaultWorkUnitBudget"]) # 3 5 32 8000000State a claim
expect Geo
Start a chain on a subject; each matcher call is one claim, answered in that call. The matcher reference lists the 26 matchers and their fields; a scalar matcher takes { value, tolerance }.
expectGeo(subject: GeoSpecSubject, options?: GeoSpecExpectOptions): GeoSpecMatcher- Parameters: the subject;
workUnitLimit, this chain's budget. - Returns: the matchers and
.not, each returning aGeoSpecAssertionwith the engine'sreport; claim ids countgeospec-claim-Nper process, shared with the assertion client. - Throws:
Geofor a failed or refused claim, carrying the same report;Spec Assertion Error ProtocolErrorinvalid-claimfor a plan the engine rejects, such as a negated query;TypeErrorfor a subject outside its live scope or aworkUnitLimitoutside 1 to the host's maximum.
const noHole = expectGeo(plate).not.toHaveCircularHole({ diameter: 12 });
console.log(noHole.report.claimId, noHole.report.polarity, noHole.report.status); // geospec-claim-1 negative passed
expectGeo(gear, { workUnitLimit: 100 }).toHaveBoundingBox({ size: { z: 5 } }); // GeoSpecAssertionError: refused, MATCHER_TIMEOUT, details.used 3744expect_geo(subject, *, claim_id=None) -> GeoSpecMatchers- Parameters: the subject;
claim_id, defaultgeospec-claim-Nfrom one counter per engine. - Returns: the matchers and
.not_, each returning aGeo; a matcher is the snake_case registry name and takes keyword fields with their camelCase names (Spec Assertion Report toleranceMm=), or one positional dict. - Raises:
Geofor a failed or refused claim, and with codeSpec Assertion Error invalid-claimfor a plan the engine rejects;AttributeErrorfor a name outside the registry;TypeErrorfor a positional dict with keyword fields.
report = expect_geo(plate).to_have_bounding_box(size={"x": 40, "y": 40, "z": 20})
print(report.claim["claimId"], report.result["status"], report.result["evidence"]["measured"]["size"]) # geospec-claim-1 passed [40, 40, 20]
try:
expect_geo(plate).to_have_circular_hole(diameter=12)
except GeoSpecAssertionError as error:
print(error.code, error.result["status"], error) # GEOSPEC_FEATURE_MISMATCH failed No circular hole matches diameter 12 within 0.02 mm.
expect_geo(gear).not_.to_have_circular_hole(diameter=4) # GeoSpecAssertionError: refused, GEOSPEC_UNSUPPORTED_EVIDENCE, under either polarityevaluate_ runs one matcher by registry name and returns the report whatever the status, with passed False for a refusal; a bad payload raises geospec_, unwrapped.
Query geometry
The five queries, analyzeMesh, analyzeBrep, inspectGeometry, analyzeMeshOverlap and queryPmi, report what the engine measured, take positive polarity only, and refuse when the evidence they read is missing or cannot decide; a query never fails. Query geometry reads the reports.
create Geo Spec Assertion Client
A client runs chains and queries outside a test runner, on subjects from create in geospec/: its loadModel returns { subjectHash, load }, releaseAll() releases them, and the root expectGeo rejects them with TypeError GEOSPEC_.
createGeoSpecAssertionClient(options: GeoSpecAssertionClientOptions): GeoSpecAssertionClient- Parameters:
engine;workUnitLimit, every claim's budget unless the chain or the subject sets one;claimId(matcher, sequence);evidenceProfile,completeorbounded;subjectSlot;maximumWorkUnitLimit. - Returns:
expectGeo(subject, options?), whose matchers return theGeodirectly, andSpec Canonical Claim Report query. - Throws: as
expectGeo; a subject of another engine, or one released byreleaseAll(), throwsProtocolErrorinvalid-claim.
import { createGeoSpecAssertionClient } from 'geospec/assertion-client';
import { createGeoSpecNativeModelLoader } from 'geospec/runner/native';
const loadModel = createGeoSpecNativeModelLoader({ engine, readSource });
const { expectGeo, query } = createGeoSpecAssertionClient({ engine });
const plate = await loadModel({ source: 'plate-with-through-bore.step', format: 'step' });
const volume = expectGeo(plate).toHaveVolume({ value: 30429.2 });
console.log(plate.subjectHash.slice(0, 16), volume.claimId, volume.status, volume.claim.workUnitBudget); // da21b0d6142fc5fb geospec-claim-1 passed 8000000Python needs no client: expect_geo, evaluate_geo and query_geo use the engine that owns the subject.
query
client.query(options: GeoSpecQueryOptions): Promise<GeoSpecCanonicalClaimReport>
client.query(options: MinimumDistanceQuery): Promise<MinimumDistanceResult>- Parameters:
capability;subject, from the native loader or a bare{ subjectHash };payload, omitted foranalyzeMeshandanalyzeBrep;claimId, which leaves the shared counter alone. - Returns: the report;
minimumDistancereturns{ status: 'complete', fact }or{ status, code, message }(unsupported-evidencewithout AP242 provenance). - Throws:
ProtocolErrorinvalid-claimfor a plan the engine rejects;TypeErrorfor a subject without exactly one ofsubjectHashandcontentHash.
const brep = await query({ capability: 'analyzeBrep', subject: plate });
console.log(brep.claimId, brep.status, Object.keys(brep.evidence).join(',')); // geospec-claim-2 passed brep,diagnostics,success
await loadModel.releaseAll();query_geo(subject, capability, payload=None, *, claim_id=None) -> GeoSpecAssertionReport- Parameters: the subject; a query name;
payload, a mapping whose regular-expression selectors areGeo, since a compiledSpec Regex( pattern, flags="") re.PatternraisesTypeError;claim_id. - Returns: the report.
- Raises:
ValueErrorfor a capability that is not a query;geospec_for an unknown payload field, unwrapped.engine_ native. Protocol Error
brep = query_geo(plate, "analyzeBrep")
print(brep.result["status"], sorted(brep.result["evidence"])) # passed ['brep', 'diagnostics', 'success']
print(query_geo(gear, "analyzeBrep").diagnostics[0]["code"]) # GEOSPEC_BREP_EVIDENCE_UNAVAILABLE
query_geo(plate, "toHaveVolume") # ValueError: GeoSpec capability 'toHaveVolume' is not a query.Run specs
geospec run, the command in geospec, runs every *.geospec.ts or *.geospec.js file under the directory it is given, with no configuration file: it compiles each spec, supplies the engine and owns the subjects, so a spec has no hooks. A spec imports describe, it and expectGeo from geospec and loadModel from geospec/model, which takes no engine and reads a path relative to that directory; it never imports geospec-engine.
import { describe, expectGeo, it } from 'geospec';
import { loadModel } from 'geospec/model';
describe('plate', () => {
it('is 40 x 40 x 20 with a 10 mm bore', async () => {
const plate = await loadModel({ source: 'plate-with-through-bore.step', format: 'step' });
expectGeo(plate).toHaveBoundingBox({ size: { x: 40, y: 40, z: 20 } });
expectGeo(plate).toHaveCircularHole({ diameter: 10 });
});
it('refuses a hole claim on the gear mesh', async () => {
const gear = await loadModel({ source: 'involute-gear.glb', sourceUnit: 'mm' });
expectGeo(gear).toHaveCircularHole({ diameter: 4 });
});
});$ npx geospec run .
FAIL plate.geospec.ts
fail plate > refuses a hole claim on the gear mesh
GEOSPEC_UNSUPPORTED_EVIDENCE: expectGeo(...).toHaveCircularHole() needs exact BRep circular-hole evidence, which this subject does not carry.
1 passed, 0 failed, 2 selectedThe exit code is the verdict: 0 only when every selected test passed; a failed test, a refused claim, a run-level issue or an empty selection exits 1. A refused claim leaves its test inconclusive, counted in neither number on the last line. --json prints one report instead: success, an accounting block with passed, failed, inconclusive, skipped and notRun, and for each test its status, diagnostics and claim reports.
--file <path>,--include <glob>and--exclude <glob>select files,-t <regex>selectssuite > testnames; a filtered run proves the selected scope only.--workers [n]runs a worker pool, sized to the host's CPUs withoutn.--test-timeout <ms>,--shard-timeout <ms>and--matcher-are wall-clock watchdogs, off by default; a timeout is no geometry verdict.wall- backstop <ms> --bailstops after the first failing file;--forensicappends timing lines.
Under Vitest, geospec/vitest registers the 26 matchers on expect. setup creates an assertion client from the create options and installs its matchers in the current file; install installs an existing client's, and create returns { matchers, flush } without installing them. A matcher returns a promise, so await it. A failed or refused claim fails the test with the claim's result as its message, and a refusal fails under .not too.
import { readFile } from 'node:fs/promises';
import { afterAll, expect, it } from 'vitest';
import { createModelLoader } from 'geospec/model';
import { setupGeoSpecVitest } from 'geospec/vitest';
import { Engine } from 'geospec-engine/node';
const engine = new Engine();
const loadModel = createModelLoader({
engine,
readSource: async (source) => new Uint8Array(await readFile(source as string)),
});
setupGeoSpecVitest({ engine });
afterAll(async () => {
await loadModel.dispose();
engine.close();
});
it('refuses a hole claim on the gear mesh', async () => {
const gear = await loadModel({ source: 'involute-gear.glb', sourceUnit: 'mm' });
await expect(gear).toHaveCircularHole({ diameter: 4 });
});npx vitest run reports 1 failed; the message under the test is the result JSON, GEOSPEC_ first among its diagnostics.
Any other runner works with an explicit engine: a failed or refused claim throws Geo, failing the test with the engine's message; release the subjects in an after hook. node --test plate.test.mjs reports fail 1.
import { readFile } from 'node:fs/promises';
import { after, test } from 'node:test';
import { expectGeo } from 'geospec';
import { createModelLoader } from 'geospec/model';
import { Engine } from 'geospec-engine/node';
const engine = new Engine();
const loadModel = createModelLoader({ engine, readSource: async (path) => new Uint8Array(await readFile(path)) });
after(async () => {
await loadModel.dispose();
engine.close();
});
test('a hole claim on the gear mesh is refused', async () => {
const gear = await loadModel({ source: 'involute-gear.glb', sourceUnit: 'mm' });
expectGeo(gear).toHaveCircularHole({ diameter: 4 });
});The wheel's pytest plugin gives each test an engine, created on its first load_model and closed at teardown, with model_root at pytest's rootpath; Geo extends AssertionError, so a failed or refused claim fails the test.
import pytest
from geospec import GeoSpecAssertionError, expect_geo, load_model
def test_plate():
plate = load_model(file="plate-with-through-bore.step", format="step")
expect_geo(plate).to_have_bounding_box(size={"x": 40, "y": 40, "z": 20})
with pytest.raises(GeoSpecAssertionError) as raised:
expect_geo(plate).to_have_circular_hole(diameter=12)
assert raised.value.code == "GEOSPEC_FEATURE_MISMATCH"$ python -m pytest test_plate.py -q
. [100%]
1 passed in 0.02sOverride the geospec_ fixture in conftest.py to set every test's model_root, work_unit_budget or limits.
Engines
initialize
Load and compile the WASM module once, before constructing an Engine from geospec-engine; the node entry needs no initialization.
initialize(input?: WasmInput, execution?: WasmExecution): Promise<void>- Parameters:
input, the module as bytes, aResponse, aURLor a string, or omitted for the packaged module;execution, single-threaded by default. - Returns: a promise resolved once the module is ready.
- Throws:
ProtocolErrorinvalid-requestfor module bytes with no initializer.
import { Engine, initialize } from 'geospec-engine';
new Engine(); // ProtocolError invalid-request: Call initialize() before using the GeoSpec WASM engine.
await initialize();
const engine = new Engine();The wheel needs no initialization: GeoSpecEngine sends the initialize request itself and exposes its answer as protocol_version, registry_version, canonical_profile, capabilities and configuration, which carries default and the engine's effective limits.
Engine
One engine owns its admitted subjects. The native addon, the WASM module and the wheel answer a claim with the same bytes: the plate's bounding-box result above is 532 canonical bytes, SHA-256 57e6453a170bada5…, from all three.
- Parameters:
new Engine(fromcache Options?, executionPermits?, limits?) geospec-engine/nodetakes an optional filesystem cache,{ root, projectRoot }, a caller-inclusive CPU allocation, default one, and the host limits;new Engine(execution?, limits?)fromgeospec-enginetakes the productinitializeloaded and the same limits. - Limits:
maxSubjectBytes(64 MiB),maxResourceBytes(64 MiB) andmaxTotalBinaryBytes(128 MiB) bound the bytes one load admits;maxVertices(2,000,000),maxTriangles(4,000,000) andmaxOccurrences(65,536) its decoded mesh and occurrence list;maxRetainedSubjects(32) the subjects one engine holds;maxMeshBytes(256 MiB) andmaxMeshEntries(256) the bytes and the entries one analysis retains or uses as scratch.maxSolidEntries(256) bounds the CSG source solids the engine keeps for reuse across claims; an operand past it is admitted for its claim alone, never refused. The values in parentheses are the entry profile,geospec-, which an omitted limit keeps; an engine with other values reportsentry- config- v1 geospec-.host- config- v1-<id> - Returns: the engine the loaders and clients take as
engine. - Throws:
ProtocolErrorinvalid-requestfor a limit that is not a positive integer up to 4,294,967,295, amaxSubjectBytesormaxResourceBytesabovemaxTotalBinaryBytes, or a name the engine does not know; afterclose(), every use throws the same code,GeoSpec Node engine is closed.orGeoSpec WASM engine is closed.
A load over an admission limit throws ProtocolError limit-exceeded with capacity, the numbers a refused claim carries in details. geospec@0.1.0-beta.6 reads the default budget from the entry profile only, so a chain, model or client on an engine with its own limits sets workUnitLimit itself.
import { readFile } from 'node:fs/promises';
import { createModelLoader } from 'geospec/model';
import { Engine, ProtocolError } from 'geospec-engine/node';
const engine = new Engine(undefined, undefined, { maxTriangles: 600 });
const loadModel = createModelLoader({
engine,
readSource: async (source) => new Uint8Array(await readFile(source as string)),
});
try {
await loadModel({ source: 'involute-gear.glb', sourceUnit: 'mm' }); // 624 triangles
} catch (error) {
if (!(error instanceof ProtocolError) || !error.capacity) throw error;
console.log(error.code, error.capacity.code, error.capacity.limit, error.capacity.required);
console.log(error.message);
}
engine.close();limit-exceeded GEOSPEC_RESOURCE_LIMIT 600 624
The decoded mesh has 624 triangles; the configured triangle limit is 600 triangles.Geo takes the ten limits in snake_case, max_subject_bytes to max_. An unknown name or a value that is not a positive integer raises ValueError; max_subject_bytes or max_resource_bytes above max_ is a ProtocolError invalid-request. engine.configuration reports the effective values under binary, analysis and maxRetainedSubjects, and the profile ids as configurationProfile and binary. A load over an admission limit raises ProtocolError limit-exceeded out of load_model, with capacity_code and capacity_details.
from geospec import GeoSpecEngine, load_model
from geospec_engine_native import ProtocolError
with GeoSpecEngine(model_root=".", limits={"max_triangles": 600}) as engine:
print(engine.configuration["configurationProfile"])
try:
load_model(file="involute-gear.glb", source_unit="mm") # 624 triangles
except ProtocolError as error:
print(error.code, error.capacity_code, error.capacity_details)
print(error)geospec-host-config-v1-1649e816d1e3c9a3
limit-exceeded GEOSPEC_RESOURCE_LIMIT {'limitClass': 'admission', 'unit': 'triangles', 'limit': 600, 'required': 624}
The decoded mesh has 624 triangles; the configured triangle limit is 600 triangles.GeoSpecEngine also admits bytes directly:
ingest_admits STEP, glTF or GLB bytes in asubject( primary, *, format, coordinate_system, source_unit, output_unit, ingest_options=None, resources=(), slot="subject") z-upory-upframe, with(name, bytes)resources andoutput_unitmm.ingest_mesh(mesh, *, slot="subject")admitsmesh-buffer-v1bytes, named bycontentHash.
from pathlib import Path
with GeoSpecEngine(model_root=".") as engine:
part = engine.ingest_subject(
Path("plate-with-through-bore.step").read_bytes(),
format="step", coordinate_system="z-up", source_unit="auto", output_unit="mm",
ingest_options={"name": "part"}, slot="part",
)
print(part.slot, part.identity_field, part.identity[:16]) # part subjectHash a53548710335079bOptions
The type tables are from geospec@0.1.0-beta.6, the release that geospec@beta installs; every TypeScript example on this page ran on it. In Python, load_model takes Geo, the snake_case spellings of Load plus file and code.
Load Model Source Options
Prop
Type
Create Model Loader Options
Prop
Type
Geo Spec Expect Options
Prop
Type
Geo Spec Assertion Client Options
Prop
Type
Geo Spec Query Options
Prop
Type
Results
Geo Spec Assertion
A root expectGeo matcher returns a GeoSpecAssertion: the claim, the engine's report and the load it ran on.
Prop
Type
Geo Spec Canonical Claim Report
The report is the record both languages keep: the canonical claim, plan and result bytes, the claim with its defaults filled in, and the result with status, diagnostics and evidence. Reproduce a result hashes canonicalResult to compare runs.
Prop
Type
In Python the same record is the frozen Geo, with canonical_*_bytes, claim, result, diagnostics, passed and provenance.
Geometry Diagnostic
A failed claim carries the matcher's mismatch code, such as GEOSPEC_; a refused claim carries the reason the common refusals list, such as GEOSPEC_, MATCHER_TIMEOUT with the budget and the units used in details, or GEOSPEC_ with the limit and what the analysis required.
Prop
Type
Errors
Geo Spec Assertion Error
Thrown by every matcher for a failed or refused claim, from geospec/, with report, claim, result, diagnostics and the three canonical byte fields. It has no code: read report.status for the outcome and diagnostics[0].code for the reason.
Raised by every matcher for a failed or refused claim, and by expect_geo and load_model for a request the engine or host rejects. It extends AssertionError and adds code (the first diagnostic's, or the protocol code), report, result, claim, diagnostics, the canonical_*_bytes and protocol_error, the wrapped native exception.
Geo Spec Model Load Error
Thrown by loadModel, from geospec/model, with diagnostics as GeometryDiagnostic rows; the first one's code names the cause.
Python has no separate load error: load_model raises Geo for a request it rejects, and lets the engine's ProtocolError through for a model over an admission limit.
Protocol Error
Thrown by the engine for a request it does not accept, with code and message; a refused claim is a result, not an error. geospec-engine exports the class and the TypeScript assertions let it through; Python's geospec_ escapes load_model, evaluate_geo and query_geo, and expect_geo wraps it in Geo with the same code.
invalid-claim: a negated query, an out-of-range budget, or a subject the engine never admitted. A malformed payload refuses its own claim withGEOSPEC_instead.INVALID_ EXPECTATION invalid-request: a closed engine, a WASM engine used beforeinitialize(), or a limit the constructor rejects.limit-exceeded: a load over an admission limit,maxTrianglesormaxRetainedSubjectsamong them.capacity(Pythoncapacity_codeandcapacity_details) carries the refusal code,GEOSPEC_orRESOURCE_ LIMIT GEOSPEC_, withANALYSIS_ LIMIT limitClass,unit,limitandrequired.
Constants
geoSpecMatcherNames, fromgeospec: the 26 matcher names in registry order,toHaveBoundingBoxfirst andtolast.Satisfy Parallel Plane Distance geo, fromSpec Maximum Work Unit Limit geospec/: 512,000,000, the largestassertion- client workUnitLimitunless a host raisesmaximumWorkUnitLimit.- Defaults: 8,000,000 work units (
configuration[in Python), a tolerance of 0.02 mm and claim ids"default Work Unit Budget"] geospec-claim-N. - Protocol version 3, registry version 5, canonical profile
geospec-jcs-v1and 32 capabilities: the 26 matchers, the five queries andminimumDistance. - Profiles:
geospec-andentry- config- v1 geospec-name the entry limits; an engine constructed with other limits reportsentry- binary- v1 geospec-andhost- config- v1-<id> geospec-, ids digested from the values.host- binary- v1-<id>
How it works
The four stages between model bytes and a verdict, what a subject carries, what a result promises and where the engine stops.
Capability cheat sheet
Every capability on one page, with its kind, admitted subjects, failure code and refusal codes, generated from the registry and the reference.