# How the Magnitude Agent Runtime Handles Worker Specialization Through Roles

> Learn how the Magnitude agent runtime uses roles to specialize workers. Discover how it maps roles to slots, enforces security, and spawns specialized agents with unique toolkits and lifecycle hooks.

- Repository: [Magnitude/magnitude](https://github.com/magnitudedev/magnitude)
- Tags: internals
- Published: 2026-09-08

---

**Magnitude's agent runtime uses a dedicated roles package to define worker capabilities, map roles to execution slots, and enforce security policies through a centralized registry that spawns specialized workers with specific toolkits and lifecycle hooks.**

The `magnitudedev/magnitude` repository implements a sophisticated worker specialization system where distinct **RoleDefinition** objects encapsulate everything a worker needs to operate. The agent runtime consumes these definitions to instantiate specialized agents with appropriate permissions, prompts, and execution contexts.

## Role Definitions and Structure

Each worker specialization begins with a `RoleDefinition` object that declaratively specifies the worker's purpose, constraints, and behavior.

### The RoleDefinition Interface

A complete role definition includes an identifier, description, prompt template, security policy array, and lifecycle messages. In [`packages/roles/src/roles/engineer.ts`](https://github.com/magnitudedev/magnitude/blob/main/packages/roles/src/roles/engineer.ts), the `createEngineerRole()` function returns a definition that implements this interface:

```typescript
export function createEngineerRole(): RoleDefinition {
  return {
    id: 'engineer',
    description: 'Implements code changes',
    prompt: definePrompt<'SKILLS_SECTION' | 'THINKING_LIMIT' | 'CHECKPOINT_SECTION'>(engineerPromptRaw),
    defaultRecipient: 'coordinator',
    agentKind: 'worker',
    spawnable: true,
    maxThoughtChars: 20000,
    policy: [
      denyForbiddenCommands(),
      denyMutatingGit(),
      denyWritesOutside(ctx => [ctx.cwd, ctx.scratchpadPath, join(homedir(), '.magnitude')]),
      denyMassDestructiveIn(ctx => [join(homedir(), '.magnitude')]),
      allowAll(),
    ],
    lifecycle: {
      coordinatorOnSpawn: 'If there are other independent changes to make, spawn additional engineers in parallel.',
      coordinatorOnIdle: "Review the engineer's work for correctness and quality.",
    },
    initialContext: { coordinatorConversation: true },
  }
}

```

The **policy** array defines security boundaries through composable restriction functions, while the **lifecycle** object provides contextual instructions for the coordinator agent when spawning or checking idle workers.

## Role Registration and Discovery

The runtime discovers available specializations through a centralized catalog that aggregates all role definitions.

### Aggregating Roles via createRoles()

In [`packages/roles/src/index.ts`](https://github.com/magnitudedev/magnitude/blob/main/packages/roles/src/index.ts), the `createRoles()` function instantiates every role definition and returns a record keyed by `RoleId`. This catalog serves as the single source of truth for available worker types:

```typescript
export function createRoles(): Record<RoleId, RoleDefinition> {
  return {
    engineer: createEngineerRole(),
    leader: createLeaderRole(),
    critic: createCriticRole(),
    // Additional roles...
  }
}

```

The agent runtime imports this function to build its internal representation of available specializations.

### Validating Role Identifiers

The [`packages/roles/src/constants.ts`](https://github.com/magnitudedev/magnitude/blob/main/packages/roles/src/constants.ts) file exports `ROLE_IDS`, a runtime array of all valid role identifiers. The agent uses the `isRoleId()` type guard imported from `@magnitudedev/roles` to validate strings against this canonical list before attempting to spawn workers.

## Mapping Roles to Execution Slots

Worker specialization extends beyond behavior definitions to physical execution scheduling through slot assignment.

### ROLE_TO_SLOT Constants

The constants file defines `ROLE_TO_SLOT`, a mapping that assigns each role identifier to a specific execution slot (such as `primary` or `secondary`). This allows the runtime to schedule resource-intensive roles on appropriate hardware:

```typescript
// packages/roles/src/constants.ts
export const ROLE_TO_SLOT = {
  engineer: 'primary',
  critic: 'secondary',
  leader: 'primary',
} as const;

```

### Slot-Based Toolkit Assignment

In [`packages/agent/src/tools/toolkits.ts`](https://github.com/magnitudedev/magnitude/blob/main/packages/agent/src/tools/toolkits.ts), the runtime imports `ROLE_TO_SLOT` to compose tool sets appropriate to the worker's assigned slot. Workers receive different capabilities based on whether they occupy primary or secondary slots:

```typescript
import { ROLE_TO_SLOT } from '@magnitudedev/roles'

function getToolsForRole(roleId: RoleId) {
  const slot = ROLE_TO_SLOT[roleId];
  return mergeToolkits(defaultToolkit, slotSpecificToolkits[slot]);
}

```

## Runtime Execution and Worker Spawning

The agent runtime transforms static role definitions into active worker processes through a coordinated registration and spawning pipeline.

### The Role Registry

[`packages/agent/src/agents/registry.ts`](https://github.com/magnitudedev/magnitude/blob/main/packages/agent/src/agents/registry.ts) initializes the runtime's role awareness by importing the roles package and building an internal registry:

```typescript
import { createRoles, isRoleId, type RoleId, type RoleDefinition } from '@magnitudedev/roles'

class RoleRegistry {
  private roles: Record<RoleId, RoleDefinition>;
  
  constructor() {
    this.roles = createRoles();
  }
  
  getRole(id: RoleId): RoleDefinition | undefined {
    if (isRoleId(id)) {
      return this.roles[id];
    }
    return undefined;
  }
}

```

This registry validates role identifiers using `isRoleId()` and provides type-safe access to `RoleDefinition` objects.

### Spawning Workers by Role

The execution manager in [`packages/agent/src/execution/execution-manager.ts`](https://github.com/magnitudedev/magnitude/blob/main/packages/agent/src/execution/execution-manager.ts) orchestrates the instantiation process. When the coordinator decides to parallelize work, it queries the registry for a spawnable role, resolves its slot via `ROLE_TO_SLOT`, and creates the worker process:

```typescript
import { ROLE_TO_SLOT } from '@magnitudedev/roles'

class ExecutionManager {
  spawnWorker(roleId: RoleId) {
    const role = roleRegistry.getRole(roleId);
    if (!role?.spawnable) {
      throw new Error(`Role ${roleId} is not spawnable`);
    }
    
    const slot = ROLE_TO_SLOT[roleId];
    return this.createWorkerProcess({ role, slot });
  }
}

```

### Enforcing Role Policies

Once spawned, workers operate under their role's security constraints. The [`packages/agent/src/execution/permission-gate.ts`](https://github.com/magnitudedev/magnitude/blob/main/packages/agent/src/execution/permission-gate.ts) module evaluates the `policy` array using the `evaluatePolicy` function from the roles package:

```typescript
import { evaluatePolicy, type RoleDefinition } from '@magnitudedev/roles'

function checkPermission(role: RoleDefinition, action: Action): boolean {
  return evaluatePolicy(role.policy, action);
}

```

This ensures that an **engineer** role cannot execute forbidden shell commands or mutate Git history, as specified in its policy definition.

## Creating Custom Worker Roles

Developers can extend the specialization system by defining new roles and registering them in the catalog. Here is a complete example implementing a "reviewer" role:

```typescript
// packages/roles/src/roles/reviewer.ts
import { definePrompt } from '../prompt'
import { allowAll } from '../policy'
import type { RoleDefinition } from '../types'

export function createReviewerRole(): RoleDefinition {
  return {
    id: 'reviewer',
    description: 'Reviews code changes and suggests improvements',
    prompt: definePrompt<'SKILLS_SECTION'>('You are a reviewer...'),
    defaultRecipient: 'coordinator',
    agentKind: 'worker',
    spawnable: true,
    maxThoughtChars: 15000,
    policy: [allowAll()],
    lifecycle: {
      coordinatorOnSpawn: 'Spawn additional reviewers if many PRs are pending.',
      coordinatorOnIdle: 'Ask the reviewer for feedback on recent changes.',
    },
    initialContext: { coordinatorConversation: true },
  }
}

```

Register the new role in [`packages/roles/src/index.ts`](https://github.com/magnitudedev/magnitude/blob/main/packages/roles/src/index.ts):

```typescript
export function createRoles(): Record<RoleId, RoleDefinition> {
  return {
    engineer: createEngineerRole(),
    reviewer: createReviewerRole(), // Added
    // ...other roles
  }
}

```

## Summary

- **RoleDefinition** objects encapsulate worker specialization through id, description, prompt templates, security policies, and lifecycle hooks defined in `packages/roles/src/roles/`.
- The **createRoles()** function in [`packages/roles/src/index.ts`](https://github.com/magnitudedev/magnitude/blob/main/packages/roles/src/index.ts) aggregates all available roles into a type-safe catalog consumed by the agent runtime.
- **ROLE_TO_SLOT** mapping in [`packages/roles/src/constants.ts`](https://github.com/magnitudedev/magnitude/blob/main/packages/roles/src/constants.ts) assigns roles to execution slots, enabling resource-aware scheduling.
- The **RoleRegistry** in [`packages/agent/src/agents/registry.ts`](https://github.com/magnitudedev/magnitude/blob/main/packages/agent/src/agents/registry.ts) validates role identifiers using `isRoleId()` and provides runtime access to role metadata.
- The **ExecutionManager** spawns workers by looking up role definitions, verifying `spawnable` status, and resolving slots through `ROLE_TO_SLOT`.
- **Permission-gate enforcement** via `evaluatePolicy` in [`packages/agent/src/execution/permission-gate.ts`](https://github.com/magnitudedev/magnitude/blob/main/packages/agent/src/execution/permission-gate.ts) applies role-specific security constraints to all worker actions.

## Frequently Asked Questions

### What is a RoleDefinition in Magnitude?

A `RoleDefinition` is a TypeScript interface that declaratively specifies a worker's capabilities, constraints, and operational context. It includes an identifier, prompt template, security policy array, lifecycle messages for coordinator interaction, and execution metadata like `maxThoughtChars` and `spawnable` status. According to the source code in [`packages/roles/src/types.ts`](https://github.com/magnitudedev/magnitude/blob/main/packages/roles/src/types.ts), these definitions enable the agent runtime to instantiate workers with specific behavioral guarantees.

### How does the agent runtime validate role identifiers?

The runtime uses the `isRoleId()` type guard function exported from `@magnitudedev/roles` to check if a string matches the `ROLE_IDS` array defined in [`packages/roles/src/constants.ts`](https://github.com/magnitudedev/magnitude/blob/main/packages/roles/src/constants.ts). The `RoleRegistry` in [`packages/agent/src/agents/registry.ts`](https://github.com/magnitudedev/magnitude/blob/main/packages/agent/src/agents/registry.ts) performs this validation before retrieving `RoleDefinition` objects, ensuring only registered specializations can be instantiated.

### What is the difference between ROLE_IDS and ROLE_TO_SLOT?

`ROLE_IDS` is a runtime array containing all valid role identifier strings, used for validation and iteration. `ROLE_TO_SLOT` is a mapping object that assigns each role identifier to an execution slot (such as `primary` or `secondary`). While `ROLE_IDS` answers "what roles exist," `ROLE_TO_SLOT` answers "where should this role execute," enabling the runtime to allocate appropriate computational resources and toolkits.

### How are security policies enforced for specialized workers?

Security policies defined in a role's `policy` array are enforced by the permission gate in [`packages/agent/src/execution/permission-gate.ts`](https://github.com/magnitudedev/magnitude/blob/main/packages/agent/src/execution/permission-gate.ts). This module imports `evaluatePolicy` from `@magnitudedev/roles` and applies it to every sensitive action. The policy array uses composable functions like `denyForbiddenCommands()`, `denyMutatingGit()`, and `allowAll()` to create layered security restrictions specific to each worker specialization.