:resolve_addr - resolve(name,from,seen,prevrcpts,prevnotifs) => {rcpts,notifs} or E_INVARG
:sends_to - sends_to(from,addr,rcpt[,seen]) ==> true iff mail sent to addr passes through rcpt.
:send_message - send_message(from,rcpt-list,hdrs,msg) -- formats and sends a mail message. hders is either the text of the subject line, or a {subject,{reply-to,...}} list.
:raw_send - Copied from Mail Distribution Center (#6418):raw_send by Nosredna (#2487) Mon Feb 24 10:46:26 1997 PST
:touch - touch(name or list,seen) => does .last_used_time = time() if we haven't already touched this in the last hour
:acceptable - Only allow mailing lists/folders in here and only if their names aren't already taken.
:check_names - ...make sure the list has at least one usable name.
:match(string) => mailing list object in here that matches string.
:match_recipient(string[,meobj]) => $player or $mail_recipient object that matches string. Optional second argument (defaults to player) is returned in the case string=="me" and is also used to obtain a list of private $mail_recipients to match against.
:make_message(sender,recipients,subject/replyto/additional-headers,body)
:parse_address_field(string) => list of objects
:display_seq_full(msg_seq[,preamble]) => {cur, last-read-date}
:display_seq_headers(msg_seq[,cur[,last_read_date]])
:rm_message_seq(msg_seq) removes the given sequence of from folder (caller)
:undo_rmm() restores previously deleted messages in .messages_going to .messages.
:list_rmm() displays contents of .messages_going.
:renumber([cur]) -- assumes caller is a $mail_recipient or a $player.
:msg_summary_line(@msg) => date/from/subject as a single string.
:parse_message_seq - parse_message_seq(strings,cur[,last_old])
:_parse_from(string with |'s in it) => object list
:new_message_num() => number that the next incoming message will receive.
:length_num_le(num) => number of messages in folder numbered <= num
:exists_num_eq(num) => index of message in folder numbered == num
:from_msg_seq(object or list[,mask])
:%from_msg_seq(string or list of strings[,mask])
:to_msg_seq(object or list[,mask]) => msg_seq of messages to those people
:%to_msg_seq(string or list of strings[,mask])
:subject_msg_seq(target) => msg_seq of messages with target in the Subject:
:body_msg_seq(target[,mask]) => msg_seq of messages with target in the body
:messages_in_seq(msg_seq) => list of messages in msg_seq on folder (caller)
:__convert_new(@msg) => msg in new format (if it isn't already)
:to_text(@msg) => message in text form (suitable for printing)
:reserved_pattern(string)
:keep_message_seq(msg_seq)
:kept_msg_seq([mask])
:msg_seq_to_msg_num_string(msg_seq) => string giving the corresponding message numbers
:msg_seq_to_msg_num_list(msg_seq) => list of corresponding message numbers
:send_log_message - send_log_message(perms,from,rcpt-list,hdrs,msg) -- formats and sends a mail message. hders is either the text of the subject line, or a {subject,{reply-to,...}} list.
:parse_misc_headers(msg,@extract_names)
:resend_message - resend_message(new_sender,new_rcpts,from,to,hdrs,body)
:time - This was inspired by Xeric's port 4632 on *Core-DB-Issues
:say - say <message>
:messages say_nothing_msg osay_nothing_msg - Is called when you use the say verb without anything to say. set say_nothing_msg. Useful for perhaps being underwater and bubbling when you open your mouth?
:acceptable - return this.free_entry;
:enterfunc - Called when an object enters the room. If the object's a player, makes them look at the room.
:add_exit add_entrance - Usage: room:add_exit(dest, aliases, ?bidirectional, ?exit_class)
:title - Returns: The name of the room prefixed with the name of the area the room is in.
:players - Used to get a list of players in the current room.
:objects - Returns a room's contents excluding any players.
:cannot_go_msg - Called when there is no exit in the direction the player's trying to go in.
:disfunc - Called when a player disconnects.
:recycle - Cancel unified physics events and remove exits before recycling.
:match_exit - Usage: match_exit(name of an exit)
:huh - Called when other command matching fails. Currently checks for an exit with the same name as the command given.
:is_dark - Use to see if a room is dark. Right now just returns the property, but it could be used as a framework for more complex darknesses.
:dark_title_msg - This is the title of a dark room. Set the prop.
:dark_msg - Called when the room is dark, for the actual description. Set the property it references...
:initialize - Initialize a room and register unified heat/gas transitions; no legacy ticker.
:look_self - Called when a player looks at a room.
:process_exits - This verb can take args which should be in the same format as returned by this:obvious_exits. If none are given, it will call that verb.
:process_players - This verb expects the same output as $room:players, namely {{connected players}, {sleeping players}}.
:process_objects - $room:process_objects: Used to get the message about what nonplayer, nonexit things are in a room.
:obvious_exits - Returns: A list of the WAIF exits in the room who have their .obvious property set to 1.
:description - Combines room description with integration messages
:ejection_msg oejection_msg victim_ejection_msg - Copied from generic old room (#3):ejection_msg [verb author Hacker (#36)] at Tue Jan 29 01:20:22 2008 EST
:process_exit_dest - Copied from new generic room (#2700):process_exits [verb author Q (#249)] at Wed Apr 10 22:16:48 2019 UTC
:do_sit - Copied from generic new furniture (#1942):do_sit [verb author Q (#249)] at Thu Dec 15 18:47:11 2022 UTC
:sit_up - Copied from generic new furniture (#1942):sit_up [verb author Q (#249)] at Thu Dec 15 18:48:04 2022 UTC
:sit_with_msg osit_with_msg - Copied from generic new furniture (#1942):sit_with_msg [verb author Q (#249)] at Thu Dec 15 18:48:52 2022 UTC
:display_occupants - Copied from generic new furniture (#1942):display_occupants [verb author Q (#249)] at Tue Jan 24 13:47:33 2023 UTC
:no_room_for_sitters_msg no_room_for_recliners_msg already_sitting_on_this_msg sit_msg recline_msg sit_up_msg lie_back_msg stand_msg sitters_msg recliners_msg sit_with_you_msg osit_with_msg orecline_with_msg recline_with_you_msg already_reclining_on_this_msg - Copied from generic new furniture (#1942):sitters_msg [verb author Q (#249)] at Tue Jan 24 13:48:58 2023 UTC
:send_markdown - Copied from generic player (#6):send_markdown [verb author Q (#249)] at Sat May 17 18:54:22 2025 UTC
:random_msg - Trigger random ambient message if conditions are met.
:who_location_msg - Return custom location text for @who display, with pronoun substitution.
:list_exits - Return list of exit titles, optionally sorted by .sort_exits property.
:weather_location - Returns the weather location for this room - checks this.weather_location first, falls back to area's weather_location
:do_subbing - Substitute condition text for announcements
:replace_for_description - Replace condition text for description display
:trigger_events - Called to trigger any registered room events and schedule next check.
:add_room_event - Usage: room:add_room_event(name, interval, verb, ?args)
:remove_room_event - Usage: room:remove_room_event(name)
:cleanup_sitters - Remove invalid objects and players not in this room from sitters and recliners lists
:notify_wanderers - Notify wanderers in this room of an event
:format_player_list - format_player_list(players, msg) -> string or 0
:viewer_position_msg - viewer_position_msg() -> string or empty string
:has_weather - True if room is outdoor and its area has weather conditions data.
:weather_description - Per-room weather description, sourced from this.area's live climate, formatted with room-local synonyms.
:light_level - Light level 0..1 for this room. Outdoor: derived from $time_engine:ambient_light_for. Indoor: ambient_light().
:ambient_light - Returns ambient light level for this room (0.0-1.0).
:light_description - Returns a light-level description string for the room, or empty string.
:gravity - Returns gravitational multiplier for this room. Delegates to area.
:gas_connections - Gas edges via exits, each endpoint resolved to its canonical :atmosphere_owner() so a room exchanges with the REAL reservoir (its planet's atmosphere, a neighbouring room's owner) instead of a stale empty delegate store.
:on_climate_change - Handle climate transitions, player-facing weather messages, and unified-physics invalidation for affected room contents.
:atmosphere - Return room-local atmosphere if independent_atmosphere; otherwise delegate to containing area.
:add_gas - Add gas to room-local atmosphere if independent_atmosphere; otherwise delegate to containing area.
:remove_gas - Remove gas from room-local atmosphere if independent_atmosphere; otherwise delegate to containing area.
:breathable - Check room-local atmosphere if independent_atmosphere; otherwise delegate to containing area.
:precipitate - Precipitate from room-local atmosphere if independent_atmosphere; otherwise delegate to containing area.
:absorb_vapor - Absorb vapor into room-local atmosphere if independent_atmosphere; otherwise delegate to containing area.
:pressure - Return room-local pressure if independent_atmosphere; otherwise delegate to containing area.
:set_pressure - Set room-local pressure if independent_atmosphere; otherwise delegate to containing area.
:add_sound_emitter - Add or replace an emitter in this room registry by emitter id.
:remove_sound_emitter - Remove emitter from this room registry by emitter or id.
:get_sound_emitter - Return one emitter from room registry by id.
:active_sound_emitters - Return active sound emitters from room registry.
:serialize_sound_scene - Serialize active room sound emitters for Client.Spatial scene snapshot.
:sky_description - sky_description() - return current sky text for outdoor rooms (delegates to $time_engine). Empty for indoor.
:ambient_temperature_c - Baseline room temperature in Celsius from area climate when available, else legacy stored scalar.
:update_heat_offset - Update room-local heat offset from in-room heat sources. Returns Celsius delta.
:temperature - Canonical effective room temperature in Celsius; analytically settles elapsed heat on read and reschedules on write.
:test_temperature_thermal_coupling - Lit heat source raises effective temperature and cooling decays, using isolated fixtures.
:test_temperature_write_override - Explicit room temperature override, isolated from production rooms.
:exchange_heat - Exchange room-local temperature offsets with connected rooms through open gas paths. Returns this room's resulting heat offset.
:test_heat_exchange - Property: hotter rooms transfer some room-local heat offset to connected cooler rooms.
:tick_smoke_effects - Apply smoke_inhalation effect to players in this room when smoke fraction exceeds threshold.
:test_smoke_inhalation - Verify tick_smoke_effects applies smoke_inhalation to players when smoke fraction > threshold.
:apply_precipitation_effects - Apply canonical precipitation through typed coalescible carriers; room owns rates and containment, not puddle mechanics.
:test_apply_precipitation_effects_rain - Liquid precipitation creates a valid puddle in an isolated outdoor room.
:test_apply_precipitation_effects_snow - Solid precipitation creates a valid snow pile in an isolated outdoor room.
:test_apply_precipitation_effects_snow_melt - Warm clear climate melts an isolated snow pile without legacy decay.
:test_weather_description_humidity - Weather description humidity prose, isolated from production.
:test_on_climate_change_applies_fog - Fog climate transition activates fog effect in an isolated room.
:scene_audience - scene_source: the listener set this scene projects to = CONNECTED occupants (online_players()). A disconnected occupant gets the scene via the reconnect-rebuild, not the live audience.
:test_scene_sounds_matches_active_emitters - scene_sounds() returns exactly the live source_sounds of the room's active emitters — emitter-based replacement for recursive :sounds().
:scene_sounds - Live $sound objects in this room's active sound scene — emitter-based replacement for the recursive :sounds() walk. Used by :listen/:stop_listen to rebuild a listener's scene on enter/reconnect. Distinct source_sounds only.
:test_media_scene_for_exposes_room_audio - media_scene_for(listener) lifts the existing room scene canon into one authoritative media scene map.
:test_gas_connections_includes_landed_ship_hatch - A landed ship hatch appears as a reciprocal connection to the ship's canonical inner-area reservoir.
:tick_pressure_hazards - SILENCED by claude (emergency, 2026-07-13): this verb's announce_all was flooding every room with pressure-hazard messages. No-op until the outdoor/reservoir atmosphere fix lands. Revert with @revert #2700:tick_pressure_hazards.
:clone - clone(?deep=false) -> copy of this room with its OWN exit waifs (source=clone; dest still points at the original's dest for an orchestrator to remap later). pass() copies props + clonable contents (if deep) via #1:clone.
:test_display_occupants_hides_invisible_occupants - display_occupants should suppress invisible sitters and recliners.
:settle_heat - Advance heat-source offsets and conservative room-to-room heat exchange by elapsed wall time.
:predict_heat_transition - Return seconds until manual override expiry, heat-source decay, or practical neighbour equilibrium.
:apply_heat_transition - Settle room heat at a predicted transition.
:test_analytic_heat_transition - Prove elapsed heat decay, prediction, unified registration, and cleanup.
:coalesce_puddles - Merge every puddle in this room into one named carrier, conserving water and invalidating duplicate transitions.
:floor_area_m2 - Canonical room floor area in square metres, derived from dimensions.
:water_mass_kg - Total room-contained surface water mass in kilograms.
:water_depth_m - Derived standing-water depth in metres: mass divided by water density and floor area.
:water_state - Read-time descriptive bin for continuous standing-water depth.
:on_carrier_change - React to physical carrier threshold crossings; descriptive bins remain read-time derivations.
:flooded - Physical flood-state query derived from continuous water depth.
:test_water_depth_transition - Standing-water depth derives from conserved mass and floor area; flood crossing occurs at 1 cm.
:atmosphere_owner - An independent-atmosphere room owns its own air; otherwise its air belongs to its area's owner.
:reap_sound_emitters() -> how many dead emitters this room let go of.
:announce - Copied from Room (#2700):announce [verb author david (#2258)] at Tue Jun 23 02:10:15 2026 UTC
:announce_all_but - Copied from Room (#2700):announce_all_but [verb author david (#2258)] at Tue Jun 23 02:10:28 2026 UTC
:announce_all - Copied from Room (#2700):announce_all [verb author david (#2258)] at Tue Jun 23 02:11:26 2026 UTC
:match - usage: match (name of something to match.)
:announce_all_lines - Copied from Room (#2700):announce_all_lines [verb author Jason (#733)] at Tue Jun 23 02:14:29 2026 UTC
:mass - Copied from Root Class (#1):mass [verb author Daiverd (#2)] at Mon Nov 10 23:04:48 2008 CET
:coord*inates coords - coordinates() -> this object's position {x,y,z}. coordinates(list) or coordinates(x,y,z) sets it.
:cube - Copied from Root Class (#1):cube [verb author Daiverd (#2)] at Mon Nov 10 23:12:50 2008 CET
:weight - Copied from Root Class (#1):weight [verb author Daiverd (#2)] at Thu Nov 13 07:04:56 2008 CET
:test_interpolated_coordinates - Position interpolation along a path, via an inert $journey fixture. Segment angles are YAW, exactly as find_path emits them: south=0, east=pi/2, north=pi, west=3pi/2. Do NOT write math-convention angles (0=east) here -- doing so encodes a fiction and hides real bugs in $journey::position.
:test_interpolated_coords_multiseg - Multi-segment interpolation (east 5m then north 5m), via an inert $journey fixture. Segment angles are YAW as find_path emits them: east=pi/2, north=pi. See $journey::position.
:test_stop_travel - Stop freezes coords at the interpolated position and clears the journey. Segment angles are YAW as find_path emits them (east=pi/2); see $journey::position.
:test_arrive - Arrival sets coords to destination and clears the journey.
:test_travel_to - travel_to returns E_INVARG with no path, and clears any prior journey even on failure.
:interpolated_coordinates() -> DEPRECATED alias. coordinates() is now the one canonical live accessor (interpolated during a journey, stored at rest). Kept so existing callers keep working; new code calls :coordinates().
:interact_with(target) - suspends until nearby, kills task if can't reach
:_arrive_and_interact() - Arrival callback that resumes suspended task
:movement_velocity() -> movement speed in m/s
:scene_sounds - Base: a physical object has no replayable sound scene. Rooms ($room #2700) override with the emitter-based scene. Keeps :listen/:stop_listen uniform across all $gpo.
:media_scene_for - Base media scene for scene_source-capable places. Non-room physical objects expose no replayable media scene by default.
:test_cancel_travel_kills_pending - cancel_travel kills the active journey's task AND the suspended walk/interact task, clears pending_interaction, WITHOUT touching coords.
:cancel_travel() - Kill the active journey's task and clear it (and any pending_interaction waiter). Does NOT change coords -- callers that already set a final position (teleport, exit) rely on this. stop_travel adds the freeze-at-interpolated behavior on top.
:test_interact_with_approaches_without_overlap - interact_with should approach into interaction range without overlapping the target.
:test_travel_to_supersede_uses_live_position - test_travel_to_supersede_uses_live_position - a travel_to issued while a journey is in flight must compute start_coords from the LIVE interpolated position, not the stale stored .coords anchor. Regression: travel_to cleared this.journey before reading this:coordinates(), so the live position was already gone. Segment angles are YAW (east=pi/2).
:test_position_honors_find_path_angles - test_position_honors_find_path_angles - $journey::position must interpolate along the SAME angle convention find_path emits (yaw: south=0, east=pi/2, north=pi). Regression: position() used {cos(a),sin(a)} (math convention), so a due-north walk interpolated due WEST for its whole duration; arrive() snapped coords to the destination at the end and hid it. This test derives its path from find_path itself rather than hand-writing angles -- do not replace it with a literal path.
:test_walk_approach_point_avoids_target - approach_point returns an in-bounds point off the target footprint that find_path can route to.
:approach_point(my_coords, my_dims, target_coords, target_dims, ?gap) -> nearest in-bounds point off the target footprint, or {} if none. Shared by walk/run/interact_with so you stop ADJACENT to a target, never on top of it.
:placement([new]) -> the object's canonical allocentric-location anchor. Verb is truth; .placement is storage/cache. With an argument, validate via $spatial_utils and store it. {} means 'free' (raw coords, no anchor). See [[allocentric-location]].
:test_sound_origin - sound_origin() -> the point a sound emanates FROM: the centre of the object's box, not its min-corner. coordinates() stays the min-corner (load-bearing for collision/pathing/bounds); sound_origin is the derived acoustic origin the GMCP scene sends. Regression guard for: every sound in a room arriving from its object's west/south/floor corner.
:sound_origin() -> {x,y,z}, the point sound emanates from for this object: the CENTRE of its box. coordinates() is the canonical live position (interpolated mid-journey) but it is the MIN-CORNER, which is load-bearing for collision/pathing/bounds and must not change. A panner needs a POINT, so without this a big object's audio arrives from its west/south/floor corner. $player overrides this to head height. Pure geometry: it knows nothing about audio.
:test_set_ambience - set_ambience(file) attaches ONE looping ambience to this object and is IDEMPOTENT: calling it repeatedly must leave exactly one sound and one emitter. The key is owner-derived ('ambience'+tostr(this)), so ensure_emitter_for_sound reuses the emitter with that id and the previous sound under the key is stopped first. Room-owned ambience is anonymous and has no key to collide with, which is how #8608 accumulated 167 orphan emitters. An object-owned sound has an identity, so it replaces instead of stacking.
:set_ambience(file, ?volume) -> attach ONE looping ambience sound to this object; returns the sound. IDEMPOTENT by construction: the handle is derived from the owner ('ambience'+tostr(this)), so ensure_emitter_for_sound finds and REUSES the emitter with that id rather than registering another, and any previous sound under the key is stopped first. Anonymous room-owned ambience has no key to collide with and therefore piles up -- #8608 holds 167 orphan emitters from exactly that. Bind sound to the thing that makes it: it gets an identity, so it replaces instead of stacking, and you can look at what you hear. Position comes from this:sound_origin() (the centre), not the min-corner. Pass a falsy file to just stop.
:test_approach_point_skips_blocked_candidate - approach_point must skip a candidate that something else is standing on, and pick one find_path can actually route to.
:footfall_origin() -> {x,y,z}, the point where this body meets the ground: its footprint CENTRE, at floor level.
:test_footfall_origin - footfall_origin() -> where a body meets the ground: the footprint CENTRE, at FLOOR level. It shares x and y with sound_origin, which centres the box, and z with coordinates, which is the min-corner. Regression guard for: footsteps emitted at the raw min-corner, half a body-width off in +x and +y from the same body's published position, a fixed diagonal displacement that followed the walker around no matter which way they faced.
:add_option(<description>, <default value>)
:set_name - set_name(newname) attempts to change this.name to newname
:set_aliases - set_aliases(alias_list) attempts to change this.aliases to alias_list
:set_description - set_description(newdesc) attempts to change this.description to newdesc
:eject eject_nice eject_basic - eject(victim) --- usable by the owner of this to remove victim from this.contents. victim goes to its home if different from here, or $unclaimed or $player_start according as victim is a player.
:set_message(msg_name,new_value)
:do_examine - do_examine(examiner)
:examine_key - examine_key(examiner)
:examine_names - examine_names(examiner)
:examine_desc - examine_desc(who) - return the description, probably
:examine_contents - examine_contents(examiner)
:examine_verbs - Return a list of strings to be told to the player. Standard format says "Obvious verbs:" followed by a series of lines explaining syntax for each usable verb.
:get_message(msg_name)
:contents objects_for_match - Returns a list of the objects that are apparently inside this one. Don't confuse this with .contents, which is a property kept consistent with .location by the server. This verb should be used in `VR' situations, for instance when looking in a room, and does not necessarily have anything to do with the value of .contents (although the default implementation does). `Non-VR' commands (like @contents) should look directly at .contents.
:examine_verb_ok - examine_verb_ok(loc, index, info, syntax, commands_ok, hidden_verbs)
:is_listening - return 1 if the object can hear a :tell, or cares. Useful for active objects that want to stop when nothing is listening.
:hidden_verbs - hidden_verbs(who)
:examine_owner - examine_owner(examiner)
:loc_enter loc_exit - syntax: what, where, levels from original
:integrate_room_msg integrate_container_msg - Select this object's integration message for the given verb (integrate_room_msg / integrate_container_msg) and pronoun_sub it.
:empty_message - Usage: :empty_msg(msgname)
:ps*c po*c pr*c pp*c pq*c psu pou pru ppu pqu - Get the appropriate pronoun.
:vitals - Pass vitals upwards, towards vehicles/ships/etc
:clone - clone(?deep=false) -> copy of this object; copies all properties. If deep, also clones each clonable content via content:clone(deep) into the copy.
:match_targets() => Returns a list of objects used for matching code. Child implementations should probably call pass() before overriding.
:visible([viewer]) - canonical visibility (verb-is-truth). Returns base .visible AND not under an invisibility effect the viewer can't pierce. .visible remains inherent/base storage; magical invisibility is an $effect ("invisibility"), pierced by the viewer's "detect_invis". NOTE: has_effect returns a waif (falsy) or 0, so compare != 0.
:test_atmosphere - Test mass-based atmosphere mutation transactionally without leaking generic state.
:atmosphere - Pure canonical atmosphere accessor. Returns the stored absolute-mass map (species -> kg) and never mutates this object or its neighbours.
:pressure - Canonical total gas pressure in Pa, derived from absolute gas mass, volume, and temperature. No parallel stored pressure.
:breathable - Returns 1 if atmosphere is breathable. O2 mass fraction must be >= 0.16.
:add_gas - Add absolute mass kg, canonicalizing species, settling old conditions first, and rescheduling affected transitions.
:remove_gas - Remove absolute mass kg after canonicalizing species; settle first and reschedule transitions.
:volume - Returns volume in cubic meters, computed from dimensions
:gas_connections - Returns list of {container, aperture, area} for gas exchange
:describe_atmosphere - Natural-language description from mass-fraction atmosphere.
:test_describe - Test descriptions transactionally; never leak fixture gas into the generic or descendants.
:test_gas_math - Test gas math: canonical species aliases, exact kg mutation, clamping, and missing species.
:absorb_vapor - Absorb vapor-phase material into this gas container's atmosphere. Args: vapor_material (waif or obj with phase=gas).
:precipitate - Precipitate gas mass out of atmosphere as liquid into target_container. Args: species, mass_kg, target_container. v1: water/H2O only.
:test_absorb_vapor_mass_preserved - Absorb-vapor mass conservation, transactionally isolated from generic state.
:test_precipitate_h2o_to_container - Precipitation mass conversion, transactionally isolated from generic state.
:test_breathable_threshold - Property: breathable uses an O2 mass-fraction threshold of 0.16; test transactionally.
:set_pressure - Set total pressure in Pa by scaling local gas masses; settle and reschedule affected transitions. Returns derived pressure.
:_raw_atmosphere - Return the stored absolute-mass map (species -> kg) without initialization, settling, or any other side effect.
:seed_standard_air - Explicitly replace local gas storage with standard air at pressure_pa, temp_k, and relative_humidity. Defaults: 101325 Pa, node ambient temperature, 50% RH.
:migrate_atmosphere_data - One-shot canonical SI gas migration for area/room stores. Clears delegates, preserves vacuum, seeds only true local owners. Wizard-only and safe to rerun.
:vacuum_environment - Return true when this gas node or any containing gas node is explicitly vacuum. Containment inheritance is bounded and cycle-safe.
:settle_atmosphere - Explicitly settle connected gas exchange from the last epoch to now. Pair updates conserve every species exactly.
:predict_gas_transition - Return seconds until the next pressure-hazard band crossing or practical gas equilibrium.
:apply_gas_transition - Settle gas at a predicted transition and fire existing room pressure/decompression state machines.
:reschedule_gas - Invalidate stale gas predictions, emit immediate hazard changes, and register the next transition for both endpoints.
:test_transition_settlement - Event-driven gas settlement reduces a pressure gap and conserves species mass across an isolated open hatch.
:test_gas_exchange_timescale_is_physical - An open door must refill a 480 m3 room at least halfway toward equilibrium within 15 minutes.
:atmosphere_owner - A dedicated gas container IS its own canonical atmosphere owner (planetary atmospheres, reservoirs, and any node that stores its own air).