On this page
- MutationResearch
MutationResearchMutationResearch.cleanup()MutationResearch.idMutationResearch.configuredMutationResearch.is_configuredMutationResearch.activatedMutationResearch.is_activatedMutationResearch.configurationMutationResearch.create_configuration()MutationResearch.create_configuration_builder()MutationResearch.configure()MutationResearch.activate()MutationResearch.deactivate()MutationResearch.research_set()MutationResearch.create_research_set()MutationResearch.list_research_set_names()MutationResearch.describe_research_composition()MutationResearch.load_recorded_composition()MutationResearch.active_campaignMutationResearch.set_active_campaign()MutationResearch.clear_active_campaign()MutationResearch.staged_ancestryMutationResearch.stage_ancestry()MutationResearch.clear_staged_ancestry()MutationResearch.record_world_entry()MutationResearch.record_promotion()MutationResearch.residency_view()MutationResearch.is_composition()MutationResearch.diff_research()MutationResearch.create_diff_engine()MutationResearch.source_view()MutationResearch.impact_view()MutationResearch.module_graph_view()MutationResearch.source_drift_view()MutationResearch.module_view()MutationResearch.part_view()MutationResearch.parts_view()MutationResearch.part_diff()MutationResearch.preview_candidate()MutationResearch.synthesize_candidate()MutationResearch.group_diff_research()MutationResearch.group_view()MutationResearch.register_group()MutationResearch.recompose_group()MutationResearch.compositions_of()MutationResearch.group_footprint_view()MutationResearch.group_drift_view()MutationResearch.group_history_view()MutationResearch.recent_activity_view()MutationResearch.group_impact_view()
MutationResearch๏
Use md.MutationResearch from the public package namespace.
- class MutationResearch(*args: object, **kwargs: object)[source]๏
Singleton mutation-research root hosted by Aether.
- Purpose:
Own the formal declaration record of research over the live spell world. The root manages ResearchSet networks by name (one guaranteed default set), carries the mutation-research configuration lifecycle, and is the ONLY object in the package that touches the crystallizer: sets emit detached payloads through an injected callback, and the root records them into the persistence layer as the MutationResearchCrystal composition payload.
Contract:
Singleton, mirroring the hosting pattern used by Nexus and Crystallizer; hosted by Aether, not by AethericFrame.
Owns configuration state and the research-set registry.
Emission is replace-on-emit through Crystallizer.emit(...) and is a NO-OP while the crystallizer records nothing; lifecycle flips ride the RecordedUnitState switch as before.
Conduits and frames carry NO mutation dimension: the old conduit/frame facades and SpellIndex-keyed sessions are out of the model and gone.
- Threading:
Class lock guards singleton identity; instance verbs serialize under the same reentrant lock. A dedicated reentrant emission lock makes the persistence emission atomic (snapshot build + replace-on-emit publication happen under one holder, so a paused emitter can never publish a stale composition over a newer one). Lock order is emission -> root -> set -> crystallizer, one-way: every path that can trigger an emission while holding the root lock (set creation, hydration) acquires the emission lock first.
- Lifecycle:
cleanup() cascades into owned sets and configuration, emits the cleaned state while the record outlives the root, and resets singleton bookkeeping; idempotent.
- Registration:
MELDER KERNEL - guarded. Reached through Aether.mutation_research; constructing or registering a second root would break the singleton contract the emission model depends on.
- IT IS THE ONLY CRYSTALLIZER TOUCHPOINT IN THE PACKAGE:
Sets do not emit. They fire an injected on_mutation callback and this root does the recording. That single-writer rule is what keeps the dependency acyclic - the record layer never has to know about research internals, and research never has to know about persistence mechanics.
It is also why the emission lock exists and why lock order is emission -> root -> set -> crystallizer, strictly one-way. Set constructors fire on_mutation while the root lock is held, so any path that can emit while holding the root - set creation, hydration - must take the emission lock FIRST. Without that, a paused emitter could publish a stale composition over a newer one, since emission is replace-on-emit.
- Subsystem Context:
The package root, hosting ResearchSet networks by name with a guaranteed default. Sets own the record; the root owns the sets, the configuration lifecycle, and the emission seam. The diff engines and the synthesizer are also root-owned, which is why they receive injected resolvers rather than reaching for custody themselves.
- System Context:
Hosted by Aether, not by a frame - deliberately. Frames carry NO mutation dimension, so research is a WORLD-scope concern that outlives any individual frame or conduit. Spellbook and Conduit reach it through borrowed read-only accessor properties rather than owning it. In the boot order it sits after the crystallizer and before Nexus, which is exactly why configuration activation must carry the recorded composition forward: the config's emission moment necessarily precedes the root's.
AGENT_ACCESS: public
- AGENT_PURPOSE:
access: public. The research root, reached through Aether. Configure and activate, then create_research_set(...) and use the read verbs: source_view, impact_view, module_view, part_view, diff_research, residency_view, group_view.
- cleanup() None[source]๏
Idempotently clear mutation-research root state and reset singleton bookkeeping.
Contract:
IDEMPOTENT under double-checked locking: it returns immediately if already cleaned, then re-checks inside the lock.
Tears down research-set state; the hosting Aether is BORROWED and is not cleaned here.
The teardown state emission is BEST-EFFORT: a raising sink is swallowed so the cascade (sets, engines, configuration) and the singleton reset always complete.
- Returns:
None.
- property id: str๏
Return the stable root id.
Contract:
Identifies the singleton instance; assigned once at first construction and stable for the process.
Distinct from any research-set or lane id.
Identifies the singleton instance, assigned once at first construction and stable for the process.
- Threading:
Unsynchronized read of a plain flag; a snapshot only.
- Lifecycle / Cleanup:
Guarded by check_cleaned().
- Raises:
RuntimeError -- If mutation research has been cleaned.
- Returns:
Stable root id.
- Return type:
str
- property configured: bool๏
Return whether a configuration is installed.
Contract:
Reports that a configuration has been INSTALLED, not that mutation research is running - that is activated.
deactivate() does NOT clear this: the configuration stays installed and only the activation flag flips. So configured and not activated is the normal deactivated state, not an inconsistency.
- Threading:
Unsynchronized read of a plain flag; a snapshot only.
- Lifecycle / Cleanup:
Guarded by check_cleaned().
- Raises:
RuntimeError -- If mutation research has been cleaned.
- Returns:
True when the root has an installed configuration.
- Return type:
bool
- property is_configured: bool๏
Alias for the configured-state flag.
Contract:
ALIAS for the configured property, kept for call-site readability.
Identical behaviour and identical guard - there is no difference to choose between.
ALIAS for the configured property, kept for call-site readability. Identical behaviour and identical guard - there is no difference to choose between.
- Threading:
Unsynchronized read of a plain flag; a snapshot only.
- Lifecycle / Cleanup:
Guarded by check_cleaned().
- Raises:
RuntimeError -- If mutation research has been cleaned.
- Returns:
True when a configuration is installed.
- Return type:
bool
- property activated: bool๏
Return whether the mutation-research root is active.
Contract:
Reports that mutation research is LIVE. Activation implies a configuration is installed; the converse does not hold.
Flipped back to False by deactivate() without losing configuration.
- Threading:
Unsynchronized read of a plain flag; a snapshot only.
- Lifecycle / Cleanup:
Guarded by check_cleaned().
- Raises:
RuntimeError -- If mutation research has been cleaned.
- Returns:
True when configuration is installed and activated.
- Return type:
bool
- property is_activated: bool๏
Alias for the activated-state flag.
Contract:
ALIAS for the activated property, kept for call-site readability.
Identical behaviour and identical guard.
ALIAS for the activated property, kept for call-site readability. Identical behaviour and identical guard.
- Threading:
Unsynchronized read of a plain flag; a snapshot only.
- Lifecycle / Cleanup:
Guarded by check_cleaned().
- Raises:
RuntimeError -- If mutation research has been cleaned.
- Returns:
True when the root is active.
- Return type:
bool
- property configuration: MutationResearchConfiguration | None๏
Return the installed configuration, if any.
Contract:
Returns the INSTALLED configuration by reference, not a copy. It stays non-None after deactivate(), because deactivation keeps the install.
None means nothing has been installed yet.
- Threading:
Unsynchronized read of a plain flag; a snapshot only.
- Lifecycle / Cleanup:
Guarded by check_cleaned().
- Raises:
RuntimeError -- If mutation research has been cleaned.
- Returns:
Installed configuration.
- Return type:
Optional[MutationResearchConfiguration]
- create_configuration() MutationResearchConfiguration[source]๏
Create a fresh mutation-research configuration object.
Contract:
FACTORY ONLY. It returns a FRESH, EMPTY, UNATTACHED configuration and does NOT install it on this singleton - installation is a separate step. Calling it twice yields two unrelated objects.
The returned configuration starts empty, so seed it before freezing.
- Threading:
Unsynchronized read of a plain flag; a snapshot only.
- Lifecycle / Cleanup:
Guarded by check_cleaned().
- Raises:
RuntimeError -- If mutation research has been cleaned.
- Returns:
New mutable config object.
- Return type:
- create_configuration_builder() MutationResearchConfigurationBuilder[source]๏
Create a fresh fluent builder for mutation-research configuration assembly.
Contract:
FACTORY ONLY. Returns a fresh builder wrapping a new configuration and does NOT install anything here.
Remember the builder's exits are one-shot: build() yields a MUTABLE configuration, finalize() a frozen one, activate() an activated one.
- Threading:
Unsynchronized read of a plain flag; a snapshot only.
- Lifecycle / Cleanup:
Guarded by check_cleaned().
- Raises:
RuntimeError -- If mutation research has been cleaned.
- Returns:
New builder instance.
- Return type:
- configure(configuration: MutationResearchConfiguration) None[source]๏
Install one configuration on the mutation-research root.
Contract:
INSTALLS ONLY - it does not validate, freeze or activate, and it accepts a configuration that is still mutable.
Type-checked: a non-MutationResearchConfiguration raises TypeError.
- Parameters:
configuration -- Configuration object to install.
- Returns:
None.
- Raises:
TypeError -- If the supplied object is not a mutation-research configuration.
RuntimeError -- If mutation research is already active.
- activate(configuration: MutationResearchConfiguration | None = None, *, hydrate_from_record: bool = True) None[source]๏
Activate the mutation-research root using one activated configuration.
Contract:
ORDERING RULE: THE CONFIGURATION MUST BE ACTIVATED FIRST. Activating with a merely-frozen configuration raises.
Two distinct failure modes: "not configured" and "configuration not activated".
- Parameters:
configuration -- Optional configuration to install before activation.
hydrate_from_record -- When True (default), an UNTOUCHED registry (nothing but the pristine default set) rebuilds itself from the active profile's recorded composition at activation - the twin docking loop: emit while live, hydrate on the way up. Live research is never clobbered; a touched registry skips hydration and re-records itself instead. Hydration runs BEFORE the root reports active, so the public ingress can never open into a registry that is about to be swapped.
- Returns:
None.
- Raises:
RuntimeError -- If no configuration is installed or the configuration has not itself been activated.
- deactivate() None[source]๏
Deactivate the mutation-research root without dropping configuration.
Contract:
KEEPS THE INSTALLED CONFIGURATION. It flips the activation switch only, so configured stays True and the recorded twin is not evicted - the state record flips to disabled instead of being removed.
That makes deactivate REVERSIBLE without re-supplying configuration, which is the whole point of separating the two flags.
Emits a state record only when the crystallizer is activated, so a missing record does not imply the deactivation failed.
- Threading:
Reads under self._lock, so the result is a coherent snapshot.
- Lifecycle / Cleanup:
Guarded by check_cleaned().
- Raises:
RuntimeError -- If mutation research has been cleaned.
- Returns:
None.
- research_set(name: str = 'default') ResearchSet[source]๏
Return one owned research set by name.
Contract:
REQUIRED lookup: an unknown name RAISES, and the error LISTS THE KNOWN NAMES so a typo is self-diagnosing.
Omitting the name resolves the default set rather than returning all sets.
- Parameters:
name -- Set name; the guaranteed default set when omitted.
- Returns:
Owned research network.
- Return type:
- Raises:
KeyError -- If no set carries the name; the error lists known names.
- create_research_set(name: str) ResearchSet[source]๏
Create one additional named research set.
Contract:
LOCK ORDER LAW: THE EMISSION LOCK IS TAKEN BEFORE THE ROOT LOCK. The set constructor fires on_mutation while the root lock is held, so the one-way order is emission -> root. Reversing it deadlocks.
Rejects a non-string or empty name up front.
- Parameters:
name -- Unique set name.
- Returns:
Newly created research network.
- Return type:
- Raises:
ValueError -- If the name is empty or already registered.
- list_research_set_names() list[str][source]๏
Return every owned research-set name, sorted.
Contract:
SORTED, so iteration order is deterministic across calls and processes.
Snapshot taken under the lock; it goes stale as sets are registered or removed.
- Threading:
Reads under self._lock, so the result is a coherent snapshot.
- Lifecycle / Cleanup:
Guarded by check_cleaned().
- Raises:
RuntimeError -- If mutation research has been cleaned.
- Returns:
Sorted set names.
- Return type:
List[str]
- describe_research_composition() dict[str, object][source]๏
Return the detached composition payload across every owned set.
Contract:
FANS OUT across EVERY registered research set, so cost scales with the number of sets - this is a whole-world description, not a targeted query.
Keys are set names; each value is that set's own composition description.
- Threading:
Reads under self._lock, so the result is a coherent snapshot.
- Lifecycle / Cleanup:
Guarded by check_cleaned().
- Raises:
RuntimeError -- If mutation research has been cleaned.
- Returns:
set name -> ResearchSet.describe_composition() payload (bounded journal windows; the twin feed).
- Return type:
Dict[str, object]
- load_recorded_composition(composition_payload: dict[str, object]) None[source]๏
Rebuild the research-set registry from a recorded composition.
- Purpose:
The hydration seam: a recorded MutationResearchCrystal composition payload replaces the current registry wholesale (the record is the truth being loaded, never merged). The guaranteed default set is recreated when the recording lacks one.
Contract:
Expects a DICT OF SET PAYLOADS keyed by set name; any other shape raises ValueError before anything is loaded.
Restores recorded composition rather than recomputing it, so it reflects what was captured, not current truth.
- Parameters:
composition_payload -- Mapping of set name -> describe_composition() payload, as recorded by describe_research_composition().
- Returns:
None.
- Raises:
ValueError -- If the payload is not a mapping of set payloads.
- property active_campaign: str | None๏
Return the ambient campaign stamp, when one is set.
Contract:
AMBIENT DEFAULT. Group operations that take a campaign=None fall back to this value, so setting it changes the attribution of later calls that did not name a campaign explicitly.
None means no ambient campaign, and calls without an explicit campaign are then unattributed rather than failing.
- Threading:
Reads under self._lock, so the result is a coherent snapshot.
- Lifecycle / Cleanup:
Guarded by check_cleaned().
- Raises:
RuntimeError -- If mutation research has been cleaned.
- Returns:
Active campaign name or None.
- Return type:
Optional[str]
- set_active_campaign(campaign: str) None[source]๏
Set the ambient research-campaign stamp.
- Purpose:
Multi-agent campaigns stamp work ACROSS lanes; once set, every runtime auto-record routed through the root facades (record_world_entry / record_promotion - i.e. every dynamic bind, staged bind, and notch) carries this stamp until cleared, so campaign membership never depends on remembering to pass it.
Contract:
Sets the AMBIENT DEFAULT that later group operations inherit when they pass campaign=None. It does not retroactively attribute earlier operations.
Rejects a non-string or empty campaign.
- Parameters:
campaign -- Non-empty campaign name.
- Returns:
None.
- Raises:
ValueError -- If campaign is empty.
- clear_active_campaign() None[source]๏
Clear the ambient research-campaign stamp.
Contract:
Removes the ambient default so later group operations are unattributed unless they name a campaign explicitly. It does NOT rewrite attribution already recorded on earlier operations.
Idempotent: clearing when nothing is set is a no-op.
- Threading:
Reads under self._lock, so the result is a coherent snapshot.
- Lifecycle / Cleanup:
Guarded by check_cleaned().
- Raises:
RuntimeError -- If mutation research has been cleaned.
- Returns:
None.
- property staged_ancestry: list[str] | None๏
Return the staged parent identities, when any.
Contract:
Returns a COPY of the staged ancestry list, so mutating the result does NOT change staged state.
None and empty list mean different things: None is "nothing staged", an empty list is "staged, with no ancestors".
- Threading:
Reads under self._lock, so the result is a coherent snapshot.
- Lifecycle / Cleanup:
Guarded by check_cleaned().
- Raises:
RuntimeError -- If mutation research has been cleaned.
- Returns:
Detached parent list or None.
- Return type:
Optional[List[str]]
- stage_ancestry(parent_spell_ids: list[str]) None[source]๏
Stage parent ancestry for the NEXT world entry (one-shot).
- Purpose:
The mint half of surgical synthesis: composition happens in the codegen workshop, but the composed candidate's binding-signature SHA does not exist until it binds - and the bind auto-record fires before the agent ever sees that SHA. Staging bridges the gap exactly like the ambient campaign stamp: stage the parents, execute the candidate, and the next fresh world entry mints the multi-parent node. Consumed ONE-SHOT by the first NEW declaration (rediscoveries do not consume it); restage for another synthesis.
Contract:
Requires a NON-EMPTY LIST of parent identities; an empty list raises rather than being treated as "no ancestry".
Staging is ambient state consumed by a later operation; clearing it is a separate explicit step.
- Parameters:
parent_spell_ids -- Non-empty list of parent identities; each must be formally declared by the time the world entry lands (the set validates residence at mint time).
- Returns:
None.
- Raises:
ValueError -- If the list is empty or carries non-string entries.
- clear_staged_ancestry() None[source]๏
Clear the staged parent ancestry without consuming it.
Contract:
Resets staged ancestry to None - the "nothing staged" state, not an empty list.
Idempotent: clearing when nothing is staged is a no-op.
- Threading:
Reads under self._lock, so the result is a coherent snapshot.
- Lifecycle / Cleanup:
Guarded by check_cleaned().
- Raises:
RuntimeError -- If mutation research has been cleaned.
- Returns:
None.
- record_world_entry(spell_id: str, *, staged: bool = False, author: str | None = None, reason: str | None = None, campaign: str | None = None) bool[source]๏
Idempotently declare one world-entry into the default set.
- Purpose:
The runtime-seam facade: the spellbook's bind and bind_inactive confirmation points call this on every dynamic-lane world entry once the root is active. Rediscovery (identical content, same SHA) is a quiet no-op - the runtime never fails on research bookkeeping.
Contract:
CAMPAIGN DEFAULTS TO THE AMBIENT ONE: campaign=None inherits active_campaign rather than meaning "no campaign".
Records against the DEFAULT research set, not a named one.
- Parameters:
spell_id -- Binding-signature SHA256 entering the world.
staged -- True for parked (bind_inactive) entries.
author -- Optional acting agent name.
reason -- Optional reason line.
- Returns:
True when a new declaration was recorded; False when the identity was already declared.
- Return type:
bool
- record_promotion(from_spell_id: str | None, to_spell_id: str, *, actor: str | None = None, reason: str | None = None, campaign: str | None = None) None[source]๏
Record one runtime selection change (notch) into the default set.
Contract:
CAMPAIGN DEFAULTS TO THE AMBIENT ONE, exactly as in record_world_entry.
Records against the DEFAULT research set.
An undeclared to_spell_id is declared first (world-entry catch-up: a promotion proves the version exists) THROUGH the root world-entry verb, so staged ancestry is consumed by the candidate it was staged for; then the promoted event records with the supplied endpoints.
- Parameters:
from_spell_id -- Previously selected identity, when known.
to_spell_id -- Newly selected identity.
actor -- Optional acting agent name.
reason -- Optional reason line.
- Returns:
None.
- residency_view(spell_id: str, *, set_name: str = 'default') dict[str, object][source]๏
Return the query-time residency join for one identity.
- Purpose:
The record stores NO active flags by design; where a version currently lives (active / parked / stored) is a runtime + custody JOIN performed at read time. This verb performs it: declared truth from the research set, runtime truth from the frames (SpellIndex membership + selection), custody truth from the crystallizer.
Contract:
Total read: never raises for unknown identities (empty sha is the only refusal); every uncertainty reports honestly.
runtime verdicts: active (some index's selected member), parked (a live index member, not selected), stored (custody only), declared_only (record only), unknown (nowhere, incl. custody unavailable).
in_custody is None when the crystallizer is not recording (inactive) - a read never fabricates or raises there.
- Parameters:
spell_id -- Binding-signature SHA256 to locate.
set_name -- Research set to read declared truth from.
- Returns:
spell_id, declared, lane_id/lane_name/lane_state (None when undeclared), runtime, frame_name/index_id (None when not live), and in_custody.
- Return type:
Dict[str, object]
- is_composition(identity: str) bool[source]๏
Return whether one identity resolves to a recorded composition.
- Purpose:
Public kind probe for mediating seams (the codegen room's polymorphic verbs pick kind-aware strategy defaults with it) without exposing node internals.
Contract:
Tests whether an identity names a GROUP NODE (a composition) rather than a single spell - the discriminator the diff verbs dispatch on.
False for an unknown identity as well as for a plain spell, so it is not an existence check.
- Parameters:
identity -- Spell or composition identity to classify.
- Returns:
True when the identity is a recorded GroupedResearchNode in any owned set; False otherwise (including unknown).
- Return type:
bool
- diff_research(left_spell_id: str, right_spell_id: str, *, strategy: str | None = None) dict[str, object][source]๏
Compute one derived diff between two version identities.
- Purpose:
The read verb behind "commits are full objects, diffs are derived": material resolves through crystallizer custody (the SHA is the SpellCrystal id) and the comparison runs in the registered strategy - nothing is stored.
Contract:
KIND DISPATCH (parity law): two COMPOSITIONS diff through the grouped engine, two SPELLS through the spell engine.
A MIXED PAIR REFUSES, teach-grade, because a spell and a subsystem share no common grain to diff at. That refusal is deliberate, not a gap.
- Parameters:
left_spell_id -- Left version identity (binding-signature SHA256).
right_spell_id -- Right version identity.
strategy -- Registered strategy name; "source" by default.
- Returns:
Detached verdict payload from the owned DiffEngine.
- Return type:
Dict[str, object]
- Raises:
RuntimeError -- If the crystallizer is not live (custody unavailable).
KeyError -- If either identity has no custody crystal, or the strategy name is unknown.
- create_diff_engine() DiffEngine[source]๏
Create one standalone diff engine over crystallizer custody.
Contract:
FACTORY: returns a FRESH engine on every call, bound to this singleton's material resolver. It is not cached and not owned by the singleton.
The engine resolves material LAZILY through that callback, so it reflects state at diff time rather than at construction time.
- Threading:
Unsynchronized read of a plain flag; a snapshot only.
- Lifecycle / Cleanup:
Guarded by check_cleaned().
- Raises:
RuntimeError -- If mutation research has been cleaned.
- Returns:
Caller-owned engine (the caller cleans it up); the root's own engine stays private to diff_research.
- Return type:
- source_view(spell_id: str, *, module_name: str | None = None) dict[str, object][source]๏
Return the code of one spell's module world (or one module of it).
- Purpose:
The agent QoL bedrock: "show me the code of this object". Text resolves recorded-first (synthetic module sources are always harvested; user module text rides the opt-in retention lane), then falls back to a live disk read through the recorded module path, and reports honestly when neither side has text.
Contract:
Per-module rows carry source, origin ("recorded" | "live_disk" | None), drifted (live text vs the sealed fingerprint; None when unknowable), and text_unavailable.
module_name narrows the answer to one module; a module the spell's world does not carry answers unknown_module: True (a read never raises on an honest miss).
- Parameters:
spell_id -- Binding-signature SHA256 whose world to read.
module_name -- Optional single module to return.
- Returns:
{"spell_id", "root_module", "modules": {name: row}, "unknown_module"?}. A COMPOSITION identity fans out per member (same verb, both node families - parity law): {"node_type": "group", "group_id", "member_count", "members": {member: <this payload>}}.
- Return type:
Dict[str, object]
- Raises:
ValueError -- If spell_id is empty.
RuntimeError -- If the crystallizer is cleaned or inactive.
KeyError -- If no custody crystal exists for the identity.
- impact_view(*, spell_id: str | None = None, module_name: str | None = None, set_name: str = 'default') dict[str, object][source]๏
Return one blast radius joined with research residency.
- Purpose:
The agent-meaningful impact answer: not just "these modules and spells sit in the radius" (the crystallizer's raw view) but "these spells, in these lanes, under these campaigns" - the join between recorded custody impact and the research record.
Contract:
Exactly one question per call (spell_id OR module_name).
The raw radius payload is preserved verbatim; the join adds one research map (affected spell_id -> declared/lane_id/ lane_name/lane_state/campaign row; undeclared spells report declared: False honestly).
- Parameters:
spell_id -- Optional spell SHA256 at the blast center.
module_name -- Optional canonical module name at the blast center.
set_name -- Research set supplying declared truth for the join.
- Returns:
The analyze_impact payload plus research.
- Return type:
Dict[str, object]
- Raises:
ValueError -- If neither or both center arguments are supplied.
RuntimeError -- If the crystallizer is cleaned or inactive.
KeyError -- If set_name names no research set.
- module_graph_view(spell_id: str) dict[str, object][source]๏
Return one spell's module world as a walkable graph payload.
- Purpose:
The "walk the graph and understand the underlying module impacts" read: every module the spell's recorded world carries, the direct dependency edges between them, the LOCAL reverse edges (who inside this world imports whom), export surfaces, sealed fingerprints, recorded paths, and the topological load order. Cross-record radius stays impact_view's job.
Contract:
Dispatches on kind: a group identity is analyzed as a composition, a plain spell as a single unit.
Rejects a non-string or empty spell_id up front.
- Parameters:
spell_id -- Binding-signature SHA256 whose world to walk.
- Returns:
{"spell_id", "root_module", "modules", "direct_dependencies", "local_importers", "export_surfaces", "fingerprints", "module_paths", "load_order"}.
- Return type:
Dict[str, object]
- Raises:
ValueError -- If spell_id is empty.
RuntimeError -- If the crystallizer is cleaned or inactive.
KeyError -- If no custody crystal exists for the identity.
- source_drift_view() dict[str, object][source]๏
Return the full recorded-vs-disk drift report.
- Purpose:
The "what will my uncommitted edits break" read: every sealed fingerprint re-hashed against the live disk, with a blast radius attached to every module that is not unchanged.
Contract:
REQUIRES LIVE CUSTODY: it reaches through the crystallizer, so it raises when recording custody is unavailable rather than returning an empty report.
Reports drift between recorded sources and current ones; it changes nothing.
- Returns:
The crystallizer's full impact describe (custody counts + drift statuses + radii).
- Return type:
Dict[str, object]
- Raises:
RuntimeError -- If the crystallizer is cleaned or inactive.
- module_view(spell_id: str, module_name: str) dict[str, object][source]๏
Return everything the crystal knows about one module in one call.
- Purpose:
The crystal-well dossier (units-and-scales philosophy 4.1): full source text labeled by kind (synthetic / user / live_disk), sealed fingerprint, recorded path, local dependency edges both ways, export surface, and drift - the single call behind "give me the module data from synthetic or physical modules".
Contract:
Rejects a non-string or empty spell_id AND module_name up front, so both arguments fail fast rather than producing an empty view.
Scoped to ONE module of one spell.
- Parameters:
spell_id -- Binding-signature SHA256 whose world carries the module.
module_name -- Module to gather.
- Returns:
Dossier payload; a module outside the world answers unknown_module: True honestly.
- Return type:
Dict[str, object]
- Raises:
ValueError -- If either argument is empty.
RuntimeError -- If the crystallizer is cleaned or inactive.
KeyError -- If no custody crystal exists for the identity.
- part_view(spell_id: str, part_name: str, *, kind: str | None = None, module_name: str | None = None) dict[str, object][source]๏
Return one named top-level part's text from a version's world.
- Purpose:
The part-grain read: locate a function or class by name across the version's resolvable module texts (recorded-first, live-disk fallback - present-tense rules, like source_view) and return its text, span, and carrying module.
Contract:
Rejects a non-string or empty spell_id AND part_name up front.
Scoped to ONE part, the finest grain of the research views.
- Parameters:
spell_id -- Binding-signature SHA256 whose world to search.
part_name -- Top-level function/class name to locate.
kind -- Optional filter: "function" or "class".
module_name -- Optional single module to search.
- Returns:
Found: {"found": True, "module_name", "source_kind", "drifted", "kind", "start_line", "end_line", "text"}. Missed: {"found": False, "searched_modules", "parse_errors"} - honest, never raising on a miss.
- Return type:
Dict[str, object]
- Raises:
ValueError -- If arguments are empty or kind is unknown.
RuntimeError -- If the crystallizer is cleaned or inactive.
KeyError -- If no custody crystal exists for the identity.
- parts_view(spell_id: str, *, module_name: str | None = None) dict[str, object][source]๏
Return every top-level part of a version's world, with code.
- Purpose:
The class-code inventory (owner ruling 2026-07-11: the agent chooses the grain - module text OR class code): all top-level functions/classes per module, each with its full text and span, without the agent knowing any names up front.
Contract:
Dispatches on kind: a group identity enumerates parts across the composition, a plain spell across itself.
Rejects a non-string or empty spell_id up front.
- Parameters:
spell_id -- Binding-signature SHA256 whose world to inventory.
module_name -- Optional single module to inventory.
- Returns:
{"spell_id", "root_module", "modules": {name: {"source_kind", "drifted", "parts": [rows] | "parse_error" | "text_unavailable"}}, "unknown_module"?}
per-module honesty, never raising on misses.
- Return type:
Dict[str, object]
- Raises:
ValueError -- If spell_id is empty.
RuntimeError -- If the crystallizer is cleaned or inactive.
KeyError -- If no custody crystal exists for the identity.
- part_diff(left_spell_id: str, right_spell_id: str, part_name: str, *, kind: str | None = None, module_name: str | None = None, set_name: str = 'default') dict[str, object][source]๏
Unified text diff of one named part between two versions.
- Purpose:
The class/function-grain comparison the owner asked for - WITH its blast radius. Part texts extract from RECORDED material only (comparison law: the live disk would compare a file with itself and lie about both versions); the radius section is the carrying module's current blast radius joined with research residency (impact stays module-grain per the grain laws - a part's honest radius IS its module's radius).
Contract:
Rejects a non-string or empty id on BOTH sides before any work, so a half-specified diff fails immediately.
Compares one part across two identities and reports differences without reconciling them.
- Parameters:
left_spell_id -- Left version identity.
right_spell_id -- Right version identity.
part_name -- Top-level function/class name to compare.
kind -- Optional filter: "function" or "class".
module_name -- Optional single module to search on both sides.
set_name -- Research set for the impact residency join.
- Returns:
{"left_spell_id", "right_spell_id", "part_name", "left_found", "right_found", "left_module", "right_module", "left_kind", "right_kind", "identical", "unified_diff", "impact"} - absent sides answer honestly (found flags), never raising.
- Return type:
Dict[str, object]
- Raises:
ValueError -- If identities/name are empty or kind is unknown.
RuntimeError -- If the crystallizer is cleaned or inactive.
KeyError -- If either identity has no custody crystal.
- preview_candidate(code: str, *, against_spell_id: str | None = None, module_name: str | None = None, set_name: str = 'default') dict[str, object][source]๏
Mock one candidate codegen and report what would happen next.
- Purpose:
The foresight centerpiece: BEFORE anything executes, binds, or records, answer what the candidate code defines, what it imports, how it differs from the version it would replace, and what blast radius that replacement would have - so an agent can guess what happens next instead of finding out.
Contract:
Read-only: nothing executes, binds, or records.
Unparseable code answers honestly (parse_error row; the analysis/diff/impact sections go None) - previewing broken code is a legitimate question.
With against_spell_id, the candidate text adopts that spell's root module name so the would-be diff compares module universes honestly; the impact section is that root module's current blast radius joined with research residency.
With only module_name, the impact section is that module's radius; with neither, impact is None (nothing to center on).
- Parameters:
code -- Candidate Python source text.
against_spell_id -- Optional current version the candidate would replace.
module_name -- Optional module identity for the candidate when no against-version exists.
set_name -- Research set for the impact residency join.
- Returns:
{"candidate_sha256", "module_name", "parse_error", "defines", "import_roots", "diff", "impact", "against_spell_id"}.
- Return type:
Dict[str, object]
- Raises:
ValueError -- If code is empty.
RuntimeError -- If an against/impact read needs custody and the crystallizer is cleaned or inactive.
KeyError -- If against_spell_id has no custody crystal.
- synthesize_candidate(base_spell_id: str, donor_spell_id: str, *, take_functions: list[str] | None = None, take_classes: list[str] | None = None, stage_ancestry: bool = False, set_name: str = 'default') dict[str, object][source]๏
Surgically compose one candidate from two recorded versions.
- Purpose:
The salvaged May "surgical mutation" verb: take named top-level parts (functions/classes) from the DONOR version's root module and splice them into the BASE version's root module, then run the composed candidate through the full foresight preview (against the base). With stage_ancestry=True, both parents stage for the next world entry, so executing the candidate mints the multi-parent node automatically - compose, preview, execute, and the record keeps the whole story.
Contract:
Read-only (staging is ambient context, not a record write).
Source resolution rides source_view (recorded-first, live-disk fallback); a root module with no resolvable text on either side answers text_unavailable honestly.
Unknown selections refuse loudly (synthesizer law); parse errors on recorded text answer honestly inside the verdict.
- Parameters:
base_spell_id -- Version being upgraded (the candidate starts as its root module text; the preview diffs against it).
donor_spell_id -- Version parts are taken from.
take_functions -- Top-level function names to take.
take_classes -- Top-level class names to take.
stage_ancestry -- Stage [base, donor] as the next world entry's parents.
set_name -- Research set for the preview's residency join.
- Returns:
{"base_spell_id", "donor_spell_id", "parents", "base_module", "donor_module", "selections", "composed_source", "parse_error", "text_unavailable", "ancestry_staged", "preview"}.
- Return type:
Dict[str, object]
- Raises:
ValueError -- If identities are empty, no selection is supplied, or a selection is unknown to the donor.
RuntimeError -- If the crystallizer is cleaned or inactive.
KeyError -- If either identity has no custody crystal.
- group_diff_research(left_group_id: str, right_group_id: str, *, strategy: str = 'members') dict[str, object][source]๏
Compute one derived diff between two recorded compositions.
- Purpose:
The grouped mirror of diff_research, dispatched through the root-owned GroupDiffEngine (its own strategy family - owner ruling 2026-07-11). The default members strategy answers added/removed members and LANE-EVIDENCED version moves; each moved pair descends into diff_research grains (source/structural/parts) on the agent's next call.
Contract:
LAZILY CONSTRUCTS AND CACHES the group diff engine on first use, so the first call pays construction and later calls do not.
Construction happens under the root lock, so concurrent first calls cannot produce two engines.
- Parameters:
left_group_id -- Left composition identity.
right_group_id -- Right composition identity.
strategy -- Registered grouped strategy name; "members" by default.
- Returns:
Detached verdict payload from the owned engine.
- Return type:
Dict[str, object]
- Raises:
RuntimeError -- If either identity is unknown or not a composition.
KeyError -- If the strategy name is unknown.
- group_view(group_id: str, *, set_name: str = 'default') dict[str, object][source]๏
Return one composition's roster with residence and drift truth.
- Parameters:
group_id -- Composition identity to gather.
set_name -- Research set to resolve within.
Contract:
Reports each member's LANE RESIDENCE, and a member with no residence is still reported rather than skipped - absence of a lane is data here, not an omission.
Read-only projection; it does not stage, promote or move anything.
- Threading:
Unsynchronized read of a plain flag; a snapshot only.
- Lifecycle / Cleanup:
Guarded by check_cleaned().
- Raises:
RuntimeError -- If mutation research has been cleaned.
- Returns:
{"group_id", "member_count", "parent_group_ids", "author", "campaign", "created_at", "members": {spell_id: {lane join + "behind": bool|None}}, "behind_count"} - behind is True when the member's lane tip has moved past the pinned version (None when the member is unresident and drift is unknowable).
- Return type:
Dict[str, object]
- register_group(member_spell_ids: list[str], *, lane: str | None = None, parent_group_ids: list[str] | None = None, author: str | None = None, campaign: str | None = None, reason: str | None = None, set_name: str = 'default') GroupedResearchNode[source]๏
Declare one composition WITH the ambient campaign stamp (parity law: compositions registered through the root carry the active campaign exactly as runtime auto-records do).
- Parameters:
member_spell_ids -- Non-empty member identities to pin.
lane -- Optional lane (name or id).
parent_group_ids -- Optional composition ancestry.
author -- Optional registering agent name.
campaign -- Optional explicit stamp (wins over the ambient one).
reason -- Optional reason line.
set_name -- Research set to register into.
Contract:
CAMPAIGN DEFAULTS TO THE AMBIENT ONE. Passing campaign=None does not mean "no campaign" - it falls back to active_campaign. To record a group with no campaign you must clear the ambient one first.
Delegates the actual registration to the named research set, so that set's rules on lanes and parents apply.
- Threading:
Unsynchronized read of a plain flag; a snapshot only.
- Lifecycle / Cleanup:
Guarded by check_cleaned().
- Raises:
RuntimeError -- If mutation research has been cleaned.
- Returns:
The recorded composition node.
- Return type:
GroupedResearchNode
- recompose_group(previous_group_id: str, *, add: list[str] | None = None, remove: list[str] | None = None, author: str | None = None, campaign: str | None = None, reason: str | None = None, set_name: str = 'default') GroupedResearchNode[source]๏
Evolve one composition WITH the ambient campaign stamp.
- Parameters:
previous_group_id -- The composition being evolved.
add -- Member identities to add.
remove -- Member identities to drop.
author -- Optional acting agent name.
campaign -- Optional explicit stamp (wins over the ambient one).
reason -- Optional reason line.
set_name -- Research set to evolve within.
Contract:
CAMPAIGN DEFAULTS TO THE AMBIENT ONE, exactly as in register_group - campaign=None inherits active_campaign rather than meaning none.
Recomposition is expressed as ADD and REMOVE against a previous group rather than as a full replacement, so unlisted members are retained.
Delegates to the named research set.
- Threading:
Unsynchronized read of a plain flag; a snapshot only.
- Lifecycle / Cleanup:
Guarded by check_cleaned().
- Raises:
RuntimeError -- If mutation research has been cleaned.
- Returns:
The new composition node.
- Return type:
GroupedResearchNode
- compositions_of(spell_id: str, *, set_name: str = 'default') list[dict[str, object]][source]๏
Return the current compositions pinning one spell (reverse lift).
- Parameters:
spell_id -- Member identity to look up.
set_name -- Research set to scan.
Contract:
Rejects a non-string or empty spell_id up front with ValueError.
Searches only CURRENT composition tips, so a spell that appears solely in superseded compositions returns nothing. An empty result means "not in any current composition", not "unknown spell".
Resolves each hit's lane residence, so the result carries placement as well as membership.
- Threading:
Unsynchronized read of a plain flag; a snapshot only.
- Lifecycle / Cleanup:
Guarded by check_cleaned().
- Raises:
RuntimeError -- If mutation research has been cleaned.
- Returns:
{"group_id", "lane_name"} rows for every lane-tip composition whose roster pins the identity.
- Return type:
List[Dict[str, object]]
- group_footprint_view(group_id: str, *, set_name: str = 'default') dict[str, object][source]๏
Return one composition's physical shadow (module footprint).
- Purpose:
The union of the pinned members' recorded module worlds - derived at read time, never stored (stored footprints would rot as compositions evolve). The per-module member map exposes SHARED matter: modules carried by more than one member are where the subsystem physically couples to itself.
Contract:
REQUIRES LIVE CUSTODY through the crystallizer; without it this raises rather than returning a partial footprint.
Reports UNKNOWN MEMBERS separately from resolved ones, so an incomplete footprint is visible in the result rather than silently short.
- Parameters:
group_id -- Composition identity to shadow.
set_name -- Research set to resolve within.
- Returns:
{"group_id", "modules", "module_members": {module: [members]}, "shared_modules", "unknown_custody_members"} - members without a custody crystal report honestly instead of raising (the composition is informational; its members may predate custody).
- Return type:
Dict[str, object]
- Raises:
RuntimeError -- If the identity is unknown/not a composition, or the crystallizer is cleaned or inactive.
- group_drift_view(group_id: str, *, set_name: str = 'default') dict[str, object][source]๏
Return recorded-vs-disk drift filtered to one composition's shadow.
- Purpose:
"What is already broken in THIS area": the full custody drift report (sealed fingerprints re-hashed against the live disk) narrowed to the composition's module footprint, with counts recomputed over the narrowed set so the numbers describe the subsystem, not the world.
Contract:
BUILT ON group_footprint_view, so it inherits the live-custody requirement and the unknown-member reporting.
Intersects the footprint's modules with the custody drift report, so a module absent from either side simply does not appear.
- Parameters:
group_id -- Composition identity to check.
set_name -- Research set to resolve within.
- Returns:
{"group_id", "statuses": {module: status}, "radii": {module: radius}, "counts": {status: n}, "footprint_size"}.
- Return type:
Dict[str, object]
- Raises:
RuntimeError -- If the identity is unknown/not a composition, or the crystallizer is cleaned or inactive.
- group_history_view(group_id: str, *, campaign: str | None = None, set_name: str = 'default') dict[str, object][source]๏
Return the journal story of one subsystem area.
- Parameters:
group_id -- Composition identity to gather around.
campaign -- Optional campaign stamp - the WHERE x WHEN join: narrow the area's story to one effort.
set_name -- Research set to resolve within.
Contract:
Straight delegation to the named research set's group history; it adds no filtering or ordering of its own.
campaign is passed through AS GIVEN here - unlike register_group and recompose_group, this one does NOT fall back to the ambient campaign, so None means "unfiltered".
- Threading:
Unsynchronized read of a plain flag; a snapshot only.
- Lifecycle / Cleanup:
Guarded by check_cleaned().
- Raises:
RuntimeError -- If mutation research has been cleaned.
- Returns:
The set's group_history payload (subsystem-lane, member, and member-lane events in journal order).
- Return type:
Dict[str, object]
- recent_activity_view(*, limit: int = 50, set_name: str = 'default') dict[str, object][source]๏
Return the newest journal events across the whole record.
- Purpose:
The cold-landing read: an agent arriving in a room asks "what happened here lately" before choosing where to work - one call, newest-first context, campaign stamps intact.
Contract:
CLAMPS limit to a non-negative integer, so a negative value becomes 0 rather than raising or reading backwards.
Reads the set's journal, which is append-only history rather than current state.
- Parameters:
limit -- Bound on the number of newest entries (the journal's bounded-window read).
set_name -- Research set to read.
- Returns:
{"set_name", "entries", "entry_count", "next_sequence"} - entries in journal order (oldest of the window first).
- Return type:
Dict[str, object]
- group_impact_view(group_id: str, *, set_name: str = 'default') dict[str, object][source]๏
Return one composition's union blast radius with the closure math.
- Purpose:
The crossing move at composition grain: every member's blast radius (custody truth) unioned, split by DIRECTION - internal (consequences landing on fellow members) vs outbound (consequences escaping the composition) - with CLOSURE (the fraction of affected spells that are members; ~1.0 = a safe workspace) and the ADJACENCY lift (which OTHER current compositions the radius touches).
Contract:
REQUIRES LIVE CUSTODY through the crystallizer.
Computes affected modules from the group's MEMBER SET, so a member that resolves to no module contributes nothing rather than failing the call.
- Parameters:
group_id -- Composition identity at the blast center.
set_name -- Research set supplying declared truth for the joins.
- Returns:
{"group_id", "member_count", "affected_modules", "affected_spells", "internal_spells", "outbound_spells", "closure", "affected_compositions", "research", "per_member"}.
- Return type:
Dict[str, object]
- Raises:
RuntimeError -- If the identity is unknown/not a composition, or the crystallizer is cleaned or inactive.