Proof before merge. Pull requests earn their merge with evidence, not claims.
Documentation · Quickstart · Checks · Examples · Changelog
A pull request says "added tests, verified on staging". That costs nothing to type. mergeproof
turns it into something CI can check: one YAML file in the repository says what a change must
prove before it merges, the action checks the proof on every push, and the same file tells
coding agents what to produce before they open the PR.
A real comment. One pill per rule, then only what blocks the merge and what to do about it; the rest is the receipt.
How it works
- One policy file. A rule pairs a
when(paths, labels, title, base branch) with what itrequires: checks with parameters. Nothing to script. - Evidence, not attestation. Tests must be in the diff. Jobs must be green on the head commit. Links must resolve at their source. Sign-off is a human approval bound to the commit; a new push invalidates it.
- One required status. Rulesets require
mergeproof; the policy decides what stands behind it, CI jobs included. When no rule applies, nothing is posted and nothing blocks. - Agents read the same file. They learn what to prove and check their own work before opening the PR. They cannot approve anything.
Quickstart
1. Write the policy. mergeproof init writes a starter; this is the shape:
# mergeproof.yaml
version: 1
rules:
- id: source-needs-tests
description: Source changes ship with tests for the touched module.
when:
paths: ["src/**/*.py"]
require:
- check: tests.changed
with: { map: { "src/{pkg}/{name}.py": "tests/**/test_{name}*.py" } }
- check: ci.job_passed
with: { name: "^unit", regex: true }
- id: fix-needs-live-evidence
description: Bug fixes show the behaviour before and after on staging.
when:
title: "^fix"
require:
- check: evidence.links
with: { key: traces, min_pairs: 1, verify: http }
- check: review.human_verified
with: { accept_approval: true }
2. Add the workflow. Full version with comments: examples/github-workflow.yml.
# .github/workflows/mergeproof.yml
on:
pull_request:
types: [opened, synchronize, reopened, edited, labeled, unlabeled]
issue_comment: { types: [created, edited] }
check_suite: { types: [completed] }
jobs:
gate:
if: github.event_name != 'issue_comment' || github.event.issue.pull_request
runs-on: ubuntu-latest
permissions: { contents: read, pull-requests: write, statuses: write, checks: read }
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.repository.default_branch }} # policy from the base branch
- uses: Aryamanz29/mergeproof@v1
env:
MERGEPROOF_PR_NUMBER: ${{ github.event.issue.number || github.event.pull_request.number }}
3. Require the mergeproof status on your default branch. Until you do, the gate only
reports, which is the right way to roll it out.
Before you push
mergeproof explain runs the policy against the working tree and prints what is still missing,
in the same words the PR comment will use. Agents run it in their loop; people run it before
opening the PR.
pip install mergeproof # or: uv tool install mergeproof, uvx mergeproof
mergeproof explain # what this diff must prove
mergeproof template # the evidence block still missing, ready to paste
mergeproof agent-prompt >> AGENTS.md
What it can check
| check | proves |
|---|---|
tests.changed |
changed source files come with changed tests, mapped by {capture} globs; existing_only skips modules that have no test file yet |
ci.job_passed |
a named check run succeeded on the head commit (newest run per name; superseded runs are ignored) |
evidence.artifacts |
links in the evidence block by kind: before/after pair, one single, or a set; verified at their source by a pluggable verifier |
evidence.links |
the pair kind under its own name: before/after link pairs |
evidence.field |
a key in the evidence block exists and has an acceptable value |
review.human_verified |
a reviewer other than the author approved the head commit, or posted /verified <sha> |
agent.verdict |
an allowed automated reviewer posted a head-bound verdict block |
files.changed |
the change touches, or avoids, certain paths |
pr.labels, pr.body |
labels present or absent; description sections, regex, length |
shell |
a command from the policy exits 0 |
Parameters for each: mergeproof checks, or the
checks reference. Anything
vendor-specific is a plugin: the Langfuse and
Braintrust verifiers are a dozen lines each.
Evidence
What the contributor supplies goes in the PR description as a fenced block that agents can write
and machines can read. mergeproof template prints the one a change still needs.
```evidence
environment: staging
traces:
- what: search with an empty query
before: https://www.braintrust.dev/app/acme/p/service/logs?r=b6f98332e178f658
after: https://www.braintrust.dev/app/acme/p/service/logs?r=db0100b2192984af
```
Everything else is found in the pull request itself: the diff, the check runs, the reviews.
Where it reports
| channel | what |
|---|---|
| comment | the scorecard above, one per PR, updated in place |
commit status mergeproof |
4 of 5 requirements satisfied, 1 missing in the merge box; the context to require |
| review comments | each open requirement on the file it concerns, updated as the PR changes, removed once satisfied |
| job annotations | the same findings on the workflow run and in the diff |
| JUnit and reviewdog files | for renderers you already use |
| receipt | on merge, the final report is committed to a mergeproof-receipts branch; mergeproof receipt <sha> answers "what proved this change?" months later |
Run without installing
uvx mergeproof explain
docker run --rm -v "$PWD":/repo ghcr.io/aryamanz29/mergeproof check --local
Images are tagged X.Y.Z, X.Y, X and latest per release, edge for main.
Documentation
aryamanz29.github.io/mergeproof
| Getting started | install, the three-step setup, what a pull request sees |
| Guides | writing a policy, evidence, GitHub setup, coding agents, rolling out without blocking anyone, the examples |
| Reference | every check and parameter, the command line, the action's inputs and outputs |
| Extending | checks and verifiers as plugins |
| Project | contributing, releasing, FAQ |
How it compares
Danger runs rules written in JavaScript and comments on the PR.
policy-bot enforces approval policies from GitHub-native
data. Checklist actions fail on unticked boxes. Hosted evidence gates evaluate your artifacts on
their servers. mergeproof is declarative, has a notion of evidence that can be verified at its
source, explains itself to agents, and runs entirely in your CI.
Contributing
make setup, then make lint typecheck test. Pull requests here are gated by this repository's own
mergeproof.yaml. See CONTRIBUTING.md. MIT licensed.
Metadata
Release files for mergeproof 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| mergeproof-1.0.0.tar.gz | 520.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| mergeproof-1.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 595.5 kB
Release files / mergeproof-1.0.0.tar.gz
| Download URL | mergeproof-1.0.0.tar.gz |
|---|---|
| Size | 520.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
695ced0e8f2e22c8e425fb0b3c4482553725095a860c17b67245042e69257760
|
|
BLAKE2b-256 checksum How to use checksums |
f927759567bd5be01c85a6a058c58e5d0e7118c604c3ede1314c37a170d1b4f2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 14, 2026.
Transparency logRelease files / mergeproof-1.0.0-py3-none-any.whl
| Download URL | mergeproof-1.0.0-py3-none-any.whl |
|---|---|
| Size | 75.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
86346a0a990b5a9bb661069c71e693757eefac67fbe6ca90173423f901fcc823
|
|
BLAKE2b-256 checksum How to use checksums |
71efbb1d02aa1871380cc86e713721433d3d8366531bc8795bf314e3ee6fc58e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 14, 2026.
Transparency log