Matrix Curator Guide

July 22, 2026 · View on GitHub

How to update existing routing matrices or create new ones.


YAML Schema

Every matrix file has three top-level fields and a roles map:

name: my-matrix
description: "Short description of the matrix's philosophy."
updated: "2026-02-28"

roles:
  general:
    description: "Balanced catch-all for unspecialized tasks"
    candidates:
      - provider: anthropic
        model: claude-sonnet-4-6
      - provider: openai
        model: "gpt-[0-9].[0-9]"

  fast:
    description: "Quick parsing, classification, utility work"
    candidates:
      - provider: anthropic
        model: claude-haiku-4-5

Top-Level Fields

FieldRequiredDescription
nameYesMatrix identifier (matches the filename without .yaml)
descriptionYesOne-line description of this matrix's design philosophy
updatedYesLast update date in YYYY-MM-DD format
rolesYesMap of role names to role definitions

Role Definition

FieldRequiredDescription
descriptionYesWhat this role is for — shown in amplifier routing show
candidatesYesOrdered list of provider/model candidates (tried top-to-bottom)

Candidate Fields

FieldRequiredDescription
providerYesModule type name (e.g., anthropic, openai, gemini) — or a multi-instance id (see Multi-Instance Providers)
modelYesExact model name or glob pattern
configNoOptional map of parameters passed to the provider session config

Provider Names

The provider field uses the module type name, not the full module identifier. Match the name the provider module registers under via coordinator.mount(...) — not the pyproject.toml entry-point key.

Write thisNot this
anthropicprovider-anthropic
openaiprovider-openai
geminiprovider-gemini, google
ollamaprovider-ollama
github-copilotprovider-github-copilot

Note (2026-04-22): Earlier versions of this guide showed google for the Gemini provider. That was wrong — the module mounts under gemini, and provider: google silently failed resolution in every matrix. Fixed in all shipped matrices; update any user-written matrices or settings.yaml overrides that still use google.


Multi-Instance Providers

Users can run multiple instances of the same provider module — for example, two provider-openai instances pointing at different endpoints (one real OpenAI, one internal vLLM server). Configure them via settings.yaml with unique id: values:

# ~/.amplifier/settings.yaml
config:
  providers:
    - module: provider-openai
      id: openai-internal
      config:
        base_url: https://internal-llm.company.com/v1
        api_key: sk-internal-inline
        default_model: codellama-70b

    - module: provider-openai
      id: openai-main
      config:
        base_url: https://api.openai.com/v1
        api_key: ${OPENAI_API_KEY}
        default_model: gpt-5.5

In a matrix, reference a specific instance by its id as the provider: value:

roles:
  coding:
    candidates:
      - provider: openai-internal     # matches coordinator key "openai-internal"
        model: codellama-70b
  reasoning:
    candidates:
      - provider: openai-main         # matches coordinator key "openai-main"
        model: "gpt-[0-9].[0-9]"
        config:
          reasoning_effort: xhigh

How it works: the id: in settings.yaml is mapped to the kernel's instance_id during mount plan assembly, and the provider is re-mounted under that key in coordinator.providers. The resolver's find_provider_by_type does an exact-key match first, so any arbitrary instance id works in the provider: field.

API key caveat: The CLI amplifier provider add wizard currently stores both instances' api_key values in the same env var (e.g. $OPENAI_API_KEY), so two instances via the wizard collide in the keyring. Until that's fixed, authors of multi-instance configs should edit settings.yaml directly and inline the keys (or use distinct env var names per instance).


Model Names: Exact vs Globs

Exact names pin a specific model version:

- provider: anthropic
  model: claude-sonnet-4-6

Use exact names when you want precision — the matrix won't silently shift to a newer model.

Glob patterns match dynamically against available models:

- provider: ollama
  model: "*"              # any model from this provider

- provider: anthropic
  model: claude-sonnet-*  # latest Sonnet variant

Use globs when you want the candidate to auto-match the latest available model. The "*" pattern is useful for providers like Ollama where the user chooses which models to install.


Required Roles

Every matrix must define these two roles:

  • general — the catch-all fallback for agents that don't specify a model role
  • fast — used by utility agents and quick classification tasks

The remaining 11 roles are optional: coding, ui-coding, security-audit, reasoning, critique, creative, writing, research, vision, image-gen, and critical-ops. If an agent requests a role that isn't defined, it falls back through its model_role list until it finds a defined role.


The config Key

The optional config map is passed directly to the provider's session configuration. Use it for model-specific parameters:

- provider: anthropic
  model: claude-opus-4-6
  config:
    reasoning_effort: high

- provider: openai
  model: "gpt-[0-9].[0-9]"
  config:
    reasoning_effort: high

Common values:

KeyValuesEffect
reasoning_effortnone, low, medium, high, xhighControls extended thinking / chain-of-thought depth

Only include config when a candidate genuinely needs different parameters from the provider default. Most candidates don't need it.


Adding a New Role

  1. Add the role definition under roles in each matrix file that should support it:
roles:
  # ... existing roles ...

  my-new-role:
    description: "What this role is for"
    candidates:
      - provider: anthropic
        model: claude-sonnet-4-6
      - provider: openai
        model: "gpt-[0-9].[0-9]"
  1. Update the context file (context/routing-instructions.md) to mention the new role so agents know it exists.

  2. New roles don't need to be added to every matrix — agents using fallback chains will gracefully skip missing roles.


Adding a New Matrix File

  1. Create a new YAML file in the routing/ directory (e.g., routing/local-only.yaml).

  2. Define at least general and fast roles.

  3. Set name to match the filename (without .yaml).

  4. The matrix becomes available immediately via amplifier routing list and amplifier routing use <name>.


Testing Your Matrix

Use the CLI to verify your matrix resolves correctly against installed providers:

# Show resolved roles for the active matrix
amplifier routing show

# Show a specific matrix
amplifier routing show quality

# List all available matrices
amplifier routing list

amplifier routing show displays each role, its resolved provider/model (based on what you have installed), and whether any roles failed to resolve.


The base Keyword (User Overrides)

Users can override specific roles in their settings.yaml without editing matrix files. This is for user configuration, not for matrix files themselves.

# In ~/.amplifier/settings.yaml
routing:
  matrix: balanced
  overrides:
    coding:
      - provider: ollama
        model: codellama:70b
      - base  # append the matrix's original candidates after this

The base keyword tells the routing hook to insert the matrix's candidates for that role at that position. Without base, the override completely replaces the matrix's candidates.

Do not use base in matrix files — it only has meaning in user override configuration.


Complete Role Taxonomy (13 Roles)

The routing system defines 13 roles organized into 5 categories. For full descriptions, fallback chain examples, and decision flowcharts, see context/role-definitions.md.

#RoleCategoryModel TierReasoningDescription
1generalFoundationMid (Sonnet, GPT base)defaultVersatile catch-all, no specialization needed
2fastFoundationCheap (Haiku, GPT luna)defaultQuick utility tasks — parsing, classification, file ops
3codingCodingMid, code-specializeddefaultCode generation, implementation, debugging
4ui-codingCodingMid, code-specializeddefaultFrontend/UI code — components, layouts, styling
5security-auditCodingMid, code-specializedxhighVulnerability assessment, attack surface analysis
6reasoningCognitiveHeavy (Opus, GPT sol)xhighDeep architectural reasoning, system design
7critiqueCognitiveMidextra-highAnalytical evaluation — finding flaws in existing work
8creativeCognitiveHeavy (Opus, GPT sol)defaultDesign direction, aesthetic judgment
9writingCognitiveHeavy (Opus, GPT sol)defaultLong-form content — docs, marketing, case studies
10researchCognitiveHeavy (Opus, GPT sol)highDeep investigation, information synthesis
11visionCapabilityMid, multimodaldefaultUnderstanding visual input — screenshots, diagrams
12image-genCapabilitySpecializeddefaultImage generation, visual mockup creation
13critical-opsOperationalHeavy (Opus, GPT sol)highHigh-reliability operational tasks — infrastructure, orchestration

Sourcing Methodology

Model selection is informed by three complementary data sources, combined with human curation.

Artificial Analysis Benchmarks

Artificial Analysis provides standardized benchmarks across providers. Use their leaderboard to compare:

  • Quality scores (MMLU, HumanEval, GPQA) for capability assessment
  • Speed (tokens/second) for latency-sensitive roles like fast
  • Cost (per million tokens) for budget-conscious matrix variants
  • Context window sizes for roles like research that benefit from long context

StrongDM Weather Report Alignment

The StrongDM Weather Report categorizes AI model capabilities into 14 areas. The following table maps those categories to our 13 roles:

Weather Report CategoryOur RoleNotes
General KnowledgegeneralBroad factual and reasoning tasks
CodingcodingCode generation and debugging
Frontend Developmentui-codingComponents, layouts, CSS
Security Analysissecurity-auditVulnerability scanning, code auditing
System DesignreasoningArchitecture, multi-step planning
Code ReviewcritiqueEvaluating existing work for flaws
Creative WritingcreativeAesthetic direction, brand voice
Technical WritingwritingDocumentation, long-form content
Research & AnalysisresearchInvestigation and synthesis
Image UnderstandingvisionScreenshot and diagram analysis
Image Generationimage-genVisual content creation
DevOps & Infrastructurecritical-opsDeployment, orchestration
Quick Tasks & ClassificationfastParsing, triage, bulk processing
Mathematical ReasoningreasoningMaps to reasoning for deep analysis

Curated Selection

After reviewing benchmark data and weather report alignment, follow this 3-step process:

  1. Shortlist candidates — For each role, identify the top 2-3 models per provider based on benchmark rankings for that role's task type.
  2. Hands-on evaluation — Test shortlisted models against representative prompts from real agent workflows. Benchmarks measure general capability; curation tests task-specific fit.
  3. Rank and commit — Order candidates by preference (best first) in the matrix YAML. The routing hook tries candidates top-to-bottom.

Curation Principles

Pin Model Names

Always use exact, versioned model names in matrix files. Globs are for user overrides and local providers only.

Good ✅ — class-scoped globs for providers whose list_models() is backed by a live API. These auto-track new releases within a class without silently jumping classes. The resolver's version-aware sort (with date-suffix stripping) ensures the highest clean version wins.

Bad ❌ — broad unbounded globs like claude-* or gpt-*. These cross class boundaries (Opus and Haiku both match claude-*) and will silently swap a premium role to a budget model when the provider reorders its list. Avoid them outside of model: "*" for user-managed providers like Ollama.

PatternWhat it matchesWhat it excludes
claude-opus-*all Opus versionsSonnet, Haiku
claude-sonnet-*all Sonnet versionsOpus, Haiku
claude-haiku-*all Haiku versionsOpus, Sonnet
gemini-*-pro-previewPro-tier previews, any generationFlash, Flash-Lite, Image, *-customtools
gemini-*-flash-previewFlash-tier previewsFlash-Lite, Image, TTS
gemini-*-flash-lite-previewFlash-Lite-tier previewsFlash, TTS
gpt-[0-9].[0-9]any single-digit major.minor base (e.g. gpt-5.5, gpt-6.0)all suffix variants (-sol, -terra, -luna, -mini, -pro, -nano), dated snapshots
gpt-?.?-sol*any dotted-version sol / flagship tier (e.g. gpt-5.6-sol)base, terra, mini, nano, luna, pro
gpt-?.?-terra*any dotted-version terra / mid tier (e.g. gpt-5.6-terra)base, sol, mini, nano, luna, pro
gpt-?.?-luna*any dotted-version luna / cheap-fast tier (e.g. gpt-5.6-luna)base, terra, mini, nano, sol, pro
gpt-?.?-mini*any dotted-version mini (e.g. gpt-5.4-mini)base, pro, nano, sol, luna, gpt-5-mini (no dot)
gpt-?.?-nano*any dotted-version nanobase, mini, pro, sol, luna

Pinned names remain appropriate when:

  1. The provider has no static fallback for list_models(). github-copilot raises ProviderUnavailableError if both the SDK and the disk cache are unavailable, so glob resolution fails catastrophically when offline. Keep all github-copilot candidates pinned until the provider gains a fallback.
  2. A specialized model does not follow its family's naming pattern and would be filtered out of list_models(). Example: nano-banana-pro-preview is excluded from gemini's listing (the provider filters to IDs containing "gemini"). Using it as an exact name bypasses list_models() entirely and passes the string directly to the API.
  3. The *-latest aliases. Exact names like gemini-pro-latest are safer than globs because they reach the API even when the listing endpoint is unreachable.

The only universal free-pass glob is model: "*" for providers like Ollama where users choose their own models.

Provider-Specific Naming

Different providers use different naming conventions for the same underlying model. Always use the provider's native format.

Modelanthropicopenaigeminigithub-copilot
Claude Sonnet 4.xclaude-sonnet-* (glob)claude-sonnet-4.6 (pin)
Claude Opus 4.xclaude-opus-* (glob)claude-opus-4.8 (pin)
Claude Haiku 4.xclaude-haiku-* (glob)claude-haiku-4.5 (pin)
GPT mid-tier (terra)gpt-?.?-terra* (glob)pinned, e.g. gpt-5.6-terra
GPT base / pre-5.6 migration fallbackgpt-[0-9].[0-9] (glob)pinned, e.g. gpt-5.5
GPT flagship (sol)gpt-?.?-sol* (glob)pinned, e.g. gpt-5.6-sol
GPT cheap-fast (luna)gpt-?.?-luna* (glob)pinned, e.g. gpt-5.6-luna
GPT-5.x minigpt-?.?-mini* (glob)pinned, e.g. gpt-5.4-mini
Gemini Progemini-*-pro-preview (glob)
Gemini Flashgemini-*-flash-preview (glob)
Gemini Imagenano-banana-pro-preview (exact)

CRITICAL — naming formats: Anthropic uses hyphens (claude-sonnet-4-6) while GitHub Copilot uses dots (claude-sonnet-4.6). Mixing these up causes silent resolution failures — the candidate won't match any installed model.

Model Blacklist

The following models must never appear in any matrix file:

ModelReason
gpt-4.1Deprecated; replaced by gpt-5.x family
claude-opus-4.6-fastNot a real model; confused with claude-opus-4-6
claude-opus-4-6-fastNot a real model; no "fast" variant of Opus exists
gpt-5.2Superseded by gpt-5.4 in the March 2026 rollout
gpt-5.2-proSuperseded by gpt-5.4-pro in the March 2026 rollout
gpt-5.3-codexCodex line consolidated back into base gpt-5.x; no longer a separate model for routing

If a blacklisted model appears in a PR, reject and request replacement with a valid alternative.

Required Roles

Every matrix must define general and fast. All 13 roles should be present in multi-provider matrices (balanced.yaml, quality.yaml, economy.yaml). Single-provider matrices may omit roles that the provider cannot fill.

balanced.yaml as Reference

The balanced.yaml matrix is the canonical reference for model selection. When creating or updating other matrices:

  • Use balanced.yaml as the starting template
  • Verify your matrix covers at least the same roles
  • Deviate from balanced.yaml only where the matrix philosophy demands it (e.g., economy.yaml trades quality for cost)

Deriving Per-Provider Matrices

Single-provider matrices (e.g., anthropic.yaml, openai.yaml) are derived from balanced.yaml using this 5-step process:

  1. Start from balanced.yaml — Copy the full role structure as your starting point.
  2. Filter to one provider — For each role, keep only candidates from the target provider. Remove all other provider entries.
  3. Fill gaps with provider equivalents — If a role has no candidates after filtering, find the provider's closest equivalent model and add it.
  4. Add glob fallback — For roles where the provider may introduce new models, consider adding a glob fallback as the last candidate (e.g., claude-sonnet-*).
  5. Validate with tests — Run python -m pytest tests/ to ensure the matrix passes all structural validation.

When to Refresh

Matrix refreshes are triggered when any of the following occur:

  • A major model release from any provider (e.g., new Claude or GPT version)
  • A model deprecation or retirement announcement
  • Significant benchmark score changes on Artificial Analysis
  • A new Weather Report edition from StrongDM
  • User-reported resolution failures or quality regressions
  • Quarterly scheduled review (even if no triggers)

Follow this 6-step refresh process:

  1. Review triggers — Identify what changed and which roles are affected.
  2. Benchmark check — Compare the new model against current candidates on Artificial Analysis.
  3. Update balanced.yaml first — Make changes in the canonical reference matrix.
  4. Propagate to derived matrices — Update quality.yaml, economy.yaml, and single-provider matrices following their respective philosophies.
  5. Run the full test suite — Execute python -m pytest tests/ across all test files.
  6. Update the updated date — Set the updated field in every modified matrix to today's date.

Checklist for Matrix Updates

  • general and fast roles are defined
  • updated field reflects today's date
  • provider uses module type names (not full module identifiers)
  • Candidates are ordered by preference (best first)
  • config is only present where needed
  • No blacklisted models (see Model Blacklist)
  • No duplicate candidates within the same role
  • Provider-specific naming correct (anthropic hyphens, copilot dots)
  • Tests pass: python -m pytest tests/
  • Verified with amplifier routing show