Skip to content

Agent Profiles

Itervox supports named agent profiles with per-profile commands, file-backed SOUL/INSTRUCTIONS guidance, and daemon-backed actions, plus SSH-based remote execution and a reviewer-profile flow. This guide covers all of these.

The Agents page provides a visual overview of all configured profiles, their backends, models, estimated context cost, and quick actions:

Agents page showing a pm profile card with Claude backend and Sonnet 4.6 model, plus three profile templates

Each profile card shows:

  • Backend and model — at a glance.
  • Profile-file preview — the first lines of the profile’s SOUL/INSTRUCTIONS guidance.
  • Context budget — estimated token cost from all loaded skills, MCP servers, and instructions. Profiles inheriting a heavy surface area are flagged.
  • Active/Inactive state — inactive profiles remain in config but are hidden from dispatch and automations.

Below the configured profiles, starter templates (Code Reviewer, QA Engineer, Input Responder) let you bootstrap common profiles with one click. Editing a schema 2 profile in the dashboard writes the profile’s SOUL.md and INSTRUCTIONS.md files separately; the old inline prompt field is compatibility-only.

The SOUL, INSTRUCTIONS and automation instruction editors suggest what you can reference as you type:

TypeSuggestsInserted
/ or @ at the start of a word (or after ()Skills and subagents from the skills inventory: project, user and plugin, each with its description and sourceThe name, after the / or @ you typed
{{The Liquid variables profile prompts get: issue.*, attempt and run.* (automation instructions also get trigger.*)The variable, and a closing }} when there is none
| inside {{ … }}Common Liquid filters (default, downcase, replace, join, …)The filter name

Use the arrow keys to move through the list, Enter to insert, and Escape to close it (a second Escape closes the dialog). Tab still moves focus. Moving the caret away closes the list, and Enter then types a newline. The list is announced to screen readers with the number of suggestions. Skills and subagents come from the inventory scan: if the editor opened before the daemon’s first scan finished, click Re-scan (or Run first scan) on the Skills card, or reopen the page, to see them. A plugin skill with the same name as a project or user skill is listed once.

When an issue has a profile assigned (via the Agents page or API), Itervox renders that profile’s SOUL.md and INSTRUCTIONS.md through the Liquid template engine and always appends them to the rendered WORKFLOW.md prompt. No agent_mode flag is needed — profile files inject unconditionally.

agent:
command: claude
max_concurrent_agents: 5
max_turns: 20
profiles:
backend:
command: claude --model claude-opus-4-6
soul_file: .itervox/agents/backend/SOUL.md
instructions_file: .itervox/agents/backend/INSTRUCTIONS.md
frontend:
command: claude --model claude-sonnet-4-6
soul_file: .itervox/agents/frontend/SOUL.md
instructions_file: .itervox/agents/frontend/INSTRUCTIONS.md
qa:
command: claude --model claude-sonnet-4-6
enabled: true
allowed_actions: [comment, move_state]
soul_file: .itervox/agents/qa/SOUL.md
instructions_file: .itervox/agents/qa/INSTRUCTIONS.md

Example profile files:

.itervox/agents/backend/SOUL.md
# backend SOUL
## Identity
You are the backend specialist for this repository.
## Purpose
Handle API design, database schemas, server-side business logic, and performance optimisation.
.itervox/agents/backend/INSTRUCTIONS.md
# backend INSTRUCTIONS
## Workflow
- Work on {{ issue.identifier }}: {{ issue.title }}.
- Focus on Go, APIs, database code, and regression tests.

Profile files support the same Liquid template variables as the main WORKFLOW.md prompt:

profiles:
backend:
command: claude
soul_file: .itervox/agents/backend/SOUL.md
instructions_file: .itervox/agents/backend/INSTRUCTIONS.md
VariableTypeDescription
issue.identifierstringIssue ID (e.g. "ENG-42", "#123")
issue.titlestringIssue title
issue.descriptionstringIssue body. Empty prints as nothing; {% if issue.description %} skips its block
issue.urlstringFull URL to the issue
issue.branch_namestringSuggested git branch name
issue.labelsstring[]Labels attached to the issue
issue.prioritynumberPriority (lower is more urgent). Unset when the issue has none: use {{ issue.priority | default: "none" }}
issue.commentsobject[]Comments (author_name, body)
issue.statestringCurrent issue state
issue.blocked_byobject[]Blocking issues (id, identifier, state)
attemptint|nullCurrent retry attempt (null on first attempt)

Plain-text profile files (without any {{ }} syntax) are passed through unchanged.

Agents run the Claude or Codex CLI inside the repository, so they load the repository’s .claude/skills, .claude/agents, CLAUDE.md and AGENTS.md like any session would. A profile’s SOUL.md and INSTRUCTIONS.md can therefore tell the agent to use them. Itervox checks those references against the skills inventory, so a typo or a skill that only exists on your laptop is caught before an agent silently runs without it.

Only these forms are checked:

FormRefers to
@agent-code-reviewerthe code-reviewer subagent (Claude Code’s @-mention form)
`verify-before-done` skillthe verify-before-done skill: the name in backticks, then the word “skill” on the same line
`code-reviewer` subagentthe subagent: the name in backticks, then the word “subagent”
`a`, `b`, and `c` skillsevery name in the list (separators , & / ; + and or, in any combination)

Names follow Claude Code’s naming rule: lower-case letters, digits and hyphens, starting with a letter, with an optional <plugin>: prefix for a plugin’s skills and agents. They are matched against the inventory case-insensitively.

@agent- counts at the start of a line or after a space or one of ( [ { , ; " ' ` * _ < >, so `@agent-x`, **@agent-x** and <@agent-x> are checked while ordinary email addresses and URLs are not (_ and * count only when they do not follow a letter or digit; a URL or address with one of the other characters right before @agent- is read as a mention). Mentions inside inline code and HTML comments are checked too. A name that runs on into letters, digits, non-ASCII characters or an underscore (@agent-foo_bar, @agent-fooBar), or that ends in a hyphen, is not a valid name and is skipped.

A list counts only as one unbroken run of backticked names right before the noun, on one line: “`a` and the `b` skills” checks only b, and “the `a` skill (or `b`)”, “etc.”, lists wrapped onto a second line and table cells are not followed. Each of these limits can only leave a reference unchecked; none of them produces a warning.

Fenced code blocks are ignored, following Markdown’s rules: a fence is three or more backticks or tildes and closes on a line holding the same character at least as many times and nothing else, indented no more than three spaces within its list item or quote; an unclosed fence runs to the end of the file. Fences inside list items and blockquotes count too, and end when their list item or quote ends. Itervox does not fully parse Markdown lists, so in unusual layouts (for example a fence indented four spaces that is not inside a list) a reference near a fence can be skipped or checked differently from how a Markdown renderer would show it.

The forms keep most prose from matching: “claude agent”, “SKILL.md skill files”, “skill-level”, and “the codex skills directory” (or file, module, option, setting and similar words after “skill”) are not read as references. Two things still are:

  • Any sentence that puts a backticked name right before “skill” or “subagent”, whatever the verb. “Do not use the legacy-deploy skill” warns if legacy-deploy is not installed, the same as “use the legacy-deploy skill”. Rephrase such sentences without the backticks.
  • Any @agent-<name> written as above, for example “cc: @agent-ops-team”.

Slash commands such as `/verify-before-done` are not checked, because the same form also names Claude Code’s built-in commands and file paths. Write “the verify-before-done skill” when you want the reference validated.

Each reference is resolved for the backend the profile runs on. Itervox works this out the way the dispatcher does, using the same helpers: it builds the command the dispatcher would hand to the agent runner from agent.command and agent.backend, then the profile’s own command (which replaces the default) and backend, and routes it the way the runner does. A requested backend that conflicts with a recognised claude or codex binary is ignored, and anything that does not route to Codex runs on Claude. A test checks this against the dispatcher’s resolver for every combination of a large set of commands and backends. Per-issue backend overrides and rate-limit fallbacks are decided at dispatch time and are not taken into account. A Claude profile sees Claude and shared skills, plugin skills and every subagent; a Codex profile sees Codex and shared skills and has no subagents. A missing reference is reported as MISSING_SKILL_REF or MISSING_SUBAGENT_REF:

  • in the dashboard, in the Skills card’s issues list (Settings → Skills);
  • by itervox doctor, as a WARNING: line. It does not change doctor’s exit code, because the agent still runs.

When agent.ssh_hosts is set, a reference that resolves only through your home directory or a plugin installed there also gets an info-level USER_SCOPE_REF_ON_SSH note: agents on remote hosts see the repository, not this machine’s home. For a Claude profile, commit the skill or subagent under .claude/ to make it travel with the repo. Codex reads no skills from the repository, so for a Codex profile install the skill on every SSH host (for example under ~/.codex/skills/); that works for either backend.

The Skills card lists subagents in their own Subagents section, with source (project, user or plugin), model, tools and file size.

When multiple profiles run on the same issue, each one can leave a Markdown deliverable in .itervox/handoff/ so subsequent profiles can read it. The orchestrator generates two values per dispatch and surfaces them in the agent’s prompt as a ## Run Context block:

ValueDescription
run.timestampThe dispatch instant, formatted ISO8601 with : replaced by - so it is filename-safe (e.g. 2026-05-26T14-30-45Z).
run.handoff_pathThe canonical destination for this run’s deliverable: .itervox/handoff/<run.timestamp>_<profile-name>.md.
run.pr_base_branchThe branch a pull request from this run should target: the blocker’s branch when the worktree is stacked, otherwise workspace.base_branch. Not in the Run Context block; a stacked run gets it in a ## Stacked Branch block.
run.evidence_pathWhere a profile with require_evidence records its evidence: .itervox/evidence/<profile>.json; empty otherwise. See Done needs evidence.

The orchestrator also prepends a ## Prior Agent Handoffs block to the prompt containing every prior agent’s deliverable on this branch in chronological order, budget-truncated to 30 KB (oldest first, with a [earlier handoffs truncated] marker).

Profile INSTRUCTIONS.md files generated by itervox init now include a Handoff Protocol section telling the agent to:

  1. Read the ## Prior Agent Handoffs block at the top of its prompt to learn what predecessors did.
  2. Write a concise Markdown deliverable to run.handoff_path before exiting, summarizing what it did, key decisions, and what the next agent needs to know.
  3. Treat earlier handoffs as read-only — never overwrite a prior agent’s file. The chronological order is the audit trail.

You can adapt this protocol per profile. A reviewer might write findings keyed by severity; an implementer might list files changed and tests added; a researcher might list the patterns it found and risks it flagged. The orchestrator does not interpret the content — it just shuttles it forward.

If a worker exits with TerminalFailed or TerminalStalled, the orchestrator renames the in-flight file to <basename>.partial.md so subsequent agents see it explicitly marked. TerminalInputRequired does not rename — the agent intentionally paused and will resume.

See the Agent Handoff guide for a worked example of chaining three profiles end-to-end.

Each profile can use a different runner binary:

agent:
command: claude
profiles:
coder:
command: codex
backend: codex # explicit backend hint (optional)
soul_file: .itervox/agents/coder/SOUL.md
instructions_file: .itervox/agents/coder/INSTRUCTIONS.md
tester:
command: claude --model claude-haiku-4-5
soul_file: .itervox/agents/tester/SOUL.md
instructions_file: .itervox/agents/tester/INSTRUCTIONS.md

The backend field is an optional hint that overrides automatic runner detection. Use it when the command string is a wrapper script that Itervox cannot introspect.


itervox init populates agent.available_models by calling the Anthropic /v1/models API (with ANTHROPIC_API_KEY) and the OpenAI /v1/models API (with OPENAI_API_KEY). When either key is missing, a hardcoded fallback list is written so the dashboard model picker still has sensible suggestions out of the box.

When Anthropic or OpenAI ship a new model — e.g. Sonnet 4.7 lands — your existing WORKFLOW.md keeps the v0.2.0-era list and the picker won’t show the new option. Two ways to refresh:

Terminal window
# Refresh both backends (default)
itervox models refresh
# Refresh just one backend
itervox models refresh --backend claude
itervox models refresh --backend codex
# Preview without writing
itervox models refresh --dry-run
# Print the current list from WORKFLOW.md without hitting the APIs
itervox models list

The command queries /v1/models with whichever API keys are loaded from .itervox/.env, merges the result into agent.available_models, and writes WORKFLOW.md atomically. Per-backend semantics: refreshed backends have their list replaced entirely; backends NOT named on the command line keep their previous entries untouched.

Settings → Models → Refresh from APIs triggers the same logic via POST /api/v1/settings/models/refresh. The body accepts {"backend": "claude" | "codex" | "all"} (default "all"). On success the server returns the new map and immediately notifies SSE subscribers, so the model picker reflects the new options without a page reload.

If the daemon is running a build that does not implement the refresh capability (e.g. the in-memory quickstart binary), the route returns 501 not_implemented with a hint pointing at the itervox models refresh CLI subcommand.

  • After every Claude/Codex release the team cares about. Anthropic and OpenAI typically publish a release note 24–48 hours before the model is broadly available via the API; refresh once you can hit /v1/models from your account and see the new ID.
  • As part of project onboarding. The first thing a new contributor does after cloning is set their API keys in .itervox/.env and run itervox models refresh so their local picker matches the team baseline.
  • Never blindly in CI. The refresh writes to WORKFLOW.md, which is a tracked file. Running it in CI would create a commit diff every time a model launches; prefer running it in a developer shell and committing intentionally.

Profiles can opt into a small set of daemon-backed actions. The profile editor exposes these as checkboxes with clear descriptions of what each action allows:

Profile editor showing daemon actions: Comment, Post structured review, Create follow-up issue, Move issue state, and Provide input to blocked run

Available actions:

  • comment — post a tracker comment on the current issue.
  • comment_pr — post a structured review comment with file/line/severity findings on the issue.
  • create_issue — open a follow-up issue in the configured tracker state.
  • merge_pr — run the daemon’s guarded gh pr merge sequence for a PR on the current issue. The daemon validates the request against the profile’s allowed_actions like every other action, then enforces the merge gate server-side (see below) — the agent never gets raw gh credentials or an ungated merge path.
  • move_state — transition the current issue to another tracker column.
  • provide_input — answer an input-required prompt and resume the blocked session.

Granting merge_pr does not mean “the agent can merge anything”. Every merge request runs through a server-side gate that refuses with a structured reason unless all of these hold:

  1. Not already merged — an already-merged PR returns already_merged (and a process-level dedup ledger prevents double-merging the same (issue, PR) pair across re-fires).
  2. Mergeable state is clean — GitHub must report the PR as MERGEABLE with merge state CLEAN; conflicts, draft state, or unmet branch protections refuse with not_mergeable:<state>.
  3. No block label — if the PR carries any label from agent.merge_block_labels (case-insensitive; default ["needs-human", "migration", "auth", "feature-flag", "breaking"]), the merge refuses with blocked_label:<label>.
  4. All required CI checks pass — gh pr checks --required must exit clean; failing or pending required checks refuse with checks_failed:<excerpt>.
  5. Valid strategy — one of squash (default), rebase, or merge, taken from the request or agent.merge_strategy. The merge runs with --delete-branch.

Example:

agent:
profiles:
input-responder:
command: claude --model claude-sonnet-4-6
soul_file: .itervox/agents/input-responder/SOUL.md
instructions_file: .itervox/agents/input-responder/INSTRUCTIONS.md
allowed_actions: [comment, provide_input]
qa:
command: claude --model claude-sonnet-4-6
soul_file: .itervox/agents/qa/SOUL.md
instructions_file: .itervox/agents/qa/INSTRUCTIONS.md
allowed_actions: [comment, create_issue, move_state]
create_issue_state: Todo

These permissions do not give the agent arbitrary shell or tracker access. They only let the daemon mint short-lived per-run bearer grants for the corresponding /api/v1/agent-actions/* routes. This is what powers things like automation helpers that comment on the issue, move it back to Todo, create a follow-up issue, or auto-resume a blocked run with provide_input.

If allowed_actions includes create_issue, create_issue_state is required.

In v0.2.0, daemon-backed action env vars and shims are injected only for local workers. SSH remote workers with allowed_actions receive a prompt warning that daemon-backed actions are unavailable remotely; they do not receive ITERVOX_ACTION_TOKEN, ITERVOX_DAEMON_URL, or the local shim PATH.


Itervox ships one built-in profile embedded in the binary: merge-bot. Its purpose is the final step of an automated pipeline — verifying a reviewed PR is safe to land and invoking the guarded merge_pr action — without you having to author the SOUL/INSTRUCTIONS files yourself.

Reference it from WORKFLOW.md like any other profile:

agent:
profiles:
merge-bot: {}

When the profile’s soul_file / instructions_file are unset, the daemon resolves the embedded SOUL.md / INSTRUCTIONS.md content from the built-in registry. Built-in defaults (applied only when the corresponding field is unset):

  • Command: claude --model claude-haiku-4-5-20251001 (a fast, cheap model — the merge gate does the safety work, not the model)
  • Backend: claude
  • allowed_actions: [comment, comment_pr, merge_pr, move_state]

File-on-disk content always wins: itervox init scaffolds the built-in files to .itervox/agents/merge-bot/ so you can customise tone or rules, and the embedded copies remain the fallback for files you delete or never create. The merge itself is still gated server-side by the merge_pr checks above — the built-in profile carries no special bypass.

There are two supported wirings:

  • Human-gated (recommended default) — pair merge-bot with a tracker_comment_added automation using body_contains / body_regex filters so it only wakes when a person posts an approval phrase. Note that Itervox ignores its own comments when polling this trigger (anti-loop), so an AI reviewer’s comment cannot fire it — the human comment is the deliberate gate.
  • Fully autonomous (advanced) — a cron sweep over completion-state issues that checks the PR for an explicit reviewer-posted approval marker before invoking merge_pr. See the autonomous merge sweep example and its caution list before enabling this.

See the Automations guide for the trigger and filter reference.


Itervox can dispatch agent turns to remote hosts over SSH. This is useful for running agents on powerful cloud machines while keeping the orchestrator local.

agent:
ssh_hosts:
- builder-1.example.com
- builder-2.example.com:2222 # custom port
dispatch_strategy: round-robin # or "least-loaded"
StrategyValueDescription
Round-robin"round-robin" (default)Issues are assigned to hosts in rotation. Simple and predictable.
Least-loaded"least-loaded"Issues are assigned to the host with the fewest active agents. Balances load dynamically.
  • The Itervox process must be able to reach each host over SSH without a password prompt. Use SSH agent forwarding or pre-shared keys.
  • The target host must have the agent runner binary (claude, codex, etc.) installed and on $PATH.
  • The repository must be accessible on the remote host (either pre-cloned or mounted via NFS/SSHFS).
  • If workspace.worktree is enabled, the bare git clone must already exist at workspace.root on each remote host.
  • Daemon-backed profile actions are local-worker only in v0.2.0; remote workers can run the model but cannot use the itervox action ... bridge.
workspace:
root: /mnt/shared/itervox/workspaces
worktree: true
clone_url: [email protected]:owner/repo.git
base_branch: main
agent:
ssh_hosts:
- gpu-node-1.internal
- gpu-node-2.internal
dispatch_strategy: least-loaded
max_concurrent_agents: 8

Itervox can dispatch a reviewer agent to check PRs before marking issues complete. The preferred configuration is reviewer_profile, which reuses a normal profile definition for review work. The reviewer runs on the same workspace as the original worker and can also be auto-dispatched with auto_review: true.

Reviewers are read-only: a reviewer records a verdict (approve or block, with reasons and line comments) that Itervox posts on the issue, and never commits or pushes. A reviewer that changes the branch is flagged and its verdict counts as a block. For two reviewers on different backends, see the cross-review preset.

agent:
reviewer_profile: code-reviewer
auto_review: true
profiles:
code-reviewer:
command: claude --model claude-opus-4-6
soul_file: .itervox/agents/code-reviewer/SOUL.md
instructions_file: .itervox/agents/code-reviewer/INSTRUCTIONS.md
allowed_actions: [comment, move_state]

Notes:

  • auto_review: true requires reviewer_profile to be set.
  • auto_review safely coexists with workspace.auto_clear as of v0.2.0 — the clear was redefined to fire only on terminal tracker states, so the reviewer gets the implementer’s workspace and the clear is deferred until after the reviewer also completes. The legacy ValidateAutoClearAutoReview config-load conflict is now a no-op kept only for API compatibility.

The older reviewer_prompt field is still supported when no reviewer_profile is configured, but it is deprecated.

agent:
reviewer_prompt: |
You are an AI code reviewer for issue {{ issue.identifier }}.
You are read-only: do not edit files, commit, push or move the issue.
Steps:
1. Read the "Changes to Review" block (the diff against the base branch).
2. Check for: correctness, test coverage, security issues, and adherence to our style guide.
3. Record your verdict as the "Review Verdict (required)" block describes:
approve, or block with the reasons and line comments to fix.

Available template variables in the legacy reviewer prompt

Section titled “Available template variables in the legacy reviewer prompt”
VariableDescription
issue.identifierIssue ID (e.g. ENG-42 for Linear, #42 for GitHub)
issue.titleIssue title
issue.branch_nameGit branch created for this issue
issue.descriptionIssue body / description

FieldDefaultDescription
command"claude"Default agent runner command
backend""Override runner detection ("claude" or "codex")
max_concurrent_agents10Maximum simultaneous agents
max_turns20Maximum turns per agent session
turn_timeout_ms3600000Hard time limit per session (ms)
stall_timeout_ms300000Inactivity timeout before retry (ms)
max_retry_backoff_ms300000Cap on exponential retry back-off (ms)
max_retries5Max retries before moving to failed_state
ssh_hosts[]Remote hosts for SSH dispatch
dispatch_strategy"round-robin"SSH routing: "round-robin" or "least-loaded"
profiles{}Named agent profiles
reviewer_profile""Profile used for code review dispatch
auto_reviewfalseAuto-dispatch reviewer after a successful worker run
reviewer_prompt(built-in)Legacy fallback reviewer template when no reviewer_profile is set
inline_inputfalsetrue hides the dashboard reply box — the tracker is the only place to reply
max_switches_per_issue_per_window2Rate-limit switch cap for rate_limited automations
switch_window_hours6Rolling window for the switch cap
switch_revert_hours0TTL after which auto-applied switches revert to the original profile/backend (0 disables)
rate_limit_error_patterns[]Custom substrings for detecting rate-limit errors in agent stderr
FieldDefaultDescription
command(required)CLI command for this profile
soul_file(required in schema 2)Path to SOUL.md, relative to WORKFLOW.md when not absolute
instructions_file(required in schema 2)Path to INSTRUCTIONS.md, relative to WORKFLOW.md when not absolute
backend""Override runner detection for wrapper scripts
enabledtrueSet to false to hide from dispatch selectors and automations
allowed_actions[]Daemon-backed actions: comment, comment_pr, create_issue, move_state, provide_input
create_issue_state""Tracker state for new issues (required when create_issue is in allowed_actions)