--- name: land description: Have the AI finish a topic branch end to end by merging its PR, removing the branch, and syncing the local base branch. Use when the user says land or asks to run the repo landing workflow. --- # Land Workflow Use this skill when the user asks the AI to land work end to end. ## Contents - [At A Glance](#at-a-glance) - [What This Is Not](#what-this-is-not) - [Trigger Rules](#trigger-rules) - [Preconditions](#preconditions) - [Default Path](#default-path) - [Stop Conditions](#stop-conditions) - [Handoff](#handoff) - [Response Examples](#response-examples) ## At A Glance 1. Decide whether the current user message is approval to land. 2. Confirm branch, PR, checks, permissions, and local worktree state. 3. Resolve the merge method, then merge. 4. Sync the local base branch, verify the topic branch actually landed before deleting it, and restore any local-changes stash created for this workflow. 5. Report the result with the actual merge method and branch state. ## What This Is Not - Not a code review, test-fixing, or implementation workflow. - Not a ship workflow for creating branches, commits, pushes, or pull requests. - Not approval to merge from the base branch, detached `HEAD`, or a branch without a matching pull request. - Not permission to bypass failing or pending checks unless the user explicitly forces landing in the current turn and the connected account can override repository protections. ## Trigger Rules - Treat a bare or direct current-turn command such as `land`, `land this`, `merge this PR`, or `land the current branch` as explicit approval to start the workflow without asking another confirmation question. - If `land` or `merge` appears as part of a broader sentence, proceed only when the user is clearly asking the AI to complete the landing workflow now. - If the message is about the land skill, landing policy, a future landing, or otherwise could be read as discussion instead of approval, ask one concise clarification question before merging. - Do not infer approval from an earlier turn. - Merging falls under `context/policies/approval-policy.md` (the target repo's copy when it exists, otherwise `${CLAUDE_PLUGIN_ROOT}/context/policies/approval-policy.md`); the direct commands above are that policy's current-turn go-ahead. ## Preconditions - Read the target repo's config file — `.orchraft.jsonc`, else `.orchraft.json` — when one exists, per `context/policies/config-policy.md` (the target repo's copy when it exists, otherwise `${CLAUDE_PLUGIN_ROOT}/context/policies/config-policy.md`). Both allow `//` comments. A missing file is the normal case and means the documented defaults apply. The config file is always read from the repository being worked in, never from the plugin's own directory. - Confirm the current branch is a named branch other than the base branch. - Confirm the branch has an upstream remote and the local `HEAD` is pushed. - Confirm the current branch has an open, non-draft pull request. - Confirm the pull request head commit matches local `HEAD`. - Confirm the pull request targets the expected base branch. Every later step means that branch — the pull request's own `base.ref`, which is where the merge actually lands. Do not substitute a literal `main`, which a repository on `master` or `develop` does not use. When `baseBranch` in the config names a different branch than the pull request targets, stop and report the mismatch rather than following either: the config says where work is meant to land, and this pull request says otherwise. - Confirm the pull request has no unresolved merge conflicts. - Confirm the connected account can merge the pull request. - Confirm required check status by reading it fresh from the connected tool in the current turn; don't assume it from an earlier turn or a prior pass. - If tracked, staged, or untracked local changes are present, stash all of them (including untracked files) before landing and record the stash ref. ## Default Path - Prefer connected GitHub tools to merge the branch pull request. - Resolve the merge method before merging, stopping at the first source that answers, per the config policy named in Preconditions: 1. What the user asked for in the current turn. 2. `merge.method` in the target repo's config file, when set to something other than `"repo"`. 3. The repository's allowed methods. GitHub exposes `allow_merge_commit`, `allow_squash_merge`, and `allow_rebase_merge` — which methods are *permitted*, not which is preferred, and they read as `null` for an account without push access. When exactly one is allowed, that is the answer and no further step runs. 4. The base branch's recent history, when more than one method is allowed and nothing above chose. A merge commit has two parents; a squashed commit has one and usually a trailing `(#123)`. Follow what the repository actually does, and say in the handoff that the method was inferred rather than configured. 5. Ask the user, when the history is empty or mixed. - A method the repository forbids is a stop, not a fallback: report that the requested method is disallowed instead of quietly using another one. - Before merging, verify whether the GitHub MCP connector exposes the specific merge action for the session, such as `_merge_pull_request`, and use that MCP action when it is available. - Use `gh` only after that exact MCP merge path has been checked and is unavailable, blocked, or fails. - Compare local `HEAD` with the PR head before merging. - Confirm required jobs, checks, and mergeability status are finished and passing before merging. - Where this workflow validates anything locally, prefer a `validate` command from the config file over discovering one, per `context/policies/verification-policy.md`. - After the merge completes, switch to the base branch locally, sync it, and confirm there are still no tracked or staged local changes before deleting the verified local topic branch. Skip the deletion entirely when `merge.deleteLocalBranch` is `false` in the target repo's config file, and say so in the handoff. - Verify the branch actually landed before deleting it, using the check that matches the merge method: - **Merge commit** — the branch's tip is an ancestor of the base branch, so `git branch --merged ` lists it and `git branch -d` succeeds. Use `-d` and let it refuse if something is wrong. - **Squash or rebase** — the commits on the base branch are new objects, so the topic branch's tip is *not* an ancestor of it. `git branch --merged` will not list the branch and `git branch -d` will refuse with "not fully merged" even though the work landed correctly. Confirm the landing another way before deleting: the pull request reports `merged: true` with a merge commit SHA, and `git diff ` is empty, meaning the branch's content reached the base branch. Only then delete with `git branch -D`. - Never reach for `git branch -D` because `-d` refused. Establish that the work landed first; a refusal that has not been explained is a stop, not a prompt to force. - After branch deletion, restore any local-changes stash created for this workflow with `git stash pop` or the repository-equivalent restore command. - If landing stops after creating a stash for this workflow, restore it once the worktree is usable. ## Stop Conditions - Stop if the current branch is the base branch. - Stop if HEAD is detached. - Stop if there is no branch pull request to merge. - Stop if local changes cannot be stashed cleanly (e.g. an existing conflicting stash or a stash command failure); do not merge or delete the branch. - Stop if a stash created for this workflow cannot be restored after landing; report the stash ref for manual recovery. - Stop if local `HEAD` does not match the PR head before merging. - Stop if required jobs, checks, or mergeability status are pending, missing, failing, or errored. - Proceed despite pending or failed checks only when the user explicitly says to force landing in the current turn and the connected account has permission to override repository protections. - Account for the actual merge method used when validating that local branch deletion is safe; see the Default Path for the check each method needs. - Stop if the merge method resolved from the config file or from the user is one the repository does not allow. - Stop if `git branch -d` refuses after a merge-commit landing — that means the branch did not land as expected. Do not escalate to `-D` to get past it. - If a required git or GitHub command is blocked by sandbox permissions, rerun it with approval instead of abandoning the workflow. ## Handoff - Unless the user asked for plain output, open with a fresh one-line success phrase in the orc voice, speaking as the Haulmaster from `context/personality.md` (the target repo's copy when it exists, otherwise `${CLAUDE_PLUGIN_ROOT}/context/personality.md`). If they did, open with the plain result instead. - Report the final branch state. - Say which merge method was used and where it came from: the user, the config file, the only method the repository allows, or an inference from the base branch's history. An inferred method is reported as inferred. - Say whether the verified local branch was deleted, and when it was kept, why — `merge.deleteLocalBranch` being `false`, or a landing that could not be verified. - If landing stops or fails, skip the landing phrase and state the blocker plainly. ## Response Examples Opening lines are samples of the orc voice; write a fresh one each time. Successful landing: ```text 🏰 Rrraaagh! Victory march into main. The branch banner rests with honor. ✨ Merged PR #123 with squash, the only method this repository allows. Local `main` is synced, and `fix/login-crash` was deleted. Restored the local stash. ``` Successful landing with no stash: ```text 🏰 Gates opened on green. Spoils carried home, feast hall ready. Merged PR #124 with a merge commit, inferred from the base branch's history. Local `main` is synced, and `feat/offline-cache` was deleted. No local stash was needed. ``` Blocked landing: ```text ⚠️ Landing is blocked: required check `build` is still pending. No merge was performed. Local branch and worktree were left unchanged. ``` Clarification needed: ```text ❓ Chief, land the current branch now? ```