StaticfindFind the first reachable Stuff matching predicate, searched in
"on your person, then the room" order: installed augmentations
(slot occupants), then carried inventory, then the surrounding
location's contents. Returns null when nothing matches.
The reach surface mirrors the canReach validator's criteria
(inventory + location contents), extended with slot occupants so
an installed implant counts as on-person, plus two legs for the
three-base capability model: the self leg (a capability
composed directly on the actor / its species) and the
descend-into-host leg (an incorporeal update Idea hosted on an
attunement host — the actor itself, an installed implant, or a
carried attuned Thing). No global index — scans only the actor and
the given location.
Order (on-your-person first): self → self's hosted updates → slot occupants (+ their hosted updates) → carried (+ carried hosts' hosted updates) → location contents.
Generalizes the old check-inventory-and-augs scans: fast travel uses it to find a credential (card Thing, or hosted credential update) or the node you're standing at, but it is deliberately predicate-agnostic.
Guardrail — findReachable vs. MQL. This answers exactly one
question: is there a reachable bearer of capability-type X for
the engine to route behavior through? — keyed on a mixin type,
first-match, returning a type-narrowed Stuff & T. It is NOT a
query engine. Anything keyed on identity / keywords / properties /
user input — argument resolution, choosing among matches,
filtering, live/subscribable results — belongs to MQL, never here.
The host-descent leg is bounded to a single concept and a single
level; do not teach this helper another leg.
StaticforceForce-bypass variant of move(). Pre-flight invariants still
fire (those are programmatic-contract guards, not policy);
canMove / canRemoveContainable / canAddContainable
witnesses still fire (so observers / audit hooks see the call)
but their veto results are ignored. Post-move on* hooks fire
identically.
Gated to the TeleportController / GotoController — the
narrow-entry pattern. Only the teleport/goto controllers can
reach this entry point; each does the AccessApi.can(giver, 'force-teleport' | 'force-goto', ...) check before invoking.
Combined, the mutation has exactly one legitimate entry path AND
that path enforces who is authorized.
Each controller is cloned per execution (teleport/goto --force),
and FromModule matches it by its class module id (code provenance),
so the cloned instances are admitted directly — an AnyOf of the two
controllers' FromModule gates, no FromTemplate arms. Direct calls
from any other module throw SecurityError.
StaticgetGet the container that holds an item
StaticgetGet contents from a container object
Usage:
const inventory = ContainmentApi.getContents(avatar);
const locationContents = ContainmentApi.getContents(location);
StaticisCheck if an object is contained in a specific container
StaticlooseFilter a contents snapshot to the loose (top-level) items — those
NOT resting on another item in the same set. Items resting on a listed
surface (the bottles on the back-bar) are represented by that surface and
discovered by examining it (look <surface>); listing them as room
contents is the clutter we avoid. Shared by every contents-presentation
surface — look, sense, and the inspection-pane projection — so they
agree. Pure: the containment walk is unchanged; this only shapes what's
presented at top level.
StaticmoveMove an item to to, or detach it (when to === null).
Pipeline:
can* Witness hooks — short-circuit on the first veto.item.setContainer(to) — atomic state mutation.on* Witness hooks (post-mutation, never veto).Zone is NOT restamped on move — it should reflect whichever
zone created the item, not whichever container it currently
sits in. Cross-zone movement rules are enforced by the
pre-flight invariants (Exitables can't cross zones via
containment) but the zone field itself is set at clone time
and stays put.
StaticplacePlace item in env without firing movement witnesses, running
capacity validators, or triggering the glob merge-on-arrival
ripple. The matter is treated as if it were already in env;
this call just records the topological fact.
Precondition: item.getContainer() === null. This is NOT a
relocation primitive — existing-env Stuffs go through
ContainmentApi.move. Throws when violated.
Use when the placement is semantically NOT an arrival:
StuffApi.clone that
deliberately bypass arrival hooks.Use ContainmentApi.move when the placement IS movement (an
existing Stuff genuinely entered env from elsewhere).
What's preserved (always):
setContainer).item must be Containable, env
must be Container. Putting a non-Containable somewhere or
accepting contents into a non-Container would corrupt the
graph regardless of who's observing.What's bypassed:
can* / on* witnesses (placement is not movement).Security: gated by SecurityPolicies.ApiOnly because the
skipped checks make this primitive more powerful than move.
The fresh-placement precondition rules out the obvious abuse
(smuggling, teleport-past-guard) — existing-env Stuffs must go
through move, period.
StaticplacePlace item on surface — the on-surface analogue of
move. Under Option D (see
docs/plans/affordance-verb-plan.md § 2), containment stays
hierarchical and exclusive; the supporting surface is an
orthogonal auxiliary pointer.
Pipeline:
canRest(item) veto. Throws on
programmatic-contract failure (validators upstream produce
friendly user-input messages).move(item, targetEnv) — this fires the usual container
change hooks AND clears any prior restingOn as part of
the change-of-container invariant.restingOn pointer to the surface.
Order matters: move() in step 3 clears restingOn; the
_setRestingOn call after it restamps to the new surface.StaticresolveResolve a spawn/landing reference into the live Container to place
something in. The reference is EITHER a Warren — land in its lazily
created (and migration-tracked) host via getHost() — or an ordinary
location: a singleton room is reused, a non-singleton is cloned fresh
(StuffApi.singletonOrClone).
Returns the resolved container plus the Warren when the ref named one,
so a caller that must follow host migration — a self-seating fixture —
can register with it; warren is null for a plain location.
This is the warren-aware landing resolution shared by avatar spawn
(game entry, Avatar.applyStartLocation) and self-seating fixtures
(FixtureMixin.seatSelf). StuffApi.singletonOrClone stays the
generic, domain-free primitive; this is its Warren-aware sibling. The
Warren value is loaded dynamically so the static import graph stays
acyclic (Warren imports ContainmentApi).
Static API for containment and movement operations.