Build Your First Broadcast-Orchestrated Substrate
This tutorial walks you through building a new substrate package in the same style as a CliSensoryCortexSubstrate.
You will build:
- A package boundary launch function with the canonical signature.
- A
RAS-driven orchestration path that requests the modulators your substrate needs. - A synthesizer that satisfies one of those requests stores the modulator in
Neuromodulatorsand returns aModulatorSuccesschild result with anartifact-ref. - A concrete substrate type that satisfies the substrate contract.
Before you start
Section titled “Before you start”You should already know how to run Python tests in your repo and use uv.
Required concepts:
- Substrate domain contracts from cerebel-substrate-core.
- Domain bundle contracts from cerebel-contracts.
Step 1: Create the package skeleton
Section titled “Step 1: Create the package skeleton”Create a new package with a launch entrypoint, a substrate module, and a synthesizer module:
src/my_new_substrate/ __init__.py launch.py synthesizers.py substrate.pyIn __init__.py, export launch:
from .launch import launch
__all__ = ["launch"]Step 2: Add a substrate class
Section titled “Step 2: Add a substrate class”Implement a concrete substrate class that inherits from Substrate and reads the injected cocktail:
from __future__ import annotations
from cerebel_substrate_core import ( ModulatorCocktail, Substrate, SubstrateHandle, SubstrateLifecycleState,)
class CliSensoryCortexSubstrate(Substrate): def __init__(self, *, context: ModulatorCocktail) -> None: super().__init__(definition_id=context.domain.definition_id) self._context = context
@property def cortex_role(self) -> str: return self._context.modulators["cortex_role"]
def handle(self) -> SubstrateHandle: return SubstrateHandle( definition_id=self.definition_id, instance_id=self.instance_id, healthy=self.health(), lifecycle_state=SubstrateLifecycleState.READY, )Step 3: Implement a synthesizer
Section titled “Step 3: Implement a synthesizer”Model your synthesizer after the biological factory pattern:
RASsubmits a request toNeuromodulators.- The synthesizer decides whether it can satisfy that request.
- If so, it creates the modulator, stores it in
Neuromodulators, and returns aModulatorSuccesswithartifact-ref.
Minimal example:
from __future__ import annotations
from cerebel_substrate_core import Modulator
from neural_relay import ModulatorRequest, ModulatorResult, ModulatorSuccess, Synthesizer
class CortexLaminaSynthesizer(Synthesizer): def __init__(self, neuromodulators: NeuromodulatorsForSynthesizer) -> None: self._neuromodulators = neuromodulators
def can_handle(self, modulator_name: str) -> bool: return modulator_name == "cortex_role"
def synthesize(self, request: ModulatorRequest) -> ModulatorResult: modulator = Modulator(name="cortex_role", value="sensory") artifact_ref = self._neuromodulators.add("cortex-role", modulator) return ModulatorSuccess( request_id=request.payload.request_id, synthesizer_id="cortex-lamina-synthesizer", artifact_ref=artifact_ref, )Step 4: Wire the package launch function
Section titled “Step 4: Wire the package launch function”Your package boundary should expose:
def launch(domain: SubstrateDomain) -> Substrate: ...Example wiring:
from __future__ import annotations
from cerebel_substrate_core import ModulatorCocktail, Substrate, SubstrateDomain
from neural_relay import ModulatorRequest, RAS
from cerebel_runtime import Neuromodulators
from .synthesizers import CortexLaminaSynthesizerfrom .substrate import CliSensoryCortexSubstrate
def launch(domain: SubstrateDomain) -> Substrate: neuromodulators = Neuromodulators() neuromodulators.register_synthesizer(CortexLaminaSynthesizer(neuromodulators)) ras = RAS(neuromodulators=neuromodulators)
cocktail = ModulatorCocktail(domain=domain) response = ras.request_modulator(ModulatorRequest(modulator_name="cortex_role")) role = neuromodulators.get(response.payload.artifact_ref) cocktail = cocktail.update_modulator(role.name, role.value) return CliSensoryCortexSubstrate(context=cocktail)Step 5: Add launch target configuration
Section titled “Step 5: Add launch target configuration”Define a cortex entry in your Domain Bundle with launch target metadata:
kind: cortexdefinition-id: comm.clirole: commsdescription: CLI comms cortexlifecycle-policy: dynamiclaunch: launcher: inprocess target: module-path: my_new_substrate symbol-name: launchThe launcher resolves this metadata and calls launch(domain).
Step 6: Validate behavior
Section titled “Step 6: Validate behavior”Write tests for three boundaries:
- Contract tests: launch returns a Substrate-compatible substrate instance.
- Modulation tests: the expected synthesizers satisfy the expected requests.
- Failure tests: missing synthesizers or unsatisfied requests fail clearly.
Step 7: Verify with a launch sequence
Section titled “Step 7: Verify with a launch sequence”The expected call flow:
sequenceDiagram
participant Caller
participant Launcher
participant Adapter
participant Pkg as launch(domain)
participant RAS
participant Neuromodulators
participant Synthesizer
participant Terminal
participant Relay
participant Cocktail
participant Substrate
Caller->>Launcher: launch(domain)
Launcher->>Adapter: dispatch
Adapter->>Pkg: invoke
Pkg->>RAS: register Synthesizers
Pkg->>RAS: request_modulator(cortex_role)
RAS->>Neuromodulators: ModulatorRequest
Neuromodulators->>Synthesizer: fanout ModulatorRequest
Synthesizer->>Neuromodulators: add(modulator)
Neuromodulators-->>RAS: ModulatorSuccess(artifact-ref)
RAS->>Neuromodulators: get(artifact-ref)
RAS->>Terminal: send transmission
Terminal->>Relay: publish transmission
Relay-->>Terminal: distribute transmission
Terminal-->>Cocktail: modulator arrives
Cocktail-->>Substrate: configure runtime
Substrate-->>Pkg: substrate instance
Pkg-->>Adapter: substrate instance
Adapter-->>Launcher: substrate instance
Launcher-->>Caller: substrate instance
You built a substrate package
Section titled “You built a substrate package”You now have the same high-level composition shape used by the current Cerebel architecture:
- One launch entrypoint.
- Neuromodulators-mediated RAS orchestration.
- Synthesizer-driven modulator production.
- Substrate contract compliance.
Next step: follow How to Build a New Substrate Package for production hardening and release checklist guidance.