# What does github.pull_request.merged prove?

> Prove that the commits of the head branch reached the base branch. Version 1.0.0, certification Community.

Canonical: https://provely.sh/verify/github/github.pull_request.merged  
Last reviewed: 2026-09-05  
Skill version: 0.1.0  
Certification: community  

**Prove that the commits of the head branch reached the base branch. A closed pull request is not a merged one, and merge_commit_sha holds a test merge commit while the pull request is open.**

*Contract identity.*

| Fact | Value |
| --- | --- |
| Contract id | `github.pull_request.merged` |
| Version | `1.0.0` |
| Hash | `f52af1e9c61889f2` |
| Completion level | `merged` |
| Certification | Community |
| Skill | [GitHub REST API 0.1.0](/verify/github) |
| Valid for provider API versions | `2022-11-28` |

## What is the intent?

Get the commits of the head branch into the base branch.

## What is the subject and the action?

| Member | Value |
| --- | --- |
| Subject type | `github.pull_request` |
| Subject identity | pull_request_number = `$action.result.number` |
| Canonical effect | `code.pull_request_open` |
| Provider operation | `POST /repos/{owner}/{repo}/pulls` |
| Idempotency | none, retry is not safe |

## How does the evidence correlate with this operation?

| Strategy | Assurance | Keys | Required |
| --- | --- | --- | --- |
| `resource_id` | strong | pull_request_number from `$action.result.number` | yes |
| `fingerprint` | weak | head from `$input.head`; base from `$input.base` | no |

## Which evidence does the contract require?

Minimum evidence level `E2`. An independent channel is required. Minimum channels: 1.

| Channel | Level | Independence | Verifier | Description |
| --- | --- | --- | --- | --- |
| `pull_request_readback` | E2 | provider readback | `http` | Read the pull request from the provider. A condition path is $observed.pull_request_readback.merged. |
| `pull_request_merged_events` | E3 | provider event | `webhook` | The pull_request.closed events of this pull request that carry merged true. The webhook verifier returns {events, count, latest, earliest, types, duplicates_dropped}. A condition reads $observed.pull_request_merged_events.count. |
| `pull_request_opened_events` | E3 | provider event | `webhook` | The pull_request.opened events for the head branch since the operation started. The webhook verifier returns {events, count, latest, earliest, types, duplicates_dropped}. A count above one shows a second pull request, and the REST API has no idempotency key to stop it. |

## Which conditions must all hold for VERIFIED?

| Condition | Meaning | Path | Operator | Expected | Evidence |
| --- | --- | --- | --- | --- | --- |
| `pull_request_merged` | The pull request resource states merged true. The state member alone does not separate a merge from a close. | `$observed.pull_request_readback.merged` | `eq` | `true` | `pull_request_readback` |
| `pull_request_merged_in_window` | The provider merged the pull request after the operation started. | `$observed.pull_request_readback.merged_at` | `time_after` | `$operation.created_at` | `pull_request_readback` |
| `pull_request_base_matches` | The commits went into the base branch that the intent named. | `$observed.pull_request_readback.base.ref` | `eq` | `$input.base` | `pull_request_readback` |
| `pull_request_merge_event` | At least one pull_request.closed event of this pull request carries merged true. | `$observed.pull_request_merged_events.count` | `gte` | `1` | `pull_request_merged_events` |

## Which conditions give CONTRADICTED?

| Condition | Class | Reason | Path | Operator | Expected |
| --- | --- | --- | --- | --- | --- |
| `pull_request_on_wrong_base` | wrong subject | The pull request targets a different base branch than the intent named. | `$observed.pull_request_readback.base.ref` | `ne` | `$input.base` |
| `pull_request_from_wrong_head` | wrong subject | The pull request carries a different head branch than the intent named. | `$observed.pull_request_readback.head.ref` | `ne` | `$input.head` |
| `pull_request_predates_operation` | pre existing state | The pull request is older than the operation. It proves nothing. | `$observed.pull_request_readback.created_at` | `time_before` | `$operation.created_at` |
| `duplicate_pull_request_present` | duplicate side effect | GitHub opened more than one pull request for the head branch since the operation started. Do not retry. | `$observed.pull_request_opened_events.count` | `gt` | `1` |

## Which observed states map to a verdict before completion?

| Rule | Match | Verdict | Reason |
| --- | --- | --- | --- |
| `still_open` | `$observed.pull_request_readback.state` eq `"open"` | PENDING | The pull request is still open. The runtime observes again later. |
| `closed_without_merge` | (`$observed.pull_request_readback.state` eq `"closed"`) and (not (`$observed.pull_request_readback.merged` eq `true`)) | CONTRADICTED | A person closed the pull request, and GitHub merged nothing. Ask a person. |

## How long does the runtime observe?

| Timing member | Value |
| --- | --- |
| Initial delay | 1000 ms |
| Poll interval | 5000 ms |
| Backoff | exponential factor 2, max 60000 ms |
| Maximum attempts | 40 |
| Timeout | 604800000 ms |
| Stale read window | 20000 ms |
| On timeout | UNVERIFIABLE (`evidence_unavailable_before_timeout`), escalated to a person |

## Where do these rules come from?

- `github.openapi#/components/schemas/pull-request/properties/merged`: [github.openapi](https://raw.githubusercontent.com/github/rest-api-description/main/descriptions/api.github.com/api.github.com.json), retrieved 2026-09-08
- `github.openapi#/components/schemas/pull-request/properties/merged_at`: [github.openapi](https://raw.githubusercontent.com/github/rest-api-description/main/descriptions/api.github.com/api.github.com.json), retrieved 2026-09-08
- `github.docs.pulls#pull-requests/the-test-merge-commit`: [github.docs.pulls](https://docs.github.com/rest/pulls/pulls), retrieved 2026-09-08
- `github.docs.pulls#pull-requests/merge-a-pull-request`: [github.docs.pulls](https://docs.github.com/rest/pulls/pulls), retrieved 2026-09-08
- `github.docs.pulls#pull-requests/the-state-of-a-pull-request`: [github.docs.pulls](https://docs.github.com/rest/pulls/pulls), retrieved 2026-09-08
- `github.openapi#/components/schemas/pull-request/properties/base`: [github.openapi](https://raw.githubusercontent.com/github/rest-api-description/main/descriptions/api.github.com/api.github.com.json), retrieved 2026-09-08
- `github.events#/events/pull_request.closed`: [github.events](https://docs.github.com/webhooks/webhook-events-and-payloads), retrieved 2026-09-08
- `github.docs.pulls#pull-requests/events`: [github.docs.pulls](https://docs.github.com/rest/pulls/pulls), retrieved 2026-09-08

### Can github.pull_request.merged return VERIFIED from the action response alone?

No. The minimum evidence level is E2. The action response is E1. The completion conditions read `pull_request_readback` and `pull_request_merged_events`.

### What happens after the timeout?

The verdict is UNVERIFIABLE with the reason `evidence_unavailable_before_timeout`. The operation goes to a person for review.

## Read next

- [Read the GitHub REST API verification page](https://provely.sh/verify/github)
- [Read the github.pull_request.open contract](https://provely.sh/verify/github/github.pull_request.open)
- [Read the github.workflow_run.succeeded contract](https://provely.sh/verify/github/github.workflow_run.succeeded)
- [Read what a completion contract contains](https://provely.sh/contracts)
- [Read what a receipt proves](https://provely.sh/docs/receipts)
