On this page
The room and its fixtures๏
๐ Advanced ยท Lesson 10
THE ROOM. Every Rift owns exactly one, and the law around it is stricter than people expect:
A RIFT OWNS ONE PRIMARY ROOM, ITS KIND IS CHOSEN ONCE AT CREATION, AND IT IS NEVER SWITCHED.
There is no room registry. There is no "active space". There is no swap, promote, or re-type verb. RiftSpaceType is the single input that fixes a Rift's capability posture FOR LIFE, and Rift reads it at creation to construct the matching RiftSpace subclass.
That is a design choice worth pausing on. A room you can re-type is a room whose permissions are a moving target, and every consumer holding a reference has to re-ask what it is allowed to do. Fixing the kind at birth makes the answer cacheable and makes an audit of "what can this rift do" a question about creation, not about now.
THE FIXTURES - every room carries the same SET, whatever its kind: space_id / space_name / owner_rift_id / space_kind / metadata frame_viewer - the read surface (TYPE VARIES BY KIND) workstation - the binding canvas (lesson 12) command_system - the verb surface (TYPE VARIES BY KIND) event_system - rift-local publish/subscribe memory_system - rift-local command/execution records action + category hooks - pre/post interception, unregister by id
So every room has the same fixtures BY NAME, and two of them differ BY TYPE: command_system and frame_viewer - what you may DO and what you may SEE. The other three are literally the same classes.
Lesson 11 takes that pair apart. (An earlier draft of these lessons claimed only command_system varied; the owner's 3.14t run proved frame_viewer varies too, and the corrected version is the better story - authority and visibility narrow TOGETHER.)
Before you run๏
Use the Advanced guide for prerequisite concepts. Run from a checkout with Melder installed and Python 3.14 free-threading selected. The collection download includes the level's local helper modules.
Run the saved script๏
python UX_and_AIX_experiences/03_advanced/10_the_room_and_its_fixtures.py
py -3.14t UX_and_AIX_experiences/03_advanced/10_the_room_and_its_fixtures.py
Public surface๏
md.RiftSpace via rift.space, the room fixtures, the one-room law. Room kind is passed as the STRING "static"; md.RiftSpaceType appears only in the doc-drift check below, where the enum itself is the subject.
Code๏
1"""
2TIER: advanced (10)
3GOAL: THE ROOM. Every Rift owns exactly one, and the law around it is
4 stricter than people expect:
5
6 A RIFT OWNS ONE PRIMARY ROOM, ITS KIND IS CHOSEN ONCE AT CREATION,
7 AND IT IS NEVER SWITCHED.
8
9 There is no room registry. There is no "active space". There is no
10 swap, promote, or re-type verb. `RiftSpaceType` is the single input
11 that fixes a Rift's capability posture FOR LIFE, and `Rift` reads it
12 at creation to construct the matching `RiftSpace` subclass.
13
14 That is a design choice worth pausing on. A room you can re-type is
15 a room whose permissions are a moving target, and every consumer
16 holding a reference has to re-ask what it is allowed to do. Fixing
17 the kind at birth makes the answer cacheable and makes an audit of
18 "what can this rift do" a question about creation, not about now.
19
20 THE FIXTURES - every room carries the same SET, whatever its kind:
21 space_id / space_name / owner_rift_id / space_kind / metadata
22 frame_viewer - the read surface (TYPE VARIES BY KIND)
23 workstation - the binding canvas (lesson 12)
24 command_system - the verb surface (TYPE VARIES BY KIND)
25 event_system - rift-local publish/subscribe
26 memory_system - rift-local command/execution records
27 action + category hooks - pre/post interception, unregister by id
28
29 So every room has the same fixtures BY NAME, and two of them differ
30 BY TYPE: command_system and frame_viewer - what you may DO and what
31 you may SEE. The other three are literally the same classes.
32
33 Lesson 11 takes that pair apart. (An earlier draft of these lessons
34 claimed only command_system varied; the owner's 3.14t run proved
35 frame_viewer varies too, and the corrected version is the better
36 story - authority and visibility narrow TOGETHER.)
37SURFACE EXERCISED: md.RiftSpace via rift.space, the room fixtures, the
38 one-room law. Room kind is passed as the STRING
39 "static"; md.RiftSpaceType appears only in the doc-drift
40 check below, where the enum itself is the subject.
41VERIFY: rides the owner's 3.14t run; asserts are the contract.
42
43DOC DRIFT FOUND AND FIXED (2026-08-02): RiftSpaceType's docstring used to
44document a FOURTH member - "dynamic: Legacy alias for codegen, retained
45temporarily so older AR configuration inputs can still normalize during
46the room rename." THERE WAS NO SUCH MEMBER, and no `_missing_` handler,
47so RiftSpaceType("dynamic") raised. The docstring has been corrected to
48state plainly that the three members are the whole set. The check below
49still runs because the enum's shape is worth proving, not because the
50docs are suspect.
51"""
52import melder as md
53
54
55def main() -> None:
56 nexus = md.Nexus()
57 system_config = nexus.create_configuration()
58 system_config.with_rift_creation_enabled(True)
59 nexus.activate(system_config)
60
61 rift_config = nexus.create_rift_configuration()
62 rift_config.with_space_type("static")
63 rift_config.with_space_name("health")
64 rift = nexus.create_rift(configuration=rift_config, rift_name="ops")
65 rift.mark_active()
66 print("rift active:", rift.is_active)
67
68 # ONE ROOM, AND THE SAME ONE EVERY TIME. `space` is not a lookup or a
69 # factory - it is THE room, identical by identity on every read.
70 room = rift.space
71 assert rift.space is room, "a rift has one room, not a room registry"
72 print("room:", type(room).__name__, "| kind:", room.space_kind)
73
74 # The room knows who owns it. Ownership is one-directional and fixed.
75 assert room.owner_rift_id == rift.id
76 assert room.space_name == "health"
77 print("space_id:", room.space_id, "| name:", room.space_name)
78 print("owner rift:", room.owner_rift_id == rift.id)
79
80 # THE FIXTURES. Same set BY NAME on every room regardless of kind.
81 # Two of them differ BY TYPE - command_system and frame_viewer, what
82 # you may DO and what you may SEE (lesson 11 takes that pair apart).
83 fixtures = {
84 "frame_viewer": room.frame_viewer,
85 "workstation": room.workstation,
86 "command_system": room.command_system,
87 "event_system": room.event_system,
88 "memory_system": room.memory_system,
89 }
90 for name, fixture in fixtures.items():
91 assert fixture is not None, f"{name} should be present on every room"
92 print(f" {name:16s} {type(fixture).__name__}")
93
94 # THE TYPE IS NOT RE-SETTABLE. There is no verb for it - not a refusal
95 # you catch, an ABSENCE you cannot call. Proving a negative honestly
96 # means naming what does not exist rather than try/except-ing.
97 for absent in ("set_space_type", "switch_space", "promote_space",
98 "retype", "activate_space"):
99 assert not hasattr(room, absent), f"{absent} should not exist"
100 print("no re-type verb exists - the kind is fixed at creation")
101
102 # The room kind matches what the configuration asked for, and that is
103 # the ONLY place it was ever decided.
104 assert room.space_kind == "static"
105 print("configured \"static\" -> room kind:", room.space_kind)
106
107 # THREE MEMBERS, AND ONLY THREE. An input naming anything else fails at
108 # normalization rather than falling back - the correct behaviour for a
109 # value that fixes the room's posture for life. (A stale docstring once
110 # promised a fourth, "dynamic"; that has been corrected.)
111 members = [kind.value for kind in md.RiftSpaceType]
112 print("members:", members)
113 assert members == ["static", "capability", "codegen"]
114 try:
115 md.RiftSpaceType("dynamic")
116 raise AssertionError("expected ValueError - there is no such member")
117 except ValueError:
118 print("an unknown room kind is refused, not defaulted")
119
120 print()
121 print("one rift, one room, one kind, decided once and never again")
122 print("same fixtures by name; command_system and frame_viewer differ by type")
123
124
125if __name__ == "__main__":
126 main()
Check the outcome๏
The script contains its own assertions or demonstrated refusal paths. Run it to evaluate those checks against your installed version. The code above is taken directly from the saved file; no run output is invented here.