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. maxRunsPerPris 3 by default. A routine that must run on every push to a pull request needs a higher value, as the review recipe shows.concurrencyis 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/fixelsewhere isconcurrency_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:
- Save the routine below.
preset: sentrymakes Forkline check theSentry-Hook-Signatureheader. - 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. - Publish the routine. In Sentry, set the integration’s webhook URL to the routine URL (
forkline routine webhook sentry-alertprints 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.