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

> Discover how the delegation enforcer protocol in oh-my-claudecode prevents improper subagent routing. Learn how it validates and sanitizes agent invocations for secure Claude subagent execution.

- Repository: [Bellman/oh-my-claudecode](https://github.com/Yeachan-Heo/oh-my-claudecode)
- Tags: internals
- Published: 2026-03-27

---

**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`](https://github.com/Yeachan-Heo/oh-my-claudecode/blob/main/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`](https://github.com/Yeachan-Heo/oh-my-claudecode/blob/main/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`](https://github.com/Yeachan-Heo/oh-my-claudecode/blob/main/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:

- **[`src/features/delegation-enforcer.ts`](https://github.com/Yeachan-Heo/oh-my-claudecode/blob/main/src/features/delegation-enforcer.ts)** – Contains the complete enforcement pipeline including `canonicalizeSubagentType()`, `enforceModel()`, and `processPreToolUse()`.
- **[`src/features/delegation-routing/types.ts`](https://github.com/Yeachan-Heo/oh-my-claudecode/blob/main/src/features/delegation-routing/types.ts)** – Defines the `DEPRECATED_ROLE_ALIASES` mapping and the `normalizeDelegationRole()` function used for alias rewriting.
- **[`src/features/delegation-routing/resolver.ts`](https://github.com/Yeachan-Heo/oh-my-claudecode/blob/main/src/features/delegation-routing/resolver.ts)** – The downstream router that consumes sanitized payloads and guarantees that only valid Claude Task calls with compatible subagents are dispatched.

## 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:

```typescript
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:

```typescript
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:

```typescript
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`](https://github.com/Yeachan-Heo/oh-my-claudecode/blob/main/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`](https://github.com/Yeachan-Heo/oh-my-claudecode/blob/main/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`](https://github.com/Yeachan-Heo/oh-my-claudecode/blob/main/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`](https://github.com/Yeachan-Heo/oh-my-claudecode/blob/main/src/features/delegation-routing/resolver.ts) handles their lifecycle management differently after sanitization.