# Does a merge_commit_sha on a pull request mean the change is in the branch?

> Does a merge_commit_sha on a pull request mean the change is in the branch? Certification Community.

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

**No. GitHub REST API returns a 201 response with a pull request object when it accepts the request. The pull_request then holds one of 3 states. Only `merged` is terminal success. Provely proves `merged`, `open` and `succeeded` as separate promises.**

*Skill facts from the signed manifest.*

| Fact | Value |
| --- | --- |
| Skill version | `0.1.0` |
| Certification | Community (score 49 of 100) |
| Last conformance run | 2026-09-05T12:00:00Z: 41 of 41 cases passed, 0 critical false VERIFIED |
| Provider API versions | `2022-11-28` |
| Default provider API version | `2022-11-28` |
| Compiled | 2026-09-05T12:00:00Z by compiler 0.1.0 |
| Manifest hash | `91f18adf2b871a89` |
| Manifest hash check | the document hashes to the value the manifest states |
| Signature | valid, key `provely-skill-2026-09`, trusted by this build |

## What does this page prove?

| Claim | Proven by | Evidence | Status |
| --- | --- | --- | --- |
| A closed pull request is not a merged one, and merge_commit_sha holds a test merge commit while the pull request is open. | [github.pull_request.merged](/verify/github/github.pull_request.merged) | E2 + E3 | proven |
| The record exists with the intended head branch and base branch. It does not prove that the commits reached the base branch. | [github.pull_request.open](/verify/github/github.pull_request.open) | E2 + E3 | proven |
| A completed run is not a successful run. The contract reads the conclusion of the run and an event that carries it. | [github.workflow_run.succeeded](/verify/github/github.workflow_run.succeeded) | E2 + E3 | proven |
| An outcome outside GitHub REST API, such as a bank credit or a person who read a message | not proven | no E5 channel | not proven |
| The meaning of the `neutral` state of `github.workflow_run` | not proven | no source states it | not proven |
| The meaning of the `skipped` state of `github.workflow_run` | not proven | no source states it | not proven |
| The meaning of the `stale` state of `github.workflow_run` | not proven | no source states it | not proven |
| The meaning of the `action_required` state of `github.workflow_run` | not proven | no source states it | not proven |
| The agent report that the action worked | never counts | E0 | not proven |

## Which completion levels does the GitHub REST API skill expose?

Each level is one promise with one contract. An agent picks the level that matches the promise it makes. It cannot upgrade a level. Read the [completion level](/glossary/completion-level) definition.

| Level | Contract | Promise | Evidence | Certification |
| --- | --- | --- | --- | --- |
| `merged` | [github.pull_request.merged](/verify/github/github.pull_request.merged) v1.0.0 | A closed pull request is not a merged one, and merge_commit_sha holds a test merge commit while the pull request is open. | E2 + E3 | Community |
| `open` | [github.pull_request.open](/verify/github/github.pull_request.open) v1.0.0 | The record exists with the intended head branch and base branch. It does not prove that the commits reached the base branch. | E2 + E3 | Community |
| `succeeded` | [github.workflow_run.succeeded](/verify/github/github.workflow_run.succeeded) v1.0.0 | A completed run is not a successful run. The contract reads the conclusion of the run and an event that carries it. | E2 + E3 | Community |

## What is the GitHub REST API lifecycle?

### Which states can a `github.pull_request` be in?

| State | Class | Verdict | Meaning | Source |
| --- | --- | --- | --- | --- |
| `open` | transitional | PENDING | The pull request is open. The commits are not in the base branch. | [github.openapi](https://raw.githubusercontent.com/github/rest-api-description/main/descriptions/api.github.com/api.github.com.json) |
| `merged` | terminal success | VERIFIED | GitHub put the commits of the head branch into the base branch. The member state is closed and the member merged is true. | [github.openapi](https://raw.githubusercontent.com/github/rest-api-description/main/descriptions/api.github.com/api.github.com.json) |
| `closed` | terminal neutral | FAILED | A person or an agent closed the pull request and merged nothing. The member state is closed and the member merged is false. | [github.openapi](https://raw.githubusercontent.com/github/rest-api-description/main/descriptions/api.github.com/api.github.com.json) |

In `github.pull_request` under provider API version `2022-11-28`, `merged` is the only state that means terminal success. Every other state gives PENDING, FAILED, or UNVERIFIABLE.

Source: https://raw.githubusercontent.com/github/rest-api-description/main/descriptions/api.github.com/api.github.com.json (retrieved 2026-09-08)

### Which states can a `github.workflow_run` be in?

| State | Class | Verdict | Meaning | Source |
| --- | --- | --- | --- | --- |
| `queued` | transitional | PENDING | GitHub accepted the dispatch. No job of the run started. | [github.openapi](https://raw.githubusercontent.com/github/rest-api-description/main/descriptions/api.github.com/api.github.com.json) |
| `in_progress` | transitional | PENDING | A job of the run is running. | [github.openapi](https://raw.githubusercontent.com/github/rest-api-description/main/descriptions/api.github.com/api.github.com.json) |
| `success` | terminal success | VERIFIED | The run is complete. The member status is completed and the member conclusion is success. | [github.openapi](https://raw.githubusercontent.com/github/rest-api-description/main/descriptions/api.github.com/api.github.com.json) |
| `failure` | terminal failure | FAILED | The run is complete and a job of it failed. | [github.openapi](https://raw.githubusercontent.com/github/rest-api-description/main/descriptions/api.github.com/api.github.com.json) |
| `timed_out` | terminal failure | FAILED | The run passed its time limit. | [github.openapi](https://raw.githubusercontent.com/github/rest-api-description/main/descriptions/api.github.com/api.github.com.json) |
| `startup_failure` | terminal failure | FAILED | GitHub could not start the run. The workflow file is not valid. | [github.openapi](https://raw.githubusercontent.com/github/rest-api-description/main/descriptions/api.github.com/api.github.com.json) |
| `cancelled` | terminal neutral | FAILED | A person or an agent cancelled the run. No work finished. | [github.openapi](https://raw.githubusercontent.com/github/rest-api-description/main/descriptions/api.github.com/api.github.com.json) |
| `neutral` | unknown | UNVERIFIABLE | The published schema lists this conclusion. No source states what it means for the work of the run. | [github.openapi](https://raw.githubusercontent.com/github/rest-api-description/main/descriptions/api.github.com/api.github.com.json) |
| `skipped` | unknown | UNVERIFIABLE | The published schema lists this conclusion. No source states what it means for the work of the run. | [github.openapi](https://raw.githubusercontent.com/github/rest-api-description/main/descriptions/api.github.com/api.github.com.json) |
| `stale` | unknown | UNVERIFIABLE | The published schema lists this conclusion. No source states what it means for the work of the run. | [github.openapi](https://raw.githubusercontent.com/github/rest-api-description/main/descriptions/api.github.com/api.github.com.json) |
| `action_required` | unknown | UNVERIFIABLE | The published schema lists this conclusion. No source states what it means for the work of the run. | [github.openapi](https://raw.githubusercontent.com/github/rest-api-description/main/descriptions/api.github.com/api.github.com.json) |

In `github.workflow_run` under provider API version `2022-11-28`, `success` is the only state that means terminal success. Every other state gives PENDING, FAILED, or UNVERIFIABLE.

Source: https://raw.githubusercontent.com/github/rest-api-description/main/descriptions/api.github.com/api.github.com.json (retrieved 2026-09-08)

## How does Provely tie the evidence to this exact operation?

A matching state that already existed must not verify. Every contract names the [correlation](/glossary/correlation) keys that bind the evidence to the operation, and the [idempotency](/glossary/idempotency) key that stops a duplicate side effect.

| Strategy | Assurance | Keys | Required | Window |
| --- | --- | --- | --- | --- |
| `resource_id` | strong | pull_request_number from `$action.result.number` | yes | none |
| `fingerprint` | weak | head from `$input.head`; base from `$input.base` | no | 600000 ms |
| `resource_id` | strong | workflow_run_id from `$action.result.workflow_run_id` | yes | none |
| `fingerprint` | weak | workflow_id from `$input.workflow_id`; ref from `$input.ref` | no | 600000 ms |

*Figure: The skill reads the channels below. It prefers the ones furthest from the action.*

- E0 agent assertion: never sufficient.
- E1 action response: the provider acknowledged the request.
- E2 provider readback: the runtime read the resource back.
- E3 provider event: the provider reported the change.
- E4 independent system: a system outside the action path agrees.
- E5 external outcome: the result is observable in the world.

## Which evidence channels does the skill read?

The runtime prefers the channel that is more independent from the action path. Read the [evidence level](/glossary/evidence-level) definition. An [acknowledgement](/glossary/acknowledgement) from GitHub REST API is E1 and never terminal success.

| Channel | Level | Independence | Verifier | Deterministic | Typical latency |
| --- | --- | --- | --- | --- | --- |
| `pull_request_action_response` | E1 | same response | `action_result` | yes | not stated |
| `pull_request_merged_events` | E3 | provider event | `webhook` | no | 3000 ms |
| `pull_request_opened_events` | E3 | provider event | `webhook` | no | 3000 ms |
| `pull_request_readback` | E2 | provider readback | `http` | yes | 300 ms |
| `workflow_run_completed_events` | E3 | provider event | `webhook` | no | 5000 ms |
| `workflow_run_readback` | E2 | provider readback | `http` | yes | 300 ms |
| `workflow_run_requested_events` | E3 | provider event | `webhook` | no | 3000 ms |

## Which ways can a GitHub REST API action look done and not be?

| Contract | Case | Rule | Verdict |
| --- | --- | --- | --- |
| [github.pull_request.merged](/verify/github/github.pull_request.merged) | wrong subject | The pull request targets a different base branch than the intent named. | CONTRADICTED |
| [github.pull_request.merged](/verify/github/github.pull_request.merged) | wrong subject | The pull request carries a different head branch than the intent named. | CONTRADICTED |
| [github.pull_request.merged](/verify/github/github.pull_request.merged) | pre existing state | The pull request is older than the operation. It proves nothing. | CONTRADICTED |
| [github.pull_request.merged](/verify/github/github.pull_request.merged) | duplicate side effect | GitHub opened more than one pull request for the head branch since the operation started. Do not retry. | CONTRADICTED |
| [github.pull_request.merged](/verify/github/github.pull_request.merged) | observed state | The pull request is still open. The runtime observes again later. | PENDING |
| [github.pull_request.merged](/verify/github/github.pull_request.merged) | observed state | A person closed the pull request, and GitHub merged nothing. Ask a person. | CONTRADICTED |
| [github.pull_request.open](/verify/github/github.pull_request.open) | wrong subject | The pull request targets a different base branch than the intent named. | CONTRADICTED |
| [github.pull_request.open](/verify/github/github.pull_request.open) | wrong subject | The pull request carries a different head branch than the intent named. | CONTRADICTED |
| [github.pull_request.open](/verify/github/github.pull_request.open) | pre existing state | The pull request is older than the operation. It proves nothing. | CONTRADICTED |
| [github.pull_request.open](/verify/github/github.pull_request.open) | duplicate side effect | GitHub opened more than one pull request for the head branch since the operation started. Do not retry. | CONTRADICTED |
| [github.workflow_run.succeeded](/verify/github/github.workflow_run.succeeded) | wrong subject | The workflow run used a different reference than the intent named. | CONTRADICTED |
| [github.workflow_run.succeeded](/verify/github/github.workflow_run.succeeded) | other | The run started from another event. It is not the run of this dispatch. | CONTRADICTED |
| [github.workflow_run.succeeded](/verify/github/github.workflow_run.succeeded) | pre existing state | The workflow run is older than the operation. It proves nothing. | CONTRADICTED |
| [github.workflow_run.succeeded](/verify/github/github.workflow_run.succeeded) | duplicate side effect | GitHub started more than one run for the reference since the operation started. Do not retry. | CONTRADICTED |
| [github.workflow_run.succeeded](/verify/github/github.workflow_run.succeeded) | observed state | The run is complete and a job of it failed. | FAILED |
| [github.workflow_run.succeeded](/verify/github/github.workflow_run.succeeded) | observed state | A person or an agent cancelled the run. No work finished. | CONTRADICTED |
| [github.workflow_run.succeeded](/verify/github/github.workflow_run.succeeded) | observed state | The run has not finished. The runtime observes again later. | PENDING |

## What did the last conformance run show?

*The six confidence dimensions of the signed manifest.*

| Dimension | Score | Maximum |
| --- | --- | --- |
| Documentation | 14 | 20 |
| Schema alignment | 12.19 | 15 |
| Lifecycle certainty | 14.29 | 20 |
| Evidence strength | 16 | 20 |
| Causal correlation | 15 | 15 |
| Sandbox conformance | 10 | 10 |
| Raw score | 81.48 | 100 |
| Score after the caps | 49 | 100 |

The raw score is 81.48. 3 hard caps apply, so the score is 49 and the level is Community.

*Why the score is capped.*

| Reason | Highest score it allows |
| --- | --- |
| A lifecycle state has unknown semantics. The compiler cannot prove the end state. | 49 |
| 6 uncertainty records are open. The lowest cap is 49. | 49 |
| 2 mandatory cases are missing. Example: github.pull_request.merged:terminal_failure. | 74 |

*The score and the level of each contract.*

| Contract | Raw score | Score after the caps | Certification |
| --- | --- | --- | --- |
| `github.pull_request.merged` | 87.27 | 49 | Community |
| `github.pull_request.open` | 86.67 | 49 | Community |
| `github.workflow_run.succeeded` | 80.23 | 49 | Community |

> The weakest contract is github.pull_request.merged. It scores 49 and reaches Community. The package level is Community, because a package level never rises above its weakest contract. Read the level of the contract you use.

The last conformance run on 2026-09-05T12:00:00Z passed 41 of 41 cases with 0 critical false VERIFIED. One critical false VERIFIED rejects a skill.

*The conformance result by case class.*

| Case class | Passed | Total |
| --- | --- | --- |
| duplicate side effect | 3 | 3 |
| error after execution | 3 | 3 |
| error before execution | 3 | 3 |
| evidence unavailable | 3 | 3 |
| pre existing state | 3 | 3 |
| stale readback | 3 | 3 |
| still transitional | 3 | 3 |
| terminal failure | 1 | 1 |
| terminal success | 3 | 3 |
| timeout after commit | 3 | 3 |
| version mismatch | 3 | 3 |
| webhook duplicate | 3 | 3 |
| webhook out of order | 3 | 3 |
| wrong subject | 3 | 3 |
| wrong terminal state | 1 | 1 |

The conformance artifact digest is `0800a56c529af7ce`. The harness signs the run, so a reader can check that these numbers come from that run.

## What remains uncertain?

> The level is Community because of it. A lifecycle state has unknown semantics. The compiler cannot prove the end state. 6 uncertainty records are open. The lowest cap is 49. Provely does not guess a rule that a source does not state.

## Where do these facts come from?

Every claim above cites a source assertion in the skill provenance. The compiler records the source, its hash, and the retrieval date. A page never states a provider rule without one.

| Source | Kind | Retrieved | Excerpt |
| --- | --- | --- | --- |
| [github.docs.pulls](https://docs.github.com/rest/pulls/pulls) | docs | 2026-09-08 | authored |
| [github.docs.versions](https://docs.github.com/rest/about-the-rest-api/api-versions) | docs | 2026-09-08 | authored |
| [github.docs.webhooks](https://docs.github.com/webhooks/using-webhooks/best-practices-for-using-webhooks) | docs | 2026-09-08 | authored |
| [github.docs.workflows](https://docs.github.com/rest/actions/workflow-runs) | docs | 2026-09-08 | authored |
| [github.events](https://docs.github.com/webhooks/webhook-events-and-payloads) | event sample | 2026-09-08 | authored |
| [github.openapi](https://raw.githubusercontent.com/github/rest-api-description/main/descriptions/api.github.com/api.github.com.json) | openapi | 2026-09-08 | trimmed |

## How do I verify a GitHub REST API action?

### Verify a GitHub REST API action with Provely

1. **Begin the operation.** Call `begin` with the contract `github.pull_request.merged` and the input. Keep the operation id.
2. **Make the GitHub REST API call you make today.** Send the request with the correlation metadata that `begin` returned.
3. **Submit the acknowledgement.** Call `action_result` with the GitHub REST API response. This is evidence level E1. It is not completion.
4. **Verify.** Call `verify`. The runtime reads `pull_request_action_response`, `pull_request_merged_events`, `pull_request_opened_events`, `pull_request_readback`, `workflow_run_completed_events`, `workflow_run_readback` and `workflow_run_requested_events` and evaluates the contract.
5. **Report the verdict exactly as returned.** VERIFIED comes with a signed receipt. PENDING comes with the operation id. CONTRADICTED and UNVERIFIABLE are not success.

## Questions developers ask

### Does the `merged` level prove the `succeeded` level?

No. A closed pull request is not a merged one, and merge_commit_sha holds a test merge commit while the pull request is open. A completed run is not a successful run. The contract reads the conclusion of the run and an event that carries it. Use `github.workflow_run.succeeded` to prove `succeeded`.

### What does the agent say while GitHub REST API reports `open`?

It says: "The action is accepted but not yet verified. Operation: <id>." The verdict is PENDING. The runtime observes again on the contract timing policy.

### Which GitHub REST API API versions does the skill support?

`2022-11-28`. An operation on another version returns UNVERIFIABLE with the reason `version_unsupported`. The runtime never guesses.

### Does Provely need write access to GitHub REST API?

No. The agent keeps its write key. The verifier reads with a separate read-only credential where GitHub REST API permits it, and it never shares that credential with the agent.

## Read next

- [Read the github.pull_request.merged contract](https://provely.sh/verify/github/github.pull_request.merged)
- [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 the Resend verification page](https://provely.sh/verify/resend)
- [Read the Shopify Admin API verification page](https://provely.sh/verify/shopify)
- [See every integration](https://provely.sh/verify)
- [Verify GitHub REST API actions from Claude Code](https://provely.sh/agents/claude-code)
- [Verify GitHub REST API actions from Cursor](https://provely.sh/agents/cursor)
- [See every agent integration](https://provely.sh/agents)
- [Open the quick start](https://provely.sh/docs/quick-start)
- [Read how Provely records integration uptime](https://provely.sh/docs/uptime)
- [Read the completion level definition](https://provely.sh/glossary/completion-level)
