Agent Runtime Profiles
An agent runtime profile is a named YAML file that bundles harness, model, and effort settings for each build tier into a reusable unit. Switching profiles changes how eforge executes builds without touching eforge/config.yaml.
Profile anatomy
Each profile lives at one of three scope tiers and contains an agents.tiers block:
# eforge/profiles/pi-anthropic.yaml
description: Anthropic models via Pi on OpenRouter.
whenToUse:
- General-purpose feature work
- Full-stack changes requiring review depth
tags:
- pi
- anthropic
agents:
tiers:
planning:
harness: pi
model: anthropic/claude-opus-4-6
effort: high
pi:
provider: openrouter
implementation:
harness: pi
model: anthropic/claude-sonnet-4-6
effort: medium
pi:
provider: openrouter
review:
harness: pi
model: anthropic/claude-opus-4-6
effort: high
pi:
provider: openrouter
evaluation:
harness: pi
model: anthropic/claude-opus-4-6
effort: high
pi:
provider: openrouterRequired fields per tier: harness, model, effort.
Optional per tier: pi.provider (required when harness: pi), pi.resources (isolated by default, or ambient to opt into ambient Pi resources), pi.thinkingLevel, pi.extensions, pi.compaction, pi.retry, thinking (boolean, enables extended thinking), fallbackModel, maxTurns, allowedTools, disallowedTools, promptAppend, toolbelt (named MCP bundle or none), and tier-local runtime choices/routing.
Metadata fields (description, whenToUse, tags) are descriptive only - they surface in list and show commands but do not affect runtime behavior.
Scope tiers
Profiles live at three scope directories, resolved highest-precedence-first:
| Scope | Directory | Committed? | Precedence |
|---|---|---|---|
| Project-local | .eforge/profiles/ |
No (gitignored) | Highest |
| Project | eforge/profiles/ |
Yes | Middle |
| User | ~/.config/eforge/profiles/ |
No | Lowest |
When two profiles share the same name, the project-local version wins over project, which wins over user.
The active profile is tracked by a marker file at the matching scope:
.eforge/.active-profile- project-local markereforge/.active-profile- project marker~/.config/eforge/.active-profile- user marker
The daemon resolves the active profile in the same precedence order: project-local marker first, then project marker, then user marker, then no profile (engine defaults apply).
Create a profile
Use /eforge:profile-new in Claude Code or /eforge:profile:new in Pi to create a profile through a guided wizard. The wizard walks through:
- Scope - project-local, project, or user
- Name - e.g.
pi-anthropic,local-qwen,mixed - Tier configuration - harness, model, effort per tier (planning, implementation, review, evaluation)
- Toolbelt preset (optional) - focused MCP server access for UI, docs, or database work
- Activation - optionally make the new profile active immediately
There is no standalone CLI profile wizard today. The host skills call the daemon's eforge_profile MCP tool with action: "create" after collecting the tier recipes.
Profile names must match [A-Za-z0-9._-]+.
Switch the active profile
/eforge:profile <name>
On success, the daemon writes the active-profile marker at project scope (eforge/.active-profile) by default. Ask the skill to use local or user scope when you need the marker at .eforge/.active-profile or ~/.config/eforge/.active-profile instead.
The standalone CLI does not have profile-management subcommands yet, but build enqueue supports a one-off override:
eforge build --profile pi-anthropic "Add rate limiting"The next build picks up the new active profile immediately - no daemon restart needed.
Inspect the active profile
/eforge:profile
Reports the active profile name, source (local, project, or user), resolved harness, metadata (description, tags), and per-tier toolbelt assignments if configured.
The same command also lists all available profiles. Output includes name, scope, harness, description, and a marker for the active profile.
Profile precedence over other selection mechanisms
The active profile sets the baseline. Other mechanisms can override it in specific contexts:
- Explicit
--profileflag -eforge build --profile <name>or the enqueueprofilefield overrides the active-profile marker for that single build. - PRD frontmatter
profile:- a profile set directly in a PRD file takes absolute precedence; no profile router is consulted. - Playbook
profile:frontmatter - applied byeforge-playbooks:run-playbookbefore generic queue handoff; it overrides the active-profile marker and any profile router for autonomous runs and is included in eforge-plan handoff metadata for planning runs. See Playbooks. - Registered profile router - an extension can register a
selectBuildProfilefunction that selects a profile per-PRD from queue context. Routers run only when no explicit profile is set in the PRD frontmatter. See Extensions API - registerProfileRouter. - Active-profile marker - the fallback when no higher-precedence mechanism applies.
- Engine defaults - used when no profile is configured at all.
When a profile router selects a profile, a queue:profile:selected event is emitted. If the router selects a profile name that does not exist, queue:profile:invalid-selection is emitted and the build proceeds under the active profile or defaults.
Build-level profile routing is separate from per-invocation runtime-choice routing. registerProfileRouter chooses which profile is active before build dispatch; runtime choices select among choices declared inside the selected tier for each agent invocation. onAgentRun can observe selected choice metadata, but it cannot change the harness, model, provider, effort, or toolbelt for that run.
Runtime choices inside profiles
The four built-in tiers remain the role-routing axis. Existing tier recipe fields equal that tier's implicit default choice. Named choices inherit from the tier default and override only the fields they list. Choices are selected after a role resolves to a tier.
agents:
tiers:
implementation:
harness: pi
model: anthropic/claude-sonnet-4-6
effort: medium
pi:
provider: openrouter
choices:
backend:
model: qwen3-coder
pi:
provider: local
toolbelt: none
ui:
effort: high
toolbelt: browser-ui
routing:
rules:
- name: ui-paths
choice: ui
when:
pathGlobs: ["packages/console-ui/**", "web/**", "**/*.{tsx,jsx,css}"]
keywords: ["ui", "frontend", "browser", "component"]
- name: backend-paths
choice: backend
when:
pathGlobs: ["packages/engine/**", "packages/client/**", "packages/monitor/**"]Declarative rules run in order before extension runtime-choice routers. If a rule matches, routers are skipped. If no rule or router selects a named choice, or a router errors, times out, or returns an invalid choice, eforge falls back to default for that invocation and does not fail the build. Events expose non-secret runtime-choice metadata such as selected choice, source, rule/router name, and fallback reason.
Harnesses
Two harnesses ship with eforge:
pi- recommended for new profiles; provider-flexible execution across Anthropic, OpenAI, Google, Mistral, Groq, xAI, Bedrock, OpenRouter, and local models. Requirespi.providerper tier. Defaults topi.resources: isolatedso ambient Pi extensions, skills, prompts, and themes are not loaded into headless eforge agents; setambientonly when you intentionally want those resources.claude-sdk- supported secondary path for Anthropic Claude Agent SDK users. Does not use apiblock.
You can mix harnesses across tiers within a single profile:
agents:
tiers:
planning:
harness: pi
model: anthropic/claude-opus-4-6
effort: high
pi:
provider: openrouter
implementation:
harness: pi
model: qwen3-coder
effort: medium
pi:
provider: local
review:
harness: claude-sdk
model: claude-opus-4-7
effort: high
evaluation:
harness: pi
model: gemini-flash
effort: high
pi:
provider: googleToolbelts inside profiles
A toolbelt filters which project MCP servers from .mcp.json reach agents in a given tier. Set toolbelt: <name> on a tier to use a named bundle, or toolbelt: none to pass no project MCP servers to that tier.
agents:
tiers:
implementation:
harness: pi
model: anthropic/claude-sonnet-4-6
effort: medium
pi:
provider: openrouter
toolbelt: browser-ui # only the playwright MCP server reaches this tier
planning:
harness: pi
model: anthropic/claude-opus-4-6
effort: high
pi:
provider: openrouter
toolbelt: none # no project MCP servers for planningOmitting toolbelt keeps the default: all servers from .mcp.json pass through.
The Claude Code /eforge:profile-new wizard and Pi /eforge:profile:new wizard include an optional toolbelt step with a preset gallery covering browser-ui, docs-research, issue-triage, repo-review, observability, database-readonly, api-testing, and design-ui. See Configuration - Guided Toolbelt Presets for the full setup instructions and Configuration Reference - Toolbelts for the schema.
Move profiles between scopes
Profiles can live at any of the three scope directories, but there are no standalone promote or demote profile commands today. To share a personal profile with the team, create a project-scope profile with /eforge:profile-new in Claude Code or /eforge:profile:new in Pi, or move the YAML file from .eforge/profiles/ to eforge/profiles/ and then switch to it with /eforge:profile <name>.
User-scope profiles (~/.config/eforge/profiles/) apply across all projects on the machine and are never committed.
Where to look next
- Configuration -
eforge/config.yamlteam defaults that profiles override - Playbooks - run recurring workflows with a profile baked in
- Extensions API - registerProfileRouter - automate profile selection per build
- Extensions API - registerRuntimeChoiceRouter - automate tier-local runtime choice selection per invocation
- Configuration Reference - Toolbelts - full toolbelt schema