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
computeCostfor 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.