---
description: Monitor an MR/PR and get it merged — auto-merge, watch CI, self-heal on failure
argument-hint: [MR/PR URL or number]
---

# Land — Get an MR/PR Merged

Monitors an MR/PR after `/ship` creates it. Enables server-side auto-merge, watches CI status, and self-heals on CI failures by invoking `/fix` + `/ship`. Kicked off automatically by `/ship` after Step 8 completes.

---

## Phase 1: Identify the MR/PR

**If $ARGUMENTS contains a URL or number:**
- Extract the MR/PR number from the URL or use the number directly.

**If $ARGUMENTS is empty:**
- Check conversation context for an MR/PR URL from a prior `/ship` invocation.
- If none found, ask the user: "Which MR/PR should I land? (URL or number)"

**Detect platform** from `git remote -v`:
- URL contains `github.com` → use `gh`
- URL contains `gitlab` → use `glab`

---

## Phase 2: Enable Auto-Merge

Attempt to enable server-side auto-merge so the platform handles merging when all gates pass.

**GitHub:**
```bash
gh pr merge <number> --auto --merge --delete-branch
```

**GitLab:**
```bash
glab mr merge <number> --auto-merge --remove-source-branch --yes
```

**IMPORTANT:** Never use `--squash`. Always use merge commits to preserve full commit history.

**If auto-merge succeeds (exit 0):**
- Inform the user: "Auto-merge enabled. Watching for completion..."
- Proceed to Phase 3 (watch mode).

**If auto-merge fails** (not enabled on repo, insufficient permissions, or required checks not configured):
- Inform the user: "Auto-merge not available — falling back to watch mode."
- Proceed to Phase 3 (watch mode) with manual merge intent.

---

## Phase 3: Watch Mode

Monitor the MR/PR until it merges, fails, or hits safety limits.

### Safety Limits

- **Max fix attempts:** 3 (prevent infinite loops on unfixable failures)
- **Max total iterations:** 5
- **Timeout:** 30 minutes from first watch

### Status Check Loop

**GitHub:**
```bash
gh pr checks <number> --watch
```
This blocks until all checks complete, then returns.

After `gh pr checks` returns, check the MR/PR state:
```bash
gh pr view <number> --json state,mergeable,reviewDecision,mergeStateStatus
```

**GitLab:**
Poll by checking pipeline status repeatedly. Run `glab ci status` to check the current state. If the pipeline is still running, wait 30 seconds (use Bash with `sleep 30`), then check again. Repeat until the pipeline reaches a terminal state (success, failed, canceled).

After the pipeline completes, check MR state:
```bash
glab mr view <number> -F json
```

### Decision Tree

After each status check, evaluate:

**If merged** (`state == "MERGED"` or `state == "merged"`):
- Confirm: "Landed: `<MR/PR URL>`"
- Clean up local branch: `git checkout main`, `git pull --ff-only`, `git branch -d <branch>`
- Done.

**If CI passed and approved (or no required reviews):**
- If auto-merge was enabled: wait for platform to merge (it will happen automatically).
- If auto-merge was NOT available: merge manually:
  - GitHub: `gh pr merge <number> --merge --delete-branch`
  - GitLab: `glab mr merge <number> --remove-source-branch --yes`
- Confirm: "Landed: `<MR/PR URL>`"
- Clean up local branch.
- Done.

**If CI failed:**
- Increment fix attempt counter.
- If fix attempts > 3: inform user "CI failed 3 times — stopping. Manual intervention needed." and stop.
- Fetch the failure details:
  - GitHub: `gh pr checks <number> --json name,conclusion` to identify which checks failed
  - GitLab: `glab ci status` to see failed jobs
- Invoke `/fix` with the failure context (failed check names, error output).
- After `/fix` completes, push the fix to the same branch: `git push`
- Return to the top of the watch loop.

**If changes requested by reviewer or unresolved discussions:**
- Increment fix attempt counter.
- If fix attempts > 3: inform user "Reviewer feedback unresolved after 3 fix attempts — manual intervention needed." and stop.
- Fetch the review comments/discussion threads:
  - GitHub: `gh api repos/{owner}/{repo}/pulls/<number>/reviews` to get review bodies, then `gh api repos/{owner}/{repo}/pulls/<number>/comments` for inline comments
  - GitLab: `glab api "projects/:id/merge_requests/<number>/discussions"` to get unresolved threads
- Evaluate each comment:
  - **Actionable code feedback** (e.g., "add error handling here", "this variable is unused", "missing null check"): invoke `/fix` with the comment content and file/line context
  - **Subjective or architectural feedback** (e.g., "I'd prefer a different approach", "should we redesign this?"): inform user "Reviewer feedback requires human judgment — manual intervention needed." and stop.
- After `/fix` completes, push the fix to the same branch: `git push`
- Return to the top of the watch loop.

**If merge conflicts:**
- Attempt rebase with conflict resolution:
  1. `git fetch origin main`
  2. `git rebase origin/main`
  3. If rebase succeeds cleanly: `git push --force-with-lease` and return to watch loop.
  4. If rebase stops with conflicts:
     - For each conflicted file: read the file, understand both sides of the conflict (ours = this branch's changes, theirs = main's changes), resolve by keeping both sets of changes in the correct order, then `git add` the resolved file.
     - Run `git rebase --continue` after resolving all conflicts in the current commit.
     - If additional commits conflict, repeat the resolution process.
     - After all commits are rebased: `git push --force-with-lease` and return to watch loop.
  5. If a conflict is too complex to resolve automatically (e.g., both sides modified the same lines with incompatible logic):
     - `git rebase --abort`
     - Inform user: "Merge conflict too complex for auto-resolution — manual intervention needed."
     - Show which files conflicted and why resolution was not possible.
     - Stop.

**If MR/PR closed (not merged):**
- Inform user: "MR/PR was closed without merging."
- Stop.

---

## Phase 4: Summary

When `/land` completes (success or failure), present a summary:

**On success:**
```
Landed: <MR/PR URL>
Branch: <branch> merged into main and deleted
Fix attempts: <N>
```

**On failure:**
```
Not landed: <MR/PR URL>
Reason: <CI failure | changes requested | merge conflicts | timeout | closed>
Fix attempts: <N> / 3
Action needed: <specific guidance>
```

---

## Integration with `/ship`

After `/ship` Step 8 creates an MR/PR and displays the URL, `/ship` should invoke `/land` automatically, passing the MR/PR URL as the argument. This makes the full flow:

`/ship` → tests → lint → changelog → review → commit → push → create MR/PR → `/land` → auto-merge → done

The user can also invoke `/land` standalone on any existing MR/PR:
```
/land                                    # Uses MR/PR from conversation context
/land https://gitlab.com/.../merge_requests/68   # Explicit URL
/land 68                                 # MR/PR number
```
