agent/docs/flow/RFC.flow-resolve-stuck-prs.md
Snider be78c27561 docs: add full RFC specs for agent dispatch
AX principles + go/agent + core/agent + php/agent specs.
Temporary — needed in-repo until core-agent mount bug is fixed.

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-30 19:51:55 +01:00

174 lines
5.5 KiB
Markdown

---
name: flow-resolve-stuck-prs
description: Use when a PR is stuck CONFLICTING after 2+ failed agent attempts. Manual merge conflict resolution using git worktrees.
---
# Flow: Resolve Stuck PRs
Manually resolve merge conflicts when an implementer has failed to fix them after two attempts, and the PR(s) are the last items blocking an epic.
---
## When to Use
All three conditions must be true:
1. **PR is CONFLICTING/DIRTY** after the implementer was asked to fix it (at least twice)
2. **The PR is blocking epic completion** — it's one of the last unchecked children
3. **No other approach worked** — "Can you fix the merge conflict?" was sent and either got no response or the push still left conflicts
## Inputs
- **Repo**: `owner/repo`
- **PR numbers**: The stuck PRs (e.g. `#287, #291`)
- **Target branch**: The branch the PRs target (e.g. `dev`, `epic/101-medium-migration`)
## Process
### Step 1: Confirm Stuck Status
Verify each PR is genuinely stuck — not just slow.
```bash
for PR in 287 291; do
echo "=== PR #$PR ==="
gh pr view $PR --repo OWNER/REPO --json mergeable,mergeStateStatus,updatedAt \
--jq '{mergeable, mergeStateStatus, updatedAt}'
done
```
**Skip if:** `mergeStateStatus` is not `DIRTY` — the PR isn't actually conflicting.
### Step 2: Check Attempt History
Count how many times the implementer was asked and whether it responded.
```bash
# Count "fix the merge conflict" comments
gh pr view $PR --repo OWNER/REPO --json comments \
--jq '[.comments[] | select(.body | test("merge conflict"; "i"))] | length'
# Check last commit date vs last conflict request
gh pr view $PR --repo OWNER/REPO --json commits \
--jq '.commits[-1] | {sha: .oid[:8], date: .committedDate}'
```
**Proceed only if:** 2+ conflict fix requests were sent AND either:
- No commit after the last request (implementer didn't respond), OR
- A commit was pushed but `mergeStateStatus` is still `DIRTY` (fix attempt failed)
### Step 3: Clone and Resolve Locally
Task a single agent (or do it manually) to resolve conflicts for ALL stuck PRs in one session.
```bash
# Ensure we have the latest
git fetch origin
# For each stuck PR
for PR in 287 291; do
BRANCH=$(gh pr view $PR --repo OWNER/REPO --json headRefName --jq '.headRefName')
TARGET=$(gh pr view $PR --repo OWNER/REPO --json baseRefName --jq '.baseRefName')
git checkout "$BRANCH"
git pull origin "$BRANCH"
# Merge target branch into PR branch
git merge "origin/$TARGET" --no-edit
# If conflicts exist, resolve them
# Agent should: read each conflicted file, choose the correct resolution,
# stage the resolved files, and commit
git add -A
git commit -m "chore: resolve merge conflicts with $TARGET"
git push origin "$BRANCH"
done
```
**Agent instructions when dispatching:**
> Resolve the merge conflicts on PR #X, #Y, #Z in `owner/repo`.
> For each PR: checkout the PR branch, merge the target branch, resolve all conflicts
> preserving the intent of both sides, commit, and push.
> If a conflict is ambiguous (both sides changed the same logic in incompatible ways),
> prefer the target branch version and note what you dropped in the commit message.
### Step 4: Verify Resolution
After pushing, confirm the PR is no longer conflicting.
```bash
# Wait a few seconds for GitHub to recalculate
sleep 10
for PR in 287 291; do
STATUS=$(gh pr view $PR --repo OWNER/REPO --json mergeStateStatus --jq '.mergeStateStatus')
echo "PR #$PR: $STATUS"
done
```
**Expected:** `CLEAN` or `BLOCKED` (waiting for checks, not conflicts).
### Step 5: Handle Failure
If the PR is **still conflicting** after manual resolution:
```bash
# Label for human intervention
gh issue edit $PR --repo OWNER/REPO --add-label "needs-intervention"
# Comment for the gatekeeper
gh pr comment $PR --repo OWNER/REPO \
--body "Automated conflict resolution failed after 2+ implementer attempts and 1 manual attempt. Needs human review."
```
Create the label if it doesn't exist:
```bash
gh label create "needs-intervention" --repo OWNER/REPO \
--description "Automated resolution failed — needs human review" \
--color "B60205" 2>/dev/null
```
The orchestrator should then **skip this PR** and continue with other epic children. Don't block the entire epic on one stuck PR.
---
## Decision Flowchart
```
PR is CONFLICTING
└─ Was implementer asked to fix? (check comment history)
├─ No → Send "Can you fix the merge conflict?" (issue-epic flow)
└─ Yes, 1 time → Send again, wait for response
└─ Yes, 2+ times → THIS FLOW
└─ Agent resolves locally
├─ Success → PR clean, pipeline continues
└─ Failure → Label `needs-intervention`, skip PR
```
## Dispatching as a Subagent
When the orchestrator detects a PR matching the trigger conditions, it can dispatch this flow as a single task:
```
Resolve merge conflicts on PRs #287 and #291 in dappcore/core.
Both PRs target `dev`. The implementer was asked to fix conflicts 2+ times
but they remain DIRTY. Check out each PR branch, merge origin/dev, resolve
all conflicts, commit, and push. If any PR can't be resolved, add the
`needs-intervention` label.
```
**Cost:** 0 Jules tasks (this runs locally or via Claude Code, not via Jules label).
---
## Integration
**Called by:** `issue-epic.md` — when a PR has been CONFLICTING for 2+ fix attempts
**Calls:** Nothing — this is a terminal resolution flow
**Fallback:** `needs-intervention` label → human gatekeeper reviews manually
---
*Created: 2026-02-04*
*Companion to: RFC.flow-issue-epic.md*