Series 1 — The Behavioral Intelligence OS • Part 2 of 8

The engine registry is the behavioral AI platform's process table. Every engine declares its identity, dependencies, cost, and activation state. This article walks through the full metadata schema and shows how engines self-register.

The Problem

Without a registry, adding a new behavioral engine means editing orchestration code. Removing one means hunting for every call site. Restricting one to a specific team requires branching logic scattered across the codebase. The registry eliminates all of that: the control plane reads it at runtime, and engine state changes take effect without a deployment.

The Engine Definition Schema

interface EngineDefinition {
  engineId:         string;          // Unique, kebab-case. e.g. 'bayesian-confidence'
  displayName:      string;          // Human-readable
  category:         EngineCategory;  // 'foundational'|'cognitive'|'temporal'|'memory'|...
  domain:           string;          // 'cross-domain'|'legal'|'sales'|'trust'
  riskLevel:        'low'|'medium'|'high'|'critical';
  computeCost:      number;          // 1–10; used by scheduler for resource budgeting
  dependencies:     string[];        // engineIds that must run before this one
  emitsTopics:      string[];        // events this engine publishes to the bus
  subscribesTopics: string[];        // events this engine consumes from the bus
  activationState:  ActivationState; // 'active'|'restricted'|'research-only'|'deprecated'
  researchOnly:     boolean;         // if true, output is logged but never returned to adapters
  restrictedToTeams?: string[];      // only relevant when activationState === 'restricted'
  version:          string;          // semver — used for audit logs
}

Every field is mandatory except restrictedToTeams. The registry validates the definition at registration time and throws if any required field is missing or if a declared dependency does not exist.

Self-Registration Pattern

Engines do not need to be manually listed anywhere. Each engine file calls registerEngine() when it is first imported. Node.js module loading handles the rest.

// engines/temporal/state-machine.engine.js
import { registerEngine } from '../../core/registry.js';
import { scoreStateTransitions } from './state-machine.logic.js';

registerEngine({
  engineId:         'state-machine-runtime',
  displayName:      'Behavioral State Machine Runtime',
  category:         'temporal',
  domain:           'cross-domain',
  riskLevel:        'medium',
  computeCost:      4,
  dependencies:     ['bayesian-confidence'],        // needs confidence scores first
  emitsTopics:      ['state.transition.detected'],
  subscribesTopics: ['confidence.updated'],
  activationState:  'active',
  researchOnly:     false,
  version:          '1.2.0',
});

export async function score(signals, context) {
  return scoreStateTransitions(signals, context);
}

Dependency Resolution

The control plane builds a directed acyclic graph (DAG) from the dependencies field, then runs a topological sort to produce execution batches. Engines in the same batch have no inter-dependencies and run in parallel.

function buildExecutionPlan(engines) {
  const graph  = buildDAG(engines);
  const sorted = topologicalSort(graph);  // Kahn's algorithm
  return groupIntoBatches(sorted);        // engines with in-degree 0 form a batch
}
// If a cycle is detected, topologicalSort throws — caught at startup, never at runtime.

What to Watch For

  • Version drift — Always bump the engine version when scoring logic changes. Audit logs use it to explain why the same input produced different outputs on different dates.
  • Compute budget — Sum computeCost for all active engines and set a hard cap. The scheduler can drop low-priority engines if the total exceeds the budget for a request.
  • Research-only to active promotions — Treat them as a release event. Run the engine in research mode for at least 2 weeks before activating it.