# Aether config two doors **🟠 Advanced · Lesson 07** TWO DOORS TO ONE CONFIG, AND THE LADDER BEHIND THEM. Aether hands you two ways to build its root configuration: aether.create_configuration() -> AetherConfiguration aether.create_configuration_builder() -> AetherConfigurationBuilder They are NOT redundant, and they do NOT end in the same place by accident - they end in the same place ON PURPOSE: config.with_*(...).finalize() -> FROZEN builder.with_*(...).build() -> FROZEN finalize() freezes and returns THE SAME OBJECT. build() freezes and HANDS OVER OWNERSHIP - it is one-shot and consuming, so the builder is spent afterwards. Reach for the builder when construction happens somewhere that should not keep a handle on the result. THE PART EVERYONE GETS WRONG: FROZEN IS NOT READY. Freezing and activating are TWO SEPARATE STATE BITS, and this is the single most confusing thing about melder's configuration model until you see it written down: frozen - "no more edits" (finalize / build / freeze) activated - "in force" (activate) A config can sit frozen for a long time before anything turns it on. So the ladder is three rungs, in this order, and the order is a RULE rather than a convention: 1. finalize() or build() -> frozen 2. configuration.activate() -> activated 3. aether.activate(config) -> Aether itself comes up Skipping rung 2 raises. Aether's own contract says so in capitals: "THE CONFIGURATION MUST BE ACTIVATED BEFORE AETHER CAN BE." The two failure modes stay distinct on purpose - "not configured" and "configuration not activated" are different sentences because they are different bugs. RUNG 3 IS NOT PERFORMED HERE, DELIBERATELY. Aether is a process-wide singleton, and `aether.activate(cfg)` installs the config BEFORE it checks the activated bit - so even the refusing call mutates the world. A lesson that shares an interpreter with every other lesson must not do that. The refusal is pinned in pytest_examples/test_advanced_probes.py, where the reset fixture owns a clean singleton. ## Before you run Use the [Advanced guide](../../advanced/index.md) for prerequisite concepts. Run from a checkout with Melder installed and Python 3.14 free-threading selected. The collection download includes the level's local helper modules. ## Run the saved script ```bash python UX_and_AIX_experiences/03_advanced/07_aether_config_two_doors.py ``` ```powershell py -3.14t UX_and_AIX_experiences/03_advanced/07_aether_config_two_doors.py ``` {download}`Download this collection <../../downloads/advanced-examples.zip>` · [Source on GitHub](https://github.com/Synaptic724/melder/blob/1211e838099a342c9dde48f064b1e772b6060e13/UX_and_AIX_experiences/03_advanced/07_aether_config_two_doors.py) ## Public surface md.Aether().create_configuration, create_configuration_builder, md.AetherConfiguration, md.AetherConfigurationBuilder, the freeze/activate split ## Code ```{literalinclude} ../../downloads/03_advanced/07_aether_config_two_doors.py :language: python :linenos: ``` ## Check the outcome The script contains its own assertions or demonstrated refusal paths. Run it to evaluate those checks against your installed version. The code above is taken directly from the saved file; no run output is invented here. [More advanced examples](index.md) · [Level guide](../../advanced/index.md) ## Related guides - [World posture and configuration](../../advanced/posture.md) - [Explicit logging setup](../../advanced/logging.md) ## API contracts - [Aether](../../reference/api/configuration/aether.md) - [AetherConfiguration](../../reference/api/configuration/aetherconfiguration.md) - [AetherConfigurationBuilder](../../reference/api/configuration/aetherconfigurationbuilder.md)