Read this first ​

Contract 2.0.0 · package sha256 a857eca95b35921109faf997094bb4192f12527d9d0fcd4be7e97f06425bad6e · generated from configurator-runtime.d.ts (its header) by services/onsite-scripter/scripts/build-contract-docs.mjs; edit the source, not this file.

Holostep configurator runtime: tenant contract 2.0. Vendored into @holostep-configurators/runtime-client by runtime:types from an immutable release manifest. Never edit by hand.

Read this first. You want to… → use … → not …

One obvious way ​

You want to… Use Not
change a configured material (priced, saved) select(group, material) a rule with { materialNodeId }: look only, never priced
change a variant, an option's visibility or a position setSceneModel, or applyPatch with 'replace' ops find().set({ visible }) or { pose }: a rule, never saved
change a connection assembly.attach / assembly.detach moving nodes by pose
address a placed model as a whole (pose, hide, follow) node(nodeId) find({ node }): that matches every part of the model
restyle authored parts find(q).set(…), reset with null writing the opposite value; writing it every frame
build the whole look from app state one apply(spec); an omitted key stays unchanged re-applying rules after every sync
keep standing rules (hidden planes, ghosts) find(q).set(…): its block comes after apply()'s in apply({ rules }): the next apply({ rules }) replaces them
show no map on a slot maps: null: it removes your value, and the GLB's map shows again
preview a flat colour over a textured finish { baseColor, maps: { baseColor: 'none' } } baseColor alone: it tints the map (dark wood goes black)
switch a whole look, keeping no field of the old one a Material from materials.create(spec), fadeMs a MaterialSpec rule write: however complete, it merges
switch between partial looks over the GLB's material apply({ rules }): its block is replaced whole a second find().set: fields only the old look set stay
let the newest write to a rule win an AbortController per write, abort the previous call order alone: an older write still shows first
retile a texture whose image select() may change a texture without url: { scale }, repeating the url: it pins the image
generate geometry collection(name).set(spec) load()
set the page's lighting, background or exposure the page document's stage; a layer: stage.set stage.set(s) with no { owner }: it rejects
show a GLB you have, with no saved design setSceneModel({ root: [{ …, enabled: true }] }) a ModelNode without enabled: true: it is never drawn
know a GLB's bounds before placing it collection(name).load(url, { key }) mixing load() keys with set() items in one collection
make several changes all or nothing, across awaits transaction(tx => …), writing through tx only session or its handles inside work
make one undo step over time (a drag, an assistant turn) history.group(), with expectGroup on each write a transaction: it holds the display
undo or redo the last step (a toolbar button) session.undo() / session.redo() session.history.undo(): History only reads and groups
sync a store into the runtime setSceneModel with origin, supersede and a label undo: 'rebase': it erases every earlier step
start history clean after a later load (a saved design) undo: 'rebase' on it; the first model needs none undo: 'rebase' on a sync that runs after shopper edits
show a value you just wrote the value you wrote, corrected from the Report reading it back while it is queued (it can take seconds)
read the scene model, null before the first one const m = getSceneModel(); if (!m) … getSceneModel()!: it hides a page with no model yet
capture what the shopper just configured await idle(); capture(); on HoloBusyError, again capture() alone: a pick still loading isn't in it
move something or the camera every frame onFrame(f => f.pose(…) / f.camera(…)) part.set or camera.set in a hook
compute layout inside every commit beforeCommit, through has(); reset its rules on('committed'): that shows one frame stale
use an experimental member or option value has(session, id); an option value: supports(id) calling the optional member unguarded
check what a frame costs (triangles, draw calls) if supports('info.scene'): idle(); info().scene timing frames in onFrame: that measures the device

Traps ​

The traps agents fell into, with only the declaration:

Conventions ​

They hold for every member; members repeat the ones that matter.