Paseo Playbooks

Playbook reference

Everything the plugin reads from your repository, and the rules it checks before a playbook can run.

Folders and layout

The plugin reads two folders in the workspace: .paseo/plugins/playbooks/ and .paseo/playbooks/. Both are loaded and merged. Each folder is self-contained: a playbook uses the roles/ and defaults/main.yaml of its own folder. The same playbook id in both folders is an error on both files.

.paseo/plugins/playbooks/
├── defaults/
│   └── main.yaml          # optional: agent defaults for this folder
├── pr.yaml                # a playbook; its id is the file name ("pr")
├── release.yaml
└── roles/
    └── reviewer/
        ├── role.yaml      # title, readonly, agent
        └── instructions.md

defaults/main.yaml

agent:              # defaults for every step in this folder
  provider: claude
  model: claude-opus-5-5
  mode: bypassPermissions
manager:            # settings of the manager agent when Start creates a new one
  model: claude-sonnet-5-5
  mode: default
timeout: 1h         # default step timeout
KeyMeaning
agentprovider, model, mode, thinking for steps. See agent settings.
managerAgent settings for the manager created by Start. When agent.provider is set, it inherits agent.provider and agent.model (not the mode). Without a model it uses Claude Opus.
timeoutDefault step timeout, such as 30m or 2h. No timeout when absent.

An invalid defaults/main.yaml makes every playbook of its folder invalid.

Roles

A role is a folder roles/<id>/ with two files. A playbook refers to it by its folder name, and several playbooks can share it.

File / keyMeaning
instructions.mdRequired and non-empty. The system prompt of every step that uses the role. The plugin appends the step protocol: allowed outcomes, required outputs, and how to call report_step_result.
role.yaml titleDisplay name. Defaults to the id.
role.yaml readonlytrue if the role must not edit files. Default false. See readonly roles.
role.yaml agentAgent settings for this role.

Playbook fields

FieldMeaning
titleDisplay title. Defaults to the file name.
descriptionShown in the Start form. Also returned to agents by list_playbooks and validate_playbooks.
hiddentrue moves the playbook to the collapsed "Hidden playbooks" section at the bottom of the Playbooks panel, for one only meant as a sub-playbook or after-task (a retro, say). It can still be started from there or by start_playbook, and used by other playbooks; while it has runs of its own, its card is in the main list. Default false.
agentAgent settings for all steps of this playbook.
inputsTyped variables. They make up the Start form, and start_playbook takes them. See variables.
outputsTyped run results, resolved when the run completes. value maps them from step outputs or playbook inputs; without it they are matched by name.
concurrencyMaximum active runs of this playbook in the Paseo project, across all worktrees, and also the number of runs that may be inside it as a sub-playbook (see Sub-playbooks). Unlimited when absent.
stepsAt least one step.
afterTasks run once the run has ended, without changing its result. See after-tasks.

Step fields

A step is an agent step (role), a human gate (gate) or a sub-playbook (playbook), exactly one of them. The order of steps in the file does not matter.

FieldApplies toMeaning
idallUnique in the playbook. end, fail and outputs are reserved.
needsallSteps that must complete first. A step without needs is ready when the run starts. Steps that are ready at the same time run in parallel.
roleagentRole folder name under roles/.
gategateMessage shown to the human.
choicesgateThe gate's buttons, in order. Each key is an outcome with label, comment and color. See gates.
promptagentTemplate for the step's first message. See prompt templates.
inputsagentA list of names (each matched automatically), or a map of name to a source template or ~ (matched automatically).
outputsagentTyped values the step must return with any outcome except failure; a failure report skips output validation. All of them are required then.
outcomesagentAllowed outcomes. Default [success, failure].
onallPer outcome: complete, end, fail, or { goto: <step>, max_loops: N }.
retriesagentExtra attempts allowed after failure. Default 0.
timeoutagentDuration such as 30m or 2h. Defaults to defaults/main.yaml.
agentagentAgent settings for this step only.

Outcomes and flow

Gates

A gate becomes ready like any step. The manager opens it with request_gate: a one-sentence summary shown on the gate, and details (your gate: text, then the manager's notes) collapsed under it. Its buttons come from choices, in order: each key is an outcome, with a label, comment: none | optional | required and color: primary | neutral | success | warning | danger (Paseo theme colours; by default the first choice is primary and the others neutral). Without choices, a gate offers approved (Approve) and rejected (Reject, optional comment, colour danger). The first choice completes the gate by default. A gate that declares choices needs an on entry for every other choice; only the default Reject fails the run without one. Each button shows where it leads (→ End, → End Release inside a block, → Implement, → Fail run). A choice that takes a comment opens a text box with Send; the comment goes to the manager and, on a goto, to the target as feedback. Decisions come from the panel, or by chat through the manager's resume tool. The first decision wins.

- id: pr_approval
  needs: [babysit]
  gate: The PR is ready. Approve to finish, or request changes to send it back.
  choices:
    approved: { label: Approve }
    rejected: { label: Request changes, comment: required, color: danger }
  on:
    rejected: { goto: implement }

Loops

Variables

KeyMeaning
typestring (default), number, boolean or enum. No objects or lists.
valuesAllowed values, for enum only (required there).
requiredPlaybook inputs: the run cannot start without a value.
defaultValue used when none is given.
descriptionShown in the Start form and to agents.

Numbers and booleans sent as text ("42", "true") are accepted. A report that misses a declared output or has the wrong type is refused, with the reason, so the agent fixes it in the same turn. The one exception is a failure report, which skips output validation, so a failed step may leave its outputs empty.

Where a step input comes from

  1. An explicit source wins: "{{ playbook.inputs.x }}" or "{{ steps.<id>.outputs.y }}", where the step must be one this step needs, directly or not.
  2. Otherwise the input takes the output with the same name from a step it needs, directly or not.
  3. Failing that, it takes the playbook input with the same name.
  4. If several earlier steps output that name, or nothing matches, the playbook is invalid until you map the input explicitly. Nothing is chosen silently.

Playbook outputs without value are matched the same way, from any step with a same-named, type-compatible output. Any type can feed a string. The detail sheet shows where each value came from, for example pr_url ← implement.pr_url (auto).

Prompt templates

Templates use plain {{ path }} substitution, with no conditionals or loops. A step prompt may use only {{ inputs.<name> }} (a declared input; the inputs: map uses the other forms, {{ playbook.inputs.x }} and {{ steps.<id>.outputs.y }}), {{ run.id }} and {{ attempt }}; steps of an after-task may also use {{ run.status }} and {{ run.failure }}. Missing or empty values render as (none). If a prompt has no placeholders, the plugin lists the step's inputs after it. The manager's brief and any loop feedback are appended to the prompt.

Sub-playbooks

A step with playbook: <id> runs another playbook of the same folder in place, as a building block. The loader expands it: its steps become pr.investigate, pr.implement… in the same run, under the same manager, and the panel draws a labelled frame around them.

steps:
  - id: pr
    playbook: pr                 # inputs match by name, or map them in `inputs`
  - id: approve
    needs: [pr]
    gate: Release it?
    on:
      rejected: { goto: pr }     # restarts the whole block, with the comment as feedback
  - id: release
    needs: [approve]
    playbook: release            # gets pr_url from the pr block's outputs

Sub-playbooks are the only way to combine playbooks: one run and one graph, so a gate or a loop can sit between two playbooks. There is no chaining from one run to another.

After-tasks

after: lists tasks that run once the run has ended, whatever its outcome: a retro that reflects on the run, a notification. Each task is an agent step (role and prompt), a gate, or a sub-playbook (playbook: <id>), with a trigger.

after:
  - id: retro
    playbook: retro
    inputs:
      run_id: "{{ run.id }}"   # run values are always mapped explicitly
  - id: notify
    role: reviewer
    trigger: manual      # default: auto
    inputs:
      status: "{{ run.status }}"
    prompt: "Post a summary of run {{ run.id }} ({{ inputs.status }})."

Agent settings

provider, model, mode and thinking merge field by field. Precedence: step > role > playbook > defaults/main.yaml > the manager's own provider and model. With no model at all, Claude steps use Claude Opus.

Agent modes

A playbook may use any agent mode, bypassPermissions included: steps that run shell commands then work without asking. Playbooks come from the repository, so any branch can ship one; only start playbooks from repositories you trust. An unset mode uses the provider's default.

Readonly roles and concurrency

Validation

The Playbooks panel and the validate_playbooks tool report, per file:

YAML pitfall

Inside a flow mapping ({ ... }), a comma ends the value. Quote text that contains a comma, or use block style:

outputs:
  changes: { type: string, description: "What you changed, per file" }