Land PR
SkillDev toolsLand 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.
No other account needed.
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— usuallymain.- 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 — baseRefName ≠ main 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:
- Identify the failing job and step from CI logs.
- Reproduce locally:
- Build:
moon run <package>:build - Tests:
moon run <package>:test -- <test-file> - Lint:
moon run :lint -- --fix - Format:
pnpm format
- Build:
- Apply the fix at the root cause — no casts, no
// @ts-ignore, no--no-verify. - Run the specific check locally to confirm the fix.
- Commit with a
scope: descriptionmessage and push.
Prohibited shortcuts:
as any,as unknown, non-null!to silence type errors.--no-verifyto 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
- Check which job failed.
- Fetch logs to diagnose.
- Apply fix locally, test locally, commit, push.
- Print updated status checklist.
- If the same check fails repeatedly (3+ times) with no progress, ask the user.
Review comment event
- Read the comment.
- If it requests a change: apply it, commit, push, optionally reply.
- If it approves: note it in checklist, check if auto-merge can now be enabled.
- If it requests changes that are out of scope or incorrect: reply explaining why.
PR merged event
- Print final success message with PR URL.
- Call
mcp__github__unsubscribe_pr_activity. - 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):
- Call
mcp__github__pull_request_readto get the current PR state. - If merged: print success, unsubscribe, stop.
- If closed: ask the user what happened.
- If open and auto_merge is null (was dequeued):
a. Sync with
main:
b. Resolve any conflicts that arise, commit, push. c. Re-enable auto-merge:git fetch origin main git merge origin/main --no-edit git push -u origin <headRefName>
d. Log: "PR was dequeued — re-synced with main and re-enabled auto-merge." e. Re-arm the alarm:mcp__github__enable_pr_auto_merge({ owner: "dxos", repo: "dxos", pullNumber: <number>, mergeMethod: "squash" })Bash("sleep 900", { run_in_background: true }). - 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 viewlocally) is the test.baseRefNameis only a hint — a stack's bottom PR targetsmain, and a PR based on another PR's branch is not necessarily in a stack. - Skip
enable_pr_auto_mergefor 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 mergelocally, 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 stackis a gh CLI extension (gh extension install github/gh-stack) — in remote environments withoutgh, 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 rebasecascades 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
/landinvocation) 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:
moonfor task running (moon run <package>:task)pnpmfor package managementmcp__github__*tools for all GitHub operations (noghCLI in remote environments)scope: descriptioncommit/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