# Waiting for a person to merge

Source: https://codeherder.com/docs/waiting-for-a-merge/

Add a stage where a task waits for a person to merge its pull request. See how it starts and ends, what its states mean, and how to find it.

By default, CodeHerder’s `merge` stage lands the pull request itself. Some teams want a person to press the merge button instead. A **merge-hold stage** supports that. A task that reaches it stops and waits. When every pull request the task waits on is merged, CodeHerder moves the task on.

This page covers setting up the stage, how a task enters and leaves it, what the task page shows while it waits, and how to find waiting tasks. For the built-in merge path, see [The merge stage](https://codeherder.com/docs/how-work-flows/#the-merge-stage).

## Before you start

- You must be an admin or owner of the workspace.
- Your plan must include **Custom workflows**, because you create a stage in your stage library. See [What your plan unlocks](https://codeherder.com/docs/plans-and-limits/#what-your-plan-unlocks).
- CodeHerder must be able to read your pull requests. Install the [GitHub App](https://codeherder.com/docs/github-app/) on the repository, or connect GitHub or GitLab under [Integrations](https://codeherder.com/docs/integrations/). A task cannot enter the stage without one of these.

No built-in workflow has a merge-hold stage. You add one to a workflow of your own. You set it up from the CLI today. The web app has no control for it. **Settings → Stages** keeps the setting on a stage that already has it, but it shows no toggle to turn it on.

## Set up a merge-hold stage

Create a stage in your library and mark it as a merge-hold stage:

```
ch stage create await_merge --stage '{"label":"Waiting for merge","spec":{"awaits_external_merge":true}}'
```

A merge-hold stage does no work itself. It has no prompt, and no agent runs there. Check it with `ch stage show await_merge`. The output has a row `awaits external merge` with the value `yes`.

Then add the stage to a workflow. This example puts it between `code` and `done`:

```
{
  "key": "hand_merge",
  "label": "Hand merge",
  "instances": [
    { "name": "todo", "stage": "todo", "class": "todo" },
    { "name": "code", "stage": "code", "class": "doing", "to": ["await_merge"] },
    { "name": "await_merge", "stage": "await_merge", "class": "doing", "to": ["done", "code"] },
    { "name": "done", "stage": "done", "class": "done", "terminal": "success" }
  ]
}
```

```
ch workspace workflow edit hand_merge --from-file composition.json
```

The stage must have class `doing`. CodeHerder rejects a merge-hold stage that has a prompt, that can write code, or that has class `done`. It reports that when you save a workflow that uses the stage. See [Composing a workflow from the library](https://codeherder.com/docs/stage-library/#composing-a-workflow-from-the-library) for the rest of the file format.

## How a task enters and leaves the stage

**Entering.** Nothing moves a task into the stage by itself. The agent in the stage before it, or a person, moves the task there. For the workflow above, that is `ch task status <taskId> await_merge`. CodeHerder refuses the move in two cases:

- The task has no pull request recorded. The error says to run `ch task merge-requests create <taskId> <pr-or-mr-url>` first. When an agent writes a pull request link into the task’s `artifact` field, CodeHerder records it for you. You can record one yourself with the same command.
- No GitHub App installation or enabled integration covers the pull request’s repository.

**Leaving.** The task leaves when **every** pull request it waits on is merged. CodeHerder learns about a merge from a webhook from your git host. It also checks again about every two minutes. If only some pull requests are merged, the task keeps waiting. The task then moves to the next stage in the workflow. If that move needs [approval](https://codeherder.com/docs/approvals/), the task waits there for it.

**Moving it by hand.** A person cannot move a waiting task forward. CodeHerder answers that the task advances on its own once the merge is confirmed. You can still send it back, block it, or cancel it, as far as the workflow allows each move.

## Reading the wait

The task page shows a banner only when the pull request needs your attention: a draft, a conflict, or a closed pull request. It lists each pull request the task waits on, with a link and how long it has waited.

The state of the pull request has one of these labels. With several pull requests, the task shows the most urgent one, in this order: closed, conflicted, draft, open and mergeable, open and still checking.

| Label | Meaning |
| --- | --- |
| Open and mergeable | The pull request is ready for someone to merge. |
| Open, checking mergeability | Your git host has not finished checking it. |
| Draft | The pull request is a draft. Mark it ready for review. |
| Conflicted, needs a decision | The pull request has a merge conflict. |
| PR closed, needs a decision | Someone closed the pull request without merging it. |

When a decision is needed, the banner offers buttons. Each button appears only if the workflow allows that move:

- **Send back to `<stage>`** moves the task to the nearest earlier stage where an agent can write code, usually `code`.
- **Cancel task** cancels the task.

CLI output shows the same facts:

```
ch task show <taskId>
```

While a task waits, the output has a row like `awaited PR: conflicted, needs a decision (move it to code or cancelled), since 13:04`. The state is one of `mergeable`, `checking`, `draft`, `conflicted` or `closed`. The time reads `just now` or `N min ago` within the last hour. Later the same day it shows a clock time. Within the past week it adds the weekday, for example `Mon 13:04`. Older times show the date, for example `Jan 2, 13:04`. The PR column of `ch task list` shows the link, then `(merged)` for a merged pull request. While the task waits, it shows `(draft)`, `(conflicted)`, `(closed)`, `(mergeable)` or `(checking)` instead.

The host state of a waiting task’s pull request refreshes on a webhook or on the two-minute check. A change on your git host can take that long to show.

On the Tasks page, a row with a recorded pull request shows a pull request icon beside its title. Hover the icon to see which pull request it is and its state. A task with several shows how many there are and how many have merged.

## When something goes wrong

**The pull request closes without a merge.** The task stays in the stage. CodeHerder files a blocker that starts with “PR/MR closed without merging”. Reopen the pull request, or use the banner buttons to send the task back or cancel it. If you reopen it, CodeHerder clears the note on a later check. See [Blockers and blocked tasks](https://codeherder.com/docs/blockers/).

**CodeHerder cannot read the pull request.** After three failed reads in a row, it files a blocker that starts with “Can’t verify merge status” and gives the reason. The reason says what failed. Fix the connection to your git host. The next successful check clears the blocker.

**The task is stuck although the pull request merged.** An admin or owner can confirm the merge by hand and give a reason:

```
ch task status <taskId> done --force-merge-confirm --reason "Merged on the host; webhook missed"
```

Use the stage the workflow moves to next in place of `done`. CodeHerder posts a task comment that records the override and your reason. There is no web control for this.

## Finding tasks that wait

In the web app, open **Tasks** and use the **PR state** filter:

- **Awaiting merge** shows tasks whose pull request is open and ready for a merge.
- **Draft** shows tasks whose pull request is a draft.
- **Needs a decision** shows tasks whose pull request is conflicted or closed.

From the CLI:

```
ch task list --merge-state awaiting --status await_merge
ch task list --merge-state needs_decision --status await_merge
```

The filter matches any task that has a recorded pull request that is not merged, at **any** stage, not only tasks in a merge-hold stage. A task in `review` with an open pull request also matches **Awaiting merge**. Combine the filter with the **Stage** filter, or with `--status`, to list only the tasks that wait in your merge-hold stage. See [Tracking work](https://codeherder.com/docs/tracking-work/#filtering-by-pull-request-state).

## Tasks that run locally

A task you run [on your own machine](https://codeherder.com/docs/local-tasks/) has no account, so CodeHerder cannot detect a merge for it. A local task in a merge-hold stage can only go back, be blocked, or be cancelled.

## Related guides

- [How work flows](https://codeherder.com/docs/how-work-flows/) for the built-in stages, including the merge stage.
- [The stage library](https://codeherder.com/docs/stage-library/) for creating and composing stages.
- [Task status on pull requests](https://codeherder.com/docs/pr-status/) for the `CodeHerder` check on a pull request.
- [Blockers and blocked tasks](https://codeherder.com/docs/blockers/) for what a blocker does.
