{
  "slug": "pilot/playbook-reference",
  "title": "Playbook Reference",
  "description": "Complete YAML schema for Cuitty Pilot playbooks — inputs, steps, actions, assertions, and captures.",
  "url": "https://cuitty.com/docs/pilot/playbook-reference",
  "markdown_url": "https://cuitty.com/docs/pilot/playbook-reference.md",
  "json_url": "https://cuitty.com/docs/pilot/playbook-reference.json",
  "frontmatter": {
    "title": "Playbook Reference",
    "description": "Complete YAML schema for Cuitty Pilot playbooks — inputs, steps, actions, assertions, and captures.",
    "order": 2,
    "section": "Pilot",
    "updatedAt": "2026-06-01"
  },
  "headings": [
    {
      "depth": 1,
      "slug": "playbook-reference",
      "text": "Playbook Reference"
    },
    {
      "depth": 2,
      "slug": "top-level-structure",
      "text": "Top-level structure"
    },
    {
      "depth": 2,
      "slug": "inputs",
      "text": "Inputs"
    },
    {
      "depth": 2,
      "slug": "outputs",
      "text": "Outputs"
    },
    {
      "depth": 2,
      "slug": "steps",
      "text": "Steps"
    },
    {
      "depth": 3,
      "slug": "selectors",
      "text": "Selectors"
    },
    {
      "depth": 2,
      "slug": "browser-actions-14",
      "text": "Browser actions (14)"
    },
    {
      "depth": 2,
      "slug": "assertions",
      "text": "Assertions"
    },
    {
      "depth": 2,
      "slug": "capture-sources",
      "text": "Capture sources"
    },
    {
      "depth": 2,
      "slug": "executor-actions",
      "text": "Executor actions"
    },
    {
      "depth": 3,
      "slug": "terraform-tf_",
      "text": "Terraform (tf_*)"
    },
    {
      "depth": 3,
      "slug": "git-git_",
      "text": "Git (git_*)"
    },
    {
      "depth": 3,
      "slug": "persist-persist_",
      "text": "Persist (persist_*)"
    },
    {
      "depth": 3,
      "slug": "shell-shell_",
      "text": "Shell (shell_*)"
    },
    {
      "depth": 3,
      "slug": "http-http_",
      "text": "HTTP (http_*)"
    },
    {
      "depth": 3,
      "slug": "composite",
      "text": "Composite"
    },
    {
      "depth": 2,
      "slug": "example",
      "text": "Example"
    },
    {
      "depth": 2,
      "slug": "see-also",
      "text": "See also"
    }
  ],
  "body_markdown": "# Playbook Reference\n\nA playbook is a YAML document that describes an automated workflow. Pilot validates every playbook against a Zod schema before execution.\n\n## Top-level structure\n\n```yaml\nplaybook:\n  id: <dotted.id>          # e.g. cloudflare.dns.add-a-record\n  version: 1               # integer, incremented on each edit\n  target: <provider>       # cloudflare, aws, vercel, etc.\n  title: Human-readable title\n  description: Optional longer description\n  executor: browser        # default executor for all steps (optional)\n  executor_config: {}      # executor-specific config map (optional)\n  inputs: []               # input definitions\n  outputs: []              # output definitions\n  preconditions: []        # conditions checked before run starts\n  steps: []                # ordered list of steps (min 1)\n```\n\nRequired fields: `id` (dotted lowercase string), `version` (integer), `target`, `title`, `steps` (min 1). Optional: `description`, `executor` (default `browser`), `executor_config`, `inputs`, `outputs`, `preconditions`.\n\n## Inputs\n\nEach input defines a variable the caller must supply. Referenced in steps as `{{name}}`.\n\n```yaml\ninputs:\n  - name: zone\n    type: string\n    required: true\n  - name: proxied\n    type: boolean\n    default: true\n```\n\nFields: `name` (pattern `^[a-z_][a-z0-9_]*$`), `type` (one of `string`, `number`, `boolean`, `ipv4`, `ipv6`, `url`, `email`, `secret`), `required` (default `true`), `default`, `description`.\n\n## Outputs\n\nOutputs reference values captured during the run: `{ name, type, source }` where `source` is e.g. `capture.record_id`.\n\n## Steps\n\nEach step is an atomic action. Steps run in order unless the composite executor orchestrates them differently.\n\n```yaml\nsteps:\n  - id: goto-dns\n    action: navigate\n    executor: browser        # override playbook-level executor\n    url: \"https://example.com\"\n    timeout_ms: 15000\n    ai_fallback: true\n    destructive: false\n    breakpoint: false\n    intent: Human-readable goal for this step.\n    observe: [text, a11y, screenshot]\n```\n\nRequired: `id` (pattern `^[a-z][a-z0-9-]*$`, unique within playbook), `action`. Optional: `executor` (overrides playbook default), `selector`, `url`, `value`, `timeout_ms` (default 15000), `ai_fallback` (default false), `destructive` (default false), `breakpoint` (default false), `intent`, `observe` (`[\"text\", \"a11y\", \"screenshot\"]`), `capture_as`, `assert`.\n\n### Selectors\n\nA selector can be a plain string or an object with fallback chain:\n\n```yaml\nselector:\n  primary: \"button:has-text('Save')\"\n  fallbacks:\n    - \"[data-testid='save-btn']\"\n    - \"role=button[name=/save/i]\"\n```\n\n## Browser actions (14)\n\nThese are the default executor's actions:\n\n| Action | Required fields | Description |\n|--------|----------------|-------------|\n| `navigate` | `url` | Navigate to a URL. Supports `{{input}}` interpolation. |\n| `click` | `selector` | Click an element. |\n| `fill` | `selector`, `value` | Type a value into an input field. |\n| `select` | `selector`, `value` | Choose a dropdown option. |\n| `set_toggle` | `selector`, `value` | Set a checkbox or toggle to true/false. |\n| `wait_for` | `selector` | Wait until an element is visible. |\n| `assert` | `assert` array | Verify page state without acting. |\n| `capture` | `capture_as`, `from` | Extract a value from the page or network. |\n| `capture_secret` | `capture_as`, `from` | Like `capture`, but the value is masked in logs. |\n| `ai_decide` | `allowed_outcomes` | Let the AI model choose from a list of outcomes. |\n| `scroll` | `selector` (optional) | Scroll the page or a specific element. |\n| `hover` | `selector` | Hover over an element. |\n| `press` | `value` | Press a keyboard key (e.g. `Enter`, `Tab`). |\n| `upload` | `selector`, `value` | Upload a file via a file input. |\n\n## Assertions\n\nPost-step assertions verify expected page state:\n\n```yaml\nassert:\n  - visible: \"[data-testid='dns-records-table']\"\n  - text_present: \"Record added\"\n  - url_matches: \"/dns\"\n  - network_complete:\n      url_pattern: \"/dns_records\"\n      method: POST\n      status: 200\n```\n\n| Assertion | Description |\n|-----------|-------------|\n| `visible: <selector>` | Element is visible on page |\n| `hidden: <selector>` | Element is not visible |\n| `text_present: <text>` | Text appears in page body |\n| `text_absent: <text>` | Text does not appear |\n| `url_matches: <pattern>` | Current URL matches pattern |\n| `network_complete` | A matching network request completed |\n\n## Capture sources\n\nThe `from` field on `capture` / `capture_secret` supports three source types:\n\n```yaml\n# From a DOM element\nfrom:\n  selector: \".result-id\"\n  attribute: textContent    # optional, defaults to textContent\n\n# From a network response\nfrom:\n  network_response:\n    url_pattern: \"/dns_records\"\n    method: POST\n    json_path: \"$.result.id\"\n\n# From OCR on a screenshot region\nfrom:\n  screenshot_ocr:\n    region: \"top-right\"\n```\n\n## Executor actions\n\nActions prefixed with the executor name are routed automatically:\n\n### Terraform (`tf_*`)\n\n`tf_init`, `tf_validate`, `tf_plan`, `tf_apply`, `tf_destroy`, `tf_output`, `tf_import`, `tf_state`\n\n### Git (`git_*`)\n\n`git_clone`, `git_branch`, `git_commit`, `git_push`, `git_pr_create`, `git_pr_merge`, `git_tag`, `git_release`\n\n### Persist (`persist_*`)\n\n`persist_provision`, `persist_migrate`, `persist_sync`, `persist_backup`, `persist_restore`, `persist_proxy_start`, `persist_proxy_stop`\n\n### Shell (`shell_*`)\n\n`shell_exec`, `shell_script`, `shell_assert`\n\n### HTTP (`http_*`)\n\n`http_request`, `http_assert`, `http_poll`\n\n### Composite\n\n`run_playbook`, `parallel`, `conditional`, `loop`\n\nSee [Executors](/docs/pilot/executors) for full field documentation on each action.\n\n## Example\n\nA condensed playbook that adds a DNS A record on Cloudflare:\n\n```yaml\nplaybook:\n  id: cloudflare.dns.add-a-record\n  version: 1\n  target: cloudflare\n  title: Add an A record to a DNS zone\n  inputs:\n    - { name: zone, type: string, required: true }\n    - { name: record_name, type: string, required: true }\n    - { name: ip_address, type: ipv4, required: true }\n  outputs:\n    - { name: record_id, type: string, source: capture.record_id }\n  steps:\n    - id: goto-dns\n      action: navigate\n      url: \"https://dash.cloudflare.com/?to=/:account/{{zone}}/dns\"\n      ai_fallback: true\n    - id: fill-name\n      action: fill\n      selector: \"[name='dns-record-name']\"\n      value: \"{{record_name}}\"\n    - id: save\n      action: click\n      selector: \"button:has-text('Save')\"\n      assert: [{ text_present: \"Record added\" }]\n    - id: capture-id\n      action: capture\n      capture_as: record_id\n      from:\n        network_response:\n          url_pattern: \"/dns_records\"\n          method: POST\n          json_path: \"$.result.id\"\n```\n\nFor a longer example using `ai_decide` with multiple outcomes and `breakpoint` on destructive steps, see the R2 playbook in `playbooks/cloudflare/r2.enable-and-create-buckets.yaml`.\n\n## See also\n\n- [Executors](/docs/pilot/executors) — detailed executor reference\n- [JavaScript SDK](/docs/pilot/sdk/javascript) — run playbooks programmatically\n- [Quickstart](/docs/pilot/quickstart) — record and replay your first workflow",
  "body_html": "<h1 id=\"playbook-reference\">Playbook Reference</h1>\n<p>A playbook is a YAML document that describes an automated workflow. Pilot validates every playbook against a Zod schema before execution.</p>\n<h2 id=\"top-level-structure\">Top-level structure</h2>\n<pre class=\"astro-code github-dark\" style=\"background-color:#24292e;color:#e1e4e8; overflow-x: auto;\" tabindex=\"0\" data-language=\"yaml\"><code><span class=\"line\"><span style=\"color:#85E89D\">playbook</span><span style=\"color:#E1E4E8\">:</span></span>\n<span class=\"line\"><span style=\"color:#85E89D\">  id</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#9ECBFF\">&#x3C;dotted.id></span><span style=\"color:#6A737D\">          # e.g. cloudflare.dns.add-a-record</span></span>\n<span class=\"line\"><span style=\"color:#85E89D\">  version</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#79B8FF\">1</span><span style=\"color:#6A737D\">               # integer, incremented on each edit</span></span>\n<span class=\"line\"><span style=\"color:#85E89D\">  target</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#9ECBFF\">&#x3C;provider></span><span style=\"color:#6A737D\">       # cloudflare, aws, vercel, etc.</span></span>\n<span class=\"line\"><span style=\"color:#85E89D\">  title</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#9ECBFF\">Human-readable title</span></span>\n<span class=\"line\"><span style=\"color:#85E89D\">  description</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#9ECBFF\">Optional longer description</span></span>\n<span class=\"line\"><span style=\"color:#85E89D\">  executor</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#9ECBFF\">browser</span><span style=\"color:#6A737D\">        # default executor for all steps (optional)</span></span>\n<span class=\"line\"><span style=\"color:#85E89D\">  executor_config</span><span style=\"color:#E1E4E8\">: {}      </span><span style=\"color:#6A737D\"># executor-specific config map (optional)</span></span>\n<span class=\"line\"><span style=\"color:#85E89D\">  inputs</span><span style=\"color:#E1E4E8\">: []               </span><span style=\"color:#6A737D\"># input definitions</span></span>\n<span class=\"line\"><span style=\"color:#85E89D\">  outputs</span><span style=\"color:#E1E4E8\">: []              </span><span style=\"color:#6A737D\"># output definitions</span></span>\n<span class=\"line\"><span style=\"color:#85E89D\">  preconditions</span><span style=\"color:#E1E4E8\">: []        </span><span style=\"color:#6A737D\"># conditions checked before run starts</span></span>\n<span class=\"line\"><span style=\"color:#85E89D\">  steps</span><span style=\"color:#E1E4E8\">: []                </span><span style=\"color:#6A737D\"># ordered list of steps (min 1)</span></span></code></pre>\n<p>Required fields: <code>id</code> (dotted lowercase string), <code>version</code> (integer), <code>target</code>, <code>title</code>, <code>steps</code> (min 1). Optional: <code>description</code>, <code>executor</code> (default <code>browser</code>), <code>executor_config</code>, <code>inputs</code>, <code>outputs</code>, <code>preconditions</code>.</p>\n<h2 id=\"inputs\">Inputs</h2>\n<p>Each input defines a variable the caller must supply. Referenced in steps as <code>{{name}}</code>.</p>\n<pre class=\"astro-code github-dark\" style=\"background-color:#24292e;color:#e1e4e8; overflow-x: auto;\" tabindex=\"0\" data-language=\"yaml\"><code><span class=\"line\"><span style=\"color:#85E89D\">inputs</span><span style=\"color:#E1E4E8\">:</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">  - </span><span style=\"color:#85E89D\">name</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#9ECBFF\">zone</span></span>\n<span class=\"line\"><span style=\"color:#85E89D\">    type</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#9ECBFF\">string</span></span>\n<span class=\"line\"><span style=\"color:#85E89D\">    required</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#79B8FF\">true</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">  - </span><span style=\"color:#85E89D\">name</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#9ECBFF\">proxied</span></span>\n<span class=\"line\"><span style=\"color:#85E89D\">    type</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#9ECBFF\">boolean</span></span>\n<span class=\"line\"><span style=\"color:#85E89D\">    default</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#79B8FF\">true</span></span></code></pre>\n<p>Fields: <code>name</code> (pattern <code>^[a-z_][a-z0-9_]*$</code>), <code>type</code> (one of <code>string</code>, <code>number</code>, <code>boolean</code>, <code>ipv4</code>, <code>ipv6</code>, <code>url</code>, <code>email</code>, <code>secret</code>), <code>required</code> (default <code>true</code>), <code>default</code>, <code>description</code>.</p>\n<h2 id=\"outputs\">Outputs</h2>\n<p>Outputs reference values captured during the run: <code>{ name, type, source }</code> where <code>source</code> is e.g. <code>capture.record_id</code>.</p>\n<h2 id=\"steps\">Steps</h2>\n<p>Each step is an atomic action. Steps run in order unless the composite executor orchestrates them differently.</p>\n<pre class=\"astro-code github-dark\" style=\"background-color:#24292e;color:#e1e4e8; overflow-x: auto;\" tabindex=\"0\" data-language=\"yaml\"><code><span class=\"line\"><span style=\"color:#85E89D\">steps</span><span style=\"color:#E1E4E8\">:</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">  - </span><span style=\"color:#85E89D\">id</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#9ECBFF\">goto-dns</span></span>\n<span class=\"line\"><span style=\"color:#85E89D\">    action</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#9ECBFF\">navigate</span></span>\n<span class=\"line\"><span style=\"color:#85E89D\">    executor</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#9ECBFF\">browser</span><span style=\"color:#6A737D\">        # override playbook-level executor</span></span>\n<span class=\"line\"><span style=\"color:#85E89D\">    url</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#9ECBFF\">\"https://example.com\"</span></span>\n<span class=\"line\"><span style=\"color:#85E89D\">    timeout_ms</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#79B8FF\">15000</span></span>\n<span class=\"line\"><span style=\"color:#85E89D\">    ai_fallback</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#79B8FF\">true</span></span>\n<span class=\"line\"><span style=\"color:#85E89D\">    destructive</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#79B8FF\">false</span></span>\n<span class=\"line\"><span style=\"color:#85E89D\">    breakpoint</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#79B8FF\">false</span></span>\n<span class=\"line\"><span style=\"color:#85E89D\">    intent</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#9ECBFF\">Human-readable goal for this step.</span></span>\n<span class=\"line\"><span style=\"color:#85E89D\">    observe</span><span style=\"color:#E1E4E8\">: [</span><span style=\"color:#9ECBFF\">text</span><span style=\"color:#E1E4E8\">, </span><span style=\"color:#9ECBFF\">a11y</span><span style=\"color:#E1E4E8\">, </span><span style=\"color:#9ECBFF\">screenshot</span><span style=\"color:#E1E4E8\">]</span></span></code></pre>\n<p>Required: <code>id</code> (pattern <code>^[a-z][a-z0-9-]*$</code>, unique within playbook), <code>action</code>. Optional: <code>executor</code> (overrides playbook default), <code>selector</code>, <code>url</code>, <code>value</code>, <code>timeout_ms</code> (default 15000), <code>ai_fallback</code> (default false), <code>destructive</code> (default false), <code>breakpoint</code> (default false), <code>intent</code>, <code>observe</code> (<code>[\"text\", \"a11y\", \"screenshot\"]</code>), <code>capture_as</code>, <code>assert</code>.</p>\n<h3 id=\"selectors\">Selectors</h3>\n<p>A selector can be a plain string or an object with fallback chain:</p>\n<pre class=\"astro-code github-dark\" style=\"background-color:#24292e;color:#e1e4e8; overflow-x: auto;\" tabindex=\"0\" data-language=\"yaml\"><code><span class=\"line\"><span style=\"color:#85E89D\">selector</span><span style=\"color:#E1E4E8\">:</span></span>\n<span class=\"line\"><span style=\"color:#85E89D\">  primary</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#9ECBFF\">\"button:has-text('Save')\"</span></span>\n<span class=\"line\"><span style=\"color:#85E89D\">  fallbacks</span><span style=\"color:#E1E4E8\">:</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">    - </span><span style=\"color:#9ECBFF\">\"[data-testid='save-btn']\"</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">    - </span><span style=\"color:#9ECBFF\">\"role=button[name=/save/i]\"</span></span></code></pre>\n<h2 id=\"browser-actions-14\">Browser actions (14)</h2>\n<p>These are the default executor’s actions:</p>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<table><thead><tr><th>Action</th><th>Required fields</th><th>Description</th></tr></thead><tbody><tr><td><code>navigate</code></td><td><code>url</code></td><td>Navigate to a URL. Supports <code>{{input}}</code> interpolation.</td></tr><tr><td><code>click</code></td><td><code>selector</code></td><td>Click an element.</td></tr><tr><td><code>fill</code></td><td><code>selector</code>, <code>value</code></td><td>Type a value into an input field.</td></tr><tr><td><code>select</code></td><td><code>selector</code>, <code>value</code></td><td>Choose a dropdown option.</td></tr><tr><td><code>set_toggle</code></td><td><code>selector</code>, <code>value</code></td><td>Set a checkbox or toggle to true/false.</td></tr><tr><td><code>wait_for</code></td><td><code>selector</code></td><td>Wait until an element is visible.</td></tr><tr><td><code>assert</code></td><td><code>assert</code> array</td><td>Verify page state without acting.</td></tr><tr><td><code>capture</code></td><td><code>capture_as</code>, <code>from</code></td><td>Extract a value from the page or network.</td></tr><tr><td><code>capture_secret</code></td><td><code>capture_as</code>, <code>from</code></td><td>Like <code>capture</code>, but the value is masked in logs.</td></tr><tr><td><code>ai_decide</code></td><td><code>allowed_outcomes</code></td><td>Let the AI model choose from a list of outcomes.</td></tr><tr><td><code>scroll</code></td><td><code>selector</code> (optional)</td><td>Scroll the page or a specific element.</td></tr><tr><td><code>hover</code></td><td><code>selector</code></td><td>Hover over an element.</td></tr><tr><td><code>press</code></td><td><code>value</code></td><td>Press a keyboard key (e.g. <code>Enter</code>, <code>Tab</code>).</td></tr><tr><td><code>upload</code></td><td><code>selector</code>, <code>value</code></td><td>Upload a file via a file input.</td></tr></tbody></table>\n<h2 id=\"assertions\">Assertions</h2>\n<p>Post-step assertions verify expected page state:</p>\n<pre class=\"astro-code github-dark\" style=\"background-color:#24292e;color:#e1e4e8; overflow-x: auto;\" tabindex=\"0\" data-language=\"yaml\"><code><span class=\"line\"><span style=\"color:#85E89D\">assert</span><span style=\"color:#E1E4E8\">:</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">  - </span><span style=\"color:#85E89D\">visible</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#9ECBFF\">\"[data-testid='dns-records-table']\"</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">  - </span><span style=\"color:#85E89D\">text_present</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#9ECBFF\">\"Record added\"</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">  - </span><span style=\"color:#85E89D\">url_matches</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#9ECBFF\">\"/dns\"</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">  - </span><span style=\"color:#85E89D\">network_complete</span><span style=\"color:#E1E4E8\">:</span></span>\n<span class=\"line\"><span style=\"color:#85E89D\">      url_pattern</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#9ECBFF\">\"/dns_records\"</span></span>\n<span class=\"line\"><span style=\"color:#85E89D\">      method</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#9ECBFF\">POST</span></span>\n<span class=\"line\"><span style=\"color:#85E89D\">      status</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#79B8FF\">200</span></span></code></pre>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<table><thead><tr><th>Assertion</th><th>Description</th></tr></thead><tbody><tr><td><code>visible: &#x3C;selector></code></td><td>Element is visible on page</td></tr><tr><td><code>hidden: &#x3C;selector></code></td><td>Element is not visible</td></tr><tr><td><code>text_present: &#x3C;text></code></td><td>Text appears in page body</td></tr><tr><td><code>text_absent: &#x3C;text></code></td><td>Text does not appear</td></tr><tr><td><code>url_matches: &#x3C;pattern></code></td><td>Current URL matches pattern</td></tr><tr><td><code>network_complete</code></td><td>A matching network request completed</td></tr></tbody></table>\n<h2 id=\"capture-sources\">Capture sources</h2>\n<p>The <code>from</code> field on <code>capture</code> / <code>capture_secret</code> supports three source types:</p>\n<pre class=\"astro-code github-dark\" style=\"background-color:#24292e;color:#e1e4e8; overflow-x: auto;\" tabindex=\"0\" data-language=\"yaml\"><code><span class=\"line\"><span style=\"color:#6A737D\"># From a DOM element</span></span>\n<span class=\"line\"><span style=\"color:#85E89D\">from</span><span style=\"color:#E1E4E8\">:</span></span>\n<span class=\"line\"><span style=\"color:#85E89D\">  selector</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#9ECBFF\">\".result-id\"</span></span>\n<span class=\"line\"><span style=\"color:#85E89D\">  attribute</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#9ECBFF\">textContent</span><span style=\"color:#6A737D\">    # optional, defaults to textContent</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#6A737D\"># From a network response</span></span>\n<span class=\"line\"><span style=\"color:#85E89D\">from</span><span style=\"color:#E1E4E8\">:</span></span>\n<span class=\"line\"><span style=\"color:#85E89D\">  network_response</span><span style=\"color:#E1E4E8\">:</span></span>\n<span class=\"line\"><span style=\"color:#85E89D\">    url_pattern</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#9ECBFF\">\"/dns_records\"</span></span>\n<span class=\"line\"><span style=\"color:#85E89D\">    method</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#9ECBFF\">POST</span></span>\n<span class=\"line\"><span style=\"color:#85E89D\">    json_path</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#9ECBFF\">\"$.result.id\"</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#6A737D\"># From OCR on a screenshot region</span></span>\n<span class=\"line\"><span style=\"color:#85E89D\">from</span><span style=\"color:#E1E4E8\">:</span></span>\n<span class=\"line\"><span style=\"color:#85E89D\">  screenshot_ocr</span><span style=\"color:#E1E4E8\">:</span></span>\n<span class=\"line\"><span style=\"color:#85E89D\">    region</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#9ECBFF\">\"top-right\"</span></span></code></pre>\n<h2 id=\"executor-actions\">Executor actions</h2>\n<p>Actions prefixed with the executor name are routed automatically:</p>\n<h3 id=\"terraform-tf_\">Terraform (<code>tf_*</code>)</h3>\n<p><code>tf_init</code>, <code>tf_validate</code>, <code>tf_plan</code>, <code>tf_apply</code>, <code>tf_destroy</code>, <code>tf_output</code>, <code>tf_import</code>, <code>tf_state</code></p>\n<h3 id=\"git-git_\">Git (<code>git_*</code>)</h3>\n<p><code>git_clone</code>, <code>git_branch</code>, <code>git_commit</code>, <code>git_push</code>, <code>git_pr_create</code>, <code>git_pr_merge</code>, <code>git_tag</code>, <code>git_release</code></p>\n<h3 id=\"persist-persist_\">Persist (<code>persist_*</code>)</h3>\n<p><code>persist_provision</code>, <code>persist_migrate</code>, <code>persist_sync</code>, <code>persist_backup</code>, <code>persist_restore</code>, <code>persist_proxy_start</code>, <code>persist_proxy_stop</code></p>\n<h3 id=\"shell-shell_\">Shell (<code>shell_*</code>)</h3>\n<p><code>shell_exec</code>, <code>shell_script</code>, <code>shell_assert</code></p>\n<h3 id=\"http-http_\">HTTP (<code>http_*</code>)</h3>\n<p><code>http_request</code>, <code>http_assert</code>, <code>http_poll</code></p>\n<h3 id=\"composite\">Composite</h3>\n<p><code>run_playbook</code>, <code>parallel</code>, <code>conditional</code>, <code>loop</code></p>\n<p>See <a href=\"/docs/pilot/executors\">Executors</a> for full field documentation on each action.</p>\n<h2 id=\"example\">Example</h2>\n<p>A condensed playbook that adds a DNS A record on Cloudflare:</p>\n<pre class=\"astro-code github-dark\" style=\"background-color:#24292e;color:#e1e4e8; overflow-x: auto;\" tabindex=\"0\" data-language=\"yaml\"><code><span class=\"line\"><span style=\"color:#85E89D\">playbook</span><span style=\"color:#E1E4E8\">:</span></span>\n<span class=\"line\"><span style=\"color:#85E89D\">  id</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#9ECBFF\">cloudflare.dns.add-a-record</span></span>\n<span class=\"line\"><span style=\"color:#85E89D\">  version</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#79B8FF\">1</span></span>\n<span class=\"line\"><span style=\"color:#85E89D\">  target</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#9ECBFF\">cloudflare</span></span>\n<span class=\"line\"><span style=\"color:#85E89D\">  title</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#9ECBFF\">Add an A record to a DNS zone</span></span>\n<span class=\"line\"><span style=\"color:#85E89D\">  inputs</span><span style=\"color:#E1E4E8\">:</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">    - { </span><span style=\"color:#85E89D\">name</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#9ECBFF\">zone</span><span style=\"color:#E1E4E8\">, </span><span style=\"color:#85E89D\">type</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#9ECBFF\">string</span><span style=\"color:#E1E4E8\">, </span><span style=\"color:#85E89D\">required</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#79B8FF\">true</span><span style=\"color:#E1E4E8\"> }</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">    - { </span><span style=\"color:#85E89D\">name</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#9ECBFF\">record_name</span><span style=\"color:#E1E4E8\">, </span><span style=\"color:#85E89D\">type</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#9ECBFF\">string</span><span style=\"color:#E1E4E8\">, </span><span style=\"color:#85E89D\">required</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#79B8FF\">true</span><span style=\"color:#E1E4E8\"> }</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">    - { </span><span style=\"color:#85E89D\">name</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#9ECBFF\">ip_address</span><span style=\"color:#E1E4E8\">, </span><span style=\"color:#85E89D\">type</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#9ECBFF\">ipv4</span><span style=\"color:#E1E4E8\">, </span><span style=\"color:#85E89D\">required</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#79B8FF\">true</span><span style=\"color:#E1E4E8\"> }</span></span>\n<span class=\"line\"><span style=\"color:#85E89D\">  outputs</span><span style=\"color:#E1E4E8\">:</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">    - { </span><span style=\"color:#85E89D\">name</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#9ECBFF\">record_id</span><span style=\"color:#E1E4E8\">, </span><span style=\"color:#85E89D\">type</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#9ECBFF\">string</span><span style=\"color:#E1E4E8\">, </span><span style=\"color:#85E89D\">source</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#9ECBFF\">capture.record_id</span><span style=\"color:#E1E4E8\"> }</span></span>\n<span class=\"line\"><span style=\"color:#85E89D\">  steps</span><span style=\"color:#E1E4E8\">:</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">    - </span><span style=\"color:#85E89D\">id</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#9ECBFF\">goto-dns</span></span>\n<span class=\"line\"><span style=\"color:#85E89D\">      action</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#9ECBFF\">navigate</span></span>\n<span class=\"line\"><span style=\"color:#85E89D\">      url</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#9ECBFF\">\"https://dash.cloudflare.com/?to=/:account/{{zone}}/dns\"</span></span>\n<span class=\"line\"><span style=\"color:#85E89D\">      ai_fallback</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#79B8FF\">true</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">    - </span><span style=\"color:#85E89D\">id</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#9ECBFF\">fill-name</span></span>\n<span class=\"line\"><span style=\"color:#85E89D\">      action</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#9ECBFF\">fill</span></span>\n<span class=\"line\"><span style=\"color:#85E89D\">      selector</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#9ECBFF\">\"[name='dns-record-name']\"</span></span>\n<span class=\"line\"><span style=\"color:#85E89D\">      value</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#9ECBFF\">\"{{record_name}}\"</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">    - </span><span style=\"color:#85E89D\">id</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#9ECBFF\">save</span></span>\n<span class=\"line\"><span style=\"color:#85E89D\">      action</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#9ECBFF\">click</span></span>\n<span class=\"line\"><span style=\"color:#85E89D\">      selector</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#9ECBFF\">\"button:has-text('Save')\"</span></span>\n<span class=\"line\"><span style=\"color:#85E89D\">      assert</span><span style=\"color:#E1E4E8\">: [{ </span><span style=\"color:#85E89D\">text_present</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#9ECBFF\">\"Record added\"</span><span style=\"color:#E1E4E8\"> }]</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">    - </span><span style=\"color:#85E89D\">id</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#9ECBFF\">capture-id</span></span>\n<span class=\"line\"><span style=\"color:#85E89D\">      action</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#9ECBFF\">capture</span></span>\n<span class=\"line\"><span style=\"color:#85E89D\">      capture_as</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#9ECBFF\">record_id</span></span>\n<span class=\"line\"><span style=\"color:#85E89D\">      from</span><span style=\"color:#E1E4E8\">:</span></span>\n<span class=\"line\"><span style=\"color:#85E89D\">        network_response</span><span style=\"color:#E1E4E8\">:</span></span>\n<span class=\"line\"><span style=\"color:#85E89D\">          url_pattern</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#9ECBFF\">\"/dns_records\"</span></span>\n<span class=\"line\"><span style=\"color:#85E89D\">          method</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#9ECBFF\">POST</span></span>\n<span class=\"line\"><span style=\"color:#85E89D\">          json_path</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#9ECBFF\">\"$.result.id\"</span></span></code></pre>\n<p>For a longer example using <code>ai_decide</code> with multiple outcomes and <code>breakpoint</code> on destructive steps, see the R2 playbook in <code>playbooks/cloudflare/r2.enable-and-create-buckets.yaml</code>.</p>\n<h2 id=\"see-also\">See also</h2>\n<ul>\n<li><a href=\"/docs/pilot/executors\">Executors</a> — detailed executor reference</li>\n<li><a href=\"/docs/pilot/sdk/javascript\">JavaScript SDK</a> — run playbooks programmatically</li>\n<li><a href=\"/docs/pilot/quickstart\">Quickstart</a> — record and replay your first workflow</li>\n</ul>",
  "links_out": [
    "/docs/pilot/executors",
    "/docs/pilot/sdk/javascript",
    "/docs/pilot/quickstart"
  ]
}