This guide shows how to add a new role that agents can declare. Roles are abstract responsibilities (orchestrator, worker, scout, reasoner) that map to model tiers at install time.
A Role is defined by these fields (stored in YAML):
| Field | Type | Required | Purpose | Example |
|---|---|---|---|---|
name |
string | Yes | Unique identifier (lowercase, no spaces) | "specialist" |
label |
string | Yes | Display name (title case) | "Specialist" |
description |
string | Yes | What this role does (1-2 sentences, used by wizard) | "Deep expertise in specific domains. Medium reasoning." |
typical_class |
string | Yes | Hint for wizard defaults | "sonnet", "opus", "haiku", "flash" |
color |
string | No | Display color in UI | "orange", "blue", "green" |
Key concepts:
-
name— Identifier used in:agents.yaml:lead: { role: specialist }- Wizard step 2:
Specialist (...)picker - Commands:
agent-notes set role specialist claude-sonnet-4 - state.json:
role_models: {specialist: "claude-sonnet-4"}
-
typical_class— Hints which model class the wizard should default-select for this role:"opus"→ high reasoning, planning, architecture"sonnet"→ balanced reasoning + execution (most roles)"haiku"→ fast, low reasoning, exploration"flash"→ ultra-fast, minimal reasoning (new tier)
The wizard shows models with matching
classas the[*]default during step 2. -
description— Shown in:- Wizard step 2:
Specialist (Deep expertise...)— helps user understand what the role is for agent-notes list rolesoutput- Documentation (e.g., this guide)
- Wizard step 2:
-
color(optional) — May be used by future UI enhancements. Current values:red,blue,green,yellow,purple,orange,pink,cyan.
Create agent_notes/data/roles/specialist.yaml:
name: specialist
label: Specialist
description: Deep expertise in specific domains. Medium reasoning, high accuracy.
typical_class: sonnet
color: orangeField explanations:
-
name: specialist— Used in agents.yaml asrole: specialist.- Must be lowercase, no spaces.
- Typically matches a job description (coder, planner, explorer, debugger, specialist).
-
label: Specialist— Shown in UI. Title case, human-readable. -
description: ...— Short explanation. Wizard shows this in step 2:Specialist (Deep expertise in specific domains. Medium reasoning, high accuracy.): 1) [*] Claude Sonnet 4 (via anthropic) 2) [ ] Claude Opus 4.7 -
typical_class: sonnet— When user selects CLIs and proceeds to step 2:- Wizard filters models to those compatible with each CLI
- Groups them by class
- Checks model(s) with
class: sonnetby default - User can uncheck and select different models
This is just a hint — users can override.
-
color: orange— Hint for future UI (e.g., status line indicators, role badges). No effect today.
python3 -c "import yaml; yaml.safe_load(open('agent_notes/data/roles/specialist.yaml'))"
# Should produce no output (success).python3 << 'EOF'
from agent_notes.role_registry import load_role_registry
registry = load_role_registry()
role = registry.get("specialist")
print(f"Name: {role.name}")
print(f"Label: {role.label}")
print(f"Description: {role.description}")
print(f"Typical class: {role.typical_class}")
print(f"Color: {role.color}")
EOFExpected output:
Name: specialist
Label: Specialist
Description: Deep expertise in specific domains. Medium reasoning, high accuracy.
Typical class: sonnet
Color: orange
Once the role exists in the registry, assign agents to it by updating agent_notes/data/agents/agents.yaml.
# agent_notes/data/agents/agents.yaml
agents:
lead:
role: orchestrator # existing agent
description: "Plans and delegates..."
mode: primary
claude: { tools: "..." }
specialist: # NEW agent
role: specialist # use the new role
description: "Domain expert for complex implementation details"
mode: primary
claude:
tools: "read, write, edit, bash"
memory: userOr reassign an existing agent:
coder:
role: specialist # changed from "worker"
description: "..."Agents in agents.yaml need:
role: <role-name>— Must exist indata/roles/(e.g.,specialist)description— Required; shown in UI and agent frontmatter<cli_name>— Per-CLI config (optional)- Example:
claude: { tools: "...", memory: "..." } - Replaces old
tier:field (removed in Phase 9)
- Example:
Example with multiple CLIs:
lead:
role: orchestrator
description: "Team lead, delegates and plans"
mode: primary
claude:
tools: read, write, edit, bash, grep, glob, webfetch
memory: user
opencode:
permission: acceptEditspython3 << 'EOF'
import yaml
from agent_notes.role_registry import load_role_registry
# Load agents
agents_yaml = "agent_notes/data/agents/agents.yaml"
with open(agents_yaml) as f:
agents_data = yaml.safe_load(f)
agents_config = agents_data.get('agents', {})
# Load roles
registry = load_role_registry()
role_names = registry.names()
# Check
missing = []
for agent_name, agent in agents_config.items():
role = agent.get('role')
if role and role not in role_names:
missing.append((agent_name, role))
if missing:
print("ERROR: Agents reference non-existent roles:")
for agent, role in missing:
print(f" {agent} → {role}")
else:
print("✓ All agents reference valid roles")
print(f" {len(agents_config)} agents, {len(role_names)} roles")
EOFExpected output:
✓ All agents reference valid roles
14 agents, 5 roles
agent-notes list rolesExpected output includes your new role:
Roles (5):
orchestrator Plans and delegates (typical: opus)
reasoner Deep debugging (typical: opus)
scout Fast discovery (typical: haiku)
specialist Domain expert (typical: sonnet)
worker Implements code (typical: sonnet)
agent-notes list agentsShows which agents use which roles:
Agents (15):
lead role: orchestrator
debugger role: reasoner
explorer role: scout
specialist role: specialist ← your agent using new role
coder role: worker
...
agent-notes installStep 1: Select Claude Code
Step 2: Model selection should show all roles including the new one:
Orchestrator (Plans and delegates — typical: opus):
1) [*] Claude Opus 4.7
Specialist (Domain expert for complex implementation details — typical: sonnet):
1) [ ] Claude Haiku 4.5
2) [*] Claude Sonnet 4
3) [ ] Claude Opus 4.7
Worker (Implements code — typical: sonnet):
...
Notice specialist defaults to Sonnet (because typical_class: sonnet).
-
Different model strategy needed — The role needs a model class that no existing role targets.
- Example: Add
analystwithtypical_class: sonnetif workers default to haiku but you want medium-reasoning analysis.
- Example: Add
-
Multiple agents need the same responsibility — If 3+ agents do similar work, group them under one role for consistency.
- Example:
lead,tech-lead,architect→ all could be roleorchestrator
- Example:
-
Clear business meaning — The role maps to a real job title or responsibility.
- Good:
orchestrator,specialist,researcher - Avoid:
tier-2,fast-model,expert-v2
- Good:
-
Per-agent override needed — If one agent needs a different model, use state.json hand-edits or Phase 10's
set rolecommand with CLI-specific targeting.- Current: Can't set per-agent model in
agents.yaml. Roles are the per-CLI grouping unit.
- Current: Can't set per-agent model in
-
Just a naming tweak — Renaming "worker" to "coder" is fine but doesn't need a new role if the model strategy is the same.
-
Capability-based filtering — Roles are NOT for "vision-capable" vs "non-vision". That's future work (Phase 12+).
Problem: You create specialist.yaml but no agent has role: specialist.
Symptom: Role appears in agent-notes list roles but never shows in wizard step 2.
Solution: Update agents.yaml to assign agents to the role. At least one agent must have role: specialist.
Problem: You set typical_class: ultrafast but no models have class: ultrafast.
Symptom: Wizard doesn't default-check any model for this role.
Solution: Use existing classes: opus, sonnet, haiku, flash. To use a new class, also add models with that class (see docs/ADD_MODEL.md).
Problem: You move all agents from "worker" to "specialist", but build expects some agents in "worker".
Symptom: Build doesn't fail (roles are just declarations), but UI/docs mention "worker" and it has no agents.
Solution: Decide on your role taxonomy upfront. If removing "worker", either:
- Delete
worker.yaml, OR - Keep it but leave at least one agent assigned to it (for docs/UI consistency)
Problem: You write typical-class (hyphen) instead of typical_class (underscore).
Symptom: Loader crashes with ValueError: Missing field 'typical_class'.
Solution: Required fields (double-check):
name, label, description, typical_class
Optional fields:
color
Problem: You create role: system or role: default, conflicting with Python keywords or Ansible conventions.
Symptom: No immediate error, but confusing for users and maintainers.
Solution: Use specific, business-meaningful names:
- Good:
orchestrator,specialist,researcher,explorer - Avoid:
default,system,main,admin(too generic)
- Created
agent_notes/data/roles/specialist.yaml - All required fields present:
name,label,description,typical_class -
typical_classmatches a value used by existing models (e.g.,opus,sonnet,haiku) -
nameis lowercase, no spaces - Assigned at least one agent to the role in
agents.yaml(viarole: specialist) - Ran
agent-notes list rolesand saw the new role - Ran
agent-notes list agentsand saw agents using the new role - Ran
agent-notes installand wizard step 2 shows the new role with model picker - (Optional) Updated CLI_CAPABILITIES.md or internal docs with role description
-
Test the full installation with the new role:
agent-notes install # Step 1: Select Claude Code # Step 2: Confirm new role appears and defaults correctly # Step 3: Pick Global # Step 4: Pick Symlink (recommended for testing) # Confirm and proceed
-
Inspect generated agents to verify the role→model mapping:
head -n 5 ~/.claude/agents/specialist.md # Should show: model: claude-sonnet-4
-
Phase 10+: Use
set rolecommand (when available):agent-notes set role specialist claude-opus-4-7 --cli claude # Updates state.json and regenerates affected agents