Which of these pull requests needs you first?
npx --yes mergewarden@0.9.0 triage <your owner/repository>8 open pull request(s) read. 7 have something a maintainer checks by hand.
#169755 feature/openAQ AI disclosed · no linked issue · oversized
#176410 unifiprotect-config-map no linked issue · oversized
#177530 tod-support-sun-events AI disclosed · no linked issue
Nothing was closed, labelled, or commented on.
Every row is a fact you can check: no linked issue, no description, a template
kept empty, a change past your review size, a Co-authored-by: trailer a
coding tool wrote about itself, a file edited outside the scope the pull
request declared. Nothing scores a contributor, guesses from writing style, or
closes anything — there is no --close flag, and a test asserts there never
is. A pull request closed by a bot in error is not reopened by the person who
gave up on it.
The same rules run as a GitHub Action. It does not execute pull-request code, load policy from the PR head, or call an LLM, and every finding carries deterministic evidence that replays locally.
MergeWarden gates its own pull requests — the MergeWarden badge above is that
live self-check (how we dogfood).
Triage your repo · Try a public PR · Install the Action · What it catches · Adopt safely · Documentation · 简体中文
See every rule fire on a bundled example — no token, no repository, no network —
then scan a real pull request, by owner/repo#number or by URL:
npx --yes mergewarden@0.9.0 demo
npx --yes mergewarden@0.9.0 scan owner/repository#123
npx --yes mergewarden@0.9.0 scan https://github.com/owner/repository/pull/123Use GH_TOKEN or GITHUB_TOKEN for private repositories or higher API rate
limits. MergeWarden intentionally has no token command-line flag.
The default output is concise; --format json or --format markdown gives the
complete machine-readable report (CLI reference).
Create .github/workflows/mergewarden.yml:
name: MergeWarden
on:
pull_request:
types: [opened, synchronize, reopened, edited, labeled, unlabeled, ready_for_review]
permissions:
contents: read
pull-requests: write
jobs:
mergewarden:
runs-on: ubuntu-latest
steps:
- uses: sjh9714/mergewarden@v0.9.0
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
mode: warn
fail-on-block: false
comment: autoNo checkout step is needed, and MergeWarden
does not publish or recommend a mutable v0 tag.
For an immutable install, pin the exact v0.9.0 release commit:
- uses: sjh9714/mergewarden@e97f47b59dcda0eb48ba88275c176fc734325659The first run works without mergewarden.yml: a confirmed 404 on the PR base
branch selects the built-in warn policy. Authentication, rate-limit, and server
errors never fall back silently.
Most agent pull requests cross no boundary, and those get a pass and no
comment — comment: auto speaks only when there is an error or a warning.
Everything else is recorded in the Actions job summary. When a boundary
is crossed you get one comment:
ERROR contract/out-of-scope: src/billing/invoice.ts changed outside the allowed contract scope.
Push a fix and that same comment updates itself to PASSED. It is never deleted, so a stale "NEEDS REVIEW" cannot outlive the problem it described. Fork pull requests get a read-only token from GitHub and so are never commented on. New here? Start here is the one-page version.
| Boundary | Deterministic evidence |
|---|---|
| Declared PR scope | Files outside allowed_paths or inside blocked_paths |
| Agent control plane | Changes to AGENTS.md, CLAUDE.md, GEMINI.md, .mcp.json, .cursor/** |
| Agentic workflow injection | Untrusted GitHub text flowing into registered agent prompts |
| Workflow privilege | Permission escalation, new write-all or OIDC access |
| Dangerous triggers | pull_request_target use of attacker-controlled PR head refs |
| Workflow supply chain | Unpinned actions, reusable workflows, and containers |
| Package execution | Added or changed install/prepare lifecycle scripts |
| Test evidence | High-risk source changes without matching test-file changes |
| Analysis integrity | Missing content, incomplete file lists, or report limits |
MergeWarden evaluates changes rather than re-reporting every pre-existing workflow condition. Findings show the rule, severity, path, canonical evidence, and a stable finding ID.
The same engine ships as an MCP server, for the person running the agent rather than the maintainer reviewing it. It answers one question — did this change stay inside the scope it was given — and emits the contract block the gate reads later.
{ "mcpServers": { "mergewarden": { "command": "npx", "args": ["-y", "mergewarden-mcp"] } } }No network, no token, no model call (details).
We scanned 2,204 recently merged AI-agent pull requests (Devin, Copilot coding agent, Codex, Claude Code, Cursor) on public repositories with the default policy. 0 of 2,204 declared their intended scope in any machine-checkable form. Of the 349 that touched workflows or package manifests, 12.9% escalated workflow permissions and 17.5% introduced unpinned actions; 3.9% changed agent control-plane files. Repositories with 10k+ stars showed roughly half the boundary-crossing rate of the long tail.
Every number reproduces from published queries: study methodology.
Already have an AI-contribution policy (Apache / OpenSSF / Bitcoin Core
lineage)? The ai-contribution-policy.yml
preset enforces its checkable clauses in one copy-paste — see
Enforce a policy. Or add
mergewarden.yml to the base branch when you are ready to tune behavior:
version: 1
mode: warn
agent_detection:
labels: [ai, agent, codex, claude]
branch_patterns: ["codex/**", "claude/**", "ai/**"]
github_actions:
checks:
permission_escalation: error
write_all: error
id_token_write: warn
pull_request_target_head: error
unpinned_action: warn
unpinned_reusable_workflow: warn
unpinned_container: warn
missing_permissions: warn
unknown_write_permission: warn
added_secret_reference: warn
workflow_deleted: warn
malformed_workflow: error
high_risk_paths:
authentication:
paths: ["src/auth/**"]
require_tests: ["test/auth/**"]
severity: errorFor an agent-authored PR, the body can declare its intended scope:
<!-- mergewarden-contract
version: 1
agent: codex
task: update session expiry handling
allowed_paths:
- src/auth/**
- test/auth/**
-->The contract is an untrusted PR declaration, not proof that the task or author is legitimate. The base-branch policy remains authoritative.
After reviewing a finding, maintainers can waive that exact evidence from the trusted base policy:
waivers:
- finding_id: agf_0123456789abcdef
reason: Approved OIDC release workflow
expires_at: "2026-09-30T00:00:00Z"Waived findings remain visible. Expired waivers reactivate the original finding
and emit policy/waiver-expired. Analysis-integrity findings cannot be waived.
See the complete configuration reference.
- Start with
mode: observeormode: warnandfail-on-block: false. - Review findings and tune per-check severity.
- Add narrow, expiring waivers only after human review.
- Move stable policy to
mode: block. - Set
fail-on-block: trueand require the check in branch protection.
Human report labels are deliberately distinct:
PASSED: analysis completed with no active warning/error findings.OBSERVED FINDINGS: observe mode found evidence without changing the pass decision.NEEDS REVIEW: warn mode requires a human decision.BLOCKED: block mode rejected active policy findings.ANALYSIS INCOMPLETE: MergeWarden could not make a trustworthy decision and fails closed.
The GitHub Action:
- reads PR metadata and file contents through GitHub APIs only
- loads configuration from the exact base commit
- never checks out or executes PR-controlled code
- never evaluates GitHub expressions from workflow YAML
- never calls an LLM during analysis
- limits API concurrency, content to 1 MiB per file side and 64 MiB per run, findings, and rendered report size
- records base/head SHAs, policy digest, analyzed file counts, and engine version
The pull-request files API has a 3,000-file maximum. MergeWarden compares the authoritative PR file count with the collected list and fails closed instead of presenting a partial pass.
Read the full security model and evidence model.
Workflow linters such as zizmor inspect workflow correctness and known misconfigurations. LLM reviewers apply semantic judgment. MergeWarden is the change-control layer between an AI-generated PR and merge: it asks whether the PR crossed repository-specific boundaries and records why. Use all three when appropriate; they solve different problems.
Every input and output, and the exact failure behavior, is in the Action reference.
pnpm install
pnpm test && pnpm typecheck && pnpm lint && pnpm build && pnpm format:checkEvery rule requires passing and failing fixtures, exact rule/severity/decision assertions, and a Markdown snapshot for user-facing findings. Start with a good first issue — each names its file, verify command, and done criteria — or the contribution guide.
- Documentation index — full list of guides and references
- Start here · Getting started · Configuration · Security model
Will it be noisy? It said nothing on 44 of 46 merged human PRs; both findings were correct. If it flags one your team decided was fine, open an issue with the scan output — the most useful bug report this project can get.
