# What does github.pull_request.open prove?

> Prove that GitHub holds a pull request for this change. Version 1.0.0, certification Community.

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

**Prove that GitHub holds a pull request for this change. The record exists with the intended head branch and base branch. It does not prove that the commits reached the base branch.**

*Contract identity.*

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

## What is the intent?

Open a pull request from 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_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_present` | The provider holds the pull request with the returned number. | `$observed.pull_request_readback.number` | `eq` | `$action.result.number` | `pull_request_readback` |
| `pull_request_head_matches` | The head branch of the pull request equals the requested head branch. | `$observed.pull_request_readback.head.ref` | `eq` | `$input.head` | `pull_request_readback` |
| `pull_request_base_matches` | The base branch of the pull request equals the requested base branch. | `$observed.pull_request_readback.base.ref` | `eq` | `$input.base` | `pull_request_readback` |
| `pull_request_created_in_window` | The provider created the pull request after the operation started. | `$observed.pull_request_readback.created_at` | `time_after` | `$operation.created_at` | `pull_request_readback` |
| `pull_request_opened_event` | At least one pull_request.opened event names the head branch of this pull request. The duplicate guard reads the same channel, so a workspace with no event endpoint cannot verify and cannot miss a second pull request either. | `$observed.pull_request_opened_events.count` | `gte` | `1` | `pull_request_opened_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?

The contract declares no verdict map. The completion block decides.

## 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 | 900000 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#/paths/~1repos~1{owner}~1{repo}~1pulls/post`: [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#/paths/~1repos~1{owner}~1{repo}~1pulls/post/responses/201`: [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/create-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/number`: [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#/paths/~1repos~1{owner}~1{repo}~1pulls~1{pull_number}/get/responses/200`: [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/head`: [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/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.openapi#/components/schemas/pull-request/properties/created_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.events#/events/pull_request.opened`: [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.open 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_opened_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.merged contract](https://provely.sh/verify/github/github.pull_request.merged)
- [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)
