Recipes

Review every pull request

Review every pull request by starting a run on github.pull_request.opened, asking Scout for a structured review, pausing for a human verdict, and posting a GitHub review comment.

What you'll build

  • An event trigger for pull requests in acme/api.
  • An agent review step.
  • A request_review interaction step.
  • A conditional child-loop step that posts the review only after approval.

Prerequisites

  • A connected GitHub integration for acme/api.
  • An agent named Scout (agt_scout).
  • At least one Mobius user who can receive the review interaction.

Build it

  1. In the app, open Build > Loops.
  2. Create a loop named Review every pull request.
  3. Add an Event trigger for github.pull_request.opened.
  4. Add an agent step named Review diff.
  5. Ask Scout to return verdict, risk, and body.
  6. Add an interaction step named Human review.
  7. Use request_review with any_of resolution.
  8. Add a child-loop step named Post review.
  9. Point it at a small post-github-review loop whose only step calls github.pull_request.create_review.
  10. Set the child-loop condition to context.review_decision == "post_comment".
  11. Create the loop as active.

You will end with two loops: the pull request review loop, and a small post-github-review child loop that owns the GitHub review action. The parent only starts the child when the saved interaction value is post_comment.

Finished spec

schema_version: "1"
name: review-every-pull-request
description: Review new pull requests and post a GitHub review comment.
concurrency: allow
triggers:
  - key: pr-opened
    name: Pull request opened
    kind: event
    enabled: true
    config:
      event_type: github.pull_request.opened
      source_id: github
      condition: event.repository.full_name == "acme/api"
steps:
  - key: scout-review
    name: Review diff
    kind: agent
    config:
      agent_id: agt_scout
      instructions: |
        Review the pull request. Return JSON with verdict, risk, and body.
        Keep body suitable for a GitHub pull request review.
    save_as: scout_review
  - key: human-review
    name: Human review
    kind: interaction
    config:
      protocol: request_review
      targets:
        - usr_platform_lead
      prompt: |
        Review Scout's pull request notes and choose whether to post them.
      resolution_policy: any_of
      spec:
        mode: select
        options:
          - value: post_comment
            label: Post comment
          - value: skip
            label: Skip
    timeout:
      duration: 4h
      on_timeout: fail
    save_as: review_decision
  - key: post-review-if-approved
    name: Post review if approved
    kind: loop
    config:
      loop_handle: post-github-review
      condition: context.review_decision == "post_comment"
      inputs:
        repo_full_name: "{{ .inputs.event.repository.full_name }}"
        pr_number: "{{ .inputs.event.pull_request.number }}"
        review_event: COMMENT
        review_body: "{{ .context.scout_review.body }}"

The child loop is intentionally small:

schema_version: "1"
name: post-github-review
description: Post an approved GitHub pull request review.
concurrency: allow
inputs:
  repo_full_name:
    type: string
    required: true
  pr_number:
    type: integer
    required: true
  review_event:
    type: string
    required: true
  review_body:
    type: string
    required: true
triggers:
  - key: manual
    kind: manual
    enabled: true
steps:
  - key: post-review
    name: Post review
    kind: action
    config:
      action_name: github.pull_request.create_review
      parameters:
        repo_full_name: "{{ .inputs.repo_full_name }}"
        pr_number: "{{ .inputs.pr_number }}"
        event: "{{ .inputs.review_event }}"
        body: "{{ .inputs.review_body }}"

Today the conditional field lives on loop steps, so the posting path is a child loop instead of a direct action step.

Run it

Open a test pull request in acme/api. The run should suspend at the interaction until a target responds:

run.started
step.started          step=scout-review kind=agent
step.completed        step=scout-review
step.started          step=human-review kind=interaction
interaction.requested step=human-review
wait.opened           kind=interaction
step.suspended        step=human-review
run.suspended

After the response, the run resumes and posts the review:

interaction.responded
wait.resumed
step.completed        step=human-review
step.started          step=post-review-if-approved kind=loop
step.completed        step=post-review-if-approved
run.completed

If the human chooses skip, post-review-if-approved is marked skipped and the posting child loop never starts.

Variations

  • Replace the final review action with github.issue.create_comment if you want a normal PR comment instead of a review.
  • Use queue if a shared review environment should not run two PR reviews at the same time.
  • Add github.pull_request.list_files as an action step before Scout if the trigger payload does not include enough diff context.

Next