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

# Bot playbook reference

> Every key a One Agent bot reads, one row each: its type, its default, what it does and which door sets it.


Every key a One Agent bot reads, one row each. This page is generated from the playbook reference template, `scripts/bot-templates/bot-playbook-reference.yaml`, so the table and the template cannot disagree. Copy the lines you need into the harness task of a bot and delete the rest; the order of keys does not matter.

The keys `posture`, `peers`, `review`, `max_runtime_s` and `workspace` apply only on `agent: one-agent`; saving them on another loop runtime is refused. See [Bots](/bots/overview#what-a-save-checks).

## Doors

| Door | What it is |
|---|---|
| YAML | Any door that saves the bot's YAML: Studio's Code tab, `POST /v1/workflows` and `PATCH /v1/workflows/{id}`, MCP `plungeai_workflow` `create` and `update`, `ocean-cli workflows create` and `update`. |
| Studio | The `bot-config` block of Studio's Think tab, which writes these keys into the YAML for you; the bot's Settings panel edits some of them too. |
| Stored policy | The permission presets and the spend cap stored on the saved bot: `PUT /v1/workflows/{id}/permissions`, MCP `permissions_set`, `ocean-cli workflows permissions set`, and Studio's Permissions and Budget fields. They are merged with the YAML on every run ([Permissions and budget](/bots/permissions-and-budget)). |

## Keys

| Key | Type | Default | What it does | Set by |
|---|---|---|---|---|
| `type` | string | none, required | A bot is exactly one task of type harness (a mission-bounded loop run). | YAML |
| `id` | string | none, required | The task id; the run's result is stored under it. | YAML |
| `agent` | string | harness-agent on a task with no agent line | The runtime that runs the loop; only one-agent reads the bot keys below. | YAML |
| `goal` | string | none, required | What this run is asked to do; `{input}` is replaced by the run's input. | YAML |
| `mission` | string | none, required | The standing instructions (system prompt); end it by telling the bot to call `task_complete`. | YAML |
| `workspace` | string | bot for a saved bot | `bot` keeps the bot's virtual files across runs; run starts every run clean. | YAML, Studio |
| `posture` | string | unset (not read-only) | `read_only` denies every write, send, delete and pay (and delegate, `invoke_workflow`, MCP tools) whatever permissions says. | YAML, Studio |
| `peers` | list | none (no 1:1 messaging) | The only bots, or `a2a:<url>` agents, that `message_bot` may reach by name or id; a `group:<name>` send needs no peers. | YAML, Studio |
| `review` | string | unset (no review) | `auto` has a model check each allowed write, send, delete or pay call before it runs (one extra call each). | YAML, Studio |
| `max_runtime_s` | number | unset (no cap beyond the turn cap) | Wall-clock cap in seconds for the whole run; the run stops with `stop_reason` timeout. | YAML, Studio |
| `budget_usd_run` | number | unset (no cap); a lower cap stored on the saved bot wins | Per-run spend cap in USD, above 0 (blank = no cap; 0 is refused by the YAML block and Settings, and the runtime ignores a non-positive stored value); the loop stops at the next turn once cost passes it. | YAML, Stored policy |
| `permissions` | map | allow, except the money floor which always asks | Per-class or per-tool presets for send, write, pay and delete; reads are allowed unless you name `read` or the tool. | YAML, Stored policy |
| `permissions.send` | string | allow, but a registry-agent call with operation send is always gated (ALWAYS_GATED) even with send: allow | Preset for outward messages (email, chat, `message_bot`); ask pauses the run for the owner's approval. | YAML, Stored policy |
| `permissions.write` | string | allow | Preset for calls that change something (files, schedule create, browser clicks). | YAML, Stored policy |
| `permissions.pay` | string | allow, but payment ops always ask | `hand_off` never runs the call and tells the owner it is theirs to do; the result ends with a `[handed off to you]` line. | YAML, Stored policy |
| `permissions.delete` | string | allow | Preset for destructive calls; deny refuses them. | YAML, Stored policy |
| `allowed_tools` | list | every tool of the runtime on agent one-agent (universal-agent's default is the 8-tool DEFAULT_FENCE); list tools to narrow it | The tool fence; only these tools reach the model and `task_complete` must be in it. | YAML, Studio |
| `allowed_tools.schedule` | tool name | available when `allowed_tools` is absent; name it to keep it in a narrowed fence | The bot schedules itself (create, list, pause, resume, delete, `run_now`, wake); needs a saved bot. | YAML, Studio |
| `allowed_tools.message_bot` | tool name | available when `allowed_tools` is absent; name it to keep it in a narrowed fence | Sends to a peer or `group:<name>` (async by default, replies arrive as `[MESSAGE from ...]` in a later run); needs a saved bot. | YAML, Studio |
| `allowed_tools.browser` | tool name | available when `allowed_tools` is absent; name it to keep it in a narrowed fence | Drives a cloud browser in one session, with the bot's saved login (click and type count as writes). | YAML, Studio |
| `allowed_tools.memory` | tool name | available when `allowed_tools` is absent; name it to keep it in a narrowed fence | Add, replace, remove, read, or forget `{query}` to purge matching entries. | YAML, Studio |
| `allowed_tools.code` | tool name | available when `allowed_tools` is absent; name it to keep it in a narrowed fence | Hands one turn (max 300 s) to the owner's OceanCode over the Local Tunnel; no daemon connected means no tool. | YAML, Studio |
| `allowed_agents` | list | all except payment agents | The registry agents `call_agent` may reach (payment agents only when named). | YAML |
| `skills` | list | none | Registry skill ids injected into the system prompt, at most 5. | YAML, Studio |
| `persona` | string | none | One persona id that sets the voice. | YAML, Studio |
| `max_turns` | number | the effort preset's cap, else the runtime's own | Turn cap for the loop (`max_iterations` is the legacy spelling). | YAML, Studio |
| `effort` | string | unset | `quick`, standard or deep sets the default turn cap and how wide the bot works. | YAML |

## The whole file

```yaml
# Playbook reference: every key a one-agent bot reads, set once, with one comment line above it.
# Each comment reads "what it does. Default: what you get when the key is absent."
# Copy the lines you need and delete the rest. Order in a real bot does not matter.
workflow:
  name: "Playbook Reference"
  description: "Every one-agent bot key, set and explained in comments. Copy the lines you need."
  tasks:
    # type: a bot is exactly one task of type harness (a mission-bounded loop run). Default: none, required.
    - type: harness
      # id: the task id; the run's result is stored under it. Default: none, required.
      id: reference
      # agent: the runtime that runs the loop; only one-agent reads the bot keys below. Default: harness-agent on a task with no agent line.
      agent: one-agent
      # goal: what this run is asked to do; {input} is replaced by the run's input. Default: none, required.
      goal: "{input}"
      # mission: the standing instructions (system prompt); end it by telling the bot to call task_complete. Default: none, required.
      mission: |
        You are Playbook Reference, a bot that exists to show every bot key in its own YAML.
        What each key does is written as a comment in that YAML, which you cannot see, so
        never explain a key from memory. Reply with REFERENCE-OK, a one-line echo of the
        goal, and the sentence "Read the comment above each key in this playbook."
        Your persona only sets the voice; this reply format always wins.
        When your task is finished, call the task_complete tool with your reply.
      # workspace: bot keeps the bot's virtual files across runs; run starts every run clean. Default: bot for a saved bot.
      workspace: bot
      # posture: read_only denies every write, send, delete and pay (and delegate, invoke_workflow, MCP tools) whatever permissions says. Default: unset (not read-only).
      posture: read_only
      # peers: the only bots, or "a2a:<url>" agents, that message_bot may reach by name or id; a group:<name> send needs no peers. Default: none (no 1:1 messaging).
      peers: ["Playbook Peer"]
      # review: auto has a model check each allowed write, send, delete or pay call before it runs (one extra call each). Default: unset (no review).
      review: auto
      # max_runtime_s: wall-clock cap in seconds for the whole run; the run stops with stop_reason timeout. Default: unset (no cap beyond the turn cap).
      max_runtime_s: 300
      # budget_usd_run: per-run spend cap in USD, above 0 (blank = no cap; 0 is refused by the YAML block and Settings, and the runtime ignores a non-positive stored value); the loop stops at the next turn once cost passes it. Default: unset (no cap); a lower cap stored on the saved bot wins.
      budget_usd_run: 1
      # permissions: per-class or per-tool presets for send, write, pay and delete; reads are allowed unless you name `read` or the tool. Default: allow, except the money floor which always asks.
      permissions:
        # permissions.send: preset for outward messages (email, chat, message_bot); ask pauses the run for the owner's approval. Default: allow, but a registry-agent call with operation send is always gated (ALWAYS_GATED) even with send: allow.
        send: ask
        # permissions.write: preset for calls that change something (files, schedule create, browser clicks). Default: allow.
        write: allow
        # permissions.pay: hand_off never runs the call and tells the owner it is theirs to do; the result ends with a [handed off to you] line. Default: allow, but payment ops always ask.
        pay: hand_off
        # permissions.delete: preset for destructive calls; deny refuses them. Default: allow.
        delete: deny
      # allowed_tools: the tool fence; only these tools reach the model and task_complete must be in it. Default: every tool of the runtime on agent one-agent (universal-agent's default is the 8-tool DEFAULT_FENCE); list tools to narrow it.
      allowed_tools: [web_search, web_fetch, memory, schedule, message_bot, browser, code, task_complete]
      # allowed_tools.schedule: the bot schedules itself (create, list, pause, resume, delete, run_now, wake); needs a saved bot. Default: available when allowed_tools is absent; name it to keep it in a narrowed fence.
      # allowed_tools.message_bot: sends to a peer or group:<name> (async by default, replies arrive as [MESSAGE from ...] in a later run); needs a saved bot. Default: available when allowed_tools is absent; name it to keep it in a narrowed fence.
      # allowed_tools.browser: drives a cloud browser in one session, with the bot's saved login (click and type count as writes). Default: available when allowed_tools is absent; name it to keep it in a narrowed fence.
      # allowed_tools.memory: add, replace, remove, read, or forget {query} to purge matching entries. Default: available when allowed_tools is absent; name it to keep it in a narrowed fence.
      # allowed_tools.code: hands one turn (max 300 s) to the owner's OceanCode over the Local Tunnel; no daemon connected means no tool. Default: available when allowed_tools is absent; name it to keep it in a narrowed fence.
      # allowed_agents: the registry agents call_agent may reach (payment agents only when named). Default: all except payment agents.
      allowed_agents: [google-gmail]
      # skills: registry skill ids injected into the system prompt, at most 5. Default: none.
      skills: [web-search]
      # persona: one persona id that sets the voice. Default: none.
      persona: richard-feynman
      # max_turns: turn cap for the loop (max_iterations is the legacy spelling). Default: the effort preset's cap, else the runtime's own.
      max_turns: 8
      # effort: quick, standard or deep sets the default turn cap and how wide the bot works. Default: unset.
      effort: quick
```
