On this page
Conduit categories as factories๏
๐ก Intermediate ยท Lesson 26
Conduit CATEGORIES are your factory layer. Instead of writing an abstract factory in front of your scopes, NAME conduits after the resolution ideas your app already has - "platform", "services", "workflows" - and late-bind the dependencies BETWEEN categories with SpellContract sockets. ORDER OF OPERATIONS IS THE LAW (owner ruling 2026-07-20). Per dependency edge, in this exact order: 1) conjure the PROVIDER conduit 2) conjure the CONSUMER conduit (the SpellContract side) 3) link() - only AFTER both conduits are built 4) the consumer pulls the provider spell into the contract 5) meld AFTER the fact - completing the late binding for that world's products Assemble the chain edge by edge, in dependency order. No factory objects exist anywhere - the conduit NAMES are the factory types. THE ARC: this is beginner lesson 25 one level UP. Spellframes categorize spells WITHIN one world; conduits categorize WORLDS - each category gains an OWNER, and permissions + contracts + links set the resolution conditions at the category boundary.
Before you run๏
Use the Intermediate guide for prerequisite concepts. Run from a checkout with Melder installed and Python 3.14 free-threading selected. The collection download includes the level's local helper modules.
Run the saved script๏
python UX_and_AIX_experiences/02_intermediate/26_conduit_categories_as_factories.py
py -3.14t UX_and_AIX_experiences/02_intermediate/26_conduit_categories_as_factories.py
Public surface๏
md.SpellContract, link, add_spell_to_contract, named category conduits
Code๏
1"""
2TIER: intermediate (26)
3GOAL: Conduit CATEGORIES are your factory layer. Instead of writing an
4 abstract factory in front of your scopes, NAME conduits after the
5 resolution ideas your app already has - "platform", "services",
6 "workflows" - and late-bind the dependencies BETWEEN categories
7 with SpellContract sockets.
8 ORDER OF OPERATIONS IS THE LAW (owner ruling 2026-07-20). Per
9 dependency edge, in this exact order:
10 1) conjure the PROVIDER conduit
11 2) conjure the CONSUMER conduit (the SpellContract side)
12 3) link() - only AFTER both conduits are built
13 4) the consumer pulls the provider spell into the contract
14 5) meld AFTER the fact - completing the late binding for that
15 world's products
16 Assemble the chain edge by edge, in dependency order. No factory
17 objects exist anywhere - the conduit NAMES are the factory types.
18 THE ARC: this is beginner lesson 25 one level UP. Spellframes
19 categorize spells WITHIN one world; conduits categorize WORLDS -
20 each category gains an OWNER, and permissions + contracts + links
21 set the resolution conditions at the category boundary.
22SURFACE EXERCISED: md.SpellContract, link, add_spell_to_contract,
23 named category conduits
24"""
25import sys
26from pathlib import Path
27from typing import Protocol
28
29sys.path.insert(0, str(Path(__file__).parent)) # local helper (see _dynamic_world)
30from _dynamic_world import dynamic_spellbook
31
32import melder as md
33
34
35class ConfigContract(Protocol):
36 def get(self, key: str) -> str: ...
37
38
39class ReportingContract(Protocol):
40 def report(self) -> str: ...
41
42
43class ConfigStore:
44 """Owned by the "platform" category."""
45
46 def get(self, key: str) -> str:
47 return f"{key}-from-platform"
48
49
50class ReportService:
51 """Owned by "services". Its config is a LATE-BOUND hole: the socket
52 names the contract identity; the provider arrives across the link."""
53
54 def __init__(
55 self,
56 config: ConfigContract = md.SpellContract(
57 spellframe=ConfigContract, binding_name="platform",
58 ),
59 ) -> None:
60 self.config = config
61
62 def report(self) -> str:
63 return f"report({self.config.get('region')})"
64
65
66class ReportWorkflow:
67 """Owned by "workflows". Same idea one level up: it wants whatever
68 satisfies the reporting contract, wherever that conduit lives."""
69
70 def __init__(
71 self,
72 service: ReportingContract = md.SpellContract(
73 spellframe=ReportingContract, binding_name="reporting",
74 ),
75 ) -> None:
76 self.service = service
77
78 def run(self) -> str:
79 return f"workflow -> {self.service.report()}"
80
81
82def main() -> None:
83 # One book per CATEGORY - each book is that category's recipe list.
84 platform_book = dynamic_spellbook()
85 config_id = platform_book.bind(
86 spell=ConfigStore, existence="unique",
87 spellframe=ConfigContract, binding_name="platform",
88 )
89 services_book = dynamic_spellbook()
90 service_id = services_book.bind(
91 spell=ReportService, existence="unique",
92 spellframe=ReportingContract, binding_name="reporting",
93 )
94 workflows_book = dynamic_spellbook()
95 workflow_id = workflows_book.bind(spell=ReportWorkflow, existence="unique")
96
97 # ---- EDGE 1: platform feeds services ----
98 platform = platform_book.conjure(dynamic=True, name="platform") # 1) provider
99 services = services_book.conjure(dynamic=True, name="services") # 2) consumer
100 platform.link(services) # 3) link AFTER both
101 services.add_spell_to_contract( # 4) pull
102 spell_id=config_id, conduit=platform, permissions="create",
103 )
104 service = services.meld(spell_id=service_id) # 5) meld after the fact
105
106 # ---- EDGE 2: services feeds workflows (same cycle, one level up) ----
107 workflows = workflows_book.conjure(dynamic=True, name="workflows")
108 services.link(workflows)
109 workflows.add_spell_to_contract(
110 spell_id=service_id, conduit=services, permissions="create",
111 )
112 workflow = workflows.meld(spell_id=workflow_id)
113
114 assert workflow.service is service # the finished product crossed the edge
115 print("category chain resolved:", workflow.run())
116 print("factory types were just conduit names:",
117 platform.name, "->", services.name, "->", workflows.name)
118
119
120if __name__ == "__main__":
121 main()
Check the outcome๏
The script contains its own assertions or demonstrated refusal paths. Run it to evaluate those checks against your installed version. The code above is taken directly from the saved file; no run output is invented here.