Paseo Playbooks

Examples

Two complete playbook folders. Copy one into your repository as .paseo/plugins/playbooks/ or .paseo/playbooks/, adjust it, then run validate_playbooks.

Pull request

From examples/.paseo/plugins/playbooks/. Steps run with bypassPermissions, so they run shell commands without asking. The Implementer pushes and opens the PR with gh.

investigate ─▶ implement ─┬─▶ review ───┬─▶ babysit ─▶ end
                  ▲       └─▶ local_qa ─┘      │
                  └───── changes_requested ────┘

pr.yaml

title: Pull request
description: Investigate a request, implement it, open a PR and babysit it until it is green.

inputs:
  request:
    type: string
    required: true
    description: What to change
  base_branch:
    type: string
    default: main
    description: Branch the PR targets

outputs:
  pr_url: { type: string }     # auto-matched from the implement step
  pr_number:
    type: number
    value: "{{ steps.implement.outputs.pr_number }}"

steps:
  - id: investigate
    role: investigator
    inputs: [request]
    outputs:
      plan: { type: string, description: Implementation plan }
    prompt: |
      Request: {{ inputs.request }}

  - id: implement
    role: implementer
    needs: [investigate]
    inputs: [request, plan, base_branch]
    outputs:
      pr_url: { type: string }
      pr_number: { type: number }
      branch: { type: string }
    retries: 1
    timeout: 1h
    prompt: |
      Request: {{ inputs.request }}

      Plan:
      {{ inputs.plan }}

      Open (or update) the pull request against {{ inputs.base_branch }}.

  - id: review
    role: reviewer
    needs: [implement]
    inputs: [pr_url]
    outputs:
      findings: { type: string }
    prompt: "Review the pull request {{ inputs.pr_url }}."

  - id: local_qa
    role: local_qa
    needs: [implement]
    inputs: [branch]
    outputs:
      report: { type: string }
      tests_passed: { type: boolean }
    prompt: "Test the branch {{ inputs.branch }}."

  - id: babysit
    role: babysitter
    needs: [review, local_qa]
    inputs:
      pr_url: ~
      review: "{{ steps.review.outputs.findings }}"
      qa: "{{ steps.local_qa.outputs.report }}"
    outcomes: [ready, changes_requested]
    timeout: 2h
    prompt: |
      Pull request: {{ inputs.pr_url }}

      Review findings:
      {{ inputs.review }}

      Local QA report:
      {{ inputs.qa }}
    on:
      changes_requested: { goto: implement, max_loops: 3 }

defaults/main.yaml

# Repo-wide agent defaults. Precedence: step > role > playbook > these > the manager's own.
agent:
  provider: claude
  model: claude-opus-5-5
  mode: bypassPermissions      # steps run unattended (shell commands included)
manager:
  mode: default                # the manager never edits files

roles/babysitter/role.yaml

title: PR Babysitter
readonly: true

roles/babysitter/instructions.md

You are the PR Babysitter of a pull-request playbook.

Watch the pull request's CI checks (`gh pr checks --watch`) and review comments. Combine
them with the review findings and the local QA report.

Choose the outcome `ready` when CI is green and nothing blocking remains. Choose
`changes_requested` otherwise, and put every point the Implementer must fix in your
summary. Do not edit files or push.

roles/implementer/role.yaml

title: Implementer
readonly: false

roles/implementer/instructions.md

You are the Implementer of a pull-request playbook.

Implement the plan in the current checkout. You are already on the run's branch; commit
there and push it. Keep changes focused on the request. Run the relevant tests before
pushing.

Open a pull request against the base branch with `gh pr create`, or update the existing
one if this branch already has a PR. If you received feedback from a previous round,
address every point of it.

Return `pr_url`, `pr_number` and `branch`.

roles/investigator/role.yaml

title: Investigator
readonly: true

roles/investigator/instructions.md

You are the Investigator of a pull-request playbook.

Read the request and the relevant code, history and documentation. Do not edit files,
switch branches or commit. Work out what needs to change and why, and identify risks and
the tests that should prove the change.

Return a concrete implementation plan as the `plan` output: the files to touch, the
approach, edge cases, and how to verify it.

roles/local_qa/role.yaml

title: Local QA
readonly: true

roles/local_qa/instructions.md

You are the Local QA of a pull-request playbook.

Run the project's tests, linters and type checks on the current checkout. Do not edit
tracked files; build output in ignored files is fine. Report what you ran and what failed
as the `report` output, and set `tests_passed`.

roles/reviewer/role.yaml

title: Reviewer
readonly: true

roles/reviewer/instructions.md

You are the Reviewer of a pull-request playbook.

Review the pull request diff (`gh pr diff`) for correctness, security, missing tests and
clarity. Do not edit files or push. Report concrete findings with file and line, ordered
by severity, as the `findings` output. Say "No findings" when the change is good.

Capability (sub-playbooks)

From examples/.paseo/plugins/playbooks/. It builds a change with the Pull request playbook, waits for a human, then releases it with the Release to dev playbook: three steps, two of them sub-playbooks. Rejecting the gate restarts the whole PR block with your comment.

# capability.yaml
title: Capability
description: Build a change with the pull-request playbook, let a human approve it, then release it to the dev environment.

inputs:
  request:
    type: string
    required: true
    description: What to change
  base_branch:
    type: string
    default: main
    description: Branch the PR targets

outputs:
  pr_url: { type: string }       # from the pr block
  env_url: { type: string }      # from the release block

steps:
  # A step with `playbook:` runs another playbook of this folder in place. Its inputs are
  # matched by name (request, base_branch); its outputs (pr_url, pr_number) become this
  # step's outputs. Its steps appear as pr.investigate, pr.implement, …
  - id: pr
    playbook: pr

  - id: approve
    needs: [pr]
    gate: The PR is green. Release it to the dev environment, or request changes to rework it.
    choices:
      approved: { label: Release to dev }
      rejected: { label: Request changes, comment: required, color: danger }
    on:
      rejected: { goto: pr }     # restarts the whole pr block, with your comment as feedback

  - id: release
    needs: [approve]
    playbook: release            # pr_url comes from the pr block
# release.yaml
title: Release to dev
description: Deploy a change to the development environment and check it there.

inputs:
  pr_url:
    type: string
    required: true
    description: Pull request (or branch) to deploy

outputs:
  env_url: { type: string }      # auto-matched from the release step
  qa_report: { type: string, value: "{{ steps.qa.outputs.report }}" }

steps:
  - id: release
    role: dev_env_releaser
    inputs: [pr_url]
    outputs:
      env_url: { type: string, description: URL of the deployed dev environment }
      log: { type: string, description: What was run to deploy }
    timeout: 30m
    prompt: "Deploy {{ inputs.pr_url }} to the development environment."

  - id: qa
    role: dev_env_qa
    needs: [release]
    inputs: [env_url, pr_url]
    outputs:
      report: { type: string }
      passed: { type: boolean }
    outcomes: [success, redeploy]
    prompt: "Check {{ inputs.pr_url }} on {{ inputs.env_url }}."
    on:
      redeploy: { goto: release, max_loops: 1 }

The Release to dev roles, dev_env_releaser and dev_env_qa, deploy and check the change with the process your repository documents, and report a failure when there is none.

Docs check

Every role only reads or edits files, so default and acceptEdits are enough. The verdict step loops back to the writer at most twice, and a rejected gate sends the work back with your comment.

investigate ─▶ write ─┬─▶ review ──────┬─▶ verdict ─▶ ◆ approve ─▶ end
                 ▲    └─▶ consistency ─┘      │           │
                 └──── changes_requested ─────┘           │
                 └───────────────── rejected ─────────────┘

docs.yaml

title: Docs check
description: Compare the docs with the code, fix the gaps, review and check in parallel, loop until clean, then a human gate.

inputs:
  focus:
    type: string
    default: README.md, OVERVIEW.md, skills/paseo-playbooks/SKILL.md and the website/ pages
    description: Which documents to check
  strictness:
    type: enum
    values: [light, thorough]
    default: light
    description: light = only wrong or missing facts; thorough = also clarity

outputs:
  changes: { type: string }                # auto-matched from the write step

steps:
  - id: investigate
    role: doc_investigator
    inputs: [focus, strictness]
    outputs:
      gaps: { type: string, description: "Numbered list of doc statements that disagree with the code" }
      gap_count: { type: number }
    prompt: |
      Documents to check: {{ inputs.focus }}
      Strictness: {{ inputs.strictness }}

  - id: write
    role: doc_writer
    needs: [investigate]
    agent: { mode: acceptEdits }
    inputs: [focus, gaps]
    outputs:
      changes: { type: string, description: "What you changed, per file" }
    timeout: 20m
    prompt: |
      Fix these gaps in {{ inputs.focus }}:
      {{ inputs.gaps }}

  - id: review
    role: doc_reviewer
    needs: [write]
    inputs: [changes]
    outputs:
      findings: { type: string }
      blocking: { type: boolean }
    prompt: |
      The writer reports these changes (uncommitted in this checkout):
      {{ inputs.changes }}

  - id: consistency
    role: consistency_checker
    needs: [write]
    inputs: [gaps, changes]
    outputs:
      remaining: { type: string, description: "Gaps still open, or none" }
      all_fixed: { type: boolean }
    prompt: |
      Gaps found earlier:
      {{ inputs.gaps }}

      Changes made:
      {{ inputs.changes }}

  - id: verdict
    role: doc_editor
    needs: [review, consistency]
    inputs: [findings, blocking, remaining, all_fixed]
    outcomes: [ready, changes_requested]
    prompt: |
      Review findings (blocking: {{ inputs.blocking }}):
      {{ inputs.findings }}

      Remaining gaps (all fixed: {{ inputs.all_fixed }}):
      {{ inputs.remaining }}
    on:
      changes_requested: { goto: write, max_loops: 2 }

  - id: approve
    needs: [verdict]
    gate: The docs are updated, uncommitted. Approve to finish, or request changes to send them back to the writer.
    choices:
      approved: { label: Approve }
      rejected: { label: Request changes, comment: required, color: danger }
    on:
      rejected: { goto: write }

defaults/main.yaml

# Local test setup: cheap step agents, a stronger manager.
agent:
  provider: claude
  model: claude-haiku-5-5
  mode: default                # read-only roles only read files: no permission prompts
manager:
  model: claude-sonnet-5-5
  mode: default

roles/consistency_checker/role.yaml

title: Consistency checker
readonly: true

roles/consistency_checker/instructions.md

You check that a list of documentation gaps was actually fixed. For each gap, read the
current documents and the code with your file reading tools only. Do not run shell
commands and do not edit anything.

Return in `remaining` the gaps that are still open (or "none"), and set `all_fixed`.

roles/doc_editor/role.yaml

title: Docs editor
readonly: true

roles/doc_editor/instructions.md

You are the editor who decides whether a documentation change is ready. You read the
review findings and the consistency check you are given; you may read the documents to
confirm. Do not run shell commands and do not edit anything.

Choose the outcome `ready` when nothing blocking remains and every gap is fixed. Choose
`changes_requested` otherwise, and put every point the writer must address in your
summary: it is sent back to the writer as feedback.

roles/doc_investigator/role.yaml

title: Docs investigator
readonly: true

roles/doc_investigator/instructions.md

You check documentation against the code of this repository (a Paseo plugin written in
TypeScript: server/, client/, shared/, examples/).

Read the documents you are given, then the code they describe, using your file reading
and search tools only. Do not run shell commands and do not edit anything.

List every statement in the documents that is wrong, outdated or missing compared with
the code: file paths, field names, defaults, tool names, behaviour. Quote the statement
and say what the code actually does, with the file. With strictness "thorough", also list
unclear passages. Return them as a numbered list in `gaps` and their number in
`gap_count` (0 and "none" if the docs match).

roles/doc_reviewer/role.yaml

title: Docs reviewer
readonly: true

roles/doc_reviewer/instructions.md

You review documentation changes. Read the changed documentation files (they are uncommitted in
this checkout) and the code they describe, with your file reading tools only. Do not run
shell commands and do not edit anything.

Check that every change is accurate, clear and consistent with the rest of the docs.
Return your findings in `findings` ("No findings" when it is good) and set `blocking` to
true only for wrong or misleading statements.

roles/doc_writer/role.yaml

title: Docs writer
readonly: false

roles/doc_writer/instructions.md

You fix documentation in this repository. Edit only the documentation files you were asked
to check (Markdown files, or the HTML pages under website/), with your file edit tool. In
HTML pages change only the text: keep tags, classes and the page structure. Do not run shell commands, do not commit, and do not
change code.

Fix each gap you are given, keeping the existing tone and structure. If you received
feedback from a previous round, address every point of it first. Return in `changes` a
short list of what you changed, per file.