On this page

Spellbook๏ƒ

Use md.Spellbook from the public package namespace.

Implementation source

class Spellbook(aetheric_frame: str = 'default', configuration: SpellbookConfiguration | None = None, logger: Any | None = None)[source]๏ƒ

Public API

The Spellbook is the primary local authority for spell binding, spell lookup, configuration ownership, and conduit conjuration inside one aetheric frame. It is the object users interact with when they register spells, freeze configuration, begin binding transactions, and conjure the conduit that will execute against those registrations.

Contract:

  • Owns the local spell registries, lookup maps, contracted-spell mirrors, version caches, validation system, and frame-local configuration state for one spellbook instance.

  • Coordinates with the shared Aether for frame creation, frame-level configuration sharing, and system-state services.

  • Admits binding and contract changes through explicit transaction windows rather than allowing uncontrolled registry mutation.

  • Supports exactly one conjured conduit per spellbook instance.

  • Becomes unusable after cleanup completes.

Warning about aetheric_frame:

  • aetheric_frame is not a cosmetic namespace. Using it joins this Spellbook to shared frame-level configuration and visibility state inside Aether.

  • Reusing a frame means sharing spell visibility, configuration posture, and change-control surfaces with other participants in that frame.

  • Use the default frame only when the shared scope is intentional.

Responsibilities:

  • Hold and register local spells through bind() and scan().

  • Maintain local and contracted spell lookup surfaces.

  • Freeze and bind configuration into Aether at the correct lifecycle point.

  • Conjure and own the runtime Conduit for this spellbook.

  • Coordinate change-control, staged binding metadata, and contracted link state.

Parameters:
  • aetheric_frame (str, optional) -- Shared frame name used to bind this Spellbook to one Aether frame. Defaults to "default". If the frame does not exist yet, Spellbook creates it.

  • configuration (Optional[SpellbookConfiguration]) -- Optional pre-configured configuration instance to reuse for this frame.

Threading / Concurrency:

  • Creates an internal RLock and uses it to guard registry mutation, cleanup staging, and transaction-sensitive local state.

  • Relies on Aether and downstream managers for cross-object coordination.

Lifecycle / Cleanup:

  • This object owns local registries, contracted registries, validators, configuration, and logging.

  • Cleanup is staged so component teardown happens under the lock first and high-level references are dropped afterward.

Notes

  • SpellbookConfiguration is locked automatically once the spellbook crosses into the conjured runtime path.

  • Frame-wide rich Spellbook configuration reuse is explicit and only occurs when the frame posture permits it and a shared rich config object already exists on the frame.

Registration:

MELDER KERNEL - guarded. The user DRIVES a Spellbook through its public API (bind/scan/conjure plus the transaction-backed SpellIndex verbs) but can never bind() the Spellbook itself. access = "public" reflects that hands-on driving.

Subsystem Context:

The primary front door of the aether/spellbook binding surface. It owns the local spell registries and O(1) spell-id maps, runs the SpellCompiler phase pipeline through PhaseScheduler, and conjures exactly ONE Conduit per instance. bind() reflects a user object into a SpellIndex (stable ULID identity + active selected spell) plus a Spell (the bind-time metadata record), and it delegates the conjure-only orchestration - hook flow, the check_system_state policy/posture gate, conduit-ownership stamping - to SpellbookCreationSystem. Existence / SpellType / SpellbookConfiguration are the value and policy surfaces it binds against.

System Context:

The Spellbook layer of the canonical boot order Aether|AetherUtilitySystem -> Crystallizer -> MutationResearch -> Nexus -> AethericFrame -> Spellbook -> Conduit|Ward. It runs AFTER a frame exists and BEFORE/DURING conjure, which is why joining a frame is a real coupling rather than a cosmetic namespace (see the aetheric_frame warning above): reusing a frame shares spell visibility, configuration posture, and change-control surfaces with every other participant. The Spell it produces is the unit of currency every downstream layer keys on - the SpellCompiler phases, SpellSystemStates validity, ChangeControl dirty-roots, and Meld resolution all resolve through it.

AGENT_ACCESS: public

AGENT_PURPOSE:

access: public. The binding authority. Call bind(...)/scan(...) to register, then conjure(...) exactly once to build the root Conduit. Owns the APPLIED SEAMS of the transaction-backed SpellIndex verbs - _apply_notch, _apply_add_to_index, _apply_remove_from_index - which run inside the held change-control window. The PUBLIC verbs are on Conduit (notch_spell, add_to_spell_index, remove_from_spell_index): the Conduit admits the transaction because it owns the SpellIndex, and Spellbook applies the membership change because it owns the index maps. Neither half is callable on its own.

cleanup() → None[source]๏ƒ

Public API

Release the Spellbook's owned runtime state and permanently retire it.

Contract:

  • Idempotent: repeated calls are safe after _cleaned flips.

  • Performs component cleanup under the Spellbook lock first, then clears high-level references outside the lock.

  • Best-effort child cleanup: downstream cleanup failures are logged and teardown continues where possible.

  • After cleanup completes, local registries, configuration, validators, conduit references, and logger references are no longer usable.

Returns:

None.

cleanup_and_remove_spell(spell: Spell | str) → None[source]๏ƒ

Public API

Remove one locally owned spell from this Spellbook and clean it.

Purpose:

Provide a single authoritative local-spell removal path that unregisters system state, removes Spellbook/Aether/Nexus/risk references, and only then performs local spell teardown.

Contract:

  • Accepts either a live Spell or its current spell_id.

  • Supports only locally owned spells in this Spellbook.

  • Refuses the active member of an index that also holds parked versions (SpellIndex.is_sole_member False) before anything is unregistered, as cleanup_spell refuses it, and changes nothing. The sole member of an index is removed with its index. Book teardown and the parked disposal never reach this method.

  • Unregisters the spell index from SpellSystemStates before local spell cleanup.

  • Removes Spellbook id maps and lookup maps before local spell cleanup so no cleaned spell stays reachable from the pools.

  • Best-effort unregisters the spell from RiskManager when a conjured conduit currently owns it.

  • Removes local spell indexes from Aether for the current conduit when conjured.

  • Cleans the local Spell and SpellIndex as the final step.

Parameters:

spell -- Live local spell instance or current versioned spell_id.

Returns:

None.

Raises:
  • RuntimeError -- If the spell is not found locally or is not owned by this Spellbook.

  • RuntimeError -- If the spell is the active member of an index that also holds parked versions; nothing changes. Notch the index to one of them, dispose them with cleanup_spell, or move them out with remove_from_spell_index first.

  • RuntimeError -- If this Spellbook has been cleaned - including one torn down while the call waited for its lock, unless that teardown cleaned the Spell given (then the call returns).

Threading:

Resolves the spell, refuses an active member whose index holds parked versions (the index's membership is read under the index lock inside the Spellbook lock - Spellbook -> index, the order _dispose_spell takes, so no new edge), unregisters its index and drops it from _spells and _lookup_spells under the Spellbook lock; releases the lock for the risk-manager and Aether/frame calls; takes it again to drop the id maps (the Nexus record is removed after); releases the frame lookup; and cleans the spell and its index last, holding no Spellbook or frame lock itself. A direct Spell.cleanup() reaches here outside its spell lock (0.2.8233), so Book teardown and conjure's ownership stamping, which take spell locks under the Spellbook lock, never wait on a removal that waits on them. A call that passes check_cleaned() and then waits for the lock while the Book is torn down re-checks under it: teardown cleaned every active spell, so a Spell it cleaned returns and anything else raises the cleaned-Book error. A teardown that starts after the first locked section is not ordered against the rest of the removal (callers quiesce removals before tearing a Book down). (direct_spell_cleanup_lock_order, 0.2.8233)

property id: str๏ƒ

Public API

Return this spellbook's generated ownership id.

Purpose:

The id is the stamp the rest of the system uses to say "this spellbook owns that thing". Spell indexes record it as their owner_spellbook_id, transaction scope keys and the spellbook:<id> initiator string are derived from it, and crystallizer removal events carry it.

Contract:

  • Generated once by IDBuilder during __init__ and never reassigned; stable for the object's whole life.

  • Not derived from the frame name. Two spellbooks on the same aetheric frame have different ids, and a book produced by create_new_preset_spellbook() does NOT inherit its source's id.

  • Identifies the BOOK, not its contents. It does not change when spells are bound, removed, or grafted.

Threading:

Unsynchronized read of a write-once slot; safe from any thread while the spellbook is live.

Lifecycle / Cleanup:

Guarded by check_cleaned(), so this raises after cleanup rather than returning a stale id - the slot itself is deleted during core teardown.

Returns:

This Spellbook's unique identifier.

Return type:

str

Raises:

RuntimeError -- If the spellbook has already been cleaned.

property spells: Mapping[SpellIndex, Spell]๏ƒ

Public API

Return a read-only view of the local spells registered in this Spellbook.

Contract:

  • Exposes a MappingProxyType wrapper over the local spell map.

  • Supports safe introspection without allowing direct registry mutation.

Returns:

Immutable map of local SpellIndex keys to spell objects.

Return type:

Mapping[SpellIndex, Spell]

property contracted_spells: Mapping[str, Mapping[SpellIndex, Spell]]๏ƒ

Public API

Return a per-conduit read-only view of all borrowed spells.

Contract:

  • Outer keys are peer conduit identifiers.

  • Each value is an immutable spell map for that peer conduit.

Returns:

Immutable map of peer conduit id to immutable borrowed-spell map.

Return type:

Mapping[str, Mapping[SpellIndex, Spell]]

snapshot_state() → dict[str, Any][source]๏ƒ

Public API

Build a read-only snapshot of the Spellbook state.

Purpose:

Provide a stable view of local and contracted spell registries while transactions may be in-flight.

Contract:

  • Returns detached copies of internal maps; mutating the snapshot does not affect the Spellbook registries.

  • Includes a snapshot id for observability.

Returns:

Snapshot payload with local and contracted spell maps, lookup maps, and version caches.

Return type:

Dict[str, Any]

Raises:

RuntimeError -- If the Spellbook has been cleaned.

Threading:

Acquires the Spellbook lock while copying state.

find_spell_by_spell_id(spell_id: str) → Spell | None[source]๏ƒ

Public API

Return the spell this Spellbook owns under spell_id, or None.

Purpose:

Look a spell up by its spell_id - the SHA256 bind and bind_inactive return - and answer with exactly the spell bound under it, active or parked. A spell_id and a SpellIndex are separate things (owner ruling, 2026-10-17): the spell an index has active is a lookup by the index's id (get_spell_by_index_id), never the answer to a spell_id.

Contract:

  • Returns the Spell whose spell_id is the argument: the active version from _spells_by_id, else the parked version from _inactive_spells. Never another version of its index, and never the spell the index has active in its place.

  • Searches THIS spellbook's registry only. Spells reachable through contracted conduits or through other books in the frame are not consulted; inspect_spell is the frame-wide search.

  • Answers from the Book's two id maps and never walks a live map. Every owned version is a key of one of them: bind publishes an active version to _spells_by_id; bind_inactive and a notch park a version in _inactive_spells; index moves and parked disposal keep that map in step; an ownership transfer migrates every version of the index.

  • O(1): at most two dict reads.

  • Returns None on a miss. This is deliberately unlike the find_spell_index / find_spell_key siblings, which raise on a miss; a None here means "this Book owns no spell under this spell_id", not "lookup failed".

  • Pure lookup: creates, registers, and mutates nothing.

  • Until 0.2.8253 this was find_spell_by_id, which answered a parked spell_id with the spell its index had active.

Threading:

Does not acquire self._lock, by design: conjure holds that lock while it waits on phase workers and runs conduit hooks, and both can call this lookup from another thread. Each map read is one atomic dict operation and no live map is iterated, so a concurrent bind, removal, notch, index move or transfer can never make this raise or skip a registered spell. A notch, and a transfer between its flip and its inactive-member migration, move spell_ids between the maps in steps; a lookup that lands between two steps answers None for that spell_id, so answers are not linearized against writers. The returned Spell is a borrowed reference, not a lease: a concurrent removal, notch or transfer can retire or move it after this returns. A parked version disposed while the lookup runs is returned as it was read, or missed once its spell_id has left the map; the lookup never raises for it.

Lifecycle / Cleanup:

Guarded by check_cleaned().

Parameters:

spell_id -- The spell_id of the spell, active or parked.

Returns:

The spell bound under spell_id, or None when this Book owns no spell under it.

Return type:

Optional[Spell]

Raises:

RuntimeError -- If the spellbook has already been cleaned.

get_spell_permissions(spell_index: SpellIndex) → str | None[source]๏ƒ

Public API

Read the capability ceiling recorded for the active spell of one LOCAL SpellIndex.

Purpose:

Report what this Spellbook is willing to expose for a SpellIndex it OWNS, which is the upper bound any contract can grant a peer.

Contract:

  • LOCAL ONLY. Contracted (borrowed) indexes are not searched, so a SpellIndex this book borrows raises rather than reporting the lender's permission. That is deliberate: the ceiling belongs to the owner, not the borrower.

  • Returns the enum's NAME as a string, not the Permissions member. Compare against Permissions.create.name, or convert with EnumHelpers.convert_enum_and_check(...), rather than comparing to the enum directly.

  • NEVER RETURNS None. Despite the Optional[str] annotation, the outcomes are a name or a raised RuntimeError.

  • This is the SPELL-side ceiling. A contract may grant less, never more - see Permissions for how the ward applies it.

Parameters:

spell_index -- The SpellIndex whose active spell's permissions are read.

Returns:

One of "read", "create", or "block".

Return type:

str

Raises:

RuntimeError -- If no LOCAL spell is registered for the given index.

Threading:

Takes no lock of its own; it delegates to the internal spell lookup.

get_spell_by_index_id(spell_index_id: str) → Spell | None[source]๏ƒ

Public API

Resolve a spell by its stable SpellIndex id.

Purpose:

Provide a runtime lookup path keyed by the stable SpellIndex identity (spell_index_id) rather than the current version id (spell_id).

Contract:

  • Searches local SpellIndex attachments first, then contracted ones.

  • Once the matching SpellIndex object is found, resolves the spell directly through the existing index-based helpers instead of bouncing back through a second spell-id lookup.

  • Returns "None" when no matching SpellIndex exists.

Parameters:

spell_index_id -- Stable SpellIndex id (ULID) to resolve.

Returns:

Matching local or contracted spell when found, else "None".

Return type:

Optional[Spell]

find_spell_index(spellframe: str, spell_name: str, binding_name: str) → SpellIndex | None[source]๏ƒ

Public API

Resolve the SpellIndex behind one logical spell address.

Purpose:

Turn the human-facing (spellframe, spell_name, binding_name) address into the durable SpellIndex, so callers can follow its selected spell rather than pinning whichever version happens to be selected right now.

Contract:

  • Searches LOCAL spells first, then every contracted (borrowed) spellbook map. A borrowed SpellIndex is therefore resolvable through the borrower's own book.

  • The three inputs are normalized into one lookup key before matching, so casing differences in the binding name do not change the result.

  • NEVER RETURNS None. Despite the Optional[SpellIndex] annotation, the only outcomes are a SpellIndex or a raised RuntimeError. Do not write a is None branch against this method - it is unreachable.

Parameters:
  • spellframe -- Logical namespace or grouping label the spell was bound under.

  • spell_name -- Name of the bound class or function.

  • binding_name -- Secondary key distinguishing several providers of the same frame.

Returns:

The SpellIndex of the addressed spell. The returned index is the stable identity - its selected_spell_id may move underneath you when the owner notches, which is the intended way to follow the index's selected spell.

Return type:

SpellIndex

Raises:

RuntimeError -- If the address matches neither a local nor a contracted spell.

Threading:

Takes the Spellbook lock for the lookup and RELEASES it before raising, so the failure path does not hold the lock while unwinding.

find_spell_key(spellframe: str, spell_name: str, binding_name: str) → tuple | None[source]๏ƒ

Public API

Confirm that one logical spell address is resolvable, returning its lookup key.

Purpose:

An EXISTENCE PROBE, not a derivation. The key is built from the three inputs before any lookup, so the returned value is fully determined by the arguments - what the call actually tells you is that a spell is registered under it.

Contract:

  • Searches LOCAL spells first, then every contracted (borrowed) map.

  • The returned key is the SAME key constructed from the arguments; nothing is read out of the registry into it. Call this to answer "is this address bound?", not to discover an address you did not already have.

  • NEVER RETURNS None. Despite the Optional[tuple] annotation, the outcomes are a key or a raised RuntimeError.

  • Use find_spell_index(...) instead when you want the SpellIndex itself; that is the call that returns information you did not already hold.

Parameters:
  • spellframe -- Logical namespace or grouping label the spell was bound under.

  • spell_name -- Name of the bound class or function.

  • binding_name -- Secondary key distinguishing several providers of the same frame.

Returns:

The normalized lookup key, shaped (frame_or_name, binding_name_or_default).

Return type:

tuple

Raises:

RuntimeError -- If the address matches neither a local nor a contracted spell.

Threading:

Takes the Spellbook lock for the lookup and, unlike find_spell_index, raises while still HOLDING it.

inspect_spell(spell: Any, aetheric_frame: str = 'default') → str | None[source]๏ƒ

Public API

Resolve a live object back to its registered spell id, if the Aether knows it.

Purpose:

Answer "is this object bound, and under what id?" starting from the object itself rather than from an id or a lookup key.

Contract:

  • Resolution is a THREE-STAGE search, in this order:

    1. IDENTITY against this Spellbook's own spells - each candidate is compared with is on both spell and user_created_object, so an existing-creation binding is findable by the very instance the caller handed to bind(...).

    2. IDENTITY frame-wide - every other conduit's Spellbook in the named frame is walked, which is what keeps foreign spells (cluster shares, dynamic-link providers) inspectable by object.

    3. CODE fallback - only if identity found nothing, the id is re-derived from the object alone.

  • Stage 3 matches exactly: a spell_id is the SHA256 of the registered object's code (an existing object's class code plus its data) with nothing from the bind call, so the id re-derived from the object equals the id recorded at bind while the object's code and data are unchanged. Identity runs first: it is cheaper, and it still finds an object whose data changed after bind.

  • Every stage confirms the candidate id against the Aether registry before returning it; a spell known locally but absent from the registry returns None.

  • THIS METHOD NEVER RAISES. Every exception is caught, logged, and converted to None - including a KeyError for an unknown aetheric_frame name. A None result is therefore ambiguous: not registered, OR inspection failed. Check the log when the distinction matters.

Parameters:
  • spell -- The object to resolve - a class, function, or live instance.

  • aetheric_frame -- Frame whose registry is consulted and whose conduits are walked in stage 2. Defaults to the lazily created "default" frame.

Returns:

The registered spell id (SHA256) when the object resolves AND the Aether confirms it, otherwise None. See the contract above - None does not prove the object is unbound.

Return type:

Optional[str]

Threading:

Holds this Spellbook's lock only while stage 1 walks this Spellbook's own spells, the lock bind and removal write them under. Stage 2, stage 3 and every registry confirmation run with no lock held here: the Aether and frame reads take frame locks, and frame teardown takes this Spellbook's lock under its frame lock, so holding it through them could hang against a racing frame teardown (until 0.2.8232 it was held for the whole search). Stage 2 walks a one-call copy of the frame's conduit map, which root registration and removal write under the frame lock, and a one-call copy of each other Spellbook's spell map (0.2.8230); a conduit or spell added or removed during the walk may be missed or still seen. (lock_residuals, 0.2.8232)

describe_spells_in_spellbook() → list[dict[str, Any]][source]๏ƒ

Public API

Return a stable authoring dump of spell targeting details currently visible through this Spellbook.

Purpose:

Provide an ACL-authoring/introspection surface that lets callers inspect the exact spell_id values available in this Spellbook alongside the logical targeting fields users may prefer when they do not want to author by SHA256 alone.

Contract:

  • Uses the Spellbook-owned spell-id pool as the visible spell set.

  • Returns one detached dictionary per spell.

  • Does not mutate local or contracted registries.

  • Includes only the user-facing selector and ownership fields: spell_id, spell_name, binding_name, spellframe, existence, resolvable, and owner_conduit_id.

  • Sorts results deterministically by spell name, effective binding name, and spell id.

Returns:

Detached spell-target description payloads for all visible spells in this Spellbook.

Return type:

list[dict[str, Any]]

cleanup_spell(*, spell: Spell) → None[source]๏ƒ

Public API

Fully dispose an owned spell. On a conjured Book every contract that holds the version drops it first, through the root conduit's cleanup_spell. Then invalidate it so the spell and its SpellIndex are flagged dirty (and dependents are rechecked) while the index and dependency edges still exist, then take it off every resolution surface, destroying its index when the spell was that index's only member. This is the disposal path remove_from_spell_index points at for a sole-member index.

Contract:

  • Only a spell owned by THIS Spellbook may be disposed.

  • The ACTIVE member of a MULTI-member index cannot be disposed (it would leave the index's parked versions without an active one): notch the index to one of them, dispose them with cleanup_spell, or move them out with remove_from_spell_index first. cleanup_and_remove_spell, and so a direct Spell.cleanup(), refuses it the same way. The sole member of an index may always be disposed -- the index is destroyed with it.

  • Invalidation happens before any teardown (spell.invalidate_spell()): the creation context is cleared and its SpellIndex is marked structurally changed so dependents are rechecked, while the index and dependency edges still exist. Teardown only runs afterwards.

  • An ACTIVE spell is then disposed through cleanup_and_remove_spell, the authoritative path that unregisters the index from SpellSystemStates (fanning the dependent closure via compute_impact_closure), removes every id/lookup map, and cleans the spell and the index. An INACTIVE member is dropped from its index directly; the shared index survives unless this empties it.

  • Conduit Creations persist in this model and are left alone here.

  • NOTE: not yet sealed by a mediator transaction (no cleanup strategy exists); the map mutations run under the Spellbook lock.

  • On a conjured Book the call goes through its root conduit's cleanup_spell: after this Book's decision and before its disposal, every contract that holds the version drops it - an index link's member Detail or a version grant, with the borrower's entry and borrowed id - and an index that dies with it leaves its index links (cleanup_releases_contracts_2026_10_19, 0.2.8261). Before conjure no contract exists and the Book disposes it directly.

Parameters:

spell -- The owned spell to dispose.

Raises:
  • RuntimeError -- If the spell has already been disposed ("cleanup_spell: spell <id> has already been disposed."; before 0.2.8231 a second call raised AttributeError), is not owned here, or is the active member of a multi-member index.

  • RuntimeError -- If another cleanup_spell of the same spell is still running ("cleanup_spell: spell <id> is already being disposed by another call."; before 0.2.8232 both calls ran the disposal).

Threading:

Ownership, activity and sole membership are read under the Book lock from the Book's own maps; a spell no longer in them is classified through the spell's own lock (Spell._spell_index_unless_cleaned), outside the Book lock, so the two locks are never nested here: Book cleanup and conjure's ownership stamping take them Book -> spell, and a direct Spell.cleanup no longer holds its spell lock when it reaches the Book (direct_spell_cleanup_lock_order, 0.2.8233). (cross_object_lock_gaps, 0.2.8231) The same locked section marks the spell id as being disposed and the disposal clears the mark when it ends, so of two calls for one spell that overlap, the second refuses before touching anything. The spell lock is not held across the disposal: that would take the Book and frame locks under it, against Book and frame teardown, which take the spell lock under theirs. (lock_residuals, 0.2.8232)

Returns:

None.

begin_transaction(transaction_type: ChangeTransactionType, *, conduit_id: str | None = None, conduit_ids: Iterable[str] | None = None, scope_keys: Iterable[str] | None = None, scope_hashes: Iterable[str] | None = None, binding_keys: Iterable[tuple[str, str]] | None = None, contract_keys: Iterable[tuple[str, str, str]] | None = None, metadata: dict[str, Any] | None = None) → None[source]๏ƒ

Public API

Begin a change-control transaction for this Spellbook.

Purpose:

Admit a mutation request through the frame-owned transaction mediator and, for bind transactions, open the binding transaction window.

Contract:

  • Only one change-control transaction may be active per Spellbook.

  • Admission is serialized by the mediator-owned change-control pipeline.

  • Bind transactions open the binding transaction window.

  • Scan is not a transaction type; it must run inside a bind transaction.

Parameters:
  • transaction_type -- Transaction type enum or string value (e.g. "bind", "link").

  • conduit_id -- Optional initiator conduit id for logging. Defaults to the conjured conduit id when available.

  • conduit_ids -- Optional list of conduits participating in the request.

  • scope_keys -- Optional normalized scope keys. These ARE the admission vocabulary: they become the request's scope claims, and the moded claim table acquires and arbitrates them.

  • scope_hashes -- Optional normalized scope hashes. ADVISORY IDENTITY ONLY - they carry NO claims and are NOT checked for conflicts. Their only reader is the retired ChangeControlConflictManager, which nothing calls. Supplying hashes declares no overlap and buys no isolation; use scope_keys to declare scope.

  • binding_keys -- Optional binding keys are affected by the request.

  • contract_keys -- Optional contract keys are affected by the request.

  • metadata -- Optional structured metadata for diagnostics.

Returns:

None.

Raises:
  • RuntimeError -- If a change transaction is already active.

  • RuntimeError -- If the binding transaction is already active for bind requests.

  • RuntimeError -- If change-control admission is denied.

  • ValueError -- If transaction_type is invalid.

  • TypeError -- If transaction_type has an invalid type.

Threading:

Admission uses the orchestrator lock; the local state uses the Spellbook lock.

end_transaction(transaction_type: ChangeTransactionType | None = None, *, success: bool = True) → None[source]๏ƒ

Public API

End the active change-control transaction for this Spellbook.

Purpose:

Finalize an admitted change-control request and release any implicit transaction state tracked through the mediator-owned change-control pipeline.

Contract:

  • Ends the active request tracked by this Spellbook.

  • Bind transactions close the binding transaction window.

  • Raises if no change transaction is active.

Parameters:

transaction_type -- Optional transaction type assertion for safety checks.

Returns:

None.

Raises:
  • RuntimeError -- If no change transaction is active.

  • RuntimeError -- If transaction_type does not match the active request.

  • ValueError -- If transaction_type is invalid.

  • TypeError -- If transaction_type has an invalid type.

Threading:

Uses the Spellbook lock for the local state; orchestrator handles admission state.

transaction(transaction_type: ChangeTransactionType, *, conduit_id: str | None = None, conduit_ids: Iterable[str] | None = None, scope_keys: Iterable[str] | None = None, scope_hashes: Iterable[str] | None = None, binding_keys: Iterable[tuple[str, str]] | None = None, contract_keys: Iterable[tuple[str, str, str]] | None = None, metadata: dict[str, Any] | None = None) → Generator[Spellbook][source]๏ƒ

Public API

Context-managed change-control transaction for this Spellbook.

Purpose:

Provide a safe begin/end wrapper for change-control transactions.

Contract:

  • Begins a change-control transaction on entry.

  • Ends the transaction on exit, even if an exception is raised.

  • Bind transactions open/close the binding transaction window.

Parameters:
  • transaction_type -- Transaction type enum or string value (e.g. "bind", "link").

  • conduit_id -- Optional initiator conduit id for logging.

  • conduit_ids -- Optional list of conduits participating in the request.

  • scope_keys -- Optional normalized scope keys. These ARE the admission vocabulary: they become the request's scope claims, and the moded claim table acquires and arbitrates them.

  • scope_hashes -- Optional normalized scope hashes. ADVISORY IDENTITY ONLY - they carry NO claims and are NOT checked for conflicts. Their only reader is the retired ChangeControlConflictManager, which nothing calls. Supplying hashes declares no overlap and buys no isolation; use scope_keys to declare scope.

  • binding_keys -- Optional binding keys are affected by the request.

  • contract_keys -- Optional contract keys are affected by the request.

  • metadata -- Optional structured metadata for diagnostics.

Yields:

Spellbook -- The current Spellbook instance for the duration of the transaction context.

Raises:
  • RuntimeError -- If a change transaction is already active.

  • RuntimeError -- If the binding transaction is already active for bind requests.

  • RuntimeError -- If change-control admission is denied.

  • ValueError -- If transaction_type is invalid.

  • TypeError -- If transaction_type has an invalid type.

Returns:

A context manager yielding this spellbook

inside a held change-control transaction window.

Return type:

Generator[Spellbook, None, None]

add_bind_hooks(*, pre: Sequence[Callable[[Any], object]] | None = None, activation: Sequence[Callable[[Spell], object]] | None = None, post: Sequence[Callable[[Spell], object]] | None = None) → None[source]๏ƒ

Register ordered callbacks for this Spellbook's binding lifecycle.

Purpose:

Check incoming references, customize newly constructed Spell definitions and observe completed registrations through the canonical bind paths.

Contract:

  • Appends each supplied sequence; validates all items before changing any stage. None and empty sequences add nothing. Repeated callables are repeated registrations, executed in registration order.

  • Pre receives the original reference and rejects by raising. Activation receives the actual new Spell before profile completion/publication. Post receives that Spell after active registration or inactive parking.

  • Callbacks are synchronous; return values are ignored. These callbacks are separate from bind's pre_hooks/activation_hooks/post_hooks kwargs, which configure later application-object creation during Meld.

  • Setup is allowed on a live book before or after conjure, independently of configuration freeze. Each bind retains one callback set; updates during a callback affect subsequent binds, including nested binds.

  • Post is not an outer-transaction commit notification. A later failure can abort that envelope; callback effects have no automatic rollback.

  • Hooks belong only to this Book, including when configuration is shared. Preset books start empty. Cleanup releases callbacks without disposing them. Crystallizer records stage presence, never callback code.

Parameters:
  • pre -- Reference-checking callback sequence; reject by raising.

  • activation -- Callback sequence receiving the actual new Spell definition.

  • post -- Callback sequence receiving a completed active or parked binding.

Threading:

Registry replacement and marker refresh serialize on the owned Bind. Callback execution holds no Bind construction lock. Existing transaction rules still govern nested and concurrent bind operations.

Returns:

None.

Raises:
  • TypeError -- If a supplied item is not callable.

  • RuntimeError -- If this Book has been cleaned.

Examples

book.add_bind_hooks(pre=[check_reference], activation=[configure_spell])
clear_bind_hooks() → None[source]๏ƒ

Remove all registration hooks from this Spellbook's future binds.

Contract:

Existing in-flight binds retain their captured callbacks. Leaves Meld creation hooks and already registered Spells unchanged. Refreshes the recorded Book twin when applicable; clearing an empty registry is a no-op. Releases references without disposing user callback objects.

Returns:

None.

Raises:

RuntimeError -- If this Book has been cleaned.

bind_inactive(*, spell: Any, spell_index: SpellIndex, existence: str | Existence, permissions: str | Permissions = 'create', spellframe: Any = None, binding_name: str | None = None, disposal_method_names: Sequence[str] | None = None, profile: str = 'general', resolvable: bool = True, managed_spell_mode: ManagedSpellMode | str | None = None, managed_spell_limit: int | None = None, managed_spell_timeout_seconds: float | None = None, managed_spell_reset_methods: Sequence[str] | None = None, **kwargs: Any) → str[source]๏ƒ

Public API

Bind a spell as an INACTIVE member of an existing owned spell_index.

Purpose:

Stage a spell off the resolution surface so a later notch can promote it. The spell is created (the Bind component mints its own fresh index), parked into _inactive_spells with existence kept in _spell_ids and _active = False, then folded onto the caller provided spell_index via _apply_add_to_index (which repoints the spell's index to the target and destroys the throwaway fresh index). The parked spell is inert and unmeldable until notch_spell promotes it.

Contract:

  • Requires an active binding transaction and a dynamic Spellbook posture.

  • spell_index must be an index this Spellbook already owns (its selected member id is in _spell_ids), enforced by _apply_add_to_index.

  • Performs no active-map registration and claims no binding signature; the spell stays inactive until notched.

  • Resolves disposal metadata once using the same ordered group composition as active bind; staging does not change the index's selection.

  • Runs this Book's captured bind callbacks: pre on the input, activation on the new Spell, and post after parking on the final target index. Post precedes outer transaction commit. Later notch does not rerun bind activation. Callback failures stop later stages.

Parameters:
  • spell (Any) -- The class, function, lambda, or existing object to register.

  • spell_index (SpellIndex) -- The already-owned index to attach the inactive spell to.

  • existence (Union[str, Existence]) -- Lifecycle scope for the spell.

  • permissions (str | Permissions) -- Permission level exposed to other conduits.

  • spellframe (Any) -- Logical interface/frame grouping key.

  • binding_name (Optional[str]) -- Secondary disambiguation key within the frame.

  • disposal_method_names (Optional[Sequence[str]]) -- Ordered per-spell candidates, combined with configured book candidates. The complete matching book block comes last by default, first with enforce_priority_disposal_methods=True. Shared names belong to that block in both modes; spell-only names keep their supplied order. Missing names are skipped and duplicates run once. Empty input still permits book methods.

  • profile (str) -- Spell profile family to attach after bind completion.

  • resolvable (bool) -- Native capability of this parked version, independent of index selection. Defaults to True; False retains existing naming and lifecycle restrictions.

  • managed_spell_mode (Optional[Union[ManagedSpellMode, str]]) -- Pool policy of an Existence.managed_spell_quantity binding: elastic (default), limited_quantity or queue - a ManagedSpellMode member or its name.

  • managed_spell_limit (Optional[int]) -- elastic: the most idle objects kept (default 256); limited_quantity and queue: the most objects alive at once (required). An int >= 1.

  • managed_spell_timeout_seconds (Optional[float]) -- queue only, required there: how long a lease waits for a release before it raises.

  • managed_spell_reset_methods (Optional[Sequence[str]]) -- Names of zero-argument methods of the class run, in order, on every release. The four managed options must stay None on any other existence; all four are part of a managed spell's id. A parked managed version keeps its own options.

  • **kwargs -- Optional lifecycle hooks (pre/activation/post).

Returns:

The SHA256 spell_id of the parked inactive spell.

Return type:

str

Raises:
  • RuntimeError -- If the spell_id collides, the Spellbook is not in a dynamic posture, or spell_index is not owned here.

  • TypeError -- If resolvable is not a bool or the target violates Bind admission rules, including a managed option of the wrong type or a managed non-class.

  • ValueError -- If a managed option is malformed or set on another existence.

  • HookExecutionError -- A Book bind callback failed. Activation failure cleans its unpublished allocation; post failure does not undo registration.

bind(*, spell: Any, existence: str | Existence, permissions: str | Permissions = 'create', spellframe: Any = None, binding_name: str | None = None, disposal_method_names: Sequence[str] | None = None, profile: str = 'general', resolvable: bool = True, managed_spell_mode: ManagedSpellMode | str | None = None, managed_spell_limit: int | None = None, managed_spell_timeout_seconds: float | None = None, managed_spell_reset_methods: Sequence[str] | None = None, **kwargs: Any) → str[source]๏ƒ

Bind a spell into the Spellbook for future instantiation and dependency injection.

Purpose:

Register one class, function, lambda, or existing object into the Spellbook's local registry so later conduit work can resolve it by spell identity, (spellframe, binding_name) lookup key, and lifecycle policy.

Contract:

  • Direct spellbook-level bind() opens a bind-family transaction automatically when none is active.

  • If a bind-family transaction is already active, the method reuses that existing window.

  • The underlying registration semantics are identical whether the bind-family transaction window was opened explicitly or implicitly.

  • Disposal names are resolved once for this binding. Both input groups contribute, and the resolved order is included in spell_id. A bind never establishes disposal candidates for a later binding.

  • Captures this Book's registration callbacks once: pre receives the reference, activation receives the actual new Spell before profile completion, and post receives the completed registration. Post runs before leaving the bind envelope, not after an outer batch commits. Changing callbacks during this operation affects subsequent binds.

Parameters:
  • spell (Any) -- The class, function, lambda, or existing object to register.

  • existence (Existence) -- Lifecycle scope for the spell.

  • permissions (str) -- Permission level exposed to other conduits ("read", "create", or "block").

  • spellframe (Optional[Any]) -- Logical interface, frame, or grouping key for the spell.

  • binding_name (Optional[str]) -- Secondary key used to distinguish this spell among others in the same frame.

  • profile (str) -- Spell profile family to attach after bind completion.

  • resolvable (bool) -- Native per-version resolution capability, default True. False registers a non-resolvable definition without changing naming or lifecycle restrictions.

  • disposal_method_names (Optional[Sequence[str]]) -- Ordered per-spell candidates, combined with configured book candidates. The complete matching book block comes last by default, first with enforce_priority_disposal_methods=True. Shared names belong to that block in both modes; spell-only names keep their supplied order. Missing names are skipped and duplicates run once. Empty input still permits book methods.

  • managed_spell_mode (Optional[Union[ManagedSpellMode, str]]) -- Pool policy of an Existence.managed_spell_quantity binding: elastic (default), limited_quantity or queue - a ManagedSpellMode member or its name.

  • managed_spell_limit (Optional[int]) -- elastic: the most idle objects kept (default 256); limited_quantity and queue: the most objects alive at once (required). An int >= 1.

  • managed_spell_timeout_seconds (Optional[float]) -- queue only, required there: how long a lease waits for a release before it raises.

  • managed_spell_reset_methods (Optional[Sequence[str]]) -- Names of zero-argument methods of the class run, in order, on every release. The four managed options must stay None on any other existence; all four are part of a managed spell's id.

  • **kwargs --

    Optional application-object lifecycle hooks, executed during Meld and separate from add_bind_hooks registration callbacks:

    • pre_hooks

    • activation_hooks

    • post_hooks

Returns:

The unique SHA256 spell_id associated with the bound spell.

Return type:

str

Raises:
  • ValueError -- If spell is None.

  • TypeError -- If spell is a primitive value (int, float, bool, complex, str, bytes, bytearray); a spell must be a class, function, lambda, or an existing object instance. Also raised when resolvable is not a bool, when a managed option has the wrong type, or when a managed binding targets a non-class.

  • ValueError -- If a managed option is malformed or set on another existence.

  • HookExecutionError -- A registration callback failed; the original error is chained. Activation failure retires its unpublished Spell/index. Post failure does not undo the already completed registration.

scan(module: ModuleType) → list[str][source]๏ƒ

Public API

Scan a module for scan_bind-decorated objects and bind them.

This is a module-only scan: it does not traverse packages or import submodules. Any object marked with scan_bind must originate from the scanned module, otherwise the scan fails.

Contract:

  • Posture gate first. The scan is refused outright when the frame configuration sets disable_bind, and additionally - once this book has conjured - when the frame sets disable_all_transactions_after_conjure or when the frame's system state is anything other than dynamic. In practice: POST-CONJURE SCANNING REQUIRES A DYNAMIC FRAME. The refusal is a RuntimeError raised here, before any binding is attempted.

  • Transaction window is chosen, not always opened: inside an active bind-capable window scan opens nothing; otherwise it opens its own "bind" transaction. Neither window makes the scan atomic: ending a bind window unregisters nothing, so when one target's bind raises, the targets bound before it stay registered and the error propagates. (Corrected 2026-10-10; this claimed a rollback.)

  • Pre-conjure books always take the "already active" branch, so a pre-conjure scan never opens a transaction of its own.

  • Post-conjure, "already active" means a live BIND session on this spellbook's transaction identity, or - failing that - on its conduit's identity, and that session must advertise the bind capability.

  • Binding is delegated to Scan.scan_module, which walks the module dict in definition order and binds each self-declared object once.

Threading:

Takes no lock itself; serialization comes from the bind lane and from the transaction window in effect.

Lifecycle / Cleanup:

Guarded by check_cleaned().

Parameters:

module (ModuleType) -- The module to scan for decorated spell targets.

Returns:

Spell IDs bound during the scan, in module dict order.

Return type:

list[str]

Raises:
  • TypeError -- If module is not a module or metadata is invalid.

  • ValueError -- If the module does not own a decorated object.

  • RuntimeError -- If the current frame posture disables the bind family (see the posture gate above), or propagated from Spellbook.bind on binding errors.

is_configuration_locked() → bool[source]๏ƒ

Public API

Report whether this spellbook's configuration is frozen.

Purpose:

Answer "can configuration still change?" before attempting a configuration-mutating call, so callers can branch instead of provoking a refusal.

Contract:

  • Starts False on a newly constructed book.

  • Becomes True on the paths that commit this book to a configuration - conjure, and adoption of a frame-shared configuration - and returns to False on the paths that replace or reset the configuration outright.

  • Reports the LOCK FLAG only. It says nothing about whether a configuration object is actually present; an unlocked book may still have no configuration at all (see get_configuration).

Threading:

Unsynchronized read of a plain bool. It is a snapshot, not a reservation - a concurrent conjure can flip it immediately after this returns, so a False here does not guarantee a subsequent configuration write will be accepted.

Lifecycle / Cleanup:

NOT guarded by check_cleaned(), and the backing slot is deleted during core teardown. Calling this on a cleaned spellbook therefore raises AttributeError rather than the RuntimeError the guarded methods raise. Do not use it as a liveness probe.

Returns:

True if the configuration is locked, False otherwise.

Return type:

bool

Raises:

AttributeError -- If the spellbook has already been cleaned.

get_configuration() → SpellbookConfiguration[source]๏ƒ

Public API

Return the active configuration object for this Spellbook.

Contract:

  • Returns the LIVE configuration object, not a copy or a snapshot. Configuration objects are deliberately shared between spellbooks on the same frame, so mutating the returned object is visible to every book sharing it. Treat the result as read-only unless you intend that reach.

  • Raises rather than returning None. A spellbook constructed without an explicit configuration holds none until one is adopted or created, and calling this in that window is an error, not an empty result.

Threading:

Unsynchronized read. The reference is stable once adopted, but the object it points at is shared mutable state.

Lifecycle / Cleanup:

NOT guarded by check_cleaned(), and the backing slot is deleted during teardown, so a call on a cleaned spellbook raises AttributeError rather than the documented RuntimeError.

Returns:

The active configuration instance.

Return type:

SpellbookConfiguration

Raises:
  • RuntimeError -- If this spellbook has no configuration yet.

  • AttributeError -- If the spellbook has already been cleaned.

configure_aether_frame(*, system_state: str | None, disposal: bool | None, disposal_method_names: list[str] | None, system_caching_enabled: bool | None = None, ai_native: bool | None = None, rift_enabled: bool | None = None, shared_framewide_spellbook_configuration: bool | None = None, system_cache_root_path: str | Path | None = None, disable_all_transactions_after_conjure: bool | None = None, disable_mutations: bool | None = None, disable_linking: bool | None = None, disable_bind: bool | None = None, disable_conduit_cluster: bool | None = None, disable_transfer_of_ownership: bool | None = None, disable_contract_mutation: bool | None = None, max_transaction_wait_time_in_seconds: float | None = None) → None[source]๏ƒ

Public API

Apply frame/runtime posture inputs, freeze configuration, and bind the result into Aether for this spellbook's frame.

Purpose:

This is the ONE public door onto a book's frame posture. The AethericFrameConfiguration this method writes through is created and retained by the spellbook itself - it is never handed to the caller - so every knob that is not a parameter here is a knob no user can reach. That made two capabilities unreachable from the public root before this door was widened:

  • rift_enabled is the frame's OPT-IN TO BEING OBSERVABLE. It gates passive Nexus publication (_refresh_nexus_publish_enabled) and static AR attachment, which raises "AR requires rift_enabled on target frame" when it is False.

  • ai_native is the AI-native runtime posture, and it is the single consistency rule AethericFrameConfiguration.validate() enforces.

Contract:

  • Uses the existing spellbook configuration and frame-configuration objects rather than creating a parallel setup path.

  • Applies only provided values; omitted values leave the current state unchanged. None means "do not touch", NOT "reset to default" - there is no way to clear a knob through this door, only to set it.

  • COVERS THE WHOLE FRAME POSTURE. Every with_* builder on AethericFrameConfiguration has a parameter here.

  • system_state IS APPLIED FIRST, DELIBERATELY. ai_native requires dynamic state and that rule is enforced at FREEZE rather than at assignment, so ordering the two lets a single call move a frame to dynamic and enable AI-native together. Passing ai_native=True without a dynamic frame (either already dynamic or made so by system_state in this same call) succeeds here and makes the later frame freeze raise ValueError.

  • PRE-SETTLEMENT ONLY. The frame posture freezes when the frame settles at conjure, and every with_* builder refuses afterwards. This method does not freeze the frame posture itself; it freezes the rich SPELLBOOK configuration and binds that to the owning Aether frame.

  • NOT ATOMIC. Values are applied in parameter order and a rejected value leaves the earlier ones already written. Re-call with a corrected argument rather than assuming the posture is untouched.

Threading:

Each with_* assignment takes the frame configuration's own lock individually; this method holds no lock spanning the group, which is why the sequence is not atomic.

Lifecycle / Cleanup:

Guarded by check_cleaned(). The frame configuration is owned by this spellbook and is not the caller's to clean.

Parameters:
  • system_state -- Optional frame system-state name, e.g. "dynamic" or "automatic". Applied before every other posture value.

  • disposal -- Optional disposal toggle for the rich spellbook configuration.

  • disposal_method_names -- Optional replacement disposal-method list.

  • system_caching_enabled -- Optional replacement system-caching-enabled toggle.

  • ai_native -- Optional AI-native frame posture. Requires the frame to be dynamic by the time it freezes.

  • rift_enabled -- Optional Rift-visibility opt-in. Must be True for a Rift to attach to this frame, for static AR to target it, and for the frame to publish passively into Nexus.

  • shared_framewide_spellbook_configuration -- Optional toggle for sharing one spellbook configuration across every book on the frame.

  • system_cache_root_path -- Optional replacement cache-root fragment, as a str or Path. MUST BE RELATIVE - it is resolved against the melder package root, not the working directory, and an absolute path raises ValueError.

  • disable_all_transactions_after_conjure -- Optional toggle that closes the transaction surface once the frame has conjured.

  • disable_mutations -- Optional toggle that refuses spell mutation on this frame.

  • disable_linking -- Optional toggle that refuses conduit linking on this frame.

  • disable_bind -- Optional toggle that refuses further binding on this frame.

  • disable_conduit_cluster -- Optional toggle that refuses conduit clustering on this frame.

  • disable_transfer_of_ownership -- Optional toggle that refuses ownership transfer on this frame.

  • disable_contract_mutation -- Optional toggle that refuses contract mutation on this frame.

  • max_transaction_wait_time_in_seconds -- Optional transaction wait ceiling, in seconds.

Returns:

None.

Raises:
  • RuntimeError -- If this spellbook has been cleaned, if the frame configuration or the spellbook configuration is unavailable, or if the frame posture has already frozen.

  • TypeError -- If a posture value is not the type its with_* builder requires.

  • ValueError -- If the spellbook configuration fails validation during the freeze that closes this call.

create_new_preset_spellbook() → Spellbook[source]๏ƒ

Internal

Create a new Spellbook on this book's frame, sharing its configuration.

Used when upgrading a lesser conduit's spellbook to a normal conduit spellbook.

Contract:

  • Shares the configuration OBJECT by reference; it is not copied. The new book and this one therefore observe each other's configuration mutations. That sharing is the point of the call - the upgraded conduit must land on the same frame policy - so callers must not treat the new book's configuration as private.

  • Carries over the aetheric frame name, so both books resolve to the same frame.

  • Carries over NOTHING else. The new book gets a fresh generated id, its own logger, and an empty spell registry; bindings, spell indexes, and conduit attachment do not transfer. Migrating spells is the caller's job.

  • Constructs only. It does not register the new book with this one, conjure it, or attach it to any conduit.

Threading:

Takes no lock. The new book is unpublished and reachable only by the caller until the caller shares it.

Lifecycle / Cleanup:

The returned book is independently owned - cleaning this spellbook does not clean it. Because the configuration is shared, however, the new book keeps that object alive.

Returns:

A new Spellbook instance ready for use by a normal conduit.

Return type:

Spellbook

property conduit: Conduit | None๏ƒ

Return this book's conjured root conduit, or None pre-conjure.

Purpose:

Public accessor (spell_index_graft 2026-07-12): borrowers - the crystallizer's graft runner first among them - previously had no public read for the conjured conduit and would have needed the _conduit slot as a documented seam.

Contract:

  • None is a normal answer, not a failure. It means the book has not conjured, and it is how borrowers detect an unconjured host rather than by catching an exception.

  • Returns the ROOT conduit only. Lesser conduits and contracted conduits are not reachable through this accessor.

  • Returns a live reference, not a handle or proxy. Reading it does not keep the conduit alive or prevent its teardown.

Threading:

Unsynchronized read. Because the value transitions from None to the root conduit at conjure, a caller racing conjure can observe either; re-read rather than caching the None.

Lifecycle / Cleanup:

Guarded by check_cleaned(). Distinguish the two negative outcomes: None means "not conjured yet", while RuntimeError means "this spellbook is gone".

Returns:

The live root conduit, or None when this book has not conjured yet.

Return type:

Optional[Conduit]

Raises:

RuntimeError -- If the spellbook has been cleaned.

property mutation_research: MutationResearch๏ƒ

Return the Aether-hosted MutationResearch WORLD root.

Purpose:

Accessor door (owner ruling 2026-07-12): hand back the one process-wide research root without routing through Aether, mirroring how this book already carries _crystallizer.

Contract:

  • The reference is BORROWED and world-scoped: it is the exact object Aether().mutation_research returns, never a book- or conduit-scoped view. This book never cleans it.

  • The door only returns the object. Activation, liveness, and recording gates are enforced by the root's own verbs; an inactive root refuses research work itself.

  • Bound once at __init__; the reference is stable for this book's lifetime and deleted during cleanup.

Returns:

The hosted mutation-research singleton root.

Return type:

MutationResearch

Raises:

RuntimeError -- If the spellbook has been cleaned.

conjure(policy: str | None = 'default', dynamic: bool = False, name: str | None = None, conduit_logger: Any | None = None, validation_warnings: bool = False) → Conduit[source]๏ƒ

Public API

Creates a new Conduit (execution channel) from this Spellbook.

This method finalizes the configuration, validates all local spells, and instantiates the Conduit. Conjuring disables the default binding transaction; post-conjure bind/scan requires an explicit bind-family transaction through begin_transaction("bind") or transaction("bind").

Parameters:
  • policy (str, optional) -- Access control policy for this conduit (dynamic-only modes). Must match a Policies enum member. Defaults to "default".

  • dynamic (bool, optional) -- If True, operate in dynamic mode and require system_state to be dynamic. Defaults to False.

  • name (str, optional) -- An optional name for the conduit.

  • conduit_logger (Any, optional) -- An optional logger instance to attach to the conduit for logging purposes.

  • validation_warnings (bool, optional) -- If True, log this Spellbook's validation warnings once, grouped by warning code, at WARNING level through the Spellbook logger. Warnings never stop conjure; they flag things that may fail later, such as a typed parameter no registered spell provides (UNRESOLVED_INPUT, which the meld that constructs that spell must supply). Useful while learning or debugging a new graph. Defaults to False, which logs nothing.

Returns:

The newly created Conduit instance.

Return type:

Conduit

Raises:
  • RuntimeError -- If this Spellbook has already conjured a Conduit (only one is allowed).

  • RuntimeError -- If dynamic-only policies are used when system_state is "automatic" or when dynamic is False.

  • RuntimeError -- If the conjure would run dynamic while the Crystallizer is active and spells were bound before this Spellbook's configuration was finalized. Refused BEFORE the frame posture is settled (0.2.8211): the frame is left as it was, so an unsettled frame stays unsettled and a later automatic conjure there still runs automatic.

  • ValueError -- If the configuration fails validation or the policy string is invalid.

Policies:

  • Non-dynamic mode (dynamic=False): only "default" is allowed (linking disabled).

  • Dynamic mode (dynamic=True and `system_state` is dynamic):

    • "default": normal per-spell rules.

    • "whitelist_all" / "block_all": override per-spell whitelist behaviour.

    • "inbound_only" / "outbound_only": directional link restrictions.

Hook integration๏ƒ

If the active SpellbookConfiguration has Conduit lifecycle hooks registered under this Spellbook's ID, they are fetched via "SpellbookCreationSystem.get_conjure_hook_map()" and invoked in the following order:

  1. "on_conduit_pre_created()"

    Fired before the Conduit is constructed. No Conduit instance is passed because it does not exist yet.

  2. "on_conduit_activated(conduit)"

    Fired immediately after the Conduit has been constructed (its "__init__" has run), but before it is wired into spells.

  3. "on_conduit_post_created(conduit)"

    Fired after the Conduit has been integrated into all local spells via "SpellbookCreationSystem.define_conduit_into_spells".

For conjured (root) conduits, these hooks receive:

  • pre : no arguments

  • act : (conduit,)

  • post : (conduit,)

Topic reference ยท Full contents