On this page

Branching the record and joining back๏ƒ

๐Ÿ”ต Expert ยท Lesson 23

BRANCHING, AND WHY A JOIN CAN REFUSE. Research lanes are melder's branches: parallel lines of versions over one world, anchored to each other, finished by joining. It looks like git until three places where it deliberately is not.

A BRANCH STARTS EMPTY research_create_lane("try-a", attach_to="default", attach_at_spell_id=v1) Anchoring records ANCESTRY ONLY. No node is copied, no node is moved, and the anchor lane is untouched - the new lane begins with zero versions and a memory of where it came from. That is the opposite of a git branch, which starts AT a commit and carries the whole history with it.

ANCHORING IS ALL OR NOTHING attach_to and attach_at_spell_id must be supplied together; exactly one of them raises before the lock is even taken. There is no "anchor to whatever the tip is" shorthand, because the anchor is the one fact the join later reasons about and a shorthand would let you record a different anchor than you meant.

THE DEFAULT LANE TYPE IS experiment, NOT development Omit lane_type and you get experiment - deliberately NOT the default lane's development. A freshly cut lane is an experiment until someone says otherwise, so the safe classification is the one you get by not thinking about it.

AND A JOIN IS DIVERGENCE-AWARE, WHICH IS THE WHOLE LESSON research_join("try-a", into="default") The CLEAN path requires two things at once: the source is anchored onto the receiver, AND the receiver's tip is STILL AT that anchor. Anything else - the receiver moved on, a foreign anchor, no anchor at all - is a DIVERGENT join and refuses unless you pass force=True. The refusal NAMES BOTH TIPS, so you are told exactly what drifted apart rather than being handed a conflict to hunt.

force=True IS NOT A MERGE. It is an EXPLICIT SUPERSEDE, and melder is blunt about the difference: "Reconciliation-by-content is not a join concern: compose in the codegen workshop, register the multi-parent result, then join." Merging content is work you do with code, in a room, and the record only ever books the outcome. A version-control system that merges FOR you is guessing, and this one refuses to guess about your source.

COLLAPSE CHOOSES HOW MUCH LINE COMES WITH IT collapse=False (default) fold the source's FULL line in collapse=True move only the TIP, leaving the rest readable in the joined source lane Residence transfers with whatever moves, and the source is marked joined - it accepts no further work afterwards.

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/23_branching_the_record_and_joining_back.py
py -3.14t UX_and_AIX_experiences/04_expert/23_branching_the_record_and_joining_back.py

Download this collection ยท Source on GitHub

Public surface๏ƒ

research_create_lane (anchored, half-anchored and refused), research_heads, research_join with and without force, Conduit.bind_inactive to move the receiver, and the lane payload create_lane returns (a lane describe() snapshot: lane_type, anchor, nodes)

Code๏ƒ

  1"""
  2TIER: expert (23)
  3GOAL: BRANCHING, AND WHY A JOIN CAN REFUSE. Research lanes are melder's
  4      branches: parallel lines of versions over one world, anchored to
  5      each other, finished by joining. It looks like git until three
  6      places where it deliberately is not.
  7
  8      A BRANCH STARTS EMPTY
  9        research_create_lane("try-a", attach_to="default",
 10                             attach_at_spell_id=v1)
 11      Anchoring records ANCESTRY ONLY. No node is copied, no node is
 12      moved, and the anchor lane is untouched - the new lane begins with
 13      zero versions and a memory of where it came from. That is the
 14      opposite of a git branch, which starts AT a commit and carries the
 15      whole history with it.
 16
 17      ANCHORING IS ALL OR NOTHING
 18      `attach_to` and `attach_at_spell_id` must be supplied together;
 19      exactly one of them raises before the lock is even taken. There is
 20      no "anchor to whatever the tip is" shorthand, because the anchor is
 21      the one fact the join later reasons about and a shorthand would let
 22      you record a different anchor than you meant.
 23
 24      THE DEFAULT LANE TYPE IS `experiment`, NOT `development`
 25      Omit `lane_type` and you get `experiment` - deliberately NOT the
 26      default lane's `development`. A freshly cut lane is an experiment
 27      until someone says otherwise, so the safe classification is the one
 28      you get by not thinking about it.
 29
 30      AND A JOIN IS DIVERGENCE-AWARE, WHICH IS THE WHOLE LESSON
 31        research_join("try-a", into="default")
 32      The CLEAN path requires two things at once: the source is anchored
 33      onto the receiver, AND the receiver's tip is STILL AT that anchor.
 34      Anything else - the receiver moved on, a foreign anchor, no anchor
 35      at all - is a DIVERGENT join and refuses unless you pass
 36      `force=True`. The refusal NAMES BOTH TIPS, so you are told exactly
 37      what drifted apart rather than being handed a conflict to hunt.
 38
 39      `force=True` IS NOT A MERGE. It is an EXPLICIT SUPERSEDE, and
 40      melder is blunt about the difference: "Reconciliation-by-content is
 41      not a join concern: compose in the codegen workshop, register the
 42      multi-parent result, then join." Merging content is work you do
 43      with code, in a room, and the record only ever books the outcome.
 44      A version-control system that merges FOR you is guessing, and this
 45      one refuses to guess about your source.
 46
 47      COLLAPSE CHOOSES HOW MUCH LINE COMES WITH IT
 48        collapse=False (default)  fold the source's FULL line in
 49        collapse=True             move only the TIP, leaving the rest
 50                                  readable in the joined source lane
 51      Residence transfers with whatever moves, and the source is marked
 52      joined - it accepts no further work afterwards.
 53SURFACE EXERCISED: research_create_lane (anchored, half-anchored and
 54                   refused), research_heads, research_join with and
 55                   without force, Conduit.bind_inactive to move the
 56                   receiver, and the lane payload create_lane returns
 57                   (a lane describe() snapshot: lane_type, anchor, nodes)
 58VERIFY: rides the owner's 3.14t harness; asserts are the contract. The
 59        SURFACE line was corrected 2026-08-05; executable code unchanged.
 60"""
 61import melder as md
 62
 63
 64FRAME = "branch-world"
 65
 66
 67class RuleV1:
 68    def __init__(self) -> None:
 69        self.tag = "v1"
 70
 71
 72class RuleV2:
 73    def __init__(self) -> None:
 74        self.tag = "v2"
 75
 76
 77def main() -> None:
 78    # Record and custody live BEFORE the world, so binds auto-record.
 79    crystallizer = md.Crystallizer()
 80    crystallizer.activate(
 81        md.CrystallizerConfigurationBuilder().with_defaults().activate(),
 82    )
 83    research = md.MutationResearch()
 84    configuration = research.create_configuration()
 85    configuration.with_defaults().activate()
 86    research.activate(configuration)
 87
 88    # A RECORDED WORLD MUST BE BORN CONFIGURED. With custody active, a
 89    # dynamic conjure REFUSES if any bind ran before the configuration
 90    # was finalized - the profile record and default bootstrap would
 91    # otherwise durably persist binds made against unsettled config.
 92    spellbook_configuration = (
 93        md.SpellbookConfiguration(FRAME).with_defaults().finalize()
 94    )
 95    book = md.Spellbook(aetheric_frame=FRAME,
 96                        configuration=spellbook_configuration)
 97    # THE FRAME POSTURE GOES BEFORE THE BIND. A plain bind auto-declares
 98    # into research only `if self._is_dynamic_posture()` - a frame-level
 99    # question answerable before conjure. Bind into a not-yet-dynamic
100    # frame and nothing is declared, silently, because research
101    # bookkeeping never gates a bind. There would then be no node in the
102    # default lane to anchor a branch at.
103    book.configure_aether_frame(
104        system_state="dynamic",
105        disposal=None,
106        disposal_method_names=None,
107        rift_enabled=True,
108        ai_native=True,
109    )
110    v1 = book.bind(spell=RuleV1, existence="unique", permissions="create",
111                   binding_name="branch-v1")
112    conduit = book.conjure(name="branch-root")
113    print("v1 recorded on the default lane:", v1[:12], "...")
114
115    nexus = md.Nexus()
116    system_configuration = nexus.create_configuration()
117    system_configuration.with_rift_creation_enabled(True)
118    system_configuration.with_allowed_target_frame_names([FRAME])
119    nexus.activate(system_configuration)
120    rift_configuration = nexus.create_rift_configuration()
121    rift_configuration.with_space_type("codegen")
122    rift = nexus.create_rift(configuration=rift_configuration,
123                             rift_name="brancher")
124    rift.mark_active()
125    rift.create_frame_link(FRAME)
126    commands = rift.space.command_system
127
128    # ANCHORING IS ALL OR NOTHING - and it refuses before doing anything.
129    try:
130        commands.research_create_lane("half-anchored", attach_to="default")
131        raise AssertionError("expected a refusal: anchor is all-or-nothing")
132    except ValueError as error:
133        print()
134        print("attach_to without attach_at_spell_id refused -")
135        print("  ", error)
136
137    # CUT A BRANCH, ANCHORED. It starts EMPTY.
138    clean = commands.research_create_lane(
139        "clean-branch",
140        attach_to="default",
141        attach_at_spell_id=v1,
142        reason="a branch that will join cleanly",
143    )
144    print()
145    print("created 'clean-branch':")
146    print("   lane_type   :", clean["lane_type"], " (default is experiment)")
147    print("   anchor_spell:", str(clean["anchor_spell_id"])[:12], "...")
148    print("   nodes       :", len(clean["nodes"]),
149          " <- EMPTY: ancestry only, nothing copied")
150    assert clean["lane_type"] == "experiment"
151    assert clean["anchor_spell_id"] == v1
152    assert len(clean["nodes"]) == 0
153
154    # A SECOND BRANCH FROM THE SAME POINT. This one will go stale.
155    commands.research_create_lane(
156        "stale-branch",
157        attach_to="default",
158        attach_at_spell_id=v1,
159        reason="a branch the receiver will move away from",
160    )
161    heads = commands.research_heads()
162    print()
163    print("research_heads() ->", len(heads), "open lanes")
164
165    # THE CLEAN JOIN. The receiver's tip is still exactly at the anchor.
166    commands.research_join("clean-branch", into="default",
167                           reason="nothing diverged")
168    print()
169    print("join('clean-branch' -> 'default') accepted")
170    print("  source anchored onto the receiver AND the receiver still at")
171    print("  the anchor: the only arrangement that is not a supersede")
172
173    # NOW MOVE THE RECEIVER. A new version lands on the default lane, so
174    # 'stale-branch' is anchored at a point the receiver has left.
175    v2 = conduit.bind_inactive(
176        spell=RuleV2,
177        spell_index=conduit.get_spell_by_spell_id(v1).spell_index,
178        existence="unique",
179        permissions="create",
180    )
181    print()
182    print("v2 recorded; the default lane moved on:", v2[:12], "...")
183
184    # THE DIVERGENT JOIN REFUSES, AND THE REFUSAL NAMES THE TIPS.
185    try:
186        commands.research_join("stale-branch", into="default")
187        raise AssertionError("expected a refusal: divergent join")
188    except RuntimeError as error:
189        print()
190        print("join('stale-branch' -> 'default') refused -")
191        print("  ", str(error)[:150])
192        print("  it names BOTH tips, so you are told what drifted rather")
193        print("  than handed a conflict to go and find")
194
195    # force=True IS A SUPERSEDE, NOT A MERGE.
196    commands.research_join("stale-branch", into="default", force=True,
197                           reason="explicit supersede, content already "
198                                  "reconciled elsewhere")
199    print()
200    print("join(..., force=True) accepted - and note what it is NOT:")
201    print("  it did not merge any SOURCE. Reconciliation by content is")
202    print("  work you do in the codegen workshop; you register the")
203    print("  multi-parent result and THEN join. The record books an")
204    print("  outcome and never guesses at your code")
205
206    # THE JOINED LANES ARE CLOSED. A join is terminal for the source.
207    final_heads = commands.research_heads()
208    print()
209    print("open lanes now:", len(final_heads), " (joined sources closed)")
210    assert len(final_heads) <= len(heads)
211
212    print()
213    print("a branch starts EMPTY and remembers where it came from")
214    print("a clean join is anchored AND un-drifted; anything else is a")
215    print("supersede you have to ask for by name")
216
217
218if __name__ == "__main__":
219    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.

More expert examples ยท Level guide

API contracts๏ƒ