How the Delegation Enforcer Protocol Prevents Improper Subagent Routing in oh-my-claudecode

The delegation enforcer protocol acts as pre-tool-use middleware that validates, normalizes, and sanitizes every agent or task invocation before it reaches the routing layer, ensuring only properly configured Claude subagents with valid model parameters are executed.

The oh-my-claudecode framework by Yeachan-Heo provides a robust multi-agent architecture where complex workflows are distributed across specialized subagents. At the heart of this system lies the delegation enforcer protocol, implemented in src/features/delegation-enforcer.ts, which serves as a critical safety gatekeeper that intercepts routing requests to prevent malformed, deprecated, or incompatible subagent invocations from ever reaching the execution layer.

The Three-Step Enforcement Pipeline

The delegation enforcer protocol operates through a coordinated three-step process that transforms raw tool invocations into sanitized, routable commands. Each step targets a specific class of routing error that could otherwise crash the delegation system or send requests to invalid endpoints.

Step 1: Canonicalizing Subagent Types with canonicalizeSubagentType()

The first line of defense ensures that every subagent reference uses a valid, canonical role name. The canonicalizeSubagentType() function strips any missing oh-my-claudecode: prefix, normalizes deprecated role aliases via normalizeDelegationRole() defined in src/features/delegation-routing/types.ts, and re-adds the required namespace prefix if absent.

This mechanism eliminates routing failures caused by outdated role names such as build-fixer (rewritten to debugger) or quality-reviewer (rewritten to code-reviewer). By guaranteeing that the tool receives a canonical role name with the proper oh-my-claudecode: namespace, the routing resolver in src/features/delegation-routing/resolver.ts never encounters unknown or misspelled identifiers.

Step 2: Enforcing Model Parameters via enforceModel()

The second enforcement layer prevents the "model missing" errors that would otherwise abort delegations. The enforceModel() function injects the default model from the agent definition unless the user explicitly set one, the configuration forces inheritance via routing.forceInherit, or a model alias such as OMC_MODEL_ALIAS_HAIKU overrides the default.

This function also normalizes full model IDs—such as converting claude-sonnet-4-6 to the short canonical alias sonnet—ensuring compatibility with downstream providers. This prevents sending unsupported Claude model IDs to non-Claude providers like AWS Bedrock or Google Vertex, which would reject the request.

Step 3: Pre-Tool Use Pipeline Integration through processPreToolUse()

The final layer integrates these validations into the execution pipeline through the processPreToolUse() hook. Registered as a pre-tool middleware, this function first checks isAgentCall() to verify that only Agent or Task tool types are processed. If the check passes, it runs the canonicalization and model enforcement functions, rewrites the input payload, logs debug warnings when OMC_DEBUG=true, and returns the sanitized object.

Any non-agent tools—such as Bash or Read—pass through untouched, eliminating accidental routing of inappropriate tool calls. This guarantees that only valid agent calls ever reach the delegation router.

Source Code Architecture and Key Files

The delegation enforcer protocol spans three critical source files that work in concert to maintain routing integrity:

Practical Implementation Examples

Normalizing Deprecated Role Aliases

When legacy code or user input references outdated role names, the enforcer automatically rewrites them to current canonical forms:

import { processPreToolUse } from './features/delegation-enforcer.js';

const rawInput = {
  description: 'Run lint',
  prompt: 'Check code quality',
  subagent_type: 'quality-reviewer', // deprecated alias
};

const { modifiedInput } = processPreToolUse('Task', rawInput);
console.log(modifiedInput.subagent_type);
// → "oh-my-claudecode:code-reviewer"

Automatic Model Injection for Default Executors

If a user omits the model parameter, the enforcer injects the appropriate default from the subagent definition:

const input = {
  description: 'Generate a README',
  prompt: 'Write a concise README.md',
  subagent_type: 'executor',
};

const { modifiedInput } = processPreToolUse('Agent', input);
console.log(modifiedInput.model);
// → "sonnet"   // injected from executor definition

Force-Inheritance for Non-Claude Providers

When detecting non-Claude providers, the enforcer strips incompatible models to force inheritance of the user-configured provider model:

process.env.ANTHROPIC_MODEL = 'glm-5'; // forces inherit mode
const input = {
  description: 'Summarise logs',
  prompt: 'Provide a brief summary',
  subagent_type: 'executor',
  model: 'sonnet',
};

const { modifiedInput } = processPreToolUse('Agent', input);
console.log(modifiedInput.model); // undefined

Configuration Options and Environment Controls

The protocol respects several configuration layers that modify its behavior:

  • OMC_DEBUG=true – Enables verbose logging of delegation rewrites and model injections
  • routing.forceInherit – When enabled, removes explicit model specifications to ensure subagents inherit the parent's provider configuration
  • routing.modelAliases – Maps custom identifiers to canonical model names like haiku, sonnet, or opus

Summary

  • The delegation enforcer protocol in src/features/delegation-enforcer.ts acts as mandatory pre-tool middleware that sanitizes all subagent invocations.
  • Canonicalization via canonicalizeSubagentType() eliminates deprecated aliases and ensures proper namespace prefixes.
  • Model enforcement through enforceModel() prevents missing model errors and normalizes identifiers for cross-provider compatibility.
  • Tool type validation using isAgentCall() restricts processing to Agent and Task tools only, preventing accidental routing of non-delegation tools.
  • The system integrates with src/features/delegation-routing/resolver.ts to guarantee that only valid, fully-configured Claude subagents receive execution requests.

Frequently Asked Questions

What happens if an unknown subagent role is provided?

If normalizeDelegationRole() encounters a role not listed in DEPRECATED_ROLE_ALIASES or the canonical role registry, the middleware throws a clear error identifying the invalid role name. This prevents the request from reaching the resolver and potentially triggering a runtime failure or routing to a non-existent subagent.

How does the delegation enforcer protocol handle non-Claude providers like Bedrock or Vertex?

When the system detects a non-Claude provider through environment variables like ANTHROPIC_MODEL, the enforceModel() function automatically enables routing.forceInherit mode. This strips any Claude-specific model identifiers from the payload, ensuring the subagent inherits the provider-compatible model configuration instead of sending unsupported model IDs that would be rejected by AWS Bedrock or Google Vertex.

Can I disable the delegation enforcer protocol?

The delegation enforcer is implemented as core middleware in the pre-tool-use pipeline and cannot be disabled without modifying the source code in src/features/delegation-enforcer.ts. This design ensures routing integrity remains protected across all tool invocations, preventing accidental misconfigurations that could disrupt the agent workflow.

What is the difference between Agent and Task calls in the enforcer?

Both Agent and Task tool types are processed by the enforcer through the isAgentCall() validation, but they represent different delegation patterns. Agent calls typically initiate long-running subagent sessions with state, while Task calls represent single-shot executions. The enforcer applies the same canonicalization and model enforcement rules to both, but the resolver in src/features/delegation-routing/resolver.ts handles their lifecycle management differently after sanitization.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →