---
title: "agents.play@1 · OpenVibe.Services"
canonical: "https://openvibe.services/docs/contracts/agents.play"
updated: null
---

# agents.play@1 · OpenVibe.Services

HTML: [agents.play@1 · OpenVibe.Services](https://openvibe.services/docs/contracts/agents.play)

# AgentsPlay `agents.play@1`

Generated at 2026-11-13 06:25:48 UTC from [`openvibe-contracts v0.419.0`](https://github.com/OpenVibers/OpenVibe.Contracts/tree/v0.419.0) and [`openvibe-sdk v0.161.0`](https://github.com/OpenVibers/OpenVibe.SDK/tree/v0.161.0).

- Version: 1.0.0
- Owner: network
- Visibility: public
- Status: active
- Compatibility: backward
- Decision: [ADR-044](/docs/adr/ADR-044)
- Schema: [`https://openvibe.network/contracts/agents/play.v1.json`](/docs/contracts/agents.play.json)

agents.play@1 (agent-engine.md §4, 2026-11-12): a named way for several agents to work on one ask, as data, so a person or an AI can write one and openvibe.run can list it. ROLES are the agents it uses (each with what it does, the size of model it wants, and how many may run at once); PHASES run in order, the roles in a phase together when PARALLEL; CHECKS say who checks whose work, against a written BAR; STOP says when the play is over; BUDGET.SPLIT gives each role its share of the parent's budget. The orchestrator turns a play into child runs (agents.run@1's PARENT\_RUN, PLAY and ROLE). Rules every play keeps, whatever it says: a child's end never ends its parent; a person's message never waits behind new work; a child's budget is a slice of its parent's.

## Fields

| Field | Type | Required | Description | Constraints |
| --- | --- | --- | --- | --- |
| `id` | string | yes |  | - `pattern ^[a-z][a-z0-9-]{0,60}$` |
| `version` | integer |  |  | - `minimum 1` |
| `name` | string | yes |  | - `minLength 1`<br>- `maxLength 120` |
| `summary` | string |  | When to use it, in a sentence or two. | - `minLength 1`<br>- `maxLength 1000` |
| `roles` | array of object | yes |  | - `minItems 1`<br>- `maxItems 12`<br>- `items: no other fields` |
| `roles[].id` | string | yes |  | - `pattern ^[a-z][a-z0-9-]{0,60}$` |
| `roles[].name` | string |  |  | - `minLength 1`<br>- `maxLength 80` |
| `roles[].does` | string | yes | What this role does, as its instructions begin. | - `minLength 1`<br>- `maxLength 2000` |
| `roles[].model` | enum |  | small: the cheapest that can; strong: the best available; same: the parent's; other-family: a model of another family than the one it checks (a grader). | - `one of "small", "strong", "same", "other-family"` |
| `roles[].agent` | string |  | A particular agent's id, when the role is always the same agent. | - `minLength 1`<br>- `maxLength 160` |
| `roles[].count` | object |  |  | - `no other fields` |
| `roles[].count.min` | integer |  |  | - `minimum 1`<br>- `maximum 64` |
| `roles[].count.max` | integer |  |  | - `minimum 1`<br>- `maximum 64` |
| `roles[].consults` | integer |  | How many times another role may consult this one (an advisor's limit). | - `minimum 0`<br>- `maximum 50` |
| `phases` | array of object |  |  | - `maxItems 20`<br>- `items: no other fields` |
| `phases[].id` | string | yes |  | - `pattern ^[a-z][a-z0-9-]{0,60}$` |
| `phases[].name` | string |  |  | - `minLength 1`<br>- `maxLength 120` |
| `phases[].roles` | array of string | yes |  | - `minItems 1`<br>- `maxItems 12`<br>- `items: pattern ^[a-z][a-z0-9-]{0,60}$` |
| `phases[].parallel` | boolean |  |  |  |
| `checks` | array of object |  |  | - `maxItems 12`<br>- `items: no other fields` |
| `checks[].by` | string | yes |  | - `pattern ^[a-z][a-z0-9-]{0,60}$` |
| `checks[].of` | string | yes |  | - `pattern ^[a-z][a-z0-9-]{0,60}$` |
| `checks[].bar` | string |  | What passing means, in words the checker can apply. | - `minLength 1`<br>- `maxLength 2000` |
| `stop` | object | yes |  | - `no other fields` |
| `stop.when` | enum | yes | done: when the last phase ends; passes: when the checks pass; rounds: after ROUNDS rounds; person: when the person says so. | - `one of "done", "passes", "rounds", "person"` |
| `stop.rounds` | integer |  |  | - `minimum 1`<br>- `maximum 20` |
| `budget` | object |  |  | - `no other fields` |
| `budget.split` | object |  | Each role's share of the parent's budget (the shares add up to at most 1). |  |
| `visibility` | enum |  |  | - `one of "private", "public"` |
| `author` | object |  |  | - `no other fields` |
| `author.type` | enum | yes |  | - `one of "user", "agent", "service"` |
| `author.id` | string | yes |  | - `minLength 1`<br>- `maxLength 160` |

## Examples

From the contract's own test fixtures: valid ones validate, rejected ones must fail.

 Valid: advisor

~~~~
{
  "id": "advisor",
  "version": 1,
  "name": "Advisor",
  "summary": "A small model does the work and asks a strong one at the few hard decisions.",
  "roles": [
    {
      "id": "executor",
      "name": "Executor",
      "does": "Do the task step by step. Ask the advisor only when a decision is hard to undo.",
      "model": "small"
    },
    {
      "id": "advisor",
      "name": "Advisor",
      "does": "Answer the executor's question in a few sentences: what to do and why.",
      "model": "strong",
      "consults": 3
    }
  ],
  "phases": [
    {
      "id": "work",
      "roles": [
        "executor"
      ]
    }
  ],
  "stop": {
    "when": "done"
  },
  "budget": {
    "split": {
      "executor": 0.7,
      "advisor": 0.3
    }
  },
  "visibility": "public",
  "author": {
    "type": "service",
    "id": "network"
  }
}
~~~~

Valid: fan-out

~~~~
{
  "id": "fan-out",
  "name": "Fan-out",
  "summary": "Split the work across many small agents, then merge.",
  "roles": [
    {
      "id": "splitter",
      "does": "Split the work into parts that do not touch each other.",
      "model": "strong"
    },
    {
      "id": "worker",
      "does": "Do one part.",
      "model": "small",
      "count": {
        "min": 1,
        "max": 16
      }
    },
    {
      "id": "merger",
      "does": "Merge the parts and check they fit.",
      "model": "strong"
    }
  ],
  "phases": [
    {
      "id": "split",
      "roles": [
        "splitter"
      ]
    },
    {
      "id": "work",
      "roles": [
        "worker"
      ],
      "parallel": true
    },
    {
      "id": "merge",
      "roles": [
        "merger"
      ]
    }
  ],
  "stop": {
    "when": "done"
  }
}
~~~~

Valid: grader-loop

~~~~
{
  "id": "grader-loop",
  "name": "Grader loop",
  "summary": "Do it again until a grader of another model family says it meets the bar.",
  "roles": [
    {
      "id": "doer",
      "does": "Do the task; take the grader's notes into the next round.",
      "model": "same"
    },
    {
      "id": "grader",
      "does": "Check the work against the bar and say pass, or what is missing.",
      "model": "other-family"
    }
  ],
  "phases": [
    {
      "id": "do",
      "roles": [
        "doer"
      ]
    },
    {
      "id": "check",
      "roles": [
        "grader"
      ]
    }
  ],
  "checks": [
    {
      "by": "grader",
      "of": "doer",
      "bar": "Every claim has a source; nothing the person asked for is missing."
    }
  ],
  "stop": {
    "when": "passes",
    "rounds": 4
  }
}
~~~~

Rejected: a-share-over-the-whole

~~~~
{
  "id": "advisor",
  "version": 1,
  "name": "Advisor",
  "summary": "A small model does the work and asks a strong one at the few hard decisions.",
  "roles": [
    {
      "id": "executor",
      "name": "Executor",
      "does": "Do the task step by step. Ask the advisor only when a decision is hard to undo.",
      "model": "small"
    },
    {
      "id": "advisor",
      "name": "Advisor",
      "does": "Answer the executor's question in a few sentences: what to do and why.",
      "model": "strong",
      "consults": 3
    }
  ],
  "phases": [
    {
      "id": "work",
      "roles": [
        "executor"
      ]
    }
  ],
  "stop": {
    "when": "done"
  },
  "budget": {
    "split": {
      "executor": 0.7,
      "advisor": 1.5
    }
  },
  "visibility": "public",
  "author": {
    "type": "service",
    "id": "network"
  }
}
~~~~

Rejected: no-roles

~~~~
{
  "id": "empty",
  "name": "Empty",
  "roles": [],
  "stop": {
    "when": "done"
  }
}
~~~~

Rejected: no-way-to-stop

~~~~
{
  "id": "advisor",
  "version": 1,
  "name": "Advisor",
  "summary": "A small model does the work and asks a strong one at the few hard decisions.",
  "roles": [
    {
      "id": "executor",
      "name": "Executor",
      "does": "Do the task step by step. Ask the advisor only when a decision is hard to undo.",
      "model": "small"
    },
    {
      "id": "advisor",
      "name": "Advisor",
      "does": "Answer the executor's question in a few sentences: what to do and why.",
      "model": "strong",
      "consults": 3
    }
  ],
  "phases": [
    {
      "id": "work",
      "roles": [
        "executor"
      ]
    }
  ],
  "budget": {
    "split": {
      "executor": 0.7,
      "advisor": 0.3
    }
  },
  "visibility": "public",
  "author": {
    "type": "service",
    "id": "network"
  }
}
~~~~

## Validate

~~~~
const contracts = require('openvibe-contracts');
contracts.validate('agents.play@1', value);   // { valid, errors: [{ path, message }] }
~~~~
