Skip to content

Build a New Substrate Package

Use this guide when you need to add a new substrate package to a repo.

You will produce a package that:

  1. Exposes launch(domain) at package boundary.
  2. Uses RAS and one or more synthesizers to produce ModulatorResult objects, including ModulatorSuccess child results with artifact-ref values.
  3. Uses Neuromodulators to resolve those references into concrete modulators, with the resulting transmissions arriving through the substrate terminal before cocktail injection.
  4. Returns a runtime compatible with substrate-core contracts.

1) Define launch-target metadata in the domain model

Section titled “1) Define launch-target metadata in the domain model”

In your cortex or substrate config entry, define:

  1. launch.launcher
  2. launch.target.module-path
  3. launch.target.symbol-name

Use values that resolve to the actual package entrypoint.

Launcher responsibilities:

  1. Resolve dispatch strategy.
  2. Enforce launch result contract.

Adapter responsibilities:

  1. Import module from module-path.
  2. Resolve symbol-name.
  3. Invoke launch(domain).

Do not put substrate composition logic into either component.

3) Define lamina structure and synthesizer responsibilities

Section titled “3) Define lamina structure and synthesizer responsibilities”

For a CliSensoryCortex-style package, keep the inheritance structure explicit:

  1. BaseLamina
  2. AgenticLamina
  3. CortexLamina
  4. SensoryMotorCortexLamina
  5. SensoryCortexLamina

Then define which synthesizers can satisfy the modulators those laminae require.

Create runtime identity once and propagate unchanged through the cocktail and runtime.

Never regenerate instance identity in a synthesizer or downstream runtime layer.

The launch function should:

  1. Wire RAS, Neuromodulators, and register the required synthesizers.
  2. Request the modulators needed by the concrete substrate.
  3. Resolve returned artifact-ref values from Neuromodulators, receive the results through a terminal registered to NeuralRelay, and inject them into the cocktail.
  4. Return final runtime.

Avoid embedding policy branching or side effects in launch wrapper.

Add tests for:

  1. launch(domain) returns Substrate-compatible runtime.
  2. Invalid launch target metadata fails with clear errors.
  3. Missing modulator requests fail with clear errors.
  4. Identity is stable across all modulators.

Package init should export launch and, if useful, leaf runtime type.

Keep internal synthesizers and substrate wiring non-public unless a separate extension seam is intentionally supported.

from cerebel_substrate_core import Substrate, SubstrateDomain
def launch(domain: SubstrateDomain) -> Substrate:
# Wire RAS and local synthesizers.
# Request needed modulators and return the runtime.
...
  1. Launcher dispatch and substrate-specific modulation mixed in one class.
  2. RAS hard-coding which synthesizer owns which modulator.
  3. Identity rebuilt at multiple layers.
  4. launch(domain) performing unrelated orchestration policy.
  5. Skipping artifact-ref resolution and attempting to treat relay payload as a concrete modulator instance.

You are done when:

  1. Package launches through generic launcher using launch target metadata.
  2. All substrate and package tests pass.
  3. Runtime handle includes definition_id, instance_id, healthy, and lifecycle_state.