mergeproof
Proof before merge. Pull requests earn their merge with evidence, not claims.
mergeproof gates pull requests on evidence. A policy file in the repository says what a change
must prove before it merges: tests for the modules it touched, a green integration job, before/after
links from a live environment, a human who opened them. CI enforces it and reports on the PR. Coding
agents read the same file through AGENTS.md, so what they are told is what CI checks.
Why
Agentic development makes outcomes cheap to claim and expensive to verify. "Added tests, checked on
staging" costs nothing to type. A checkbox is self-attestation. What holds up is an artifact someone
else can open: a changed test file, a trace id that resolves, a screenshot pair, a sign-off bound to
the exact commit. mergeproof checks those, verifies them at their source when it can, and keeps
the last word with a person who is not the author.
Quickstart
1. Install and write a policy
pip install mergeproof # or: uv tool install mergeproof
mergeproof init # writes mergeproof.yaml
# 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.field
with: { key: environment, equals: staging }
- check: evidence.links
with: { min_pairs: 1 }
- check: review.human_verified
2. Add the workflow (full version in 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:
name: 🛡️ mergeproof
if: github.event_name != 'issue_comment' || github.event.issue.pull_request
runs-on: ubuntu-latest
permissions: { contents: read, pull-requests: write, statuses: write, checks: write }
steps:
- uses: actions/checkout@v4
with: { ref: ${{ github.event.repository.default_branch }} }
- uses: Aryamanz29/mergeproof@v0
env:
MERGEPROOF_PR_NUMBER: ${{ github.event.issue.number || github.event.pull_request.number }}
3. Require the mergeproof status in a ruleset on your default branch. That single required
check stands for everything the policy asks for, CI jobs included. See docs/github.md.
What a pull request sees
One comment updated in place, a mergeproof commit status in the merge box that rulesets can
require, and file annotations on the job for anything that points at a file. Contributors put evidence in the PR description as a fenced
block that agents can write and machines can read:
```evidence
environment: staging
links:
- what: search with an empty query
before: https://langfuse.example.com/project/p1/traces/trace-before
after: https://langfuse.example.com/project/p1/traces/trace-after
```
mergeproof explain tells anyone, human or agent, what the current diff still has to prove:
# What this change must prove (fail)
## source-needs-tests [block]
Triggered by `src/api/search.py`.
- ❌ unit tests touched: Each changed source file needs a changed test file.
- src/api/search.py: expected a changed test matching tests/**/test_search*.py
- ⏳ CI check run matching `^unit` is green on the head commit.
- now: only visible in GitHub mode
## Evidence block to add to the PR description
```evidence
environment: staging
links:
- what: <what was exercised>
before: https://<host>/<path-to-run>
after: https://<host>/<path-to-run>
```
Built-in checks
| check | proves |
|---|---|
tests.changed |
changed source files come with changed tests, mapped by {capture} globs |
files.changed |
the change touches, or avoids, certain paths |
evidence.field |
a key in the evidence block exists and has an acceptable value |
evidence.links |
before/after link pairs, optionally verified at their source |
ci.job_passed |
a named check run on the head commit succeeded |
review.human_verified |
a non-author human posted /verified <sha>; a new push invalidates it |
agent.verdict |
an allowed automated reviewer posted a head-bound verdict block |
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 docs/policy.md. Anything vendor-specific
is a plugin: docs/plugins.md.
For coding agents
mergeproof agent-prompt >> AGENTS.md # the rules, rendered from the policy
mergeproof explain # what the current diff still has to prove
Agents learn what to prove and check their own work before opening the PR. They cannot approve anything: human verification is the one requirement no token of theirs can satisfy. See docs/agents.md.
Documentation
| docs/policy.md | Rules, when matchers, severity, the evidence block, every check and its parameters |
| docs/github.md | Workflow, action inputs, rulesets, the three report channels, posting as your own App, JUnit and reviewdog output |
| docs/agents.md | AGENTS.md generation, the agent loop with the CLI, LLM reviewers |
| docs/cli.md | Every command, the JSON plumbing, output formats, exit codes |
| docs/plugins.md | Writing checks and verifiers, packaging them |
| examples/ | Three example projects with scenario fixtures the test suite runs, and a Langfuse verifier plugin |
| CONTRIBUTING.md | Development setup, conventions, how releases happen |
Run without installing
uvx mergeproof explain
docker run --rm -v "$PWD":/repo ghcr.io/aryamanz29/mergeproof check --local
Images: ghcr.io/aryamanz29/mergeproof tagged X.Y.Z, X.Y, X, latest per release, edge for main.
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.
License
MIT. Contributions welcome; see CONTRIBUTING.md.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file mergeproof-0.6.0.tar.gz.
File metadata
- Download URL: mergeproof-0.6.0.tar.gz
- Upload date:
- Size: 110.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0444e0ac48c6c0a7ddd65783596a6dffcdd4c28472529f8766f41627c4731248
|
|
| MD5 |
63787478dbaf76c68ba4e2b62af780fc
|
|
| BLAKE2b-256 |
4d272d77cb0c09902bbe4a17698ce6bf45909793929317feccd788bb75fd6ef0
|
Provenance
The following attestation bundles were made for mergeproof-0.6.0.tar.gz:
Publisher:
release.yml on Aryamanz29/mergeproof
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mergeproof-0.6.0.tar.gz -
Subject digest:
0444e0ac48c6c0a7ddd65783596a6dffcdd4c28472529f8766f41627c4731248 - Sigstore transparency entry: 2811748690
- Sigstore integration time:
-
Permalink:
Aryamanz29/mergeproof@2a94b08668f1542fc810edaf984044852bde914c -
Branch / Tag:
refs/heads/main - Owner: https://github.com/Aryamanz29
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@2a94b08668f1542fc810edaf984044852bde914c -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file mergeproof-0.6.0-py3-none-any.whl.
File metadata
- Download URL: mergeproof-0.6.0-py3-none-any.whl
- Upload date:
- Size: 49.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
79292860a9e1018eaeae1ba7a2a2e73e9beba0e1b5ef49a7947cd13db6440082
|
|
| MD5 |
969289a34fad66c3683085e1f445c4b1
|
|
| BLAKE2b-256 |
d6607b15970dde1cfff1ac741d30558a0f2689dc437e4e479cc0bc37d3019c51
|
Provenance
The following attestation bundles were made for mergeproof-0.6.0-py3-none-any.whl:
Publisher:
release.yml on Aryamanz29/mergeproof
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mergeproof-0.6.0-py3-none-any.whl -
Subject digest:
79292860a9e1018eaeae1ba7a2a2e73e9beba0e1b5ef49a7947cd13db6440082 - Sigstore transparency entry: 2811748721
- Sigstore integration time:
-
Permalink:
Aryamanz29/mergeproof@2a94b08668f1542fc810edaf984044852bde914c -
Branch / Tag:
refs/heads/main - Owner: https://github.com/Aryamanz29
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@2a94b08668f1542fc810edaf984044852bde914c -
Trigger Event:
workflow_dispatch
-
Statement type: