Persistent UI state slot. Keys come from
clientStateSchema; values are the JSON-shape the schema
declares. Round-trips via the Hydrator like any other
persistent field; populated wholesale on session-establish
via the welcome payload, updated by client-state-write.
Persistent storage. Default [] matches the Alias.aliases
legacy-tolerant pattern — existing avatar docs without the
field hydrate cleanly.
Per-Detail Material overrides — flat map from detailKey to the
Material's templatePath. Stored as a plain Record (not a
Map) so default JSON serialization handles it without a
marshaller.
Path to the bulk default Material singleton. Resolved lazily on
each getMaterial() call so HMR replacement is observed
immediately.
Persistent storage. Default [] matches the Contacts / Alias
legacy-tolerant pattern — existing avatar docs without the field
hydrate cleanly.
Protected_Runtime-only auxiliary support pointer — Pattern B live ref.
Holds a direct reference to the supporting Surfaced host
(null when no support). NOT in persistentFields; resets to
null on hydrate. R2.3 self-heal in getRestingOn clears the
slot if the supporter has been destructed.
Pattern B chosen over Pattern A templatePath stamping because non-singleton surfaces (e.g., multiple identical tables in a dining hall) can't be addressed unambiguously by templatePath. The cross-restart loss is small — items reappear in their container, just without the on-surface precision.
Path to the Species singleton this organism belongs to. Resolved lazily on each getSpecies() call so HMR replacement is observed immediately.
Transient client-state slot — keys whose schema entry is
transient. In-memory only: deliberately NOT in persistentFields,
so it never reaches the Hydrator and resets to defaults on a fresh
login. Per-bar input modes live here (ephemeral input scoping that
belongs to the session, not the character).
Years (or species-appropriate units; v1 doesn't enforce). 0 at birth/clone-time. Aging is deferred to follow-on builds.
OptionalaliasesOptional + default {}. Matches the legacy-tolerant
Propertied.savedProps pattern so existing avatar docs without
the field hydrate cleanly.
Transient. Not in persistentFields.
ProtectedalternateClosed-choice origin/aspiration key (or null if unset).
Medium-tag override ('air'/'water'/'vacuum'); null = default.
Claimed narrative prose. Seeded at char-gen; editable later.
Cached resolved humidity (%) for the wet-bulb read.
Persistent cwd in the content tree. Defaults to / rather
than reading workspace.home at construction time — the
Hydrator hasn't loaded settings yet at the moment fields
default-initialise.
ProtectedcontentsThe contained items. Read access goes through getContents();
mutation goes through addContainable / removeContainable,
which only Containable.setContainer may legitimately invoke.
The digestion-buffer per-tag pools (%-fill to deliver per tag).
Cached effective ambient (raw K) — biome + warmth + clo + transforms.
Stored employment records (plain data). Sparse: null on an
unemployed Character (nothing written to the doc). Wrapped into
Employment value objects on read.
ProtectedenvironmentLive reference to the container. NOT a persistent field —
cross-Stuff references would round-trip badly through the
Hydrator's reflection. The container relationship is rebuilt
at clone time via the applyContainer instruction-field path
(see static instructionFields above) or by direct
ContainmentApi.move calls after hydration.
Auxiliary restingOn is different — it's a Pattern A
path-string (_restingOnPath) that DOES persist; see static
persistentFields below.
Protected OptionalhonorificProtectedillustrationProtectedinteractivesConnected Interactives. Host-internal storage; external consumers
use addInteractive / removeInteractive / getInteractives().
ProtectedisAnonymous-guest marker. Runtime-only (NOT persisted — guests never
save). Stamped in postRegister from the clone context. This is the
character axis (is this body a throwaway persona?), distinct from
the session's auth state (User.anonymous). Every guest behavior
— don't-flush, destroy-on-disconnect, reserved name, client badge —
keys off this, never the session.
Cached resolved ambient (raw K) the object is drifting toward.
Last-eaten material name — for vomit prose only (no accounting).
Current lifecycle state — one of the species' valid set
('alive', 'dead', 'undead', 'powered', 'unpowered',
'destroyed'). Initial value lives on the leaf template's
data per slate. Empty default keeps unhydrated test fixtures
trivially constructable.
Liquid sub-volume (litres) — filled by drink / sip.
ProtectedlongGame-time (seconds) of the last reconcile; 0 = unseeded.
ProtectednameProtected OptionalnameOptionalpersistentSchema-declared persistent overrides. Optional + default {}
matches the legacy-tolerant Propertied.savedProps pattern, so
existing avatar docs don't need migration.
ProtectedplayerCharacter slot id (key under /obj/Avatar/<playerId> and in User.playerIds).
Runtime-only: the template path encodes it, so it does not need to be
mirrored into the doc. Stamped by postRegister from the clone
context, or seeded by the test/direct-construction data blob.
ProtectedpronounsStorage — public for the Hydrator.
The only persisted field — the drain's elapsed anchor.
Schema-declared session overrides PLUS ad-hoc vars. Transient; lives only as long as the in-memory instance.
Setpoint (K) the body defends — the movable fever/magic seam.
ProtectedshortProtectedslotsLive runtime occupancy. Direct refs (proxy framework intercepts
on use). Each slot's value is the set of current occupants;
empty set when unoccupied. Not in persistentFields — starts
empty on clone / hydrate.
Solid sub-volume (litres) — filled by eat.
Persistent cwd in the source tree. Same defaulting story as
contentCwd.
Stamped temperature T0 (raw K) — the decomposed scalar.
Authoring data — only used by the default getSlotNames /
getSlotSpec implementation. Hosts that override the universe
surface (BodyPlanSlots, Adornable) leave this empty.
ReadonlystuffRuntime ID for this object (generated using nanoid). This is NOT the MongoDB _id - it's a runtime identifier.
Protected OptionalsurnameGame-time (seconds) of the last reconcile / re-stamp; 0 = unseeded.
Game-time (seconds) of the last regulation reconcile; 0 = unseeded.
Sparse per-toxin body burdens (Wave 2; created on first exposure).
Protected OptionaluserRuntime-only pointer to the owning User. Stamped by postRegister
from the clone context; NOT persisted. Ownership lives on
User.playerIds. Host-internal storage; external callers use
getUser() / setUser().
Static_Augment-gated marker: AetherMixin is composed natively on
every Avatar (so methods + state exist) but is inert unless
an installed augment confers it. The AetherImplant
(/lib/augmentation/AetherImplant) is the v1 conferrer;
MixinApi.getActiveMixins reflects implant presence in real
time.
Static_Modalities AetherMixin contributes to the sensorium when
active. PerceptionApi.sensorium walks active mixins and
unions their _grantsModalities; combined with the BodyPlan
organ walk, this is how an implant-bearing Avatar gains the
ESP modalities (no native ESP organ in v1).
Static_StaticclientClient-state schema. Declares every persisted UI key + its default. One flat array — no chain walker, no per-feature mixin layer. Add an entry to ship a new key.
Scope rule: only put a key here if it's substrate-level (meaningful for every HasInteractive-bearing thing). If a key is owned by a narrower mixin, the right move is to promote a small registry / walker — but defer that until the array genuinely outgrows a single file.
StaticcommandCommand provider for Avatar-specific commands (diagnostic/system)
StaticDEFAULT_Empty portraitUrl sentinel — "no image; client renders a
generated placeholder" (initials/icon from the name). We don't
mint a fake asset URL server-side; the empty string is the
honest "default" the client interprets.
Staticdefaultv1 default set is empty by design. The mechanism is wired:
future content mixins declare static defaultAliases on
themselves and collectDefaultAliases picks them up.
StaticfieldField-marshaller binding. mass round-trips via the kg-bound
QuantityMarshaller; the runtime accessor pair stays strict on
Quantity<'kg'>. Authoring-shape coercion (mass: heavy,
mass: "12000 g", bare numeric) lives in the marshaller's
fromStored and only runs on the persistence path.
StaticinstructionInstruction field — the avatar's durable spawn/recall reference.
Sibling to the container instruction (from ContainableMixin) but
distinct: it holds a singleton room or a Warren, and resolves at
hydration via applyStartLocation. Only avatars have a
spawn/recall location, so it lives directly here (no mixin). The
Hydrator's Phase 2 auto-dispatches any declared instruction field, so
the declaration alone wires it. See
[docs/requirements/multilocation-lounge-requirements.md].
StaticmarkupMarkup-augmenter contribution. senseStripAugmenter reads the
per-call filter from AugmentOpts and the viewer's sensorium
(derived from BodyPlan.getModalities()), and drops
<sense channel="X">…</sense> regions and <detail sense="X">
wrappings whose channel isn't in filter ∩ sensorium.
Lives on VisibleMixin (not DetailedMixin) because <sense>
regions can appear in any Visible-mixed long, with or without
detail authoring. Ordering: VisibleMixin sits above Detailed
in the typical composition chain, so the parent-first walker
runs senseStripAugmenter BEFORE wrapDetailKeysAugmenter —
strip-then-wrap is correct because wrapping inside a region
destined for the strip is wasted work.
StaticpersistentThe hydrator round-trips _notifyRules by reflection. Entries are
plain-JSON objects (the ContactsMixin._contacts precedent), so no
marshaller is needed.
Static ReadonlySEED_Reserved playerId for the seed avatar at
/obj/Avatar/seed — the orphan template every new user's
avatar is forked from. 4 chars; nanoids are 21, so it can't
collide with a real playerId.
Static ReadonlySEED_Convenience: the seed avatar's template path.
StaticsettingsSchema entries declared by Avatar. Picked up by the schema walk
(EnvironmentMixin's prototype-chain traversal — see
docs/subsystems/shell-environment.md); class-level
static settings are unioned alongside mixin-level entries.
world.autosave.interval controls the cadence of the periodic-
save backstop installed in Avatar.enter. Resolved once at
startAutoSave() time; mid-session changes don't restart the
timer (documented limitation).
Schema-on-owner principle: the setting lives wherever the concept lives. Autosave is purely Avatar-lifecycle policy, so Avatar is the right home. A future persistence/autosave mixin would pull this entry up to that mixin and Avatar would compose it.
StaticsubscribableLive-query subscribable fields. The focus descriptor's primary
job is to install the meta-bus dependency-index entry at
(FieldChangedEvent.KIND, 'field', 'focus') so the setFocus
/ clearFocus field-change fires don't sail into the void.
No v1 client field-set asks for focus directly — the canonical
me.focus subscription resolves an MQL query against the
giver's focus fragment and projects the targets, not the
fragment string itself. The descriptor still exists because
subscriptions flagged focusDependent (Wave 3's canonical-kind
machinery) install a holder-level dependency entry on this
same key, and a descriptor whose read returns a string is
the cleanest way to keep the substrate's prototype-chain walk
uniform.
StaticsyntheticSynthetic shell vars. Read-only — var set PWD foo is rejected
by EnvironmentMixin.setVar because the ShellApi resolution
order picks synthetic vars first.
Static ReadonlyTEMPLATE_Template path prefix for avatars in the domain collection.
Avatar templates live at /obj/Avatar/<playerId> — instances
of a class share the same /obj/<ClassName> namespace as
singleton templates of that class (/obj/EventRegistry),
with a per-instance suffix.
ProtectedfullHost-internal accessor for the formal canonical form. Public
surface is getFullName(). Used at introductions, character
info screens, disambiguation — not the default for everyday
prose. Most callers should read getName() directly.
Layout:
[honorific] [name] [surname][, nameSuffix]
The comma before nameSuffix is universal for credentials
("John Smith, MD", "John Smith, PhD", "John Smith, Esq.") and
a defensible older-style for generational suffixes ("John
Smith, Jr.") — common in legal documents and personal
signatures even where AP/Chicago have dropped it. Regnal
numerals ("Henry VIII") would render incorrectly here, but
those aren't a v1 use case; if they become one, model them
via alternateNames rather than nameSuffix.
ProtectedmassClear slot's engagement. Same lockdown as _setEngagement.
Idempotent on absent slots.
Run a pre-bound { command, model } through the dispatcher's
bound tail — resolve → validator-preload → validators → execute —
minting a fresh per-command CommandContext and returning it
(with its accumulated notes). This is the scripting interpreter's
dispatch primitive: a scripted command is resolved (scope
re-checked), validated, and executed exactly like a typed one,
but the envelope is the script driver's to assemble (the run
aggregates per-statement notes), not this method's.
Distinct from the inline bound branch in executeCommand (which
runs on the outer ctx and emits the envelope); this mints its own
ctx so each scripted statement carries its own attribution + note
set. Mirrors that branch's resolve/validate/execute sequence.
Clone-per-execution controller dispatch. The clone is destructed
in finally regardless of outcome.
Option E (per-subcommand controller): when the bound model
carries a subcommand and the subcommand declares its own
controller:, that template wins; otherwise falls back to the
verb-level controller. A subcommanded verb with no resolvable
controller (subcommand omitted, no verb-level fallback) returns
a player-facing failure rather than throwing.
Optionalopts: { async?: boolean }Privileged: drop the record at businessPath. ApiOnly.
Walk the verb's match list at the assemble stage. Shape
errors fall through to the next match; the first claiming
match (bind succeeds + validators pass) runs _executeOne
exclusively. Bind errors stop the chain on the outer ctx.
Returns the CommandContext whose accumulator the dispatcher
uses for the dispatch-response envelope: a fresh per-attempt
ctx for the claiming match, or the outer ctx for pre-match
failures (unknown verb, all-shape-fall-through, bind error).
Chain-of-responsibility lives at the assemble stage only;
pass: true retired with CommandResult. Content patterns
that need "I might handle this depending on state" use
dynamic contributions on the recency stack.
Privileged: set an existing record's status. ApiOnly.
Set the engagement for slot. Locked down by
@CallSecurity(SecurityPolicies.ApiOnly) — only callers under
mud/api/ (in practice, SchedulerApi) may invoke. @Final @Unshadowable because the engagement map is a framework
invariant — a subclass override or shadow that lied about the
write would leave the scheduler's timer set out of sync with the
map.
Privileged setter for the auxiliary restingOn pointer.
Reachable only from ContainmentApi.move /
ContainmentApi.placeOn. Pass null to clear.
Stores the supporting Surfaced ref directly (Pattern B); runtime-only — see the field declaration's JSDoc for the persistence rationale.
ProtectedabsorbStep 1 — drain each pooled tag toward its reserve at its own rate, and shrink the sub-volumes proportionally so the stomach empties as it digests (reopening the cap). Wave 2 routes toxin pools into burdens here.
ProtectedabsorbDrain a toxin's un-absorbed pool dose into its body burden. Alcohol
(storeRaw) accumulates the raw absorbed grams; every other toxin
accumulates absorbed × potency / bodyMass (dose-response). The
burden entry is created on first exposure and never widened.
Append a contact entry. Duplicates (same kind+ref+label) are no-ops.
State-mutation primitive. Locked down — only callable from
Containable.setContainer. Use ContainmentApi.move(item, container) from application code.
Fires FieldChangedEvent { field: 'contents' } after a real
addition so the MQL subscription substrate's dependency index
picks up containment-shape changes for the contents descriptor.
The substrate matches on (KIND, 'field', 'contents') only —
oldValue / newValue are inspected by the diff pass via
re-projection of the host, not by the index, so the count
delta carried here is informational (debugging / future
coarse-grain optimizations) rather than load-bearing.
Add a condition (a Trauma value or an AfflictionRecord).
Every record across all realms, as a flat read-only snapshot. The persistence layer's evict/flush path iterates this; the naming path never does.
All stored entries (read-only snapshot).
Antidote = accelerated clearance. Crashes a toxin burden in one
shot (far faster than natural clearance would), so its banded
condition clears on the next reconcile. The minimal consumer of
the vitals ResolutionSpec treatment seam (no treatment verb is
required by this build — the callable seam + crash mechanism is).
Phase 2 applier — see the interface docstring for semantics.
Compare-and-move idempotency: no-op when the current container's
templatePath matches the declared path; otherwise resolve the
target via StuffApi.singleton and ContainmentApi.move into
it. The singleton-target invariant is enforced at template-save
time by TemplateApi.validateSingletonContainerTarget.
ProtectedapplyPhase 2 applier for data.startLocation. The avatar's spawn/recall
reference is either a Warren (land in its lazily-created host —
the Warren is never the avatar's container; container stays
honest) or an ordinary room (a singleton room is reused, a
non-singleton one is cloned fresh — StuffApi.singletonOrClone). The
Warren check is a real instanceof against the canonical base class
(unspoofable); the generic clone-vs-singleton decision stays in
StuffApi.
Compare-and-move idempotency is unnecessary here (spawn fires once at
clone time); Avatar.restore() re-fires it harmlessly (the move is a
no-op when already in place, or a clean re-seat).
Auto-introduce on arrival — the player half of the
auto-introduce feature (social NPCs use the introduces brain;
they have no settings store). A mover who opted in
(social.autoIntroduce) names themselves to a room that doesn't
already know them; any present opted-in occupant likewise
introduces to the newcomer. Real recognition via
SoulMixin.introduceSelf. Best-effort — never breaks movement.
Fire sense on this mover as a forced command. Used after a
successful traversal to render the new room, and by the goto
verb's raw-move fallback (-l flag) when Mobile.teleport's
polished path was bypassed.
Switched from look to sense by the senses build so the
mover perceives the new room across every channel they
possess (vision, hearing, smell, touch, taste) without
typing five commands. Existing vision-only rooms render
identically because the augmenter's filter ∩ sensorium =
vision for any room with no <sense> regions authored.
No-op for movers that aren't CommandGivers — scripted NPCs
can move without auto-sensing. Errors are swallowed: a flaky
sense shouldn't fail the movement; the mover already received
an arrival narration from announceArrival, so the
auto-sense is additive context.
Resets focus to "here" first when the mover is Focused —
bare sense is default: "$focus", so without the reset the
auto-sense would carry stale drilled focus from the prior
room into the new one (typical case: focus was on widget,
the player walks into a room with no widget, the auto-sense
comes up empty).
ProtectedbasalStep 2 — basal metabolic drain on satiation + hydration, scaled by body mass and the (inert v1) thermal-strategy multiplier.
ProtectedbodyPassive-drift time constant (R·C) for the body — the rate the core
floats toward effective ambient when regulation is off. Reuses
ThermalMixin.getTau (the body's mass × material × medium/wall
resistance); a heavier body drifts slower.
Identity default — the framework's canSee answer passes
through unchanged. XRay / Blindfold shadows override.
ProtectedclearClear path once value re-enters threshold by the hysteresis margin.
Drop the whole in-memory working set (persistence evict).
ProtectedclearStep 4 — burden clearance. Each live burden falls at its seed's
(zero-order) clearanceRate; the entry is deleted at zero.
Bulk-remove every entry of the given label. Returns count removed.
Drop the imposed disguise. Worn coverings are unaffected.
Drop every connection in one call. Used during destruct.
Distinct labels used by the owner. Sorted.
Read entries filtered by label. Empty array when label has no entries.
ProtectedcontentsHeat capacity (J/K) of a vessel's interior fluid, or 0 if empty.
ProtectedcoupledStep 3 — coupled recovery: at rest the body rebuilds endurance
by spending satiation + hydration (hydration the tighter
leash), rate-limited and scaled by posture × restQuality ×
spo2-throttle (the last inert in v1). This is the keystone — why
recovery lives in metabolism, not encumbrance.
ProtectedcurrentThe restQuality of the host whose posture slot this body
occupies, or 1.0 (the floor / standing with no host). Only an
actor that can occupy slots (Slottable) has a host; a bare
Creature reads the default.
ProtecteddefaultProtecteddefaultProtecteddefaultProtecteddefaultProtecteddefaultProtecteddefaultProtecteddefaultProtecteddefaultDestroy this object.
Locked down by @CallSecurity(ApiOnly) — only callers under
mud/api/ (in practice, StuffApi.destruct) may invoke it.
@Unshadowable because the unregistration path must always run;
a shadow that intercepts and skips it would leak the object into
the registry forever. @Final because subclass overrides would
defeat the same invariant — the loader hook throws
FinalViolationError at import time on any subclass redefinition.
Subclass cleanup belongs on the optional onDestruct() witness
(consulted by StuffApi.destruct while the target is still live);
refusal logic belongs on canDestruct(). This terminal destroy()
is the unshadowable mark-and-unregister step only.
ProtecteddispatchBuild the Scene at the right topic and dispatch self + peers frames. Mobile is constrained to Containable bases, so toPeers is always the right broadcast scope (see Vessel rule §7.3 — Containable wins).
Optionalexit: defaultDraw down endurance for one loaded self-powered traverse.
ProtecteddriftPassive Newton's drift of the core toward the effective ambient.
ProtectedeffectiveResolve the body's effective ambient — biome ambient + occupied
warming-slot warmth + the wind-chill (cold side) / heat-index
(hot side) transforms, read through the surrounding medium's
conductivity (immersion). async and run only at re-stamp events
(placement/move, ambient shift, don/doff); the per-slice reconcile
reads the cache effectiveAmbientK synchronously. (Worn clo is
applied as a band-widening in integrateThermalSlice, not here.)
ProtectedeffectiveSeries heat-exchange resistance R (K/W).
ProtectedemitIn-room catalog emote: render + compose Scene + send.
Optionalopts: EmoteOptionsIn-room free-form emote: render + compose Scene + send.
Optionaltarget: StuffOptionalinReactionTo: stringProtectedensureBegin this Avatar's playable session. Called by Login.enter
after the Interactive has been transferred from Login to this
Avatar. Pure session-start ceremony:
startAutoSave().sense so the player perceives their starting
location across every channel they possess — delegated
to MobileMixin.autoSenseOnArrival (the same hook that
fires after a traversal), so we share the focus-reset and
forceCommand plumbing instead of reimplementing sense.Events.PlayerLoggedIn for engine-level observers.Starting location and default loadout are NOT this method's
concern. The Avatar's container is set declaratively by the
template's data.container field (Phase 2 applyContainer
during clone) or by Avatar.restore() re-hydrating saved
state; default loadout (currently the AetherImplant) is
installed in postRegister. Both run before enter fires.
One call per session-start, not per connection. When a second
Interactive multiplexes onto an already-playing Avatar,
ConnectionApi.transfer adds it to the avatar's interactives
set directly — enter is NOT re-invoked. The two unguarded
idempotency-sensitive steps here (welcome scene, PlayerLoggedIn
emit) would double-fire if a caller did re-invoke; treat the
method as session-start-only.
Let the held breath out — clear the grace.
ProtectedfilterShadowable extension point for envelope frames. Default returns the envelope unchanged. Treat the input as immutable; return a fresh copy when transforming.
ProtectedfilterShadowable extension point. Default checks per-frame modality
attribution: when frame.meta.modality is set, drops the frame
unless the recipient's PerceptionApi.sensorium includes the
named modality. Actor self-frames (carrying audience:actor)
always deliver — you always perceive your own acts.
Game content shadows this to drop frames (return null) or transform them (return a modified copy — treat the input as immutable). Per-recipient interception, lifecycle bound to the shadow's host.
ProtectedfindDrop the record entirely.
Partial forget — clear one field, keep the record. 'knownAs' nulls
the name (familiar-face-lost-name); a payload field is removed. No-op
when the record doesn't exist.
The first still-active (not quit/fired) employment, or undefined.
Walk the recency stack newest-first and pair every command with
the resolved Stuff that affords it and its bucket. The 'self'
sentinel resolves to the giver instance, so every record's
source is a concrete Stuff. Standing state — depends only on
the current stack, not on any command being in flight. The
dispatch chain decides ordering; we don't dedup.
Lazily seeds the 'self' entry on first read so callers that
skip postRegister (test helpers like makeStuff, ad-hoc
scripts) still see the giver's own contributions. Production
code goes through postRegister; this branch is the safety net.
Resolve a name through the lookup chain. Returns undefined when the name is unknown OR has a tombstone-suppressed default.
Resolved view: defaults layered with persistent layered with
session, tombstones honored. Read-only — mutate via setAlias /
removeAlias.
Filtered view of alternate names. Pass a kind to narrow.
Returns a copy, so callers can iterate without disturbing
mutation order.
Optionalkind: NameKindNewest-first list of every command the giver can run. The
flattened projection of getAffordances (which carries the
affording source + bucket); callers that only need the verbs use
this, callers that need attribution use getAffordances.
Blood-alcohol concentration via Widmark — the alcohol burden
stores ethanol mass (grams), normalized at the read by body mass
and the sex-derived r. BAC (g/L) = grams / (mass_kg × r);
converted to g/dL (the 'bac' scale unit). Storing mass keeps
the read state-dependent (a lighter / dryer body reads higher).
Total weighted weight this body bears (carried + worn + held).
The species breathable set (default ['air']; water-breaters ['water']).
What this body can comfortably carry (physiology-derived).
The accessible band over blood-volume fraction + vitals-out-of-
band. Reserves and trauma fold additional
load in at the marked seam. A corpse (lifecycleState: 'dead')
reads dead; otherwise the band reflects the substrate — it
can read critical from a floored vital with NO lifecycle
transition (the deferred driver owns transitions).
Mixin names conferred by every on-shift Employment's Position.
Consciousness below death — derived from blood volume + SpO₂.
Gates animate verbs like death but is recoverable. A corpse reads
dead. Head trauma is folded in below.
Snapshot of contained items as an array.
Held-fluid temperature (vessels) — the object's own temperature in v1.
Walk the containment tree depth-first pre-order, starting
from this Container's immediate contents, and return every
Containable encountered. Used by MQL's :I transform and
any controller that wants the full nested inventory.
Effective merged disguise (worn + imposed), or null if unmasked.
The record at businessPath, or undefined.
All employment records as value objects.
Effective-kg draft this creature bears for its hitched cart (0 if none).
Vomit — flush the un-absorbed digestion pools (those tags
never reach their handlers) and empty both sub-volumes. Leaves
absorbed state intact: reserves already filled and toxinBurdens
already accumulated stay. That asymmetry IS the purge mechanic —
inducing it early (pools un-absorbed) saves you; late doesn't.
Called by the vomit verb and by the involuntary cascade trigger.
Read the recency timestamp. Read by the residency sweep — which
calls it on the raw target (via RAW_TARGET) so the sweep's own
introspection never counts as a touch.
Burden / capacity (dimensionless; 0 capacity → 0/Infinity).
Get the long description with fallback to short, then default.
Affordance-annotated long description — see the interface
docstring for the augmenter pipeline contract. Calls
Mml.augment with the host (this), the supplied viewer,
and the per-call opts (the senses build threads
opts.filter through here for verb-specific sense filtering).
Every contributing mixin's augmenters run in
parent-first → child-last order.
Optionalopts: AugmentOptsMass override that lazy-seeds the body-grounded default. When the
instance authored no mass of its own (still 0), resolve
species → bodyPlan → baseMass and adopt it; an explicitly-authored
mass is the deviation that wins (the guard short-circuits and this is
a plain super.getMass()).
Runs lazily here rather than from a post-hydrate hook: the
postRegister chain is not uniformly threaded below CommandGiver
(e.g. Avatar.postRegister does not super-call), so a read-time
seed is the one seam that makes mass honest for every reader of
every Creature subclass, the moment a body plan is resolvable. The
"is mass still 0?" check is the idempotency guard — no persistent
"seeded" flag (the project rule); a genuinely 0-mass sessile body
simply re-checks cheaply.
OptionaldetailKey: stringReturn the user-explicit override for key without falling back to
the schema default. undefined when nothing has been
setSetting'd — even when the schema declares a default. Lets
consumers distinguish "user explicitly set this" from "schema
default is in effect" for chain-resolution flows (e.g.,
LocomotionApi.defaultModeFor defers to the bodyplan default
only when there's no explicit override).
The body's parts, each merged with its instance delta. Walks the
shared BodyPlan.bodyParts and overlays bodyPartDeltas. Returns
[] for a body with no species/bodyplan (no anatomy without a
species) — graceful, not an error.
Postmortem-progression seam. Death is living-stop + postmortem-start:
living processes freeze and postmortem changes (algor / rigor / livor
/ decomposition) would begin here. v1 ships ZERO — returns []; the
seam exists for a future forensics wave.
Self-presentation — the casual-register render string for this object, the answer to "what does this Stuff call itself?" Three- step resolution:
Named.name if present and non-empty — the object's
proper name ("Alice", "Excalibur", "Town Square").Visible.shortDescription if present and non-empty —
the object's visual identity ("a heavy oak door").For a Globbable stack (quantity !== 1) the count folds in as
an affix — "30 coins" — pluralized via GrammarApi.pluralize
(which honors host-side getPluralForm() overrides for
irregulars). Named takes precedence over Visible so a
Named-with-description renders by its proper name; code that
needs the formal register calls getFullName() when typed as
Named.
Viewer-blind by design. This is the shared baseline every
Stuff exposes; the viewer-aware naming step (recognition /
identification — see
docs/subsystems/belief.md) composes on top
of it. Left shadowable (NOT @Final) so masking / disguise
effects can override the rendered identity via a method shadow.
Build the composable Mml fragment for this object's display name —
the Mml sibling of getPresentation. Mml.ref (and so every
<item> / <name> / … identity tag) renders this, not a raw
string, so a name joins the compose chain as a fragment like
everything else. The label is the already-resolved, viewer-aware
name (recognition runs in the render layer and hands it in).
Return null for the plain default — Mml.ref then wraps the label
in Mml.text, which escapes it exactly once, so player-authored
names / status decoration are safe by construction and the fragment
is never re-escaped downstream. Override to build a richer fragment
(a TPA terminal wraps its name in <color> to tint by state). The
plain-string getPresentation stays the surface for non-prose
consumers (logs, context.note, MQL scalars).
Resolve the auxiliary restingOn pointer. Pattern B live ref;
R2.3 self-heal clears the slot if the supporting surface has
been destructed since the last set.
Returns null when no support OR the supporter has been
destructed. The caller can't tell the two apart from the
return value; that's deliberate — absence of support is the
same observable as a stale ref.
Walk the container chain to the topmost non-null environment.
Returns null when already at the root.
Containment is acyclic by construction (a Container can't
contain its own ancestor — setContainer's atomic update is
the chokepoint), so the loop is bounded by the depth of the
world's nesting.
Get the short description with fallback.
The effective status (runtime override, else authored default).
Absolute lift cap = capacity × OVERLOAD_FACTOR.
This owner's stored subscription for subjectId, or undefined.
Exterior temperature — ≈ ambient for an insulated object, ≈ core for a bare one.
Time constant τ = R·C.
The lazy temperature read (SYNC) — reconcile-on-read against game-time.
Read seam. Instance method, but unwraps via ProxyApi.unwrap
before reaching the # slot — this inside an instance method
called through the proxy is the proxy, and the # slot lives
on the raw target.
Lookup the valid sex set from the host's species'
sex-determination system. If the host doesn't compose
OrganismMixin (degenerate), or the species is unset, returns
an empty set — the setter then rejects every value.
Identity default — return null so VisionModality.viewerVisionProfile
falls back to its constant human-shaped profile. The Organism
subsystem populates per-species profiles via a shadow.
The survivable band for a sign — from the host's species
vitalProfile, or the universe default. Requires OrganismMixin
(runtime-guarded). The "always composed with Organism" rule lives
here, not in a comment.
Get the spatial zone. Unwraps via RAW_TARGET because the
# slot lives on the raw target only and this inside an
instance method called through the proxy is the proxy.
ProtectedhandleSensorMixin.handleEnvelope override — multiplex the envelope to
every connected Interactive. Reached after filterEnvelope (the
shadowable extension point on SensorMixin). When interactives
is empty (netdead Avatar) the for-each is a no-op but the
handleEnvelope body itself runs, so server-side reactions
(shadows, scripted behavior) fire regardless of wire state.
ProtectedhandleSensorMixin.handleMessage override — deliver to every connected
Interactive (multiplexing). Reached after filterMessage (the
shadowable extension point on SensorMixin) has had its say.
True iff getAlias(name) would return a defined entry.
True if this owner has any stored subscription for subjectId.
Make ingest real: route the consumed material into the
digestion buffer. Picks the sub-volume by phase (the verb
decides — eat solid, drink/sip liquid), caps intake at the
remaining sub-capacity (the bulk partial-transfer shape — refuse
the excess), and fans the material's nutrient tags into per-tag
pools scaled by the accepted volume.
Returns the litres actually accepted, so a caller can hold to
result.applied-style semantics (the eat verb consumes a
discrete item only on full acceptance).
Take and hold a breath — extend the breath-hold grace (free-diving).
Install the default biological reserves if absent (idempotent).
ProtectedintegrateOne integration slice (game-seconds), flows in fixed order: (1) digestion absorption, (2) basal drain, (3) coupled recovery. (Wave 2 inserts toxin clearance at step 4.) Each slice is small enough that the per-flow linear approximation is honest and the coupling is respected slice-to-slice.
ProtectedintegrateOne integration slice (game-seconds). Resolves the effective ambient (cached), the setpoint, and the clo-widened band, then branches on strategy: an ectotherm / dead / no-fuel body drifts; an endotherm spends to hold the setpoint within the affordable regimes.
Introduce yourself to everyone in range — a modality-neutral social
act (the shared core behind the introduce verb, the NPC
introduces brain, and the player auto-introduce hook). Emits the
introduction scene and writes each in-range perceiver's recognition
of you (learnIdentity). Returns false (no-op) if you have no proper
name to give.
Check if this object has been destroyed.
@Unshadowable: the destroyed-state read is a framework invariant —
any shadow that lied about it would let consumers touch a torn-down
Stuff. @Final: subclasses overriding this would defeat the same
invariant; the loader hook throws FinalViolationError at import
time on any subclass that redefines it.
Whether a cart is currently hitched.
Whether a voluntary/automatic breath-hold is currently active.
True iff any employment is currently on shift (sync hot-path read).
Whether this body respires at all (the respires opt-out).
Coarse part→slot coupling: a missing part disables its slots.
Upsert what the viewer knows about referent in realm. A new
record stamps firstSeen/lastSeen; an existing one advances
lastSeen (preserving firstSeen) and coalesces: a non-null
update.knownAs raises the name, an absent/null one leaves a
learned name intact. Payload flags merge.
Bare per-tier override view. Defaults excluded. Used by
diagnostics (alias list rendering, debug introspection) and for
round-trip persistence assertions.
Install a hydrated record directly (the persistence hydrate path). Bypasses the upsert/coalesce logic AND the write-through — the record is the stored truth. NOT for consumer use; consumers go through know.
ProtectedmediumThe dominant series conductivity (W/(m·K)): the barrier medium
when sealed (a Sealable host that is closed → vacuum), else
the barrier override, else air (the default surrounding
medium for a bare Phase-1 object; the body lifts this to its
resolved immersion medium in Phase 2).
ProtectedmetabolicIn-session game-time (seconds), or null when no world clock is
present in the world. Metabolism ticks only when a WorldClock
registry is bootstrapped (indexed by templatePath) — production
always has one; a unit test that hasn't set one up reads null
and stays idle (no getNow lazy-create with a wall-clock anchor).
ProtectednoteProtectednoteStored rules in list order (precedence order). Read-only snapshot.
Mark the schema-subscription gate open. The first connection attach is when client-side schema delivery becomes meaningful — earlier pushes are pre-subscription bookkeeping.
Cleanup hook. Fires a final persist-back save, cancels the periodic-save timer, unregisters from PlayerApi, and detaches every live Interactive.
The save is fire-and-forget (onDestruct is synchronous per
the Stuff lifecycle contract). Correctness lives in
TemplateApi.snapshotToTemplate's synchronous prefix — field
values + container ref are captured BEFORE the first await,
so the snapshot reflects pre-cleanup state even if the MongoDB
write doesn't complete during shutdown. The periodic backstop
covers any prior state loss; concurrent in-flight save is fine
(MongoDB last-write-wins).
Envelope template method — DO NOT override. Override
handleEnvelope instead, or shadow filterEnvelope for
content-driven interception. Parallels onMessage.
HasInteractive Witness hook — fires when the last live connection drops (count crosses 1 → 0). Engine-level event for observers that care about player presence.
Invoked by the backend connection layer when an avatar's
last live Interactive disconnects (the 1 → 0 presence edge).
Witness — react to the player going linkdead (reap a guest
body, freeze in-session clocks, persist, etc.). Override and
chain super.onLinkdead() to keep base presence handling.
Template method — DO NOT override in subclasses. Override
handleMessage instead, or shadow filterMessage for content-
driven interception.
Containment-move witness — the onMoved hook ContainmentApi.move
fires on every mover (carried items, dropped corpses, vessels,
bodies). Re-stamps so the object freezes its current temperature
under the old scope's ambient and starts drifting toward the new
scope's. Load-bearing under the cached-ambient model (Step 1.6
trigger 1): with a sync read there is no lazy re-resolve, so a
move that didn't re-stamp would drift toward a stale ambient
forever. This is the one event thermal listens to (the genuine
divergence from metabolism's pure-lazy model). Chains any inner
onMoved witness first.
The traverse hook (the Mobile.onTraversed shape) — fires reassess on every move.
Post-registration setup called by the clone pipeline (Spring
@PostConstruct-style). Stamps runtime-only references (user,
playerId) from the caller-supplied context, registers with
PlayerApi so later lookups by playerId resolve to this instance,
and installs the v1 default-issuance loadout (currently just the
AetherImplant in the cranial slot).
Default loadout install lives here — at clone time, alongside
the rest of the instance wiring — rather than in Avatar.enter
(which is session-start ceremony, not setup). When char-gen
ships, the loadout install moves there with the rest of
character creation.
Optionalcontext: AvatarInitContextPush an authoritative client-state value out to every
connected Interactive on this host. Parallel to the existing
client-state-write inbound flow but flips the direction —
server mutates, client follows. Used by the style verb
(and future server-initiated client-state changes) so the
client re-renders without waiting for a reconnect snapshot.
Caller MUST have already called setClientState(key, value)
save(). This is the push half; persistence is upstream.Append a new source to the stack. No-op (with warning) if the source is already present — guards the orchestration against double-fires under hot-reload or buggy callers.
ProtectedreadThe central event-driven re-resolve: engage/cancel the crisis drain.
Point-get, O(1). Returns null for an unknown referent. Lazy
liveness-GC: a record whose referent no longer resolves to any
live Stuff is dropped and null returned.
Every record in realm, keyed by referent. Read-only snapshot.
ProtectedreconcileMirror MetabolicMixin.reconcileCascade for the single anoxia
affliction: while spo2 dwells at/below the lethal floor, accrue
dwell-time on the asphyxiation affliction and fire the death seam
when it exceeds the lethal dwell; clear it on recovery above the
floor. Returns whether death fired this tick.
ProtectedreconcileThe cascade — metabolism is the first condition-driver. After the
sub-stepped integration, ensure each floored biological reserve's
floorEffect-named condition exists, clear it on recovery
(hysteresis), and progress the lethal ones toward the death seam.
Integrate elapsed in-session game-time since the last reconcile. Sub-steps the gap into fixed slices so the coupled flows (basal drain, digestion absorption, recovery, burden clearance) stay honest over a long mixed-activity gap. Freezes on absence (linkdead now; logout via the far-past guard) — real-life absence never starves you. Cheap no-op when too little time has passed or the world clock isn't running.
Lazy reconcile — drift the stamped temperature over elapsed game-time.
ProtectedreconcileAfter the slices, spawn / clear the thermal conditions off the
driven core (hypothermia / torpor below survivableMin,
hyperthermia above survivableMax), accrue dwell, and fire the
death seam on lethal accrual. Mirrors metabolism's cascade.
Lazy reconcile — drive coreTemperature over elapsed game-time.
ProtectedreconcileFor each live toxin burden, ensure its one banded condition
exists and recompute its severity live — from the derived BAC for
alcohol (storeRaw), or the raw burden otherwise — against the
seed-authored bands. Cleared when the level falls below the
lowest band (no per-band spawn/clear churn; one condition per
toxin, severity is a live read on stage).
Re-evaluate this character's competence-conferred verbs and reconcile
the affordance stack: drop the prior conferral entry, then re-push
the current set (idempotent pop + conditional push, mirroring the
hosted-update delta). Called by AdvancementApi after each Transcript
append. No-op before the Catalog warms.
ProtectedregRemove a condition by reference; true if it was present.
Drop an alias. Returns true iff something was effectively removed (or a tombstone was newly written for a default-only name). Tombstones the persistent tier when removing a name that has a default; otherwise just deletes the override.
Remove the first alternate matching value. Returns true if
something was removed.
Remove a contact entry by ref+label. Returns true if anything was removed. For avatar entries, ref = playerId; for npc entries, ref = templatePath.
Remove primitive. Same lockdown as addContainable. Fires the
matching FieldChangedEvent { field: 'contents' } on a real
removal.
Drop the rule keyed on groupRef. Returns true if anything changed.
Drop the stored subscription for subjectId. True if one was removed.
Rename every entry of oldLabel to newLabel. Returns count
relabeled. No-op if no entries have oldLabel.
Per-audience prose for a catalog emote.
Optionalopts: EmoteOptionsPer-audience prose for a free-form emote body.
Optionaltarget: StuffMove the rule keyed on groupRef directly above / below anchor.
Both must already be stored. Returns true on a successful move.
ProtectedreserveDrop every entry whose bucket is 'environment' or 'peers'.
Used during a self-move; ContainmentApi follows up with the
appropriate pushCommandSource calls for the new environment.
ProtectedresolveArrival-message resolver. Mirrors resolveDepartureMessage.
ProtectedresolveDeparture-message resolver. Override in subclasses to change
the precedence chain or inject defaults; per-location overrides
should go on the location via getDepartureMessage.
ProtectedresolveResolve a toxin's authored rate params off its Condition seed.
Emission body of the drain engagement (delegated from RespirationDrain).
ProtectedrespirationIn-session game-time (seconds), or null when no world clock runs
(pre-boot, or a unit test that hasn't bootstrapped one) — the
metabolism presence guard verbatim, so unit tests stay idle.
Re-hydrate this Avatar's in-memory state from its current template doc. Operates on the existing live instance, preserving identity / stuffId / connected Interactives.
v1: developer/admin operation — no multi-connection synchronization. Should not be invoked during the initial clone cascade (the in-flight-clone guard catches recursive clones, not parallel hydrate on a registered instance).
ProtectedrouteFan the accepted intake through the per-tag router into the
digestion pools. Wave 1 routes the energy tags; Wave 2 extends
routeTag to the full macro set + toxins. A static switch over
the curated vocabulary, NOT a registry — unknown tags are a
no-op so authoring a novel tag never crashes intake.
ProtectedrouteThe static tag dispatch — one caller (the ingest decomposition), so a private method, not a security-gated Api. Maps a known nutrient tag to its routing; unknown tags return null (no-op).
Snapshot this Avatar's persistentFields chain back to its
per-player template doc. Two-line shim: TemplateApi captures
state into the returned Template; tpl.save() commits it.
Concurrent saves (periodic timer + linkdead hook + manual eval)
each produce a valid full-state snapshot; MongoDB resolves
ordering as last-write-wins. See
docs/subsystems/templates.md § Persist-Back for the
snapshot-before-await ordering invariant the substrate honors.
Invoked by the backend lifecycle (periodic autosave timer,
the linkdead hook, and manual eval) to persist the avatar's
full state back to its template. Witness (async) — snapshots
before the first await so concurrent saves each write a valid
full-state snapshot (last-write-wins). The only v1 persist-back
consumer; not a general mixin surface.
Optionaltarget: StuffProtectedseedResolve the body-plan baseMass and adopt it as this body's mass,
returning the seeded quantity (or null when there is no plan /
baseMass is absent or 0). Does not read getMass() — the
zero-guard lives in getMass, so this stays recursion-free and
is the single place the body-grounded default is applied.
Send a message to all connected Interactives (broadcast).
Set or override an alias. Persistent unless opts.lifetime === 'session'. Validates name shape and body shape at set-time;
throws on invalid input.
Seed the static authored default (hydrator + authoring).
State-mutation chokepoint. Reachable only from
ContainmentApi.move; cross-Container contents mutation must
not be subclass-extensible (@Final) or shadow-bypassable
(@Unshadowable).
Atomic across three updates: detach from the old container,
attach to the new, update the field. null argument is the
detach case; the policy rejects calls from anywhere other than
ContainmentApi, so setContainer(null) outside the Api
throws — the legitimate detach is ContainmentApi.move(item, null).
Set the fluid temperature directly and re-anchor the drift clock — the bulk-coupling primitive (refill to incoming, calorimetric mix blend, pour-preserve-at-reduced-C). Freezes the current temperature to now under the existing ambient first, then adopts the supplied value and restarts drift from it.
ProtectedsetImpose a transient disguise (active-effect; not persisted).
Mark this avatar's next presence drop as a deliberate leave (sign out
/ switch character) rather than an involuntary linkdead. The
connection layer calls this at the network boundary when the client
closed with the intentional-leave close code; onLinkdead() consumes
it. Idempotent.
Strict on Quantity<'kg'>. Callers holding a raw number wrap
via Quantity.of(n, 'kg') at the call site; tag / alt-unit
authoring is the marshaller's job, not a runtime API concern.
OptionaldetailKey: stringSet the runtime status; empty reverts to the authored default.
Upsert the stored subscription for subjectId.
Stamp this Stuff's templatePath and re-key the
byTemplatePath index so future findByTemplatePath lookups
see the new path. No-op when path matches the current value.
Locked down by @CallSecurity(ApiOnly) because flipping a
Stuff's identity post-clone would break FromTemplate
policies and any caller-side caching of template-path
identity. @Final @Unshadowable because the index update has
to run for every successful set — a subclass override that
forgot the index call (or a shadow that intercepted) would
silently desync byTemplatePath.
Unwraps via ProxyApi.unwrap so the #-slot access lands on
the raw target (see comment on #templatePath above).
Set the spatial zone. Gated by FromSpatialZone — only the
SpatialZone class and its subclasses (CartesianZone,
SphericalZone) may call this through the proxy. The
addLocation / removeLocation chokepoints on the zone side
are the legitimate callers; everyone else is rejected.
Clone-time seeding from StuffApi.#cloneInner doesn't go
through this method — it uses the caller-allowlisted
_stampZone seam below.
@Final @Unshadowable because the index of substrate
invariants that consult getZone() (containment's
cross-zone gate, Mobile.traverse, MQL scope walks) trusts
the slot's value; a subclass override or shadow that lied
about it could break those invariants. No legitimate
subclass needs to extend this anyway — the only legitimate
write paths are the SpatialZone chokepoints and clone-time.
Optionaltarget: StuffProtectedspo2Respiration read — low spo2 would throttle recovery. v1 reads
the sign for the seam's presence but returns 1.0 unconditionally;
lights up when respiration drives spo2.
Install the periodic-save timer. Idempotent — calling twice is
a no-op. The interval is resolved once at install time from
the world.autosave.interval setting; mid-session changes do
not restart the timer in v1.
Cancel the periodic-save timer. Idempotent.
ProtectedstrategyEvery subjectId this owner has a stored subscription for.
Instantly move to a container. Default: narrate departure (if
the mover had a previous environment) and arrival. Pass
{ silent: true } to suppress both.
silent: true is what Login spawning uses — newly-cloned
avatars shouldn't be announced as "vanishing" from nowhere or
"appearing out of thin air" before a player has even seen the
location.
Optionalopts: TeleportOptionsProtectedthermalHeat capacity C = mass × specificHeat (J/K). For a vessel whose
Thermal IS its contents (Bulkable interior with fluid), C
derives from the held fluid — more contents → larger C → slower
cooling, free (a full thermos holds heat longer than a near-empty
one). Falls back to the host's own mass × material when empty / not
a vessel.
ProtectedthermalQ10 temperature coefficient on basal metabolic rate. Reads the
driven coreTemperature (the thermal build's getVitalSign
override drives it) and scales basal drain by
Q10 ^ ((core − reference) / 10). For an endotherm pinned at its
setpoint this is ≈ 1 (the core sits at the reference); for an
ectotherm whose core floats to the weather, basal drain swings —
a cold reptile burns far less fuel. The strategy difference falls
out of where the core sits, so no branch is needed here. The dials
live in METABOLIC_DEFAULTS (metabolism owns its rates).
ProtectedthermalIn-session game-time (seconds), or null when no world clock is
bootstrapped — thermal stays idle (the metabolism now-source guard
verbatim). Production always has a clock; a unit test that hasn't
set one up reads null and never drifts.
Get a string representation of this object (for debugging).
Refresh the recency timestamp to now. Timestamp-fixed (no caller-supplied value). Called on the raw target by the security gate on every successful dispatch (Phase 2) and by the residency presence walk.
Traverse an Exit. Two-layer hook dispatch:
canTraverse on the mover,
canExit on the source location, canEnter on the
destination location — all fire before announcement and the
containment move. After the move: onTraversed (mover),
onExited (source), onEntered (destination).ContainmentApi.move.
canMove / onMoved on the item; canRemove* /
canAdd* / on* on the source/destination.The Phase 7 contract stands: the caller has already validated
traversal via exit.canTraverse(this) — that's the door's
"is this passable?" gate. The new Witness hooks layer
additional pre-move vetos, not a replacement.
Release the hitched cart, if any (clears both sides).
Replace the rule with the same groupRef in place, or append at the
tail when none exists. The caller supplies the fully-built rule.
Like upsertNotifyRule, but a newly-inserted rule lands at
position (clamped to the list bounds) rather than at the tail. An
existing rule with the same groupRef is still replaced in place and
never moved. The caller owns the position semantics (e.g. reserved-rank
ordering) — this stays dumb storage.
Flush the un-absorbed digestion pools (vomit) — absorbed state stays.
ProtectedwetWet-bulb temperature (K) — the simplified Stull approximation.
Optionaltarget: StuffProtectedwornWorn-slot clo sum, expressed as a Kelvin band-widening.
Would lifting candidate (as loose carry) push burden past the ceiling?
Static__Composition constraint: Perceiver requires Sensor to be
present on the same host. The perception verbs (look /
scry / locate) render scenes to the perceiver's own
channel via handleMessage, which lives on Sensor. The
public-shape interface declares Perceiver extends Sensor
so the type narrowing in MixinApi.isPerceiver exposes the
Sensor surface; without runtime co-composition the narrowing
would lie. MixinConstructor doesn't carry an enforceable
bound here (loose by design — see lib/mixin.ts), so the
check rides on __validateComposition__ and fires at first
registration.
StaticcleanupStaticgetRead seam. Instance method, but unwraps via ProxyApi.unwrap
before reaching the # slot — this inside an instance method
called through the proxy is the proxy, and the # slot lives
on the raw target.
Constructor. Subclasses should call super() and then initialize.