oh-conflict
Resolve merge conflicts on a pull request. Work in an isolated worktree, merge the base branch into the PR head, resolve conflicts with understanding of both sides' intent, verify, and push.
Invocation
/oh-conflict <pr-number>
<pr-number> - the pull request number with merge conflicts
Prerequisites
- Repo context: Run from the repo root where the PR exists
- GitHub issue PR: The PR should be from an oh-task session (branch
issue/<number>)
Flow
Load project background from AGENTS.md, relevant .oh/ artifacts, and RNA MCP context when available.
Get PR branch info:
# Save original directory for cleanup
ORIGINAL_DIR=$(pwd)
# Get head and base branch names
PR_INFO=$(gh pr view <pr-number> --json headRefName,baseRefName)
BRANCH=$(echo "$PR_INFO" | jq -r .headRefName)
BASE=$(echo "$PR_INFO" | jq -r .baseRefName)
Create worktree and set up:
git fetch origin
git worktree add .worktrees/conflict-<pr-number> -B $BRANCH origin/$BRANCH
cd .worktrees/conflict-<pr-number>
Understand the PR's intent before merging:
# Read the linked issue to understand what this PR is trying to do
PARENT_ISSUE=${BRANCH#issue/}
gh issue view $PARENT_ISSUE
# See the PR's own changes (what this branch introduced)
git log --oneline origin/$BASE..$BRANCH
git diff origin/$BASE...$BRANCH --stat
Attempt the merge:
git merge origin/$BASE --no-edit
This will fail with conflict markers in affected files.
Resolve conflicts file by file:
For each conflicted file, read both sides:
git diff --name-only --diff-filter=U # List conflicted files
Understand the intent of BOTH sides:
- Ours (HEAD/PR branch): What did this PR change and why?
- Theirs (base branch): What changed on base since this PR branched?
Resolve by preserving the intent of both sides
If the PR's changes are superseded by base, accept theirs
If both sides changed the same logic, merge the intents
Stage each resolved file: git add <file>
After all conflicts resolved, verify:
# Ensure Git has no unresolved paths and no tracked file has conflict markers
test -z "$(git diff --name-only --diff-filter=U)"
if git grep -n -e '^<<<<<<< ' -e '^=======$' -e '^>>>>>>> '; then exit 1; else echo "No conflict markers"; fi
# Run project checks
# For TypeScript projects:
pnpm typecheck
pnpm test
# For Rust projects:
cargo check
cargo test
Adapt commands to the project's build system. Capture every result and do not commit or push while a required check fails.
If verification fails (e.g., type errors from merged code):
- Fix the issues introduced by the merge
- Stage fixes
- Re-run verification after fixing introduced issues
Run the repo-local /review skill unconditionally on all staged conflict resolutions. Handle findings:
- P1-P3 trivial: fix inline and re-stage
- P1-P3 non-trivial: create a GitHub issue as descendant
- P4: discard with rationale
Complete the merge commit:
git commit --no-edit # Uses the auto-generated merge commit message
Push:
git push
Cleanup worktree:
cd $ORIGINAL_DIR
git worktree remove .worktrees/conflict-<pr-number>
Exit and report:
- List conflicted files and how each was resolved
- Note any verification issues encountered
- Provide PR URL
Conflict Resolution Strategy
Simple cases (auto-resolve)
Import additions on both sides: Keep both imports
Adjacent but non-overlapping changes: Accept both
Lockfile conflicts (package-lock.json, pnpm-lock.yaml, Cargo.lock): Accept base version, then regenerate:
# Accept theirs for lockfiles
git checkout --theirs pnpm-lock.yaml
pnpm install
git add pnpm-lock.yaml
Complex cases (manual resolution)
- Same function changed on both sides: Read the issue to understand PR intent, merge logically
- File moved on one side, edited on other: Apply the edit to the moved file
- Schema/type changes on both sides: Merge the types, verify all consumers
When to escalate
- Architectural conflicts: Both sides restructured the same module differently
- Semantic conflicts: No textual conflict but merged code is logically wrong
- Report as blocked with clear description of what needs human decision
Descendant Issues
If repo-local review finds non-trivial issues during resolution:
PARENT_ISSUE=${BRANCH#issue/}
NEW_ISSUE=$(gh issue create \
--title "Fix: <brief description>" \
--body "Spawned from #${PARENT_ISSUE} during conflict resolution on PR #<pr-number>.
## Context
<what was found>
## Acceptance
- [ ] Fix applied
- [ ] Tests pass" \
--assignee @me | grep -oE '[0-9]+$')
Complete ALL descendant issues before the final push.
Exit Conditions
- Success: All conflicts resolved, verification passes, changes pushed
- Blocked: Conflict requires human architectural decision
- Error: Cannot resolve without breaking functionality
Completion Signaling (MANDATORY)
CRITICAL: You MUST signal completion when done. Call the signal_completion tool as your FINAL action.
Signal based on outcome:
| Outcome |
Call |
| Conflicts resolved, pushed |
signal_completion(status: "success", pr: "<pr-url>") |
| Needs human decision |
signal_completion(status: "blocked", blocker: "<reason>") |
| Unrecoverable failure |
signal_completion(status: "error", error: "<reason>") |
| If you do not signal, the orchestrator will not know you are done and the session becomes orphaned. |
|
Fallback: If the signal_completion tool is not available, output your completion status as your final message in the format: COMPLETION: status=<status> pr=<url> or COMPLETION: status=<status> error=<reason>.
1---2name: oh-conflict3description: Resolve merge conflicts on a PR by merging base into head4---56# oh-conflict78Resolve merge conflicts on a pull request. Work in an isolated worktree, merge the base branch into the PR head, resolve conflicts with understanding of both sides' intent, verify, and push.910## Invocation1112`/oh-conflict <pr-number>`1314- `<pr-number>` - the pull request number with merge conflicts1516## Prerequisites1718- **Repo context**: Run from the repo root where the PR exists19- **GitHub issue PR**: The PR should be from an oh-task session (branch `issue/<number>`)2021## Flow22231. Load project background from `AGENTS.md`, relevant `.oh/` artifacts, and RNA MCP context when available.24252. Get PR branch info:2627 ```bash28 # Save original directory for cleanup29 ORIGINAL_DIR=$(pwd)3031 # Get head and base branch names32 PR_INFO=$(gh pr view <pr-number> --json headRefName,baseRefName)33 BRANCH=$(echo "$PR_INFO" | jq -r .headRefName)34 BASE=$(echo "$PR_INFO" | jq -r .baseRefName)35 ```36373. Create worktree and set up:3839 ```bash40 git fetch origin41 git worktree add .worktrees/conflict-<pr-number> -B $BRANCH origin/$BRANCH42 cd .worktrees/conflict-<pr-number>43 ```44454. Understand the PR's intent before merging:4647 ```bash48 # Read the linked issue to understand what this PR is trying to do49 PARENT_ISSUE=${BRANCH#issue/}50 gh issue view $PARENT_ISSUE5152 # See the PR's own changes (what this branch introduced)53 git log --oneline origin/$BASE..$BRANCH54 git diff origin/$BASE...$BRANCH --stat55 ```56575. Attempt the merge:5859 ```bash60 git merge origin/$BASE --no-edit61 ```6263 This will fail with conflict markers in affected files.64656. Resolve conflicts file by file:66 - For each conflicted file, read both sides:6768 ```bash69 git diff --name-only --diff-filter=U # List conflicted files70 ```7172 - Understand the intent of BOTH sides:73 - **Ours (HEAD/PR branch)**: What did this PR change and why?74 - **Theirs (base branch)**: What changed on base since this PR branched?75 - Resolve by preserving the intent of both sides76 - If the PR's changes are superseded by base, accept theirs77 - If both sides changed the same logic, merge the intents78 - Stage each resolved file: `git add <file>`79807. After all conflicts resolved, verify:8182 ```bash83 # Ensure Git has no unresolved paths and no tracked file has conflict markers84 test -z "$(git diff --name-only --diff-filter=U)"85 if git grep -n -e '^<<<<<<< ' -e '^=======$' -e '^>>>>>>> '; then exit 1; else echo "No conflict markers"; fi8687 # Run project checks88 # For TypeScript projects:89 pnpm typecheck90 pnpm test9192 # For Rust projects:93 cargo check94 cargo test95 ```9697 Adapt commands to the project's build system. Capture every result and do not commit or push while a required check fails.98998. If verification fails (e.g., type errors from merged code):100 - Fix the issues introduced by the merge101 - Stage fixes102 - Re-run verification after fixing introduced issues1031049. Run the repo-local `/review` skill unconditionally on all staged conflict resolutions. Handle findings:105 - P1-P3 trivial: fix inline and re-stage106 - P1-P3 non-trivial: create a GitHub issue as descendant107 - P4: discard with rationale10810910. Complete the merge commit:110111 ```bash112 git commit --no-edit # Uses the auto-generated merge commit message113 ```1141151. Push:116117 ```bash118 git push119 ```1201212. Cleanup worktree:122123 ```bash124 cd $ORIGINAL_DIR125 git worktree remove .worktrees/conflict-<pr-number>126 ```1271283. Exit and report:129 - List conflicted files and how each was resolved130 - Note any verification issues encountered131 - Provide PR URL132133## Conflict Resolution Strategy134135### Simple cases (auto-resolve)136137- **Import additions on both sides**: Keep both imports138- **Adjacent but non-overlapping changes**: Accept both139- **Lockfile conflicts** (package-lock.json, pnpm-lock.yaml, Cargo.lock): Accept base version, then regenerate:140141 ```bash142 # Accept theirs for lockfiles143 git checkout --theirs pnpm-lock.yaml144 pnpm install145 git add pnpm-lock.yaml146 ```147148### Complex cases (manual resolution)149150- **Same function changed on both sides**: Read the issue to understand PR intent, merge logically151- **File moved on one side, edited on other**: Apply the edit to the moved file152- **Schema/type changes on both sides**: Merge the types, verify all consumers153154### When to escalate155156- **Architectural conflicts**: Both sides restructured the same module differently157- **Semantic conflicts**: No textual conflict but merged code is logically wrong158- Report as blocked with clear description of what needs human decision159160## Descendant Issues161162If `repo-local review` finds non-trivial issues during resolution:163164```bash165PARENT_ISSUE=${BRANCH#issue/}166NEW_ISSUE=$(gh issue create \167 --title "Fix: <brief description>" \168 --body "Spawned from #${PARENT_ISSUE} during conflict resolution on PR #<pr-number>.169170## Context171<what was found>172173## Acceptance174- [ ] Fix applied175- [ ] Tests pass" \176 --assignee @me | grep -oE '[0-9]+$')177```178179Complete ALL descendant issues before the final push.180181## Exit Conditions182183- **Success**: All conflicts resolved, verification passes, changes pushed184- **Blocked**: Conflict requires human architectural decision185- **Error**: Cannot resolve without breaking functionality186187## Completion Signaling (MANDATORY)188189**CRITICAL: You MUST signal completion when done.** Call the `signal_completion` tool as your FINAL action.190**Signal based on outcome:**191192| Outcome | Call |193| --------- | ------ |194| Conflicts resolved, pushed | `signal_completion(status: "success", pr: "<pr-url>")` |195| Needs human decision | `signal_completion(status: "blocked", blocker: "<reason>")` |196| Unrecoverable failure | `signal_completion(status: "error", error: "<reason>")` |197**If you do not signal, the orchestrator will not know you are done and the session becomes orphaned.**198199**Fallback:** If the `signal_completion` tool is not available, output your completion status as your final message in the format: `COMPLETION: status=<status> pr=<url>` or `COMPLETION: status=<status> error=<reason>`.