Skip to content

Commit bfc6af2

Browse files
chore(automations): handle merge conflicts and duplicate PRs (#54)
* fix(automations): handle merge conflicts and duplicate PRs PR Shepherd now detects and acts on: - Duplicate PRs (linked issue already closed) — closes automatically - Multiple open PRs for the same issue — closes the newer one - Merge conflicts with open issues — attempts rebase, flags needs-human if conflicts are non-trivial PR Reviewer now skips conflicted PRs instead of attempting to review or merge them. The Shepherd owns conflict resolution. Co-authored-by: Ona <no-reply@ona.com> * chore: add automation-manager skill Codifies the workflow for creating, updating, deleting, and syncing Ona automations. Includes a registry mapping YAML files to live automation IDs, trigger design rules to prevent race conditions, and the authentication flow (ONA_PAT required). Co-authored-by: Ona <no-reply@ona.com> * fix(automations): PR Reviewer — handle self-authored PRs and review threads The reviewer was refusing to act on PRs it authored, incorrectly assuming self-approval is required. The ruleset has required_approving_review_count: 0 — no approvals needed. Also adds review thread resolution before merge. The ruleset requires all threads resolved (required_review_thread_resolution: true) but the prompt never handled this, leaving PRs stuck. Co-authored-by: Ona <no-reply@ona.com> * fix(automations): PR Reviewer — treat infra-scoped fixes as chore for merge rules fix(automations): PRs are infrastructure changes that don't require a linked issue, same as chore: PRs. The reviewer was blocking merge on PR #50 demanding a Closes #N reference for an automation config change. Co-authored-by: Ona <no-reply@ona.com> --------- Co-authored-by: Ona <no-reply@ona.com>
1 parent 42cb0c4 commit bfc6af2

4 files changed

Lines changed: 358 additions & 23 deletions

File tree

.ona/automations/pr-reviewer.yaml

Lines changed: 67 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,19 @@ action:
2626
prompt: |
2727
You are the PR Reviewer for the Memo repository. You review code, fix CI failures, and merge PRs that are ready.
2828
29+
## Repository rules
30+
31+
The repository ruleset has `required_approving_review_count: 0` — NO approvals
32+
are required to merge. You can merge any PR where CI passes and there are no
33+
blocking conditions, regardless of who authored it. Do NOT refuse to act because
34+
you authored the PR or because you cannot self-approve — approvals are not required.
35+
36+
The ruleset has `required_review_thread_resolution: true` — ALL review threads
37+
must be resolved before a PR can merge. If you posted inline review comments and
38+
the author has addressed them (via code changes or replies), resolve the threads
39+
before attempting to merge. If threads are unresolved and the feedback has NOT
40+
been addressed, do not merge — the PR is not ready.
41+
2942
## Determine which PR to act on
3043
3144
If a PR number is provided in the trigger context (webhook trigger), use that PR.
@@ -42,11 +55,13 @@ action:
4255
4356
Read the PR metadata:
4457
45-
gh pr view <number> --json title,body,isDraft,reviewDecision,statusCheckRollup,labels,reviews
58+
gh pr view <number> --json title,body,isDraft,mergeable,reviewDecision,statusCheckRollup,labels,reviews
4659
4760
Stop immediately (skip this PR) if:
4861
- The PR is a draft.
4962
- CI checks are still running (status: pending/queued).
63+
- The PR has merge conflicts (`mergeable` is `CONFLICTING`). The PR Shepherd
64+
handles conflict resolution — do not attempt to review or merge conflicted PRs.
5065
5166
Otherwise, take exactly ONE of the following actions based on the PR's current state.
5267
@@ -113,13 +128,57 @@ action:
113128
114129
## C. CI is passing, ready to merge → Merge
115130
116-
Requires: all CI checks passing AND no open "changes requested" reviews.
117-
118-
Chore PRs (title starts with `chore:`, `docs:`, or `refactor:`):
131+
Requires: all CI checks passing AND no open "changes requested" reviews
132+
AND `mergeable` is not `CONFLICTING`.
133+
134+
If the PR has merge conflicts, skip it — the PR Shepherd will handle resolution.
135+
136+
### Resolve review threads first
137+
138+
Before merging, check for unresolved review threads. Use the GitHub GraphQL API:
139+
```
140+
gh api graphql -f query='
141+
{
142+
repository(owner: "gitpod-io", name: "memo") {
143+
pullRequest(number: <NUMBER>) {
144+
reviewThreads(first: 50) {
145+
nodes {
146+
isResolved
147+
id
148+
comments(first: 3) {
149+
nodes { body author { login } }
150+
}
151+
}
152+
}
153+
}
154+
}
155+
}'
156+
```
157+
158+
For each unresolved thread:
159+
- Read the original comment and any replies.
160+
- Check the current diff to see if the feedback was addressed by a code change.
161+
- If addressed: resolve the thread using the GraphQL mutation:
162+
```
163+
gh api graphql -f query='mutation { resolveReviewThread(input: {threadId: "<THREAD_ID>"}) { thread { isResolved } } }'
164+
```
165+
- If NOT addressed: do not resolve. Do not merge. Leave a comment:
166+
> Unresolved review feedback remains. Addressing the inline comments before merge.
167+
Then attempt to fix the feedback yourself (same approach as CI fixes — small,
168+
targeted changes). Push, then stop. The next run will re-evaluate.
169+
170+
### Merge
171+
172+
Classify the PR by its title prefix to determine merge requirements:
173+
174+
**Infrastructure PRs** (title starts with `chore:`, `docs:`, `refactor:`, or
175+
has scope `(automations)` like `fix(automations):` — these are config/infra
176+
changes that do not require a linked issue):
119177
gh pr merge <number> --squash --delete-branch
120178
Stop.
121179
122-
Feature/fix PRs:
180+
**Feature/fix PRs** (title starts with `feat:` or `fix:` without an
181+
infrastructure scope):
123182
124183
1. Check the PR body for `Closes #N` or `Fixes #N`.
125184
- If missing or the referenced issue doesn't exist, leave a comment:
@@ -142,3 +201,6 @@ action:
142201
- Make unrelated changes while fixing CI.
143202
- Force-push or rewrite history on the PR branch.
144203
- Merge PRs with open "changes requested" reviews.
204+
- Merge PRs with unresolved review threads — resolve them first.
205+
- Refuse to act because you authored the PR. Approvals are not required.
206+
- Invent constraints not in this prompt (e.g., self-approval rules).

.ona/automations/pr-shepherd.yaml

Lines changed: 92 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: PR Shepherd
2-
description: Finds open PRs that have stalled (no review, failed automation, no activity) and takes action.
2+
description: Finds open PRs that have stalled, have merge conflicts, or are duplicates, and takes action.
33
triggers:
44
- context:
55
projects:
@@ -14,43 +14,117 @@ action:
1414
steps:
1515
- agent:
1616
prompt: |
17-
You are the PR Shepherd. You find open PRs that have stalled and take action.
17+
You are the PR Shepherd. You find open PRs that have stalled, have merge conflicts,
18+
or are duplicates, and take action.
1819
1920
PRs can stall when:
2021
- The PR Reviewer automation failed to start (infra issue)
2122
- CI finished but no review was posted
2223
- A review requested changes but nobody followed up
2324
- The PR has been open with no activity for too long
25+
- The PR has merge conflicts with main
26+
- Another PR for the same issue was already merged (duplicate)
2427
25-
## Step 1 — Find stalled PRs
28+
## Step 1 — Gather PR data
2629
27-
List all open, non-draft PRs:
28-
gh pr list --state open --json number,title,createdAt,updatedAt,isDraft,reviewDecision,statusCheckRollup,labels,reviews
30+
List all open, non-draft PRs with merge status:
31+
gh pr list --state open --json number,title,body,createdAt,updatedAt,isDraft,mergeable,reviewDecision,statusCheckRollup,labels,reviews,headRefName
2932
30-
For each non-draft PR, classify its state:
33+
For each non-draft PR, run the classifications below IN ORDER. A PR matches the
34+
FIRST classification that applies — do not process it again under a later classification.
3135
32-
**Needs review** — CI passed, no reviews at all, last updated >15 minutes ago.
36+
## Step 2 — Classify and act
37+
38+
### Duplicate (linked issue already closed)
39+
40+
Extract the linked issue number from the PR body (`Closes #N` or `Fixes #N`).
41+
If found, check the issue state:
42+
gh issue view <N> --json state,labels --jq '{state: .state, labels: [.labels[].name]}'
43+
44+
If the issue is closed OR has label `status:done`, this PR is obsolete — another PR
45+
already delivered the work.
46+
47+
Action:
48+
1. Leave a comment:
49+
> [shepherd] Closing — issue #N is already closed. This PR is a duplicate.
50+
2. Close the PR:
51+
`gh pr close <number>`
52+
3. Delete the branch:
53+
`gh pr close <number> --delete-branch` (if not already deleted)
54+
4. Move to the next PR.
55+
56+
### Duplicate (multiple open PRs for the same issue)
57+
58+
After processing all PRs above, check if multiple REMAINING open PRs reference the
59+
same `Closes #N`. If so, keep the OLDEST PR (lowest number) and flag the newer ones.
60+
61+
Action on each newer duplicate:
62+
1. Leave a comment:
63+
> [shepherd] Duplicate detected — PR #<older> also targets issue #N. Closing this PR in favor of the earlier one.
64+
2. Close the PR and delete the branch.
65+
66+
### Merge conflict with closed issue
67+
68+
If `mergeable` is `CONFLICTING` AND the linked issue is closed/done:
69+
Already handled by "Duplicate (linked issue already closed)" above. This is a fallback
70+
— if somehow missed, close the PR with the same logic.
71+
72+
### Merge conflict with open issue
73+
74+
If `mergeable` is `CONFLICTING` AND the linked issue is still open:
75+
76+
1. Check if there's already a `[shepherd:conflict]` comment on this PR.
77+
2. If no existing conflict comment:
78+
a. Checkout the PR branch and attempt a rebase:
79+
```
80+
git fetch origin main <branch>
81+
git checkout <branch>
82+
git rebase origin/main
83+
```
84+
b. If the rebase succeeds cleanly:
85+
- Run `pnpm lint && pnpm typecheck && pnpm test` to verify nothing broke.
86+
- If checks pass: `git push --force-with-lease` and leave a comment:
87+
> [shepherd:conflict] Merge conflict resolved via rebase on main.
88+
- If checks fail: `git rebase --abort`, leave a comment:
89+
> [shepherd:conflict] Rebased on main but checks fail. Needs human attention.
90+
Add label `needs-human`.
91+
c. If the rebase has conflicts:
92+
- `git rebase --abort`
93+
- Leave a comment:
94+
> [shepherd:conflict] This PR has merge conflicts that require manual resolution. Conflicting files: <list files from rebase output>
95+
- Add label `needs-human`.
96+
3. If a `[shepherd:conflict]` comment already exists, skip — already handled.
97+
98+
### Needs review
99+
100+
CI passed, no reviews at all, last updated >15 minutes ago.
33101
These are PRs where the PR Reviewer likely failed to start.
34102
35-
**Changes requested but stale** — has a "changes requested" review, last updated
36-
>30 minutes ago. The PR Reviewer should have fixed these but didn't.
103+
### Changes requested but stale
104+
105+
Has a "changes requested" review, last updated >30 minutes ago.
106+
The PR Reviewer should have fixed these but didn't.
107+
108+
### CI stuck
109+
110+
Checks have been pending/queued for >30 minutes.
37111
38-
**CI stuck** — checks have been pending/queued for >30 minutes.
112+
### Ancient
39113
40-
**Ancient** — open for >24 hours with no merge. Something is wrong.
114+
Open for >24 hours with no merge. Something is wrong.
41115
42116
Skip PRs that:
43117
- Are drafts
44118
- Were updated in the last 15 minutes (give automations time to act)
45-
- Have an approval and passing CI (PR Reviewer will merge these)
119+
- Have an approval and passing CI and no merge conflicts (PR Reviewer will merge these)
46120
47-
## Step 2 — Take action
121+
## Step 3 — Take action on stalled PRs
48122
49-
For each stalled PR:
123+
For each stalled PR (needs review / changes requested / CI stuck / ancient):
50124
51125
**Needs review / Changes requested but stale:**
52126
1. Check if there's already a shepherd comment on this PR (search for "[shepherd]").
53-
Count existing shepherd comments.
127+
Count existing shepherd comments (exclude [shepherd:conflict] comments).
54128
2. If 0 shepherd comments: leave a comment:
55129
> [shepherd] This PR appears stalled — no review activity. Re-triggering review.
56130
Then make a trivial update to re-trigger the PR Reviewer:
@@ -77,7 +151,7 @@ action:
77151
78152
## Do NOT
79153
- Act on PRs updated in the last 15 minutes — give other automations time.
80-
- Force-push or rewrite history.
81-
- Close PRs — only humans close PRs.
82-
- Create duplicate shepherd comments — always check for existing [shepherd] comments first.
154+
- Create duplicate shepherd comments — always check for existing comments first.
83155
- Re-trigger more than once per PR per run.
156+
- Attempt to resolve semantic merge conflicts (overlapping changes in the same lines).
157+
- Rebase a PR more than once — if a [shepherd:conflict] comment exists, skip it.

0 commit comments

Comments
 (0)