> ## Documentation Index
> Fetch the complete documentation index at: https://vetta.sh/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Rosters & versioning

> How a coordinator's roster is snapshotted and version-pinned, what is checked when you save it, how harnesses mix, and the commands that manage it.

A team is only reproducible if its members can't shift under it. Vetta guarantees that by **snapshotting the whole coordinator config — including the roster — at create and update time**, and pinning each member to a specific version.

## Version pinning

Each id-referenced roster member is stored with a version:

```jsonc theme={"system"}
{
  "multiagent": {
    "type": "coordinator",
    "agents": [
      { "type": "agent", "id": "agt_9f2c…", "version": 4 },
      { "type": "agent", "id": "agt_4a71…" },   // pinned at save time
      { "type": "self" }
    ]
  }
}
```

* Provide `version` to pin a specific one.
* Omit it and Vetta pins the member's **current version at the moment you save the coordinator**. The version is resolved server-side and written into the stored roster, so what comes back always carries an explicit pin. A member never floats.
* Once pinned, a member does **not** auto-adopt later edits to that agent. To use a newer version, **update the coordinator's roster** — an explicit, reviewable act.

This is what makes a team behave the same today and next month: editing a member in isolation cannot silently change how the coordinator runs.

## What a save checks

Saving a roster is not a bookkeeping write. Every id-referenced member is loaded and checked, and a failure is refused **at save**, naming the entry and the reason — never left to surface as a broken delegation an hour into a run.

| Check              | Refused when                                                                                 |
| ------------------ | -------------------------------------------------------------------------------------------- |
| The agent exists   | No agent by that id in your organization.                                                    |
| The version exists | The pin names a version that agent does not have.                                            |
| One level deep     | That agent is itself a coordinator. A team is one level; nothing bounds the spend of a tree. |
| Runnable here      | Its harness cannot run on this deployment — see below.                                       |
| Capability match   | Its harness cannot serve its own configuration — see below.                                  |

One more check reads **this** agent rather than a member: a coordinator whose own harness declares `injected_tools: false` is refused, naming `multiagent`, because the team tools would never reach its model. No published harness declares `false`, so it stands as a guard rather than a rule you meet. See [Mixing harnesses](#mixing-harnesses).

## Mixing harnesses

**Members may mix harnesses freely.** A member's `harness` is its own — fixed when that agent was created — and the coordinator's harness does not have to match. A `pi` coordinator with a `vetta` member is an ordinary team, and so is the reverse.

**Any published harness can be a coordinator.** Delegation is a *tool*: `send_to_agent` reaches the coordinator's model either as part of the toolset Vetta assembles for the turn, or — where the agent is a CLI process in a machine of its own — over the session-scoped tool endpoint that machine is given. One caveat is worth knowing before you pick: on the second kind, `wait_for_agents` does not pause the turn in flight. The coordinator parks once the turn goes idle and the delegated threads open correctly; it costs a few extra model calls, not a wrong answer. A harness declaring [`injected_tools: false`](/docs/concepts/harness-capabilities) would still be refused as a coordinator, naming `multiagent` at save and `harness` at session start, but none is published.

A **member** on the same harness is fine, and that distinction is the point: a member is delegated *to*. `send_to_agent` runs in the coordinator's loop, and what reaches the member is a brief through its own ordinary session. It never needs a team tool of its own.

What a member's harness must satisfy is that it can serve **its own** configuration, and the check is at save:

* **Runnable on this deployment.** A harness whose agent is a *process in a machine* needs a base image configured for it in that environment. Staging may have one where production does not, so this is a per-deployment fact, not a property of the harness — the refusal reads `runs the … harness, which is not runnable on this deployment`. Delegating to such a member would otherwise buy nothing but a retry ladder against a failure knowable at save time.
* **Typed results.** If the member's own config demands a structured output, its harness must declare `structured_output`. See [Harness capabilities](/docs/concepts/harness-capabilities) for which do — and for the one that declares it and cannot yet serve it.
* **Approvals.** If the member's toolset carries `permission: "ask"` anywhere, its harness must be able to hold a tool call for a decision. A harness without that seam does not prompt — it silently denies — so admitting the pair would drop an approval policy without a trace.

<Note>
  Which members can return a typed result is also visible to the coordinator at runtime: `list_agents` reports `typed` per member, and a `send_to_agent` carrying an `output_schema` for one that cannot is [refused by name](/docs/team/delegation#nothing-throws--a-refusal-is-a-tool-result).
</Note>

What a mixed team then *costs* while it runs is decided by each member's harness independently — see [What a member holds while it runs](/docs/team/context-and-budgets#what-a-member-holds-while-it-runs).

## Managing the roster

<CodeGroup>
  ```bash CLI theme={"system"}
  vetta team set release-manager --member changelog-writer@4 --member release-notes-qa@2 --board

  vetta team add release-manager --member doc-linter@1

  vetta team remove release-manager --member release-notes-qa

  vetta team show release-manager
  ```

  ```typescript TypeScript theme={"system"}
  await vetta.agents.update(coordinator.id, {
    expected_version: coordinator.current_version,
    multiagent: {
      type: "coordinator",
      agents: [
        { type: "agent", id: "agt_9f2c…", version: 4 },
        { type: "agent", id: "agt_4a71…", version: 2 },
      ],
      board: true,
    },
  });
  ```
</CodeGroup>

Each `--member` is `name@version`, a bare `name`, or an `agt_` id — plus `self`. The name spelling is resolved by the CLI against your org's agents; the wire only ever carries `{ type: "agent", id, version? }`.

`set` replaces the roster outright and is the only one that takes `--board`; `add` and `remove` keep the rest of the roster as it stands, including its board setting. Removing the last member clears the roster to `null`, because an empty coordinator is not a team.

Because the roster lives on the coordinator's config, a roster change is an ordinary agent [update](/docs/concepts/agents#versioning): it creates a new coordinator version, under the same optimistic-concurrency guard. Every one of these commands is a read-modify-write under `expected_version`, so two people editing a roster from two terminals cannot silently overwrite each other.

## Data residency

Vetta does **not** yet expose a per-agent data-region or residency control — there is no `data_region` field on an agent or a coordinator today, so there is nothing for a team to reconcile across its members. Everything runs in Vetta's default region.

<Note>
  Configurable inference regions and a residency posture have not shipped. When region pinning does, a team will be required to keep a single region across the coordinator and every member (or leave it unset everywhere) so work is never split across regions by accident. Until then, do not assume any residency guarantee.
</Note>

## Limits

| Bound                     | Limit                                             |
| ------------------------- | ------------------------------------------------- |
| Unique agents per roster  | 1 to 20                                           |
| `self` entries per roster | 1                                                 |
| Delegation depth          | 1 level (a member cannot itself be a coordinator) |

<Card title="Next: context & budgets" icon="wallet" href="/docs/team/context-and-budgets">
  What sessions share, what a member holds while it runs, and how spend flows across a team.
</Card>
