# Notch swapping a live object **🔵 Expert · Lesson 17** LINEAGES, AND A HOT SWAP TAKEN END TO END. An index is a version LINEAGE with exactly one SELECTED member. This lesson builds one, stages a second version onto it, shows that staging changes nothing about what resolves - which is why the swap is a two-step act - then takes the swap by the new version's spell_id, melds it at the index's address, and notches back. Everything runs on public calls. THE THREE VERBS book.bind(...) v1 active on a fresh index conduit.bind_inactive(...) v2 PARKED on the same index; returns v2's spell_id conduit.notch_spell( v2 becomes the active member spell_index=index, spell_id=v2_id) EVERY VERSION IS BOUND THE WAY THE FIRST ONE WAS. An index keeps its first version's binding for life: the kind of spellframe (none, a string category or a Protocol) and the address that gives - the spellframe's name, or a bare class's own name, and the binding name. `bind_inactive` refuses a version bound any other way before anything is registered, so a retry with the right arguments is admitted. That is why a notch never moves the address, and whoever melds by the address follows the notch. Two classes cannot share a bare address, so v1 and v2 are both bound under one string category, "pricing" (2026-10-25; until then a version could be staged at any address and a notch moved the address with it). NAME THE VERSION BY ITS SPELL_ID. A spell_id names one version, and the public spell_id->object lookups (`get_spell_by_spell_id`, `find_spell_by_spell_id`) answer exactly that version, active or parked (owner ruling 2026-10-17; until 0.2.8253 they answered a parked spell_id with the ACTIVE member). `notch_spell` takes the spell_id too and resolves the exact version inside the call. The Spellbook is still not the surface you notch from - the conduit's verb does the resolving (owner ruling 2026-10-16; until then the verb took only the parked OBJECT, no public door handed one out, and this lesson stopped at staging). WHAT STAGING ACTUALLY BUYS YOU `bind_inactive` puts a real, bound, owned spell into the lineage and leaves the selection alone. The candidate is inert and unmeldable until something promotes it. That separation is what lets a swap be PREPARED long before it is TAKEN - the expensive, failure-prone half (compiling and registering a new version) happens while the old version is still serving. AND WHAT A NOTCH DOES WHEN IT HAPPENS 0. Checks the version: it must belong to the conduit's Spellbook and to the index you passed, or the call raises ValueError before anything moves. 1. Moves the address's frame claim from the outgoing version to the incoming one in one compare-and-set. When another spell holds the address it raises RuntimeError naming the holder, and nothing moves. 2. Parks the outgoing active spell off the four active owned maps, then TEARS DOWN ITS CREATION CONTEXT so the door epoch bumps and the warm fast-door cannot serve the stale spell. 3. Promotes the parked candidate into the active maps and repoints the index pointer, old spell_id -> new spell_id. 4. Re-registers the index gated + dirty, so revalidation recompiles LAZILY on the next resolve rather than inside the window. All of it happens under one seal, and the sealed conduits' creation gates are quiesced for the duration, so no in-flight meld can straddle the repoint. You do not arrange any of that; the verb does, the same way `link` does in expert 16. THE OUTGOING VERSION IS NOT DESTROYED. It stays a member of the lineage, parked, with the object it built, which is why a rollback is structurally the same act as the swap - a notch back by its spell_id - rather than a restore. MIND THE LAYER WHEN YOU READ THE SOURCE. `_apply_notch` says contracted borrowers are "NOT yet fanned out HERE (owner-side only)". That HERE is load-bearing: the seam is the local switch, and `Conduit.notch_spell` one layer up does walk the links. A statement read out of its scope becomes a false one. ## Before you run Use the [Expert guide](../../expert/index.md) 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 ```bash python UX_and_AIX_experiences/04_expert/17_notch_swapping_a_live_object.py ``` ```powershell py -3.14t UX_and_AIX_experiences/04_expert/17_notch_swapping_a_live_object.py ``` {download}`Download this collection <../../downloads/expert-examples.zip>` · [Source on GitHub](https://github.com/Synaptic724/melder/blob/1211e838099a342c9dde48f064b1e772b6060e13/UX_and_AIX_experiences/04_expert/17_notch_swapping_a_live_object.py) ## Public surface Conduit.bind_inactive, Conduit.notch_spell(spell_id=...), Conduit.meld(spellframe=...), SpellIndex.selected_spell_id / spells_in_index, Conduit.get_spell_by_spell_id ## Code ```{literalinclude} ../../downloads/04_expert/17_notch_swapping_a_live_object.py :language: python :linenos: ``` ## 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](index.md) · [Level guide](../../expert/index.md) ## Related guides - [Preview, stage, and governed change](../../expert/governed-change.md) ## API contracts - [SpellIndex](../../reference/api/binding/spellindex.md) - [Conduit](../../reference/api/resolution/conduit.md)