Skip to main content

PR Follow-up

ProPR turns implementation work into pull requests automatically, then keeps follow-up work in the pull request. You review the result, leave normal GitHub comments, request AI review, apply the suggestions you keep, switch models, and ask for branch help without leaving the PR. This page describes how that loop works; command syntax and rules live in PR Comment Commands.

Automatic Pull Request Creation​

When ProPR finishes an implementation task, it handles the GitHub plumbing around it:

  • Creates the feature branch (named with the issue number and model identifier)
  • Commits the agent's changes
  • Pushes to GitHub
  • Opens a pull request linked to the source issue
  • Posts status back to GitHub
  • Attaches focused visual previews when the repository enables them and the change has a visible result
  • Updates task and label state (<trigger>-processing → <trigger>-done, or <trigger>-failed-* on failure)

This keeps the agent focused on code while ProPR handles the repeatable workflow around the code.

The pull request title and description follow ProPR's default layout. To add required sections, ticket references or a checklist, use a pull request template. When the repository has none, ProPR adds the repository's GitHub pull request template under its summary.

Use ProPR On Any Pull Request​

You can skip ProPR-driven PR creation entirely and still use its review and fix tools. The pull request is an entry point on its own, so you can apply ProPR to PRs opened by a teammate, another agent, or yourself outside ProPR:

  • Review or fix only: comment /review on any eligible PR to get AI review feedback, then /fix to resolve its merge blockers, or /fix F2 S1 to pick exactly the records you want.
  • Take over an existing PR: add a configured processing label (for example AI or propr) to the open PR. From then on, normal follow-up comments are picked up just like on a ProPR-created PR, and ProPR continues the work in place.

Once ProPR is engaged, follow-up comments and slash commands behave the same way on a handed-over PR as on a ProPR-created one. Other details can still differ — a handed-over PR keeps its original branch name, may link no source issue, and carries no prior ProPR task history — but the command and follow-up behavior is identical.

Natural Follow-Up Comments​

For ordinary refinement, plain comments are all you need. Post a normal PR comment that describes the requested change:

Please update the empty state copy and add a regression test for the loading spinner.

ProPR picks up the comment, includes PR context and the comment content, and queues the requested change for processing. What that pickup gives you:

  • Comments posted while a job for the same PR is already running are batched and handled together once the active job finishes.
  • Line-level review comments carry their file path, line, and diff hunk to the agent, so "fix this" on a specific line has real context.
  • Images attached to comments are available to the agent — paste a screenshot of the bug and the agent sees it.

Comment pickup is gated by processing labels, trigger keywords, author permissions and, when it is turned on, the assignment gate; see Who Can Trigger Commands for the rules.

The Review And Fix Loop​

When you want a quality pass on top of your own reading, the loop goes: ask, prune, apply.

  1. /review posts a review with numbered merge blockers (F1, F2, …), optional suggestions (S1, S2, …) and a score — the code is untouched. The numbers continue across every review on the PR and are never reused.
  2. You decide what to apply: copy the review's /fix F1 S1 line and drop the IDs you don't want, or edit the comment itself to sharpen or remove a record.
  3. /fix resolves every pending merge blocker in one implementation pass, /fix all adds every pending suggestion, and /fix F1 S2 applies exactly the records you name. See /fix.

The split by feedback source keeps intent clear: plain user comments start follow-up work directly the moment you post them, and /fix handles the records from /review.

For more autonomous cleanup, /ultrafix alternates review and fix cycles until the review score reaches its goal, waiting for CI and PR inactivity between cycles; a visible PR label acts as its circuit breaker. If CI goes red after an automatic fix, the loop pauses rather than ending. Once a follow-up fix or your own push turns the required checks green, check-run and check-suite events, or the polling reconciliation, wake the loop and schedule its next review. The cycle limit, the goal, and the label are still respected at that point, and a per-PR lock with BullMQ job ID deduplication stops simultaneous check events from queuing duplicate cycles. See Recovery From CI Failures. When the base branch has moved, /merge brings the base branch into the PR branch and resolves conflicts with agent help — you merge the PR itself when you are satisfied.

Full syntax, parameters, and trigger rules for every command are in PR Comment Commands.

Withdrawing Work​

Closing a PR without merging cancels queued and running follow-up, review and Ultrafix work for that PR, with terminal reason cancelled_pr_closed. Its Ultrafix loop is cleared. Polling checks active and queued PRs each cycle, and workers recheck the live PR before starting, so missed webhooks cannot start work on a closed PR. Cancellation uses the same stop mechanism as the task view and propr task stop; cancelled attempts are never automatically retried.

Closing the source issue or removing its processing trigger cancels only that issue's implementation work. Existing PRs and their independent follow-ups remain open. Removing a model label does not cancel work. See task state and terminal reasons for reason codes and issue state labels.

Automatic Follow-Up For Failed CI​

Auto CI follow-up (Repositories → repository → Automation, or propr repo toggle owner/repo --auto-ci-followup) is off by default. When enabled, a failing check run or commit status on the current head of a pull request makes ProPR post one comment naming the check, the commit, and the failure output; that comment starts follow-up work like any other, without a processing label or trigger keyword. Each failing check is reported at most once per commit. Check runs that GitHub delivers without a PR number, which is common for fork PRs, are matched to open PRs by commit, so they are reported too. Enable it only where CI failures are trustworthy signals.

While Ultrafix is waiting for CI, its periodic check also recovers failed-CI follow-ups whose webhook was missed, such as during a restart. Recovery respects this setting and the repository's non-blocking checks, ignores replaced commits and closed PRs, and shares duplicate protection with webhook delivery.

A cancelled check is not a failure, and neither is a check that failed only because its workflow run was deliberately cancelled. For example, an aggregate gate that runs under always() and fails closed when its shards were cancelled. Without this rule, cancelling obsolete checks during follow-up would start a new follow-up from the gate's failure, which would then cancel the next commit's checks. ProPR skips the comment only when all of the following hold:

  • The check itself concluded failure. A check that timed out, failed to start or is waiting for approval is always reported, even if the cancellation evidence points elsewhere.
  • The run attempt was cancelled by ProPR's own recorded suspension, or GitHub reports that attempt as cancelled.
  • No other job of that attempt failed or timed out.
  • The check failed after the cancellation stopped its sibling jobs.
  • The check's own failure annotations show it failed because of the cancellation: at least one reports a needed job as cancelled, and every other one is the runner's exit-code notice or a recognized consequence of cancelled shards, such as shard coverage verification failing.
  • Every step of the check that failed is the recognized gate that enforces its needed jobs' results (or the shard coverage verification that fails only because cancelled shards uploaded nothing), and the gate itself failed. Any other failed step — including a plain shell command whose only annotation is Process completed with exit code 1. — is reported.

Timing and provenance alone are not enough: a job that runs under always() and fails its own step after the cancellation still produces a follow-up.

If ProPR can't read that evidence, it posts the comment. The check keeps its failing conclusion, so required merge gates are unchanged, and a genuine failure on a later attempt of the same commit is still reported.

Automatic Pull Request Assignment​

When work on a ProPR pull request completes, ProPR can assign the pull request to someone, and optionally request their review, so it lands in a person's queue instead of waiting unowned. It is off unless you turn it on. See Task Assignment for how assignment works in general.

Where it is configured​

OptionWhereValues
Assign pull requestsRepositories → repository → Automation → Assign the pull request when ProPR finishes, MCP update_repository_configuration (autoAssignPullRequests), propr repo add|toggle owner/repo --auto-assign <on|off>on / off (default off)
Default assigneeThe Assignee field under that option, MCP update_repository_configuration (autoAssignDefaultAssignee), propr repo add|toggle owner/repo --auto-assign-to <login|none>A GitHub login, or empty for the source issue author (default empty)
Request a reviewAlso request a review from the assignee under that option, MCP update_repository_configuration (autoAssignRequestReview), propr repo add|toggle owner/repo --auto-assign-review <on|off>on / off (default off)

There is no instance default: each repository opts in for itself. The options are repository-wide, so every branch entry of a monitored repository shares them. none (or null through MCP, or clearing the Assignee field) removes the default assignee. A leading @ is accepted, and a value that is not a valid GitHub login is rejected. Clients that do not send a field never change it. MCP get_repository_configuration returns all three fields, and propr repo list shows them in its Auto-assign PRs column, for example Off or On (@octocat, review).

When it runs​

Assignment is tied to work completing, never to work starting:

  • Implementation done. When an implementation publishes its pull request and the issue moves to <trigger>-done. This also covers a final validation that finds or recreates a pull request that post-processing missed.
  • Follow-up done. When a follow-up, including /fix, pushes a commit. If the work was published to a continuation pull request, that pull request is assigned instead.

Resumed work triggers nothing. ProPR switches the label back to <trigger>-processing and posts its "starting work" comment when a follow-up begins, and neither assigns anyone nor requests a review. A /review run and a follow-up that pushed no commit do not change the pull request, so they trigger nothing either.

Each commit is one opportunity. A completed attempt is remembered per repository, pull request and head commit, so a retry or redelivery for the same commit does nothing, while the next follow-up commit is a new opportunity. An attempt that a crashed worker left unfinished holds off other attempts for that commit for 10 minutes. Nothing retries it on a schedule: a later completion for the same commit after that, such as a redelivery or a final validation, tries again, and otherwise the next follow-up commit does.

Who is assigned​

The assignee is resolved in this order:

  1. The repository's default assignee, when one is set.
  2. Otherwise the author of the source issue: the issue the implementation was created from, or for a follow-up, the first issue the pull request body closes (Closes #123). Bot authors are skipped. An issue opened by a bot, such as a ProPR planner issue, leaves the pull request unassigned unless a default assignee is set.

The person who wrote a follow-up comment is never the target. A follow-up keeps the pull request with the issue author or default assignee.

Manual assignees are preserved. ProPR only adds its assignee and never removes anyone already assigned, by hand or otherwise. If the target is already assigned, nothing is written.

Review requests​

With Also request a review from the assignee on, ProPR asks the assignee for a GitHub review after assigning them:

  • A review is requested at most once. Nothing is sent while a review from that person is still pending, and a commit that was already handled sends nothing again, so the implementation and a follow-up never stack two requests. Only after the assignee has answered the earlier request can a later follow-up commit ask them again.
  • The pull request author is never asked to review their own pull request. GitHub refuses such requests, so ProPR skips the request and records why.
  • When GitHub refused the assignment, no review is requested either.

A failed review request does not undo the assignment, and a retry of the same commit tries the review request again.

Why a pull request was not assigned​

Each decision is recorded on the task timeline (event pull_request.auto_assignment) and logged as Pull request auto-assignment decision, ... skipped or ... failed with { repository, prNumber, opportunity, reason }. A repository with the option off records nothing on the timeline. Every decision not to assign:

StatusReasonMeaning
disableddisabled for the repositoryAutomatic assignment is off for this repository. Nothing is read from GitHub.
already_assignedalready handled for head <sha>This commit was already handled, for example by an earlier attempt or a redelivery.
already_assigned<login> is already assignedThe target is already assigned. Only the review request, if enabled, is considered.
skippedanother attempt for head <sha> is in progressAnother worker is handling this commit right now.
skippedthe pull request has no linked source issueNo default assignee, and no source issue to take the author from.
skippedthe source issue has no authorNo default assignee, and GitHub returned no author for the source issue.
skippedthe source issue author <login> is a botNo default assignee, and the issue was opened by a bot.
skippedno task to record the assignment onThe completion had no ProPR task to record the decision on.
not_assignedGitHub did not assign <login>, who may lack repository accessGitHub refused the assignee, usually because they cannot access the repository. Check the default assignee.
failedThe error messageReading the pull request or issue, or assigning, failed. The failure never fails the implementation or follow-up itself.

Review requests record their own outcome alongside the assignment: requested, skipped (<login> authored the pull request, or a review from <login> is already requested), or failed with the error.

Automatic Merge-Conflict Resolution​

When the base branch of a ProPR pull request moves on (typically because another pull request merged) and the two now conflict, ProPR can merge the base branch into the pull request and let an agent resolve the conflicts. It is off unless you turn it on.

Where it is configured​

LevelWhereValues
Instance defaultSettings → Workflow & PR Rules → Repository defaults → Auto-Resolve Merge Conflicts (default for repositories), MCP update_execution_settings (auto_resolve_merge_conflicts), propr setting update auto_resolve_merge_conflicts <true|false>on / off (default off)
Repository overrideRepositories → repository → Automation → Auto-resolve merge conflicts, MCP update_repository_configuration (autoResolveMergeConflicts), propr repo add|toggle owner/repo --auto-resolve-conflicts <on|off|inherit>Always / Never / Use instance default

The effective value is the repository override when one is set, otherwise the instance default, otherwise off. The override is repository-wide: every branch entry of a monitored repository shares it. inherit (or null through MCP) removes the override. Clients that do not send the field never change it. MCP get_repository_configuration returns the stored override and effective.autoResolveMergeConflicts with enabled, source (repository or instance), repositoryOverride and instanceDefault; propr repo list shows On, Off or Inherit (On/Off).

When it runs​

  • Base branch push. A push to branch X evaluates up to 30 open pull requests whose base is X. Every one is read once first so GitHub starts computing mergeability, then each is evaluated.
  • Pull request events. opened, reopened, synchronize and ready_for_review.
  • Sweep. A safety net evaluates open pull requests of every repository with the setting on: every 5 minutes with GITHUB_EVENT_INTAKE_MODE=polling (which never receives push events) and every 15 minutes otherwise. MERGE_CONFLICT_SWEEP_INTERVAL_MS overrides the interval.

GitHub computes mergeability lazily and answers mergeable: null right after a push, so ProPR re-reads the pull request after 2, 5, 10 and 20 seconds before giving up. A pull request is conflicted when GitHub reports mergeable: false or mergeable_state: "dirty".

Only ProPR pull requests are touched: those carrying the PR label, a processing label or the AI primary tag, an llm-* model label, or that ProPR has a task for. Human pull requests are never modified. Each head+base pair is queued once (for 30 minutes), and a pull request gets at most 3 automatic attempts in 24 hours so a constantly moving base cannot cause a loop.

The resolution job merges the base into the pull request branch, asks the agent to resolve the conflicted files, refuses to continue while any conflict marker remains, records a merge commit (hooks disabled) whose parents are the previous head and the base, and pushes it. Any failure aborts the merge and leaves the remote branch untouched.

The /merge comment is unaffected by these settings: it is an explicit request and always runs for a ProPR pull request.

Why a pull request was not resolved​

Every decision not to act is logged at info (warn for mergeability_unknown and attempt_limit) with { repository, pullNumber, trigger, reason }:

ReasonMeaning
auto_resolve_disabledThe effective setting for the repository is off.
pull_request_closedThe pull request is no longer open.
draft_pull_requestDrafts are left alone until they are ready for review.
fork_pull_requestThe head repository no longer exists.
not_propr_pull_requestNo ProPR label and no ProPR task for the pull request.
mergeability_unknownGitHub had still not computed mergeability after polling.
not_conflictedGitHub reports the pull request as mergeable.
already_queuedThis exact head+base state was already queued.
attempt_limit3 automatic attempts in the last 24 hours.

Queued jobs are logged with their job ID, head and base.

Cancelling Obsolete Checks During Follow-Up​

While a follow-up implements, the checks running on the commit it is about to replace are already obsolete, and on a busy repository they keep runners occupied for work nobody will read. GitHub's own cancel-in-progress concurrency only helps once a replacement workflow starts, which is after the new commit is pushed.

The repository setting Cancel CI while follow-up implementation is in progress (Repositories → repository → Automation, off by default) closes that window:

  • Cancellation happens only once a follow-up is authorized and actually implementing, including /fix. Comments, pending requests, reviews and rejected requests never cancel anything.
  • You choose the workflows; ProPR never guesses. Next to the option, Validation workflows to cancel holds the exact workflows it may cancel for this repository. Nothing else is ever cancelled — not a workflow with a similar name, not a new workflow somebody adds later. This is deliberate: a workflow called Build or CI is free to deploy, and only you know whether cancelling it is safe. While the list is empty this repository selects nothing of its own, and the instance-wide CANCEL_CI_FOLLOWUP_WORKFLOWS fallback below decides instead; with neither, nothing is cancelled. The settings screen says so in its empty state rather than promising that nothing is cancelled.
  • Select each workflow by any of its complete identities: its file name (pr-build-check.yml), its path (.github/workflows/pr-build-check.yml), the name shown on the pull request (Build & Lint Check), or its numeric workflow ID. Matching is case-insensitive and exact — never a substring, so pr-build-check.yml does not select pr-build-check-matrix.yml. Separate entries with commas.
  • Of a selected workflow, only queued or running GitHub Actions runs that GitHub itself associates with that exact pull request and that exact head commit are cancelled. Manual, scheduled, branch, tag and other pull requests' runs, other revisions and non-Actions checks are never touched, and a branch name alone never qualifies a run. A selected workflow is eligible on both pull_request and pull_request_target, because selecting it is the decision that it is safe to cancel.
  • Instances configured outside the Web UI can set CANCEL_CI_FOLLOWUP_WORKFLOWS to the same kind of comma-separated list. It is a fallback, not an addition: it applies only to repositories whose own selection is empty, and a repository selection always wins over it. A selection ProPR cannot read at all is not an empty one: nothing is cancelled for that repository until the configuration is readable again, because its stored selection may name workflows the fallback does not.
  • When implementation pushes a replacement commit, its checks run normally. When it produces no commit, fails or is cancelled, ProPR restarts the cancelled runs for the still-current commit — after re-reading the live pull request head, including after every wait for a cancellation to finish, and without duplicating a run GitHub already restarted. Only a run of the same workflow that validates that same pull request head on a pull request event counts as that replacement: a push or another pull request's run of the same commit validates something else and never settles the obligation. Cancellations stay visibly cancelled in the meantime; ProPR never reports a check as successful and never relaxes required checks.
  • The obligation is stored durably before each cancel request is sent, so a crash or a lost response can never leave CI cancelled without a restart obligation; reconciliation reads each run's real outcome and restarts only what GitHub really cancelled. The attempt that outcome is read for is the one ProPR's request affected, established from the run's own attempts rather than from whatever attempt the run shows later: if somebody reruns a cancelled attempt before ProPR does, that rerun already restored the validation, and ProPR never restarts the newer attempt on their behalf, however it ends. A denied retry never discards an obligation an earlier attempt already took on, and a restart that GitHub refuses or that keeps failing keeps its runs recorded in a blocked state that every later reconciliation retries, however long that takes — cancelled checks are released only once their restart is confirmed, the pull request closes or its head is replaced. It also picks up runs that GitHub queued after the cancellation, and releases the suspension if you switch the option off mid-task.
  • Every step for one pull request — starting, sweeping, restoring and releasing — runs while holding a shared database lease, so two workers can never act on the same pull request at once and no run is ever cancelled after a restart has begun. A pull request another worker is already handling is left alone and picked up by the next reconciliation.

Prerequisite: the GitHub App installation needs Actions: Read and write. With read-only Actions access the option is inert — the attempt is logged as a permission error and implementation continues with CI untouched. If access is lost after checks were already cancelled, the obligation to restart them survives and is honoured as soon as the access is granted back. Fork contributions that ProPR publishes to a continuation pull request are handled through that continuation; runs GitHub does not associate with a pull request are left alone.

Closed pull requests​

The same opt-in cancellation policy also applies when a pull request closes or merges. A worker reconciles the recorded closure and cancels selected queued or running validation for that PR head. It rechecks whether the head is still under review before cancelling, so reopening a PR protects its validation. These are cancellations, never successful-check results.

Checks That Never Block Automation​

Some checks are worth running but should not decide whether ProPR moves a pull request forward, for example slow packaging or platform checks that occasionally fail on hosted runners. List them under Checks that never block automation (Repositories → repository → Automation). Each entry is a check run name, matched case-insensitively; * matches any text, so Validate unsigned * package covers every platform leg.

A failure of a listed check:

  • does not hold back auto-merge or ultrafix continuation;
  • does not start an automatic failed-CI follow-up;
  • is shown to reviews as neutral and marked (non-blocking), not as a failure of the change.

GitHub still shows the check and its result. Unlisted checks keep blocking, and if the repository configuration cannot be read, every check blocks.