# Model Profiles

Model profiles control which Claude model each GMD agent spawn uses. Selection is governed by a **universal profile × complexity table** — a single 9-cell matrix that replaces the earlier per-agent ceiling table.

Agent type intentionally drops out of the primary resolution path: the orchestrator author chooses complexity at each spawn site (the only place where blast-radius is actually known), and the active profile scales that decision across haiku / sonnet / opus. See [model-profile-resolution.md](./model-profile-resolution.md) for the CLI + resolution flow.

**Source of truth:** `bin/lib/core.cjs` — look for the `COMPLEXITY_MODEL` constant (currently around lines 21–25). This document mirrors that table; if the two disagree, `core.cjs` wins.

## Profiles

Three profiles control how aggressively GMD spends tokens on reasoning:

| Profile | Haiku ever? | Opus ever? | When to use |
|---|---|---|---|
| **quality** | **Never** — simple tasks still get sonnet | Yes, for complex work | Critical architecture work, when quota is not the concern |
| **balanced** (default) | Yes, for simple tasks | Yes, for complex work | Normal development; full haiku → opus range |
| **budget** | Yes, for simple tasks | **Never** — complex work caps at sonnet | High-volume work, cost pressure, less critical phases |

**Behavioral promises** (these are load-bearing — the test harness regression-guards them):

1. `quality` profile **never drops to haiku.** Even `simple` tasks resolve to sonnet.
2. `budget` profile **never reaches opus.** Even `complex` tasks cap at sonnet.
3. `balanced` covers the full haiku → sonnet → opus range.

## Complexity Tiers

Every spawn site declares one of three complexity tiers. The tier describes the **blast radius of being wrong** at that spawn point, not the difficulty of the task.

| Tier | Meaning | Typical examples |
|---|---|---|
| `simple` | Low blast radius, constrained scope, mostly pattern work | Incremental codebase reindex, single-file verification, quick 1-task plan execution |
| `standard` | Normal task size; default when complexity is omitted | Most `resolve-model` calls; phase verification; plan-checker against one plan |
| `complex` | High blast radius — a mistake here poisons downstream work | Foundational codebase map at project kickoff, planning a multi-wave phase, integration audit at milestone boundary |

**Default:** If a caller omits the complexity tag, the resolver uses `'standard'`. This keeps rollout additive — any legacy call site without an explicit tag behaves as it did pre-retrofit.

**Vocabulary alignment:** The tier names match Phase 5 cross-model validation timeout tiers (60s / 180s / 300s) on purpose — one vocabulary across subsystems.

## The 9-Cell Matrix

This is the universal table. Every resolve-model call without a `model_overrides` entry flows through this lookup.

| Profile \ Complexity | simple | standard | complex |
|---|---|---|---|
| **quality** | sonnet | sonnet | opus\* |
| **balanced** | haiku | sonnet | opus\* |
| **budget** | haiku | sonnet | sonnet |

\* `opus` resolves to the literal string `"inherit"` so the parent session's opus version is used — this avoids org-policy conflicts with specific opus model versions and is the historical behavior preserved across the retrofit.

The JavaScript shape in `bin/lib/core.cjs` is literally:

```js
const COMPLEXITY_MODEL = {
  quality:  { simple: 'sonnet', standard: 'sonnet', complex: 'opus'   },
  balanced: { simple: 'haiku',  standard: 'sonnet', complex: 'opus'   },
  budget:   { simple: 'haiku',  standard: 'sonnet', complex: 'sonnet' },
};
```

Reading the table:

- **Row = profile** (set once per project via `.planning/config.json` or `/gmd:set-profile`)
- **Column = complexity tier** (set per spawn site by the workflow author)
- **Cell = resolved model** (`haiku`, `sonnet`, or `opus` → `"inherit"`)

## Escape Hatch: `model_overrides`

`model_overrides` pins a specific agent to a specific model, bypassing the 9-cell table entirely. This is the single documented escape hatch for "I know what I want — don't be clever."

```json
{
  "model_profile": "balanced",
  "model_overrides": {
    "gmd-executor": "opus",
    "gmd-planner": "haiku"
  }
}
```

**Resolution precedence:** overrides are checked **first**. When an override matches, the complexity tag is ignored entirely. This means:

- If `model_overrides["gmd-executor"] = "opus"` is set, every `gmd-executor` spawn resolves to `"inherit"` (opus alias) regardless of whether the spawn site declared `--complexity simple` or `--complexity complex`.
- If no override exists for the agent, resolution falls through to the 9-cell table.

Valid override values: `opus`, `sonnet`, `haiku`. `opus` still resolves through the `opus → inherit` alias on the way out.

**Why per-agent overrides (and not per-complexity overrides):** Per-complexity overrides (e.g. `{"gmd-executor": {"complex": "opus"}}`) were explicitly rejected during Phase 04.1 — they reintroduce per-agent ceilings through a config side door and duplicate the spawn-site blast-radius thinking that complexity tagging already provides.

## Switching Profiles

**Runtime:** `/gmd:set-profile <profile>` — writes to `.planning/config.json`, takes effect for the next spawned agent.

**Per-project default:** set in `.planning/config.json`:

```json
{
  "model_profile": "balanced"
}
```

If the key is missing, the resolver defaults to `balanced`.

## Design Rationale

**Why a universal table instead of per-agent ceilings?**
Because complexity is declared explicitly at each spawn site, the workflow author is already thinking about blast radius *for this particular call*. A per-agent ceiling table adds a second layer of decisions that duplicates that thinking and goes stale whenever new agents are added. Under the universal rule, new agents (including Phase 7 self-authored skills) inherit the same 9 cells with zero config.

**Why "quality never haiku" and "budget never opus"?**
These are the hard promises of the profile labels. If `quality` could drop to haiku for simple tasks, the profile name would be misleading — a user picking `quality` is saying "token budget is not the concern here." Symmetrically, `budget` capping at sonnet guarantees cost predictability even when a workflow declares `--complexity complex` by mistake.

**Why `inherit` instead of passing `opus` literally?**
Claude Code's `"opus"` alias maps to a specific model version. Organizations may block older opus versions while allowing newer ones. GMD returns `"inherit"` for opus-tier results, causing the spawned agent to use whatever opus version the parent session has — avoiding version conflicts and silent fallbacks to sonnet.

**Why `standard` as the default?**
Most work is standard. Making it the default keeps legacy 2-arg callers (`resolveModelInternal(cwd, agent)`) working unchanged during incremental rollout, and shifts the cognitive cost of tagging onto the cases that genuinely deviate from "normal task size."
