On this page
Research sets lanes and residency๏
๐ต Expert ยท Lesson 03
MUTATION RESEARCH - melder's record of how a world CHANGED, not what it currently is. Everything up to here described a runtime. This describes its history.
THE SHAPE MutationResearch the subsystem (same ladder as Aether and Crystallizer - you activate the config, then the subsystem; advanced 17 settled that it is 3-to-1 with Nexus the exception) ResearchSet one named body of history ResearchLane a track within it ResearchJournal the ordered record
TWO ENUMS, AND THEY ANSWER DIFFERENT QUESTIONS LaneState open / joined / archived - can I still write here? LaneType development / experiment / production / test - what KIND of work is this? A lane has both. State is lifecycle; type is intent. Collapsing them would mean you could not have an archived production lane and an open one at the same time, which is exactly what a real promotion history looks like.
LANE TYPE ENFORCEMENT IS A KNOB set_lane_type_enforcement(enabled) Off, the types are labels. On, they are rules. Melder ships the choice rather than assuming which one you are doing - a research set exploring a hypothesis and a research set tracking a production promotion chain want opposite answers.
RESIDENCY IS THE IDEA WORTH TAKING AWAY. residence_of(spell_id) -> lane name or None A spell LIVES IN exactly one lane at a time. That is what makes "where is this thing now?" a question with one answer, and it is why promotion is a MOVE rather than a copy. heads() gives you the current tip per lane; walk(lane) gives the ordered contents; history(spell_id) follows one spell across lanes.
CAMPAIGNS group work that belongs together across lanes: set_active_campaign / clear_active_campaign / campaign_view Set one and subsequent records join it, so you can ask "what did this campaign touch" without threading an id through every call.
ANCESTRY IS STAGED, THEN RECORDED. stage_ancestry(parent_spell_ids) -> record_world_entry(...) You declare the parents BEFORE the entry that uses them. Staging is explicit and clearable, so a half-built lineage is visible as staged-but-unrecorded rather than silently attached to the wrong thing.
Before you run๏
Use the Expert 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/04_expert/03_research_sets_lanes_and_residency.py
py -3.14t UX_and_AIX_experiences/04_expert/03_research_sets_lanes_and_residency.py
Public surface๏
md.MutationResearch, md.ResearchSet, md.LaneState, md.LaneType - lanes, residency, campaigns, ancestry
Code๏
1"""
2TIER: expert (03)
3GOAL: MUTATION RESEARCH - melder's record of how a world CHANGED, not
4 what it currently is. Everything up to here described a runtime.
5 This describes its history.
6
7 THE SHAPE
8 MutationResearch the subsystem (same ladder as Aether and
9 Crystallizer - you activate the config, then
10 the subsystem; advanced 17 settled that it
11 is 3-to-1 with Nexus the exception)
12 ResearchSet one named body of history
13 ResearchLane a track within it
14 ResearchJournal the ordered record
15
16 TWO ENUMS, AND THEY ANSWER DIFFERENT QUESTIONS
17 LaneState open / joined / archived - can I still write here?
18 LaneType development / experiment /
19 production / test - what KIND of work is this?
20 A lane has both. State is lifecycle; type is intent. Collapsing
21 them would mean you could not have an archived production lane and
22 an open one at the same time, which is exactly what a real
23 promotion history looks like.
24
25 LANE TYPE ENFORCEMENT IS A KNOB
26 set_lane_type_enforcement(enabled)
27 Off, the types are labels. On, they are rules. Melder ships the
28 choice rather than assuming which one you are doing - a research
29 set exploring a hypothesis and a research set tracking a production
30 promotion chain want opposite answers.
31
32 RESIDENCY IS THE IDEA WORTH TAKING AWAY.
33 residence_of(spell_id) -> lane name or None
34 A spell LIVES IN exactly one lane at a time. That is what makes
35 "where is this thing now?" a question with one answer, and it is
36 why promotion is a MOVE rather than a copy. `heads()` gives you the
37 current tip per lane; `walk(lane)` gives the ordered contents;
38 `history(spell_id)` follows one spell across lanes.
39
40 CAMPAIGNS group work that belongs together across lanes:
41 set_active_campaign / clear_active_campaign / campaign_view
42 Set one and subsequent records join it, so you can ask "what did
43 this campaign touch" without threading an id through every call.
44
45 ANCESTRY IS STAGED, THEN RECORDED.
46 stage_ancestry(parent_spell_ids) -> record_world_entry(...)
47 You declare the parents BEFORE the entry that uses them. Staging is
48 explicit and clearable, so a half-built lineage is visible as
49 staged-but-unrecorded rather than silently attached to the wrong
50 thing.
51SURFACE EXERCISED: md.MutationResearch, md.ResearchSet, md.LaneState,
52 md.LaneType - lanes, residency, campaigns, ancestry
53VERIFY: RUN GREEN 2026-08-03 on the owner's 3.14t harness.
54"""
55import melder as md
56
57
58def main() -> None:
59 # THE TWO ENUMS. Different questions, so different types.
60 states = [state.name for state in md.LaneState]
61 types = [kind.name for kind in md.LaneType]
62 print("LaneState (lifecycle):", states)
63 print("LaneType (intent): ", types)
64 assert set(states) == {"open", "joined", "archived"}
65 assert set(types) == {"development", "experiment", "production", "test"}
66
67 # THE LADDER, third subsystem to use it (aether 07, crystallizer 17).
68 # AETHER-HOSTED, and reached the same way as md.Crystallizer() and
69 # md.Nexus(): Aether builds all three roots when it boots, so this call
70 # is a LOOKUP, not a construction. One process, one Aether, one research
71 # root under it - there is no free-standing research world to make.
72 research = md.MutationResearch()
73 assert research.activated is False
74 print()
75 print("start - configured:", research.is_configured,
76 "activated:", research.activated)
77
78 config = research.create_configuration()
79 config.with_defaults().finalize()
80 assert config.activated is False, "finalize seals; it does not enable"
81 config.activate()
82 research.activate(config)
83 assert research.activated is True
84 print("activated via the caller-driven ladder (not nexus's shortcut)")
85
86 # A RESEARCH SET is one named body of history.
87 research_set = research.create_research_set("promotion-history")
88 assert isinstance(research_set, md.ResearchSet)
89 assert research_set.name == "promotion-history"
90 assert "promotion-history" in research.list_research_set_names()
91 print()
92 print("research set:", research_set.name, "| id:", research_set.set_id)
93
94 # Every set opens with a default lane, so history has somewhere to go
95 # before you have made any decisions about structure.
96 default_lane = research_set.default_lane
97 assert default_lane is not None
98 assert research_set.lane_names(), "a set always has at least one lane"
99 print("lanes:", research_set.lane_names())
100
101 # LANE TYPE ENFORCEMENT IS A CHOICE, and it is readable.
102 enforcement = research_set.lane_type_enforcement
103 assert isinstance(enforcement, bool)
104 research_set.set_lane_type_enforcement(True)
105 assert research_set.lane_type_enforcement is True
106 research_set.set_lane_type_enforcement(enforcement)
107 print("lane_type_enforcement is a knob, currently:", enforcement)
108
109 # RESIDENCY. One spell, one lane, one answer - and a miss is None
110 # rather than an exception, the same honest-absence shape as
111 # ConduitCloud.find_conduit_id_by_name (intermediate 37).
112 assert research_set.residence_of("no-such-spell") is None
113 print()
114 print("residence_of() on an unknown spell -> None, not an exception")
115
116 # THE READ SURFACE, ON A SET NOTHING HAS BEEN REGISTERED INTO. An
117 # empty record is PRESENT AND EMPTY, not absent - which is the shape
118 # you have to be able to tell apart from "no record at all".
119 heads = research_set.heads()
120 assert isinstance(heads, dict)
121 assert "default" in heads, heads
122 assert heads["default"] is None, heads
123 print()
124 print("heads() on a fresh set ->", heads)
125 print(" the default lane is THERE with a tip of None. Open-but-empty")
126 print(" and absent are different facts, and melder spells them")
127 print(" differently: a missing key means `not open`, a None value")
128 print(" means `open, nothing in it yet`")
129
130 walked = research_set.walk("default")
131 assert isinstance(walked, list) and walked == [], walked
132 print("walk('default') ->", len(walked),
133 "rows - somewhere for history to go before any has happened")
134 print(" (history / campaign_view are driven in expert 29 and 15,")
135 print(" where there is something recorded to read)")
136
137 # CAMPAIGNS - group work across lanes without threading an id around.
138 assert research.active_campaign is None
139 research.set_active_campaign("q3-promotion")
140 assert research.active_campaign == "q3-promotion"
141 print()
142 print("active campaign:", research.active_campaign)
143 research.clear_active_campaign()
144 assert research.active_campaign is None
145 print("cleared - campaigns are explicit at both ends")
146
147 # ANCESTRY IS STAGED FIRST. Declaring parents is a separate, visible
148 # step from recording the entry that uses them.
149 assert research.staged_ancestry is None
150 research.stage_ancestry(["parent-a", "parent-b"])
151 assert research.staged_ancestry == ["parent-a", "parent-b"]
152 print()
153 print("staged ancestry:", research.staged_ancestry)
154 research.clear_staged_ancestry()
155 assert research.staged_ancestry is None
156 print("cleared - a half-built lineage is visible, never auto-attached")
157
158 print()
159 print("state is lifecycle, type is intent - a lane carries both")
160 print("residency means one spell, one lane, one answer to 'where is it'")
161
162
163if __name__ == "__main__":
164 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.