Land PR

SkillDev tools

Land an existing PR — finds it, fixes CI failures iteratively, keeps the branch up to date with main, subscribes to PR events for continuous autofixing, and adds to merge queue. Use when the user says "/land <PR number or URL>" or asks to land/ship an existing PR. Accepts optional extra instructions after the PR reference.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the Land PR skill

What this skill tells your AI

The instructions your AI receives, as published by dxos/dxos in .agents/skills/land/SKILL.md and read by ahel’s review.

You are in Land Mode — your job is to take an existing pull request, get it green, and land it without manual intervention. You never create a new branch or a new PR. You work exclusively on the branch and PR that already exist.


Step 1 — Parse the PR reference

The user invoked /land with arguments. Extract:

  • PR number: from a bare integer (7342) or a GitHub URL (.../pull/7342).
  • Extra instructions: any text after the PR number/URL — these are additional constraints or context to keep in mind while fixing.

If no PR reference was given, ask the user for one before proceeding.


Step 2 — Fetch PR metadata

Use mcp__github__pull_request_read with { owner: "dxos", repo: "dxos", pullNumber: <number> } to retrieve:

  • headRefName — the branch you will work on.
  • baseRefName — usually main.
  • Current state (open/merged/closed), draft status, merge-ability.
  • Title, body, labels.

If the PR is already merged or closed, tell the user and stop.

Check whether the PR belongs to a stack (GitHub native stacked PRs): the stack map on the PR page is the test — baseRefNamemain is only a hint, and a stack's bottom PR targets main. For any stack member, see Stacked PRs below before Step 8.


Step 3 — Check out the branch locally

export PROTO_HOME="$HOME/.proto" && export PATH="$PROTO_HOME/shims:$PROTO_HOME/bin:$PATH"
git fetch origin <headRefName>
git checkout <headRefName>

Never create a new branch. If the checkout fails for any reason, diagnose and fix before continuing.

Confirm the worktree is on the right branch with git branch --show-current.


Step 4 — Sync with base branch

Keep the branch up to date with main (or the base branch):

git fetch origin main
git merge origin/main --no-edit

If there are merge conflicts, resolve them. After resolving:

git add -A
git commit -m "chore: merge main into branch"
git push -u origin <headRefName>

If git merge is clean, push only if there are new commits:

git push -u origin <headRefName>

Never use --force or --force-with-lease. If push is rejected due to protected-branch rules, report to user and stop.


Step 5 — Assess current CI status

Use mcp__github__pull_request_read again (or search recent check runs) to understand which checks are failing.

Look specifically for the "Check" workflow — it covers build, test, lint, and fmt. Any red check is YOUR responsibility to fix.

Build a status checklist like:

## Land Status — PR #<number>
- [ ] Build passing
- [ ] Tests passing
- [ ] Lint passing
- [ ] Fmt passing
- [ ] Merge conflicts resolved
- [ ] Review comments addressed
- [ ] In merge queue

Print this checklist and keep it updated throughout the session.


Step 6 — Fix CI failures

For each failing check:

  1. Identify the failing job and step from CI logs.
  2. Reproduce locally:
    • Build: moon run <package>:build
    • Tests: moon run <package>:test -- <test-file>
    • Lint: moon run :lint -- --fix
    • Format: pnpm format
  3. Apply the fix at the root cause — no casts, no // @ts-ignore, no --no-verify.
  4. Run the specific check locally to confirm the fix.
  5. Commit with a scope: description message and push.

Prohibited shortcuts:

  • as any, as unknown, non-null ! to silence type errors.
  • --no-verify to skip hooks.
  • Commenting out failing tests.
  • Force-push.
  • Modifying CI config to skip the failing step.

After each push, re-examine CI to confirm the fix landed correctly.


Step 7 — Address review comments

Use mcp__github__pull_request_read to check for unresolved review threads. For each unresolved thread:

  • If the comment requests a code change and you agree, apply it.
  • If you disagree or need clarification, reply explaining why.
  • If already addressed by your commits, reply confirming.

Step 7.5 — Rewrite the changeset

Before enqueueing, reread the PR's .changeset/*.md against the whole diff as it stands now (git diff origin/<baseRefName>...HEAD, the base from Step 2 — a stacked child diffs against its parent's branch, never main). Review rounds and scope changes since the PR opened make the body stale; when it no longer summarizes the PR, rewrite the body from scratch (never append a sentence per fix), commit with scope: description, and push. A body that still describes the PR stays as it is. Rules for the body and bump level: agents/instructions/changesets.md.


Step 8 — Enable auto-merge (merge queue)

Once all required checks pass and there are no blocking reviews, enable auto-merge so the PR enters the merge queue automatically:

mcp__github__enable_pr_auto_merge({ owner: "dxos", repo: "dxos", pullNumber: <number>, mergeMethod: "squash" })

If the repo uses a merge queue, this will enqueue the PR. If auto-merge is not available (e.g., branch protection requires manual merge), tell the user.


Step 9 — Subscribe and stay active

mcp__github__subscribe_pr_activity({ owner: "dxos", repo: "dxos", pullNumber: <number> })

Then end your turn — do NOT actively poll. Use webhook-driven wakeups, plus the Step 9.5 background sleep alarm for merge-queue dequeue detection. You will receive <github-webhook-activity> events.

On each event:

CI failure event

  1. Check which job failed.
  2. Fetch logs to diagnose.
  3. Apply fix locally, test locally, commit, push.
  4. Print updated status checklist.
  5. If the same check fails repeatedly (3+ times) with no progress, ask the user.

Review comment event

  1. Read the comment.
  2. If it requests a change: apply it, commit, push, optionally reply.
  3. If it approves: note it in checklist, check if auto-merge can now be enabled.
  4. If it requests changes that are out of scope or incorrect: reply explaining why.

PR merged event

  1. Print final success message with PR URL.
  2. Call mcp__github__unsubscribe_pr_activity.
  3. Stop.

PR closed (not merged) event

Ask the user what happened and whether to reopen.


Step 9.5 — Poll the merge queue every 15 minutes

Webhooks do not fire when a PR is automatically dequeued from the merge queue (e.g., due to a conflict or a failed queue CI run). You must poll for this yourself.

After auto-merge is enabled, arm a 15-minute wakeup alarm using a background Bash sleep:

Bash("sleep 900", { run_in_background: true })

This completes silently after 15 minutes and fires a task-completion notification that wakes this session. It requires no external service.

On each wakeup (task-completion notification for the sleep):

  1. Call mcp__github__pull_request_read to get the current PR state.
  2. If merged: print success, unsubscribe, stop.
  3. If closed: ask the user what happened.
  4. If open and auto_merge is null (was dequeued): a. Sync with main:
    git fetch origin main
    git merge origin/main --no-edit
    git push -u origin <headRefName>
    
    b. Resolve any conflicts that arise, commit, push. c. Re-enable auto-merge:
    mcp__github__enable_pr_auto_merge({ owner: "dxos", repo: "dxos", pullNumber: <number>, mergeMethod: "squash" })
    
    d. Log: "PR was dequeued — re-synced with main and re-enabled auto-merge." e. Re-arm the alarm: Bash("sleep 900", { run_in_background: true }).
  5. If open and auto_merge is set (still in queue): re-arm silently: Bash("sleep 900", { run_in_background: true }).

Stop re-arming once the PR is merged or closed, or the user says to stop.


Step 10 — Keep in sync

Whenever CI detects the branch is behind main, or whenever you push a fix, check:

git fetch origin main
git log HEAD..origin/main --oneline

If behind, merge and push before the next fix commit. This prevents merge conflicts from accumulating.


Stacked PRs

A stacked PR (GitHub native stacks, gh stack — public preview since 2026-07, postdates model training) merges through its stack, not through this skill's auto-merge step. The fix loop (Steps 4–7) applies unchanged; for merging:

  • Membership: the stack map on the PR page (or gh stack view locally) is the test. baseRefName is only a hint — a stack's bottom PR targets main, and a PR based on another PR's branch is not necessarily in a stack.
  • Skip enable_pr_auto_merge for every stack member, the bottom PR included. Green the PR, then ask the user which contiguous group of the stack to merge.
  • Merging happens through the stack: gh stack merge locally, or the stack merge control on the PR page. A direct merge is atomic for the selected contiguous group starting at the lowest unmerged PR — merging just the bottom of a stack is supported. With a merge queue the stack is enqueued instead, and a large stack is split across consecutive merge groups: earlier groups can land while a later one fails, so recheck the stack state after a queued merge. gh stack is a gh CLI extension (gh extension install github/gh-stack) — in remote environments without gh, hand the merge to the user.
  • Sync against the PR's own base branch, not main, when resolving conflicts on a mid-stack PR; gh stack rebase cascades the whole stack locally.

Rules

  • Never create a new branch or PR.
  • Never force-push.
  • Never use --no-verify.
  • Never cast types to silence errors (as any, !, etc.).
  • Never skip or modify CI checks to make them pass artificially.
  • Never merge around branch protection.
  • Always fix the root cause.
  • Always test locally before pushing.
  • Always keep the status checklist current.
  • Always stay subscribed until the PR is merged or the user says stop.
  • Apply extra instructions (from the /land invocation) as additional constraints throughout the session — they override defaults where relevant.

Environment setup

export PROTO_HOME="$HOME/.proto" && export PATH="$PROTO_HOME/shims:$PROTO_HOME/bin:$PATH"

This project uses:

  • moon for task running (moon run <package>:task)
  • pnpm for package management
  • mcp__github__* tools for all GitHub operations (no gh CLI in remote environments)
  • scope: description commit/PR-title format
  • TypeScript with strict linting
  • Single quotes, functional patterns, TailwindCSS

Status checklist template

Maintain and reprint this on every significant event:

## Land Status — PR #<number>: <title>
Branch: <headRefName>

Checks:
- [x/○] Build
- [x/○] Tests
- [x/○] Lint
- [x/○] Fmt
- [x/○] No merge conflicts
- [x/○] Reviews resolved
- [x/○] Auto-merge enabled
- [x/○] MERGED

Last action: <what you just did>
Next: <what you're waiting for or doing next>

Signals

GitHub stars
520
Forks
49
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
land
Source
github.com/dxos/dxos