# How Model Profiles Are Resolved Across GSD Agents: A Complete Guide

> Learn how GSD Build resolves model profiles across agents. Discover the orchestration startup process, overrides, and consistent AI tier usage for your sub-agents.

- Repository: [GSD/get-shit-done](https://github.com/gsd-build/get-shit-done)
- Tags: deep-dive
- Published: 2026-02-16

---

**GSD-Build resolves the model profile once at orchestration startup by reading [`.planning/config.json`](https://github.com/gsd-build/get-shit-done/blob/main/.planning/config.json), applying optional per-agent overrides, and mapping the result to concrete Claude models via a lookup table, ensuring consistent AI tier usage across all spawned sub-agents.**

The `gsd-build/get-shit-done` repository implements a deterministic **model profile resolution** system that governs which Claude model tier each agent uses during workflow execution. Understanding this resolution mechanism is essential for optimizing both performance and API costs across complex multi-agent orchestrations.

## The Three-Step Model Profile Resolution Process

GSD-Build follows a strict three-step pipeline to determine which model every agent will use. This process is documented in [`get-shit-done/references/model-profile-resolution.md`](https://github.com/gsd-build/get-shit-done/blob/main/get-shit-done/references/model-profile-resolution.md) and executed once at the beginning of each orchestration run.

### Step 1: Reading the Project-Wide Configuration

The system first attempts to read the `model_profile` field from [`.planning/config.json`](https://github.com/gsd-build/get-shit-done/blob/main/.planning/config.json). If the file is missing or the field is absent, it defaults to `balanced`.

```bash
MODEL_PROFILE=$(cat .planning/config.json 2>/dev/null \
  | grep -o '"model_profile"[[:space:]]*:[[:space:]]*"[^"]*"' \
  | grep -o '"[^"]*"$' | tr -d '"' || echo "balanced")

```

This resolution happens **once** and the value is cached for the lifetime of the run.

### Step 2: Applying Per-Agent Overrides

If the configuration contains a `model_overrides` map, the system checks for an entry matching the specific agent being spawned. This allows fine-grained control without changing the global profile.

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

```

In this example, `gsd-executor` would use the **Opus** tier despite the global `balanced` setting, while `gsd-planner` would use **Haiku**.

### Step 3: Mapping to Concrete Models

Finally, the system consults the lookup table defined in [`get-shit-done/references/model-profiles.md`](https://github.com/gsd-build/get-shit-done/blob/main/get-shit-done/references/model-profiles.md) to translate the abstract profile name into a specific Claude model identifier.

| Agent | quality | balanced | budget |
|-------|---------|----------|--------|
| gsd-planner | opus | opus | sonnet |
| gsd-executor | opus | sonnet | sonnet |

For agents that normally run on **Opus-tier** models, GSD-Build substitutes the special value `"inherit"` instead of a hardcoded model name. This ensures the agent inherits the specific Opus version configured for the user's session, avoiding organization-level blocks on older Opus versions.

## How the Resolution Pattern Ensures Consistency

The **Resolution Pattern** documented in [`get-shit-done/references/model-profile-resolution.md`](https://github.com/gsd-build/get-shit-done/blob/main/get-shit-done/references/model-profile-resolution.md) mandates that model profile resolution occurs exactly once at orchestration startup. This design prevents model switching mid-workflow, which could cause context window inconsistencies or unexpected API cost spikes.

When the orchestrator spawns sub-agents, it passes the resolved model value through the `model` parameter of the `Task` constructor:

```python
resolved_model = resolve_model_profile(agent_type)   # executes steps 1-3

Task(
    prompt="Analyze the codebase structure...",
    subagent_type="gsd-planner",
    model=resolved_model   # "inherit", "sonnet", "haiku", etc.

)

```

This cached approach ensures that a `balanced` profile selected at startup remains `balanced` for every agent spawned during that run, regardless of when the Task is created.

## Configuring Model Profiles in Practice

### Resolving the Profile in Bash Scripts

For custom tooling that interacts with GSD-Build, you can implement the same resolution logic used by the core system:

```bash
#!/bin/bash

# Resolve once at orchestration start

MODEL_PROFILE=$(cat .planning/config.json 2>/dev/null \
  | grep -o '"model_profile"[[:space:]]*:[[:space:]]*"[^"]*"' \
  | grep -o '"[^"]*"$' | tr -d '"' || echo "balanced")

echo "Using model profile: $MODEL_PROFILE"
export MODEL_PROFILE

```

### Spawning Agents with Profile-Aware Models

When implementing custom workflow steps, map the abstract profile to concrete models using the same logic as [`get-shit-done/references/model-profiles.md`](https://github.com/gsd-build/get-shit-done/blob/main/get-shit-done/references/model-profiles.md):

```python

# Pseudo-code used in workflow files

def get_planner_model(profile):
    if profile == "quality":
        return "inherit"  # Inherits user's Opus version

    elif profile == "balanced":
        return "sonnet"
    else:  # budget

        return "haiku"

planner_model = get_planner_model(os.environ["MODEL_PROFILE"])

Task(
    prompt="Plan the next milestone...",
    subagent_type="gsd-planner",
    model=planner_model
)

```

### Using Model Overrides for Specific Agents

To force a specific agent to use a different tier without changing the global profile, edit [`.planning/config.json`](https://github.com/gsd-build/get-shit-done/blob/main/.planning/config.json):

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

```

The orchestrator checks `model_overrides` before consulting the profile table, so `gsd-executor` will use **Opus** while other agents use the standard **balanced** mappings.

## Summary

- **GSD-Build resolves model profiles once** at orchestration startup and caches the result for the entire workflow run.
- The resolution follows a **three-step pipeline**: read [`.planning/config.json`](https://github.com/gsd-build/get-shit-done/blob/main/.planning/config.json), apply optional `model_overrides`, and map to concrete models via [`get-shit-done/references/model-profiles.md`](https://github.com/gsd-build/get-shit-done/blob/main/get-shit-done/references/model-profiles.md).
- **Default profile** is `balanced` when no configuration is found.
- **Per-agent overrides** allow specific agents to use different model tiers without affecting the global profile.
- **Special "inherit" value** prevents Opus-tier agents from being blocked by organization policies, allowing them to use the user's configured Opus version.

## Frequently Asked Questions

### Where is the model profile configuration stored?

The primary configuration lives in [`.planning/config.json`](https://github.com/gsd-build/get-shit-done/blob/main/.planning/config.json) at the project root. This JSON file contains the `model_profile` field (defaulting to `balanced` if missing) and optionally a `model_overrides` map for per-agent customizations. The resolution logic is documented in [`get-shit-done/references/model-profile-resolution.md`](https://github.com/gsd-build/get-shit-done/blob/main/get-shit-done/references/model-profile-resolution.md).

### Can I override the model profile for specific agents only?

Yes. Add a `model_overrides` object to [`.planning/config.json`](https://github.com/gsd-build/get-shit-done/blob/main/.planning/config.json) with keys matching agent names (e.g., `gsd-executor`, `gsd-planner`). These entries take precedence over the global `model_profile` when spawning those specific agents, allowing you to force high-quality models for critical agents while keeping others on budget tiers.

### What happens if the config.json file is missing?

If [`.planning/config.json`](https://github.com/gsd-build/get-shit-done/blob/main/.planning/config.json) is absent or unreadable, the system defaults to the `balanced` profile. This fallback behavior ensures workflows continue running without manual configuration, using mid-tier models defined in the [`get-shit-done/references/model-profiles.md`](https://github.com/gsd-build/get-shit-done/blob/main/get-shit-done/references/model-profiles.md) lookup table.

### How does GSD-Build handle Opus-tier model restrictions?

For agents that normally require Claude Opus, GSD-Build uses the special value `"inherit"` instead of a hardcoded model name. This allows the agent to inherit the specific Opus version configured for the user's session, preventing organization-level blocks that might occur if the system requested a specific older Opus version. This behavior is defined in the model profiles reference documentation.