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

# agents.run@1 · OpenVibe.Services

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

# AgentsRun `agents.run@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.3.0
- Owner: actor
- Visibility: public
- Status: active
- Compatibility: backward
- Decision: [ADR-044](/docs/adr/ADR-044)
- Schema: [`https://openvibe.network/contracts/agents/run.v1.json`](/docs/contracts/agents.run.json)

agents.run@1 (one agent system, 2026-11-12): one run of an agent, the same shape on every product (Actor, Codes, Run and anything else on openvibe-sdk/agents). The owner is the person or agent the work belongs to; AGENT names the agent or harness that runs it and the product it comes from; PARENT\_RUN makes sub-agent runs a tree; BUDGET is the hard limit and COST what the run has spent; STATUS is where the run is now. The run is stored by its product, not here: this contract is the wire shape. GET /api/v1/runs, /api/v1/runs/{id} and /api/v1/runs/{id}/steps serve it (OpenVibe.Actor) and the SDK's runs client reads it. The product's own id is kept, never reused. A run that ends keeps its end status. Since 1.1.0 (agent-engine.md) a run also says what a person calls it (TITLE), what sort it is (KIND), where it lives (REF on the Frame, URL on its product), which model runs it and where, what it is doing now (DOING), what it waits for (WAITING), its last line (PREVIEW), where it was asked (CONTEXT) and what it made (OUTPUTS); a chat with Vibe is a run of KIND chat that is waiting for a message. Every added field is optional. This v1 contract may grow optional fields; a step is agents.step@1 and an approval is agents.approval@1. Since 1.2.0 (agent-engine.md §§3-5, from the code study) a run may also say why it exists (MANDATE: an ask taken in from the feed, a room, an issue), what it continues (LINEAGE: a fork, a hand-off, a regenerated turn), the play its tree follows and a child's ROLE in it, what its agent's harness can do (AGENT.CAPABILITIES), and what it spent on each rung of the ladder (COST.BY). Every added field is optional.

## Fields

| Field | Type | Required | Description | Constraints |
| --- | --- | --- | --- | --- |
| `id` | string | yes | The product's own run id (Actor's tsk\_…, Codes' tsk\_…, a Run job's job\_…). Unique within its product and never reused. | - `minLength 1`<br>- `maxLength 160` |
| `owner` | any | yes | The person or agent the run belongs to: the subject the work is done for. |  |
| `agent` | object | yes | The agent or harness that runs it. ID is the product's own agent id or slug; NAME is what a person reads; PRODUCT is the product it comes from (actor, codes, run, chat, …). GLYPH, HUE and AVATAR\_URL are its face; HARNESS is the loop that runs it. Since 1.2.0, CAPABILITIES says what its harness can do. Since 1.3.0, BLUEPRINT is the blueprint version it ran. | - `no other fields` |
| `agent.id` | string | yes |  | - `minLength 1`<br>- `maxLength 160` |
| `agent.name` | string |  |  | - `minLength 1`<br>- `maxLength 160` |
| `agent.product` | string |  |  | - `minLength 1`<br>- `maxLength 40` |
| `agent.glyph` | string |  | The agent's face when it has no picture: a glyph from the registry. A chat's persona sets it per run. | - `pattern ^[a-z][a-z0-9-]{0,40}$` |
| `agent.hue` | integer |  | The hue its face is tinted with. | - `minimum 0`<br>- `maximum 359` |
| `agent.avatar_url` | string |  | The agent's picture, when it has one. | - `format uri`<br>- `maxLength 2000` |
| `agent.harness` | string |  | The loop that runs it: openvibe (the SDK's own), or another harness run through an adapter (codex, claude-code, opencode, deepseek, …). | - `pattern ^[a-z][a-z0-9-]{0,59}$` |
| `agent.capabilities` | array of enum |  | What the agent's harness can do, so the Dispatcher chooses by capability and never by name: resume and fork a thread, ask approvals, read images, start sub-agents, use desks, browse, search the web, use MCP tools, plan before acting, read and write files, run a shell, speak and listen. | - `maxItems 20`<br>- `unique items`<br>- `items: one of "resume", "fork", "approvals", "images", "subagents", "desks", "browser", "web-search", "mcp", "plan-mode", "files", "shell", "voice"` |
| `agent.blueprint` | string |  | Since 1.3.0: the blueprint version this agent ran (agents.blueprint@1), when it was made from one. | - `pattern ^bp_[A-Za-z0-9_-]{3,40}@[1-9][0-9]{0,5}$` |
| `parent_run` | string \| null |  | The run that started this one (a sub-agent), or null. The parent's run card nests children; the child's budget is a slice of the parent's. | - `maxLength 160` |
| `budget` | object |  | The hard limit the run may not exceed. A field that is absent is not limited. USD is money; TOKENS counts model tokens in and out; SECONDS is wall time; STEPS counts the appended steps. | - `no other fields` |
| `budget.usd` | number |  |  | - `minimum 0` |
| `budget.tokens` | integer |  |  | - `minimum 0` |
| `budget.seconds` | number |  |  | - `minimum 0` |
| `budget.steps` | integer |  |  | - `minimum 1` |
| `status` | enum | yes | planning: accepted, no step yet. running: a step is under way. waiting: the run waits for its person (an approval or a takeover) and costs nothing. done, failed and stopped are the end statuses; an end status never changes. | - `one of "planning", "running", "waiting", "done", "failed", "stopped"` |
| `created_at` | string | yes |  | - `format date-time` |
| `updated_at` | string | yes | When the run last changed: its newest step, its end, or its creation when nothing has happened yet. | - `format date-time` |
| `result_summary` | string \| null |  | What the run answered or what is left, in plain words, for a run card. The full result stays the product's (Actor's result, Codes' result.summary). Null until there is one. | - `maxLength 8000` |
| `cost` | object \| null |  | What the run has spent so far, to the product's own precision. Null before anything is known. |  |
| `title` | string |  | What a person calls this run: a chat's name, a task's goal in a few words. | - `minLength 1`<br>- `maxLength 200` |
| `kind` | string |  | What sort of run it is. chat: a conversation that stays open. ask: one question and its answer. task: work an agent does for its person. code: a coding task. machine: a job on a computer or a robot. goal: a standing goal that runs on its own schedule. A product may add its own; a reader treats an unknown kind as task. | - `pattern ^[a-z][a-z0-9-]{0,30}$` |
| `ref` | string |  | The run's address on the Frame: its product, a colon, the product's own id (chat:…, codes:tsk\_…, actor:tsk\_…). /api/frame/runs/{ref} reads it wherever it lives. | - `pattern ^[a-z][a-z0-9-]{0,30}:[A-Za-z0-9._~-]{1,160}$`<br>- `maxLength 200` |
| `url` | string |  | The run's own page on its product. | - `format uri`<br>- `maxLength 2000` |
| `model` | object |  | The model that runs it, as the picker chose it (frame.choices@1's choice, with the name a person reads). EFFORT is the reasoning effort where the model has one. | - `no other fields` |
| `model.id` | string | yes |  | - `pattern ^[A-Za-z0-9._:/@+\| -]+$`<br>- `minLength 1`<br>- `maxLength 200` |
| `model.source` | enum |  | openvibe: Exchange's directory, paid in credits. keys: the person's own key. machines: a model on the person's own computer (Link). plans: a signed-in plan through a harness. | - `one of "openvibe", "keys", "machines", "plans"` |
| `model.machine` | string |  |  | - `maxLength 40` |
| `model.name` | string |  |  | - `minLength 1`<br>- `maxLength 200` |
| `model.effort` | string |  |  | - `minLength 1`<br>- `maxLength 40` |
| `where` | object |  | Where the run's work happens. openvibe: OpenVibe's own computers. machine: the person's own computer or robot through Link (ID is the device). runner: a Codes runner. this-computer: the machine that serves the run itself (Link's agent serve). | - `no other fields` |
| `where.kind` | enum | yes |  | - `one of "openvibe", "machine", "runner", "this-computer"` |
| `where.id` | string |  |  | - `minLength 1`<br>- `maxLength 160` |
| `where.name` | string |  |  | - `minLength 1`<br>- `maxLength 200` |
| `target` | one of |  | What a code run works on. new: a new project in the person's own space. openvibe: OpenVibe itself; the orchestrator finds the repositories and opens pull requests, never merges. An object names one repository. |  |
| `doing` | object \| null |  | What the run is doing now, for its agent's face: a few words and a glyph. Null when it is not working. |  |
| `waiting` | object \| null |  | Why a waiting run waits. message: it is the person's turn to speak (a conversation between turns). approval: it asked before an act (APPROVAL is the agents.approval@1 id). takeover: it met something only its person can do (a sign-in, a code). payment: it needs funds. WHAT says it in words. Null unless STATUS is waiting. |  |
| `preview` | object \| null |  | The last thing said in the run, for a list: the words, who said them and when. |  |
| `context` | object |  | Where the run was asked: the site, the page and what the page is about. A run may use it; it never grants access. | - `no other fields` |
| `context.site` | string |  |  | - `minLength 1`<br>- `maxLength 60` |
| `context.url` | string |  | The page's address. | - `format uri`<br>- `maxLength 2000` |
| `context.title` | string |  |  | - `maxLength 300` |
| `context.kind` | string |  |  | - `maxLength 60` |
| `context.id` | string |  |  | - `maxLength 160` |
| `outputs` | array of object |  | What the run made, newest last: for its card, and for the feed when it is shared. | - `maxItems 50`<br>- `items: no other fields` |
| `outputs[].kind` | enum | yes |  | - `one of "link", "file", "image", "video", "audio", "pr", "repo", "page", "post", "run"` |
| `outputs[].title` | string | yes |  | - `minLength 1`<br>- `maxLength 300` |
| `outputs[].url` | string |  | Where it is. | - `format uri`<br>- `maxLength 2000` |
| `outputs[].thumb` | string |  | A small picture of it. | - `format uri`<br>- `maxLength 2000` |
| `outputs[].media_id` | string |  |  | - `maxLength 60` |
| `outputs[].state` | string |  | A pull request's or a check's state in a word (open, green, red, merged). | - `maxLength 40` |
| `counts` | object |  | Counts a list shows without reading the steps. | - `no other fields` |
| `counts.steps` | integer |  |  | - `minimum 0` |
| `counts.children` | integer |  |  | - `minimum 0` |
| `counts.files` | integer |  |  | - `minimum 0` |
| `counts.approvals` | integer |  |  | - `minimum 0` |
| `counts.unread` | integer |  |  | - `minimum 0` |
| `visibility` | enum |  | Who may read the run: its owner only, people it was shared with, or anyone with its address. | - `one of "private", "shared", "public"` |
| `ended_at` | string \| null |  | When the run reached an end status. Null while it has not. | - `format date-time` |
| `mandate` | object \| null |  | Why the run exists when it came through intake (agent-engine.md §5): SOURCE is where the ask was made; THREAD is the conversation it was made in (a post, a room, an issue: its service, kind and id, and its address), where the run answers and from which later messages reach it; ASKED\_BY is who asked; STRENGTH says whether they asked outright (explicit) or the Source's rule read an ask in what they wrote (inferred: such a run confirms before it acts); SCOPE says in words what the run may do for them. Null (or absent) for a run a person started themselves. |  |
| `lineage` | object \| null |  | Where the run came from when it continues another (agent-engine.md, from the code study): FROM is that run's ref; AT\_TURN is the turn it was taken from (agents.step@1's TURN; the steps before it are the new run's history); HOW is fork (a person branched the conversation), handoff (another harness or model took it on, with a written summary as its first step) or regenerate (the same turn answered again). Null for a run that starts fresh. |  |
| `play` | string |  | The play (agents.play@1's ID) this run's tree follows: solo, advisor, plan-execute, fan-out, team, grader-loop, research-build, issue-to-pr, watch, or a person's own. On a child run it is its parent's. | - `pattern ^[a-z][a-z0-9-]{0,60}$` |
| `role` | string |  | A child run's role in its parent's play (one of the play's role ids: planner, executor, advisor, grader, reviewer, searcher, worker…). | - `pattern ^[a-z][a-z0-9-]{0,60}$` |

## Examples

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

 Valid: a-fork-at-turn-three

~~~~
{
  "id": "c_8f3k2mx",
  "ref": "chat:c_8f3k2mx",
  "kind": "chat",
  "title": "Keyboard Scout",
  "owner": {
    "type": "user",
    "id": "usr_01K9R2V6M8Q4T0B7C3D5E9F2GH"
  },
  "agent": {
    "id": "vibe",
    "name": "Keyboard Scout",
    "product": "chat",
    "glyph": "search",
    "hue": 212,
    "harness": "openvibe"
  },
  "status": "waiting",
  "waiting": {
    "for": "message",
    "since": "2026-11-12T10:02:00Z"
  },
  "preview": {
    "text": "Three picks under $90, with sources.",
    "from": "agent",
    "at": "2026-11-12T10:02:00Z"
  },
  "model": {
    "id": "openvibe/auto",
    "source": "openvibe",
    "name": "Auto"
  },
  "where": {
    "kind": "openvibe"
  },
  "context": {
    "site": "deals",
    "url": "https://openvibe.deals/",
    "title": "Deals"
  },
  "url": "https://openvibe.chat/vibe/c_8f3k2m",
  "counts": {
    "steps": 6,
    "unread": 1
  },
  "visibility": "private",
  "created_at": "2026-11-12T10:00:00Z",
  "updated_at": "2026-11-12T10:02:00Z",
  "lineage": {
    "from": "chat:c_8f3k2m",
    "at_turn": 3,
    "how": "fork"
  }
}
~~~~

Valid: a-reviewer-in-a-play

~~~~
{
  "id": "tsk_01K9R4C5D6E7F8G9H0J1K2M3NP",
  "ref": "codes:tsk_01K9R4C5D6E7F8G9H0J1K2M3NP",
  "kind": "code",
  "title": "Review: a dark mode toggle",
  "owner": {
    "type": "user",
    "id": "usr_01K9R2V6M8Q4T0B7C3D5E9F2GH"
  },
  "agent": {
    "id": "orchestrator",
    "name": "Codes",
    "product": "codes",
    "glyph": "code",
    "hue": 262,
    "harness": "codex"
  },
  "target": "openvibe",
  "status": "running",
  "doing": {
    "text": "Reading OpenVibe.Live's overlay code",
    "glyph": "book-open"
  },
  "model": {
    "id": "plans/codex|gpt-6-sol",
    "source": "plans",
    "name": "GPT-6 Sol",
    "effort": "xhigh"
  },
  "where": {
    "kind": "runner",
    "id": "run_ws1",
    "name": "Workstation"
  },
  "outputs": [],
  "counts": {
    "steps": 6
  },
  "budget": {
    "usd": 5,
    "steps": 400
  },
  "cost": {
    "usd": 0.84,
    "steps": 41
  },
  "created_at": "2026-11-12T10:00:00Z",
  "updated_at": "2026-11-12T10:09:30Z",
  "ended_at": null,
  "parent_run": "tsk_01K9R2V6M8Q4T0B7C3D5E9F2GH",
  "play": "issue-to-pr",
  "role": "reviewer"
}
~~~~

Valid: an-agent-from-a-blueprint

~~~~
{
  "id": "tsk_01K9R2V6M8Q4T0B7C3D5E9F2GH",
  "owner": {
    "type": "user",
    "id": "usr_01K9R2V6M8Q4T0B7C3D5E9F2GH"
  },
  "agent": {
    "id": "agt_01K9R2V6M8Q4T0B7C3D5E9F2GH",
    "name": "Vibe",
    "product": "actor",
    "blueprint": "bp_01JD8C4QJ7Z3K9W2X5V6N1M0PA@3"
  },
  "parent_run": null,
  "budget": {
    "usd": 2.5,
    "tokens": 200000,
    "seconds": 600,
    "steps": 40
  },
  "status": "running",
  "created_at": "2026-11-12T10:00:00Z",
  "updated_at": "2026-11-12T10:00:12Z",
  "result_summary": null,
  "cost": {
    "usd": 0.031,
    "tokens": 4210,
    "seconds": 12,
    "steps": 3
  }
}
~~~~

Valid: an-ask-from-the-feed

~~~~
{
  "id": "tsk_01K9R2V6M8Q4T0B7C3D5E9F2GH",
  "ref": "codes:tsk_01K9R2V6M8Q4T0B7C3D5E9F2GH",
  "kind": "code",
  "title": "A dark mode toggle on the overlay page",
  "owner": {
    "type": "user",
    "id": "usr_01K9R2V6M8Q4T0B7C3D5E9F2GH"
  },
  "agent": {
    "id": "orchestrator",
    "name": "Codes",
    "product": "codes",
    "glyph": "code",
    "hue": 262,
    "harness": "codex",
    "capabilities": [
      "resume",
      "approvals",
      "subagents",
      "files",
      "shell",
      "mcp"
    ]
  },
  "target": "openvibe",
  "status": "running",
  "doing": {
    "text": "Reading OpenVibe.Live's overlay code",
    "glyph": "book-open"
  },
  "model": {
    "id": "plans/codex|gpt-6-sol",
    "source": "plans",
    "name": "GPT-6 Sol",
    "effort": "xhigh"
  },
  "where": {
    "kind": "runner",
    "id": "run_ws1",
    "name": "Workstation"
  },
  "outputs": [
    {
      "kind": "pr",
      "title": "Live: a goal bar overlay",
      "url": "https://git.openvibe.codes/OpenVibers/OpenVibe.Live/pulls/871",
      "state": "open"
    }
  ],
  "counts": {
    "steps": 41,
    "children": 2,
    "files": 5
  },
  "budget": {
    "usd": 5,
    "steps": 400
  },
  "cost": {
    "usd": 0.84,
    "steps": 41,
    "by": {
      "machine": {
        "tokens": 182000
      },
      "credits": {
        "usd": 0.84,
        "tokens": 61000
      }
    }
  },
  "created_at": "2026-11-12T10:00:00Z",
  "updated_at": "2026-11-12T10:09:30Z",
  "ended_at": null,
  "mandate": {
    "source": "feed",
    "thread": {
      "service": "community",
      "kind": "post",
      "id": "pst_01K9R3A1B2C3D4E5F6G7H8J9KM",
      "url": "https://openvibe.community/p/pst_01K9R3A1B2C3D4E5F6G7H8J9KM"
    },
    "asked_by": {
      "type": "user",
      "id": "usr_01K9R2V6M8Q4T0B7C3D5E9F2GH"
    },
    "strength": "explicit",
    "scope": "Change OpenVibe.Live and open pull requests for the owner to review; never merge."
  },
  "play": "issue-to-pr"
}
~~~~

Valid: chat-waiting-for-a-message

~~~~
{
  "id": "c_8f3k2m",
  "ref": "chat:c_8f3k2m",
  "kind": "chat",
  "title": "Keyboard Scout",
  "owner": {
    "type": "user",
    "id": "usr_01K9R2V6M8Q4T0B7C3D5E9F2GH"
  },
  "agent": {
    "id": "vibe",
    "name": "Keyboard Scout",
    "product": "chat",
    "glyph": "search",
    "hue": 212,
    "harness": "openvibe"
  },
  "status": "waiting",
  "waiting": {
    "for": "message",
    "since": "2026-11-12T10:02:00Z"
  },
  "preview": {
    "text": "Three picks under $90, with sources.",
    "from": "agent",
    "at": "2026-11-12T10:02:00Z"
  },
  "model": {
    "id": "openvibe/auto",
    "source": "openvibe",
    "name": "Auto"
  },
  "where": {
    "kind": "openvibe"
  },
  "context": {
    "site": "deals",
    "url": "https://openvibe.deals/",
    "title": "Deals"
  },
  "url": "https://openvibe.chat/vibe/c_8f3k2m",
  "counts": {
    "steps": 6,
    "unread": 1
  },
  "visibility": "private",
  "created_at": "2026-11-12T10:00:00Z",
  "updated_at": "2026-11-12T10:02:00Z"
}
~~~~

Valid: code-on-openvibe

~~~~
{
  "id": "tsk_01K9R2V6M8Q4T0B7C3D5E9F2GH",
  "ref": "codes:tsk_01K9R2V6M8Q4T0B7C3D5E9F2GH",
  "kind": "code",
  "title": "A goal bar for my sub goal",
  "owner": {
    "type": "user",
    "id": "usr_01K9R2V6M8Q4T0B7C3D5E9F2GH"
  },
  "agent": {
    "id": "orchestrator",
    "name": "Codes",
    "product": "codes",
    "glyph": "code",
    "hue": 262,
    "harness": "codex"
  },
  "target": "openvibe",
  "status": "running",
  "doing": {
    "text": "Reading OpenVibe.Live's overlay code",
    "glyph": "book-open"
  },
  "model": {
    "id": "plans/codex|gpt-6-sol",
    "source": "plans",
    "name": "GPT-6 Sol",
    "effort": "xhigh"
  },
  "where": {
    "kind": "runner",
    "id": "run_ws1",
    "name": "Workstation"
  },
  "outputs": [
    {
      "kind": "pr",
      "title": "Live: a goal bar overlay",
      "url": "https://git.openvibe.codes/OpenVibers/OpenVibe.Live/pulls/871",
      "state": "open"
    }
  ],
  "counts": {
    "steps": 41,
    "children": 2,
    "files": 5
  },
  "budget": {
    "usd": 5,
    "steps": 400
  },
  "cost": {
    "usd": 0.84,
    "steps": 41
  },
  "created_at": "2026-11-12T10:00:00Z",
  "updated_at": "2026-11-12T10:09:30Z",
  "ended_at": null
}
~~~~

Valid: done

~~~~
{
  "id": "job_01K9R2V6M8Q4T0B7C3D5E9F2GH",
  "owner": {
    "type": "user",
    "id": "usr_01K9R2V6M8Q4T0B7C3D5E9F2GH"
  },
  "agent": {
    "id": "codes",
    "name": "Claude Code",
    "product": "codes"
  },
  "parent_run": null,
  "status": "done",
  "created_at": "2026-11-12T09:00:00Z",
  "updated_at": "2026-11-12T09:04:30Z",
  "result_summary": "Opened a pull request with the two fixes.",
  "cost": {
    "usd": 0.42,
    "tokens": 88000,
    "seconds": 270,
    "steps": 18
  }
}
~~~~

Valid: running

~~~~
{
  "id": "tsk_01K9R2V6M8Q4T0B7C3D5E9F2GH",
  "owner": {
    "type": "user",
    "id": "usr_01K9R2V6M8Q4T0B7C3D5E9F2GH"
  },
  "agent": {
    "id": "agt_01K9R2V6M8Q4T0B7C3D5E9F2GH",
    "name": "Vibe",
    "product": "actor"
  },
  "parent_run": null,
  "budget": {
    "usd": 2.5,
    "tokens": 200000,
    "seconds": 600,
    "steps": 40
  },
  "status": "running",
  "created_at": "2026-11-12T10:00:00Z",
  "updated_at": "2026-11-12T10:00:12Z",
  "result_summary": null,
  "cost": {
    "usd": 0.031,
    "tokens": 4210,
    "seconds": 12,
    "steps": 3
  }
}
~~~~

Rejected: a-capability-nobody-has

~~~~
{
  "id": "tsk_01K9R2V6M8Q4T0B7C3D5E9F2GH",
  "ref": "codes:tsk_01K9R2V6M8Q4T0B7C3D5E9F2GH",
  "kind": "code",
  "title": "A dark mode toggle on the overlay page",
  "owner": {
    "type": "user",
    "id": "usr_01K9R2V6M8Q4T0B7C3D5E9F2GH"
  },
  "agent": {
    "id": "orchestrator",
    "name": "Codes",
    "product": "codes",
    "glyph": "code",
    "hue": 262,
    "harness": "codex",
    "capabilities": [
      "files",
      "telepathy"
    ]
  },
  "target": "openvibe",
  "status": "running",
  "doing": {
    "text": "Reading OpenVibe.Live's overlay code",
    "glyph": "book-open"
  },
  "model": {
    "id": "plans/codex|gpt-6-sol",
    "source": "plans",
    "name": "GPT-6 Sol",
    "effort": "xhigh"
  },
  "where": {
    "kind": "runner",
    "id": "run_ws1",
    "name": "Workstation"
  },
  "outputs": [
    {
      "kind": "pr",
      "title": "Live: a goal bar overlay",
      "url": "https://git.openvibe.codes/OpenVibers/OpenVibe.Live/pulls/871",
      "state": "open"
    }
  ],
  "counts": {
    "steps": 41,
    "children": 2,
    "files": 5
  },
  "budget": {
    "usd": 5,
    "steps": 400
  },
  "cost": {
    "usd": 0.84,
    "steps": 41,
    "by": {
      "machine": {
        "tokens": 182000
      },
      "credits": {
        "usd": 0.84,
        "tokens": 61000
      }
    }
  },
  "created_at": "2026-11-12T10:00:00Z",
  "updated_at": "2026-11-12T10:09:30Z",
  "ended_at": null,
  "mandate": {
    "source": "feed",
    "thread": {
      "service": "community",
      "kind": "post",
      "id": "pst_01K9R3A1B2C3D4E5F6G7H8J9KM",
      "url": "https://openvibe.community/p/pst_01K9R3A1B2C3D4E5F6G7H8J9KM"
    },
    "asked_by": {
      "type": "user",
      "id": "usr_01K9R2V6M8Q4T0B7C3D5E9F2GH"
    },
    "strength": "explicit",
    "scope": "Change OpenVibe.Live and open pull requests for the owner to review; never merge."
  },
  "play": "issue-to-pr"
}
~~~~

Rejected: a-lineage-from-no-run

~~~~
{
  "id": "c_8f3k2mx",
  "ref": "chat:c_8f3k2mx",
  "kind": "chat",
  "title": "Keyboard Scout",
  "owner": {
    "type": "user",
    "id": "usr_01K9R2V6M8Q4T0B7C3D5E9F2GH"
  },
  "agent": {
    "id": "vibe",
    "name": "Keyboard Scout",
    "product": "chat",
    "glyph": "search",
    "hue": 212,
    "harness": "openvibe"
  },
  "status": "waiting",
  "waiting": {
    "for": "message",
    "since": "2026-11-12T10:02:00Z"
  },
  "preview": {
    "text": "Three picks under $90, with sources.",
    "from": "agent",
    "at": "2026-11-12T10:02:00Z"
  },
  "model": {
    "id": "openvibe/auto",
    "source": "openvibe",
    "name": "Auto"
  },
  "where": {
    "kind": "openvibe"
  },
  "context": {
    "site": "deals",
    "url": "https://openvibe.deals/",
    "title": "Deals"
  },
  "url": "https://openvibe.chat/vibe/c_8f3k2m",
  "counts": {
    "steps": 6,
    "unread": 1
  },
  "visibility": "private",
  "created_at": "2026-11-12T10:00:00Z",
  "updated_at": "2026-11-12T10:02:00Z",
  "lineage": {
    "from": "not a run",
    "at_turn": 3,
    "how": "fork"
  }
}
~~~~

Rejected: a-mandate-without-who-asked

~~~~
{
  "id": "tsk_01K9R2V6M8Q4T0B7C3D5E9F2GH",
  "ref": "codes:tsk_01K9R2V6M8Q4T0B7C3D5E9F2GH",
  "kind": "code",
  "title": "A dark mode toggle on the overlay page",
  "owner": {
    "type": "user",
    "id": "usr_01K9R2V6M8Q4T0B7C3D5E9F2GH"
  },
  "agent": {
    "id": "orchestrator",
    "name": "Codes",
    "product": "codes",
    "glyph": "code",
    "hue": 262,
    "harness": "codex",
    "capabilities": [
      "resume",
      "approvals",
      "subagents",
      "files",
      "shell",
      "mcp"
    ]
  },
  "target": "openvibe",
  "status": "running",
  "doing": {
    "text": "Reading OpenVibe.Live's overlay code",
    "glyph": "book-open"
  },
  "model": {
    "id": "plans/codex|gpt-6-sol",
    "source": "plans",
    "name": "GPT-6 Sol",
    "effort": "xhigh"
  },
  "where": {
    "kind": "runner",
    "id": "run_ws1",
    "name": "Workstation"
  },
  "outputs": [
    {
      "kind": "pr",
      "title": "Live: a goal bar overlay",
      "url": "https://git.openvibe.codes/OpenVibers/OpenVibe.Live/pulls/871",
      "state": "open"
    }
  ],
  "counts": {
    "steps": 41,
    "children": 2,
    "files": 5
  },
  "budget": {
    "usd": 5,
    "steps": 400
  },
  "cost": {
    "usd": 0.84,
    "steps": 41,
    "by": {
      "machine": {
        "tokens": 182000
      },
      "credits": {
        "usd": 0.84,
        "tokens": 61000
      }
    }
  },
  "created_at": "2026-11-12T10:00:00Z",
  "updated_at": "2026-11-12T10:09:30Z",
  "ended_at": null,
  "mandate": {
    "source": "feed",
    "thread": {
      "service": "community",
      "kind": "post",
      "id": "pst_01K9R3A1B2C3D4E5F6G7H8J9KM",
      "url": "https://openvibe.community/p/pst_01K9R3A1B2C3D4E5F6G7H8J9KM"
    },
    "strength": "explicit",
    "scope": "Change OpenVibe.Live and open pull requests for the owner to review; never merge."
  },
  "play": "issue-to-pr"
}
~~~~

Rejected: a-rung-the-ladder-has-not

~~~~
{
  "id": "tsk_01K9R2V6M8Q4T0B7C3D5E9F2GH",
  "ref": "codes:tsk_01K9R2V6M8Q4T0B7C3D5E9F2GH",
  "kind": "code",
  "title": "A dark mode toggle on the overlay page",
  "owner": {
    "type": "user",
    "id": "usr_01K9R2V6M8Q4T0B7C3D5E9F2GH"
  },
  "agent": {
    "id": "orchestrator",
    "name": "Codes",
    "product": "codes",
    "glyph": "code",
    "hue": 262,
    "harness": "codex",
    "capabilities": [
      "resume",
      "approvals",
      "subagents",
      "files",
      "shell",
      "mcp"
    ]
  },
  "target": "openvibe",
  "status": "running",
  "doing": {
    "text": "Reading OpenVibe.Live's overlay code",
    "glyph": "book-open"
  },
  "model": {
    "id": "plans/codex|gpt-6-sol",
    "source": "plans",
    "name": "GPT-6 Sol",
    "effort": "xhigh"
  },
  "where": {
    "kind": "runner",
    "id": "run_ws1",
    "name": "Workstation"
  },
  "outputs": [
    {
      "kind": "pr",
      "title": "Live: a goal bar overlay",
      "url": "https://git.openvibe.codes/OpenVibers/OpenVibe.Live/pulls/871",
      "state": "open"
    }
  ],
  "counts": {
    "steps": 41,
    "children": 2,
    "files": 5
  },
  "budget": {
    "usd": 5,
    "steps": 400
  },
  "cost": {
    "usd": 0.84,
    "steps": 41,
    "by": {
      "machine": {
        "tokens": 182000
      },
      "credits": {
        "usd": 0.84,
        "tokens": 61000
      },
      "gift": {
        "usd": 1
      }
    }
  },
  "created_at": "2026-11-12T10:00:00Z",
  "updated_at": "2026-11-12T10:09:30Z",
  "ended_at": null,
  "mandate": {
    "source": "feed",
    "thread": {
      "service": "community",
      "kind": "post",
      "id": "pst_01K9R3A1B2C3D4E5F6G7H8J9KM",
      "url": "https://openvibe.community/p/pst_01K9R3A1B2C3D4E5F6G7H8J9KM"
    },
    "asked_by": {
      "type": "user",
      "id": "usr_01K9R2V6M8Q4T0B7C3D5E9F2GH"
    },
    "strength": "explicit",
    "scope": "Change OpenVibe.Live and open pull requests for the owner to review; never merge."
  },
  "play": "issue-to-pr"
}
~~~~

Rejected: ref-without-a-product

~~~~
{
  "id": "c_8f3k2m",
  "ref": "c_8f3k2m",
  "kind": "chat",
  "title": "Keyboard Scout",
  "owner": {
    "type": "user",
    "id": "usr_01K9R2V6M8Q4T0B7C3D5E9F2GH"
  },
  "agent": {
    "id": "vibe",
    "name": "Keyboard Scout",
    "product": "chat",
    "glyph": "search",
    "hue": 212,
    "harness": "openvibe"
  },
  "status": "waiting",
  "waiting": {
    "for": "message",
    "since": "2026-11-12T10:02:00Z"
  },
  "preview": {
    "text": "Three picks under $90, with sources.",
    "from": "agent",
    "at": "2026-11-12T10:02:00Z"
  },
  "model": {
    "id": "openvibe/auto",
    "source": "openvibe",
    "name": "Auto"
  },
  "where": {
    "kind": "openvibe"
  },
  "context": {
    "site": "deals",
    "url": "https://openvibe.deals/",
    "title": "Deals"
  },
  "url": "https://openvibe.chat/vibe/c_8f3k2m",
  "counts": {
    "steps": 6,
    "unread": 1
  },
  "visibility": "private",
  "created_at": "2026-11-12T10:00:00Z",
  "updated_at": "2026-11-12T10:02:00Z"
}
~~~~

Rejected: unknown-status

~~~~
{
  "id": "run_01K9R2V6M8Q4T0B7C3D5E9F2GH",
  "owner": {
    "type": "user",
    "id": "usr_01K9R2V6M8Q4T0B7C3D5E9F2GH"
  },
  "agent": {
    "id": "actor"
  },
  "status": "queued",
  "created_at": "2026-11-12T09:00:00Z",
  "updated_at": "2026-11-12T09:00:00Z"
}
~~~~

Rejected: waiting-for-nothing-known

~~~~
{
  "id": "c_8f3k2m",
  "ref": "chat:c_8f3k2m",
  "kind": "chat",
  "title": "Keyboard Scout",
  "owner": {
    "type": "user",
    "id": "usr_01K9R2V6M8Q4T0B7C3D5E9F2GH"
  },
  "agent": {
    "id": "vibe",
    "name": "Keyboard Scout",
    "product": "chat",
    "glyph": "search",
    "hue": 212,
    "harness": "openvibe"
  },
  "status": "waiting",
  "waiting": {
    "for": "luck"
  },
  "preview": {
    "text": "Three picks under $90, with sources.",
    "from": "agent",
    "at": "2026-11-12T10:02:00Z"
  },
  "model": {
    "id": "openvibe/auto",
    "source": "openvibe",
    "name": "Auto"
  },
  "where": {
    "kind": "openvibe"
  },
  "context": {
    "site": "deals",
    "url": "https://openvibe.deals/",
    "title": "Deals"
  },
  "url": "https://openvibe.chat/vibe/c_8f3k2m",
  "counts": {
    "steps": 6,
    "unread": 1
  },
  "visibility": "private",
  "created_at": "2026-11-12T10:00:00Z",
  "updated_at": "2026-11-12T10:02:00Z"
}
~~~~

## Validate

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