View as Markdown

Cookbook

Agent hint

Copy the closest recipe, change repositories, then run forkline routine validate <file>, forkline routine backtest <file> and forkline routine push <file>.

  • Every recipe states the events it uses and why its filters and template variables are legal for all of them. Keep that property when you edit: a filter or variable that one listed event does not provide is a 422.
  • Every recipe is a complete file (name, description, then the spec keys) and passes the validator as written.
  • maxRunsPerPr is 3 by default. A routine that must run on every push to a pull request needs a higher value, as the review recipe shows.
  • concurrency is in the CI recipes (Gaps). Without it the defaults apply: a new event for the same pull request or issue replaces the older run, and pushes, schedules, Run now and custom webhooks skip while a run of the same routine is active. See Concurrency and limits.
  • maxActive (default 10) caps the routine’s active runs across all repositories; at the cap a new /fix elsewhere is concurrency_limited, not queued.
  • Reserved events (pr.conflicted, ci.succeeded, pr.labeled, issue.labeled, issue.assigned, pr.review_requested) are refused. See Gaps.

Each recipe has a one-line What it costs note. A run costs at most timeoutMinutes × size multiplier credits (basic 1, advanced 2, pro 4). Credits are charged for the time a run actually uses, so the real cost is usually lower. The runs per week are rough guesses for a mid-sized repository, not measurements: use backtest for your own numbers. See Run time, size and credits.

Review pull requests

Events: pr.opened, pr.updated. Both are pr.* events, so branches, labels, draft, fork, bots and self all apply to both, and pr.number, pr.title, pr.baseBranch and repository.fullName exist for both.

name: review-prs
description: Review every ready pull request that a person opens or pushes to
on:
  events: [pr.opened, pr.updated]
repositories:
  include:
    - name: "acme/api"
filters:
  draft: exclude
  bots: exclude
  self: exclude
  labels:
    exclude: [no-review]
agent:
  prompt: |
    Review pull request #{{ pr.number }} "{{ pr.title }}" into {{ pr.baseBranch }} in {{ repository.fullName }}.
    Look for bugs, missing tests and unclear naming. Post one summary comment.
    Do not push commits.
  runnerSize: advanced
  timeoutMinutes: 20
limits:
  maxRunsPerPr: 10

draft: exclude, bots: exclude and self: exclude are the defaults. They are written out so the intent is visible. A draft that is marked ready for review fires pr.opened again, so it is reviewed then. maxRunsPerPr is raised to 10 because the default 3 would stop reviewing a pull request after its third run: every later push is recorded as run_limit and starts nothing, and the count never resets. The bots and self filters judge who pushed, not who wrote the pull request; to skip pull requests written by a bot, use when: '!pr.authorIsBot' as in Who to skip.

What it costs: at most 40 credits a run (20 minutes, advanced). About 40 runs a week for a repository with 15 pull requests and two or three pushes each, so up to about 1,600 credits a week.

Review only pull requests Forkline opened

Event: pr.opened. The filter is self: only, which applies to every pr.* event.

name: review-forkline-prs
description: Review the pull requests that Forkline itself opened
on:
  events: [pr.opened]
repositories:
  include: all
filters:
  self: only
  fork: exclude
agent:
  prompt: |
    Forkline opened pull request #{{ pr.number }} "{{ pr.title }}" in {{ repository.fullName }}.
    Review it as a second pair of eyes and post one summary comment.
  runnerSize: basic
  timeoutMinutes: 15

self looks at the actor of the event, not at the author of the pull request. For pr.opened the actor is whoever opened (or reopened, or marked ready) the pull request. A pull request that Forkline opened and a person later reopens has a person as actor and is skipped. On GitLab and Forgejo connectors without an automation account, Forkline posts as a person, so self cannot tell its actions apart, see self and bots.

What it costs: at most 15 credits a run (15 minutes, basic). About 5 runs a week if Forkline opens a few pull requests.

Fix CI with a ci.failures loop

Event: ci.failed. It has no actor and no draft, fork, labels, bots or self filters; branches and when apply. The prompt reads ci.* and pr.*. A failed run that belongs to no pull request has no pr, so when requires a pull request number.

name: fix-ci
description: Fix a failed CI run on a pull request and push to its branch
on:
  events: [ci.failed]
repositories:
  include:
    - name: "acme/api"
filters:
  branches:
    exclude: [main]
  when: 'pr.number != null'
agent:
  prompt: |
    CI "{{ ci.name }}" failed for pull request #{{ pr.number }} on {{ ci.branch }} ({{ ci.headSha }}).
    Failed runs:
    {% for f in ci.failures %}- {{ f.name }} ({{ f.conclusion }}): {{ f.url }}
    {% endfor %}
    Fetch the logs of each failed run with the git-api tool, find the cause,
    fix it and push the fix to {{ pr.headBranch }}. If the failure is not caused by
    the code (an outage, a flaky service), say so in a comment and do not push.
    Other workflows of the same commit may have failed too: list the failed runs of {{ ci.headSha }} with the git-api tool and fix them all in one push.
  runnerSize: advanced
  timeoutMinutes: 30
concurrency:
  group: pr
  onConflict: skip
limits:
  maxRunsPerPr: 3

ci.failures has name, runId, url and conclusion per entry and no logs. On GitHub and Forgejo each failed workflow sends its own event, and the entries of one event are only the failed run itself. With the default onConflict: replace, each later failure of the same pull request would cancel the running fix and every run would count toward maxRunsPerPr, so three failing workflows on one commit would use up the default 3. The recipe sets onConflict: skip: the first failure starts one run, the others arrive while it is active and are recorded as skipped_active (they start nothing and do not count), and the prompt tells the agent to fix every failed run of the commit. maxRunsPerPr: 3 (the default 3, written out) is the loop guard: an agent whose fix fails CI again is stopped after three runs for the pull request. It never resets on its own.

What it costs: at most 60 credits a run (30 minutes, advanced), so at most 180 credits per pull request. About 5 to 10 runs a week for a repository with a busy CI.

Triage new issues

Event: issue.opened. labels, authors, bots and self apply, and issue.* exists. pr, draft and fork do not, so none is used.

name: triage-issues
description: Label and answer new issues
on:
  events: [issue.opened]
repositories:
  include: all
filters:
  labels:
    exclude: [wontfix, duplicate]
agent:
  prompt: |
    Triage issue #{{ issue.number }} "{{ issue.title }}" in {{ repository.fullName }} ({{ issue.url }}).
    Find out whether it is a bug, a feature request or a question, add the matching label,
    and ask for the missing details (version, steps, logs) in one comment.
    Do not change code.
  runnerSize: basic
  timeoutMinutes: 10

What it costs: at most 10 credits a run (10 minutes, basic). About 10 runs a week for an active repository.

A /fix command in comments

Event: comment.created. command applies only to this event. The comment is on a pull request or an issue, so pr and issue can both be empty in the prompt; the template guards with if.

name: fix-command
description: Run when a trusted person comments /fix
on:
  events: [comment.created]
repositories:
  include: all
filters:
  command:
    names: [fix]
agent:
  prompt: |
    {{ comment.author }} commented "/fix {{ comment.command.args }}" on {{ comment.url }}.
    {% if pr.number %}This is pull request #{{ pr.number }}. Check out its branch with git-api, make the fix and push to it.
    {% else %}This is issue #{{ issue.number }} "{{ issue.title }}". Make the fix on a new branch and open a pull request.
    {% endif %}
  runnerSize: advanced
  timeoutMinutes: 30

maxRunsPerPr (default 3) also counts command runs on a pull request, replaced ones included, so the fourth /fix on one pull request is run_limit. Raise it if people use the command often.

Names are written without the slash and match in any case. Only the first command line of a comment counts. On GitHub and Forgejo a comment on a pull request conversation has no pr.headBranch, so the prompt tells the agent to look the branch up. Who may issue the command is the repository trigger policy (default collaborators), not part of the file.

What it costs: at most 60 credits a run (30 minutes, advanced). About 5 runs a week, one per command.

Weekly dependency update

Trigger: on.schedule with cron and timezone. A schedule routine accepts only the when filter, and when sees only repository. The prompt reads repository.* and schedule.scheduledAt. It runs once for each selected repository. For one report across several repositories, select one repository and tell the agent in the prompt which others to read.

name: weekly-deps
description: Update outdated dependencies every Monday morning
on:
  schedule:
    cron: "0 6 * * 1"
    timezone: Europe/Berlin
repositories:
  include:
    - name: "acme/*"
  exclude:
    - name: "acme/sandbox-*"
filters:
  when: '!repository.private'
agent:
  prompt: |
    Update outdated dependencies in {{ repository.fullName }} (scheduled for {{ schedule.scheduledAt }}).
    Group them by risk, run the tests, and open one pull request.
  runnerSize: basic
  timeoutMinutes: 30

What it costs: at most 30 credits a run (30 minutes, basic), once a week per repository: 10 matching repositories cost up to 300 credits a week.

Sentry or any custom webhook

Trigger: on.webhook. Your system posts JSON to the routine URL, see Custom webhooks. The body is payload.* in the prompt and in when. Name one repository: a webhook routine runs once for each selected repository.

Setup for a Sentry internal integration:

  1. Save the routine below. preset: sentry makes Forkline check the Sentry-Hook-Signature header.
  2. Paste the integration’s client secret into Forkline: forkline routine webhook sentry-alert --set-secret secret.txt (the file holds the secret). Forkline never shows it back.
  3. Publish the routine. In Sentry, set the integration’s webhook URL to the routine URL (forkline routine webhook sentry-alert prints it) and subscribe to issue events.

The when skips what the agent should not look at: only newly created issues of level error or fatal start a run. Everything else is recorded as filtered. Sentry bodies carry the whole event, up to 5 MiB with the stack trace. That is fine: only the fields when reads count toward the payload limits.

name: sentry-alert
description: Investigate a Sentry alert that was posted to the routine webhook
on:
  webhook:
    auth: { type: hmac, preset: sentry }
repositories:
  include:
    - name: "acme/api"
filters:
  when: 'has(payload.action) && payload.action == "created" && has(payload.data) && has(payload.data.issue) && has(payload.data.issue.level) && payload.data.issue.level in ["error", "fatal"]'
agent:
  prompt: |
    Sentry reports an issue for {{ repository.fullName }}.
    Title: {{ payload.data.issue.title | default("(no title)", true) }}
    Culprit: {{ payload.data.issue.culprit | default("unknown", true) }}
    Link: {{ payload.data.issue.web_url | default("", true) }}
    {% if payload.raw_body %}Raw body: {{ payload.raw_body }}{% endif %}
    Find the cause in the code. If you can fix it safely, open a pull request.
  runnerSize: advanced
  timeoutMinutes: 30

A body that is not JSON arrives as payload.raw_body. Sentry’s deliveries carry no idempotency key Forkline reads, so each delivery runs. Use the integration’s issue webhook, not an alert-rule action: issue alerts post a different body, and this when would filter every one of them. For a sender that does retry, set on.webhook.idempotencyHeader (default Idempotency-Key) so a repeated delivery starts no second run. The field names under payload are those of your sender: replace the Sentry ones above with what your integration posts.

What it costs: at most 60 credits a run (30 minutes, advanced). One run per alert; use default(..., true) and your sender’s own filtering to keep this to about 10 a week.

Monorepo: the paths filter

Event: push. paths applies to push only, together with branches, authors, bots and self. The prompt reads push.*.

name: billing-service-changelog
description: Update the billing changelog when billing code changes on main
on:
  events: [push]
repositories:
  include:
    - name: "acme/platform"
filters:
  branches:
    include: [main]
  paths:
    include: ["services/billing/**"]
    exclude: ["services/billing/**/*.md"]
agent:
  prompt: |
    {{ push.commitCount }} commits landed on {{ push.branch }} ({{ push.before }}..{{ push.after }}).
    Update services/billing/CHANGELOG.md for the changes under services/billing.
  runnerSize: basic
  timeoutMinutes: 15

The push runs when at least one changed file matches. A push whose file list is incomplete also runs. A push to main while a run is active is skipped, not queued.

What it costs: at most 15 credits a run (15 minutes, basic). About 20 runs a week if the service changes daily.

A when condition

Events: pr.opened, pr.updated. when reads typed fields only, and pr.* exists for both. It runs after the other filters.

name: review-feature-prs
description: Review feature pull requests and anything labelled needs-review
on:
  events: [pr.opened, pr.updated]
repositories:
  include: all
filters:
  when: '!pr.title.startsWith("chore") && (pr.headBranch.startsWith("feat/") || "needs-review" in pr.labels)'
agent:
  prompt: |
    Review pull request #{{ pr.number }} "{{ pr.title }}" from {{ pr.headBranch }}.
  runnerSize: basic
  timeoutMinutes: 20
limits:
  maxRunsPerPr: 5

when cannot read the files of a pull request, and has no comment.command; use comment.commandName there. Prefer startsWith() and contains() to matches(): they cost less of the 50,000 unit budget. See when conditions.

What it costs: at most 20 credits a run (20 minutes, basic). About 20 runs a week, since the condition skips many pull requests.

Built-in automations written as routines

Forkline’s built-in automations (CI auto-fix, auto PR review, bot mentions and mention commands, merge-conflict auto-fix) are settings, not routines. A routine can approximate three of them and cannot reproduce them exactly. Each section gives the closest routine and a table of every difference. The routines below are starting points: none reaches parity.

Turn the built-in off, or both run

A built-in and a routine that react to the same event both run and start two tasks. Forkline does not merge them. When you move a feature to a routine, turn the built-in off for the repository first: CI auto-fix, auto PR review, merge-conflict auto-fix (each page says where, under the repository Automation tab). Bot mentions have no switch, see Bot mentions; narrow the routine with repositories and filters so that it covers events the built-in does not. See also Routines vs built-in automations.

CI auto-fix

Built-in: CI auto-fix. Closest routine: the fix CI recipe, with the failing runs listed in the prompt and the agent fetching logs itself.

name: ci-auto-fix
description: Closest routine to the CI auto-fix built-in
on:
  events: [ci.failed]
repositories:
  include: all
filters:
  when: 'pr.number != null'
agent:
  prompt: |
    CI "{{ ci.name }}" failed for pull request #{{ pr.number }} on commit {{ ci.headSha }}.
    {% for f in ci.failures %}- {{ f.name }} ({{ f.conclusion }}): {{ f.url }}
    {% endfor %}
    Fetch the failing job logs with the git-api tool, fix the cause and push
    directly to {{ pr.headBranch }}.
    Other workflows of the same commit may have failed too: list the failed runs of {{ ci.headSha }} with the git-api tool and fix them all in one push.
  runnerSize: basic
  timeoutMinutes: 30
concurrency:
  group: pr
  onConflict: skip
limits:
  maxRunsPerPr: 3

Where this routine differs from the built-in:

Area Built-in CI auto-fix This routine
Failure logs Fetched by Forkline (failed jobs, up to 50 KiB per failure) and put in the prompt No logs in ci.failures (name, runId, url, conclusion only). The agent fetches them itself, which costs runner time
Several failures on one commit A 5 minute collection window (+1 minute per extra failure, at most 10 minutes) gives one task with all failures One event per failed run or pipeline. With the default replace, each later failure of the same pull request cancels the running fix, and every run counts toward maxRunsPerPr: three failing workflows on one commit use up the default 3. The recipe sets onConflict: skip, so the first failure starts one run and the others, while it is active, are skipped_active (they start nothing and do not count); the prompt tells the agent to fix every failed run of the commit
Dedupe per head SHA One collection per repository and commit None per commit. A forge redelivery of the same event is deduplicated, a second failed workflow of the same commit is not
Attempt limit Setting per repository or account (default 3), counts consecutive attempts, shared by all of a repository’s CI failures maxRunsPerPr (default 3), counts every run of this routine for the pull request including replaced ones. One routine per distinct limit
Reset and resume /reset or /reset-ci-auto-fix zeroes the counter and re-checks CI. A human push pauses it (GitHub, Forgejo) No /reset, no resetOn, no pause on a human push. At the limit, runs are recorded as run_limit and no comment is posted
Cancel on success A later passing run cancels pending fixes of the same check ci.succeeded is reserved. Nothing is cancelled when CI passes again; a newer ci.failed for the pull request replaces the older run by default
CI sources GitHub workflow_run, check_run and status; GitLab pipelines; Forgejo action_run_failure, check_run and Gitea workflow_run; external CI that reports a check or status workflow_run (GitHub, Forgejo), pipeline (GitLab) and action_run_failure only. External CI that reports only check_run or status is not seen
timed_out Ignored Included: ci.failed fires for failed and timed-out runs
Fork pull requests Skipped fork does not apply to ci.failed and pr.fork is always false there. Add a when on the head branch if you need to avoid forks
Bots No bot check: CI failures on Forkline’s own commits are processed self and bots do not apply to ci.failed; maxRunsPerPr is the only guard
Progress and result comments Posts a started, success and failure comment, and a paused comment at the limit No comments. The agent posts what its prompt asks for
Task priority CI fixes outrank reviews Routine runs have the default priority, not the CI-fix priority
Settings inheritance A toggle per repository that overrides an account default repositories selects repositories by name or id. A per-repository toggle becomes exclude entries, and a different limit becomes another routine
Forges Per-forge log fetching One routine for all forges, but the agent needs the forge tooling to fetch logs

Auto PR review

Built-in: Auto PR review. The built-in default scope is Forkline-authored pull requests only; the all pull requests scope is the first routine below, the default scope is the second.

name: auto-pr-review-all
description: Closest routine to auto PR review with scope all pull requests
on:
  events: [pr.opened]
repositories:
  include: all
filters:
  draft: exclude
  fork: exclude
  labels:
    exclude: [no-auto-review]
agent:
  prompt: |
    Review pull request #{{ pr.number }} "{{ pr.title }}" by {{ pr.author }}
    ({{ pr.headBranch }} into {{ pr.baseBranch }}) in {{ repository.fullName }}.
    Provide your feedback as review comments. Do not make any code changes.
  runnerSize: basic
  timeoutMinutes: 20
name: auto-pr-review-forkline-only
description: Closest routine to auto PR review with the default scope
on:
  events: [pr.opened]
repositories:
  include: all
filters:
  self: only
  draft: exclude
  fork: exclude
  labels:
    exclude: [no-auto-review]
agent:
  prompt: |
    Review pull request #{{ pr.number }} "{{ pr.title }}" ({{ pr.headBranch }} into {{ pr.baseBranch }}).
    Provide your feedback as review comments. Do not make any code changes.
  runnerSize: basic
  timeoutMinutes: 20

Where these routines differ from the built-in:

Area Built-in auto PR review These routines
Trigger Opened only pr.opened also fires when a pull request is reopened or a draft is marked ready for review (GitHub, Forgejo), so a draft that becomes ready is now reviewed, and a reopened pull request is reviewed again
Scope forkline_only Forkline-authored pull requests: the author is the bot self: only judges the actor, so it matches only while the bot opens, reopens or readies the pull request. A person who reopens a Forkline pull request is skipped. On GitLab and Forgejo connectors without an automation account it matches nothing
Own bot rule Scope all reviews Forkline’s pull requests too self defaults to exclude, so scope all must keep the default and does not review Forkline’s own pull requests. Use self: include to cover both
Other bots Reviews them bots defaults to exclude and judges the actor: events caused by renovate[bot] and similar (opening or pushing to a pull request) are skipped, while a person’s push to a bot’s pull request still runs. Use bots: include to review bot activity, or when: '!pr.authorIsBot' to skip pull requests written by a bot (on GitLab only when the merge request is first opened, see Filters)
Draft and fork Skipped Same (draft: exclude, fork: exclude)
Skip label no-auto-review labels.exclude: [no-auto-review] as written, a label you must set yourself
Duplicate reviews Skipped while a review of the pull request is pending or running A new event for the pull request replaces the running one by default (pr group), and maxRunsPerPr (default 3) counts the runs
Action commit Prompt asks for fixes on the PR branch Write it in the prompt; the default ref is the pull request head branch (the base branch for forks)
Custom prompt per repository or account Setting with fallback Part of the file: one routine per distinct prompt, each with its own repositories
Repository toggle Per repository, with an account default repositories include and exclude
Trigger policy Not checked Not checked either: opened events are not subject to the trigger policy. Use authors to restrict who opens the pull request
Task priority Reviews have a fixed priority Default priority
Result Posted as review comments by Forkline Posted by the agent as the prompt asks

Bot mentions and issue response

Built-in: Bot mentions. Comments that mention @forkline start a pull request review task on a pull request and an issue response task on an issue. A routine can answer mentions in comments with the command.mention filter.

name: forkline-mention
description: Closest routine to bot mentions in comments
on:
  events: [comment.created]
repositories:
  include: all
filters:
  command:
    mention: true
agent:
  prompt: |
    {{ comment.author }} mentioned you on {{ comment.url }}:

    {{ comment.body }}

    {% if pr.number %}This is pull request #{{ pr.number }}.{% else %}This is issue #{{ issue.number }} "{{ issue.title }}".{% endif %}
    Read the conversation on the forge, then answer in a comment or make the change that was asked for.
  runnerSize: basic
  timeoutMinutes: 30

Where this routine differs from the built-in:

Area Built-in mentions This routine
Prompt Only the text after the mention, up to the next mention; /model removed The whole comment as comment.body, mention included. No comment.prompt
Several mentions in one comment One task per mention One run per comment
Mention in a pull request or issue description Starts a task when opened Not supported: command filters comments only
Label, assignment and review-request triggers (forkline, ai-task, forkline-review labels, bot assigned or asked to review) Start tasks Not supported: pr.labeled, issue.labeled, issue.assigned and pr.review_requested are reserved. Those webhook actions are dropped
Inline review comments Passed with file path, line and diff hunk Event fires, but without path, line or diff hunk
Conversation history Fetched and attached to the task Not attached; the agent reads the thread itself
Model selection /model <id> and model: or forkline: labels, with a disambiguation comment None. agent.model is fixed in the file
Task type pr_review, issue_response or autolabel, chosen from the text One routine, one prompt; branch in the template with if pr.number
Who may trigger Sender trigger policy (default collaborators) for mentions, descriptions, labels and assignments The same policy applies to comment.created, not to the other triggers, which a routine cannot express anyway
Bot-authored mentions Create work on purpose self: exclude (default) skips Forkline’s own comments. self: include allows them (comments from a plain forkline account that is not the connector’s automation account are still subject to the trigger policy), with maxRunsPerPr (default 3) as the only loop guard; there is no content-based loop check
Fork and draft Not checked fork and draft do not apply to comment.created; for comments on a pull request pr.fork and pr.draft are false
Reactions and replies Reacts to the comment and replies to it Neither: the routine run has no link to the comment beyond comment.id and comment.url in the prompt
Several users with the same repository URL Picks the registering user’s repository record repositories selects the repositories of the routine owner only
Switch No toggle A routine is on or off per routine

Mention commands

Built-in: the commands in a mention, /review, /autolabel, /model and /reset. /review and /autolabel can be written as slash commands with command.names. /model and /reset cannot.

name: review-and-label-commands
description: Closest routine to the review and autolabel mention commands
on:
  events: [comment.created]
repositories:
  include: all
filters:
  command:
    names: [review, autolabel]
agent:
  prompt: |
    {{ comment.author }} wrote "/{{ comment.command.name }} {{ comment.command.args }}" on {{ comment.url }}.
    {% if comment.command.name == "autolabel" %}Add the labels that fit to {% if pr.number %}pull request #{{ pr.number }}{% else %}issue #{{ issue.number }}{% endif %}. Do not change code.
    {% else %}Review {% if pr.number %}pull request #{{ pr.number }} and post review comments{% else %}the linked change{% endif %}. Do not change code.
    {% endif %}
  runnerSize: basic
  timeoutMinutes: 20

Where this routine differs from the built-in:

Area Built-in commands This routine
Command syntax Recognised in the text of a mention command.names matches the first line that starts with /name only. A mention does not need the slash, and mention: true is a separate condition that matches without any command
/autolabel natural language “please label this” also forces the autolabel task Not recognised. Only the exact command
/model <id> Overrides the model for that task Not available: no per-event model
/reset and /reset-ci-auto-fix A platform action, not a task: zeroes the CI fix counter and re-checks CI Not expressible. A routine run costs credits and cannot touch the built-in counter or maxRunsPerPr
Who may trigger Trigger policy Same policy for comment.created
Case Command names are case-insensitive Same
Bot-authored commands Allowed Skipped unless self: include
Reactions, history, inline context Reaction on the comment, history attached Not available, see the mention table above

Merge conflicts

Built-in: Merge conflict auto-fix. A routine cannot detect conflicts. The event that would carry it, pr.conflicted, is reserved and rejected at save. The pr fields have no mergeability flag, and there is no event for a push to the base branch of a pull request. The nearest thing is a routine that runs on every push to the default branch and asks the agent to find the open pull requests that no longer merge.

name: merge-conflict-sweep
description: After a push to main, find open pull requests with conflicts and resolve them
on:
  events: [push]
repositories:
  include: all
filters:
  branches:
    include: [main]
agent:
  prompt: |
    {{ push.commitCount }} commits landed on {{ push.branch }} in {{ repository.fullName }}.
    Use git-api to list the open pull requests that target {{ push.branch }} and
    cannot be merged because of conflicts. For each, merge origin/{{ push.branch }}
    into its head branch (do not rebase), resolve the conflicts, run the tests and push.
    Skip pull requests from forks and say what you skipped.
  runnerSize: advanced
  timeoutMinutes: 45

Where this routine differs from the built-in:

Area Built-in merge-conflict auto-fix This routine
Detection Forkline checks mergeability on pull request open, update and reopen, on commit statuses, and when the base branch is pushed; it re-checks unknown mergeability after 60 seconds, up to 5 times None. The run starts on every push to the branch, whether or not any pull request conflicts, and the agent does the checking
One task per pull request A task per conflicting pull request, with its own head branch One run per push, covering all pull requests. push carries no pull request, so pr.* is not available and limits.maxRunsPerPr (default 3) cannot protect a single pull request
Attempt budget Per pull request and head commit: a new push resets it, runs that never started do not count; setting default 3 None per pull request or commit. Cap the cost with timeoutMinutes and the skip default (a push while a run is active is skipped)
Cost Only when a conflict exists A run for every push to main, conflict or not
Forks Skipped Asked for in the prompt only
Drafts Not excluded Not looked at in the file, the agent decides
GitLab Not supported The routine runs on GitLab pushes, but the agent must find merge requests itself
Priority, status comments Fixed priority, started and resolved comments Default priority, no comments unless the prompt asks
Conflicting files The task knows the pull request, branches and SHAs The agent has to find them

Gaps the recipes cannot close

These cannot be written as a routine today. Do not try them: the validator refuses them or the routine would not do what it says.

Wanted Why not Closest
React to a merge conflict (pr.conflicted) The event is reserved and refused at /on/events/{i} with `pr.conflicted` is not supported yet Merge conflicts sweep on push
Stop when CI passes (ci.succeeded) Reserved A newer ci.failed replaces the older run by default
React to a label, an assignment or a review request pr.labeled, issue.labeled, issue.assigned, pr.review_requested are reserved, and those webhook actions are dropped A when on pr.labels for pr.updated runs on every push, not when the label is added
Command in a pull request or issue description command applies to comment.created only None
CI logs in the prompt ci.failures has no logs The agent fetches logs with git-api
One run for all failures of a commit One event per failed run onConflict: skip and a prompt that asks the agent to fix every failed run of the commit. No collection window
Trigger policy for events other than comments The policy checks comment.created only authors restricts who opens or pushes
Reset or pause the run limit No resetOn, no /reset Raise maxRunsPerPr (default 3, at most 50)
Cap parallel runs or change the replace behaviour Not a gap: use concurrency See Concurrency and limits

A routine that must not be replaced mid-run sets onConflict: skip, as both CI recipes above do. A second event for the same pull request while a run is active is then skipped instead of replacing the run. See Concurrency and limits.