Project Info
Inspiration
AI coding agents have made code cheap to produce. A repository that used to get a dozen pull requests a week now gets hundreds. Many of them are near-identical attempts at the same fix, many are automated dependency floods, and most arrive with a confident description that claims more than the diff actually does. Maintainers now spend most of their review time figuring out which PRs are even worth reading. The bottleneck in open source is no longer writing code. It is verifying it. That is the problem Pull Guard addresses: a pull request description is a claim, not a fact, so we built a system that checks the claims before a human has to.
What it does
Pull Guard sits between a pull request and a merge. It fingerprints every incoming PR, deduplicates overlapping attempts into a single comparison, detects flood waves of near-identical submissions, and filters out low-quality or policy-violating work early. For the PRs that survive screening, Codex generates adversarial tests based on the claims in the description, and those tests run in disposable sandboxes and get graded. Pull Guard also tests PRs in pairs to find the safest merge order. The result is a funnel: $$500 \text{ open PRs} \;\rightarrow\; 60 \text{ candidates} \;\rightarrow\; 15 \text{ review targets} \;\rightarrow\; 5 \text{ decisions}$$ Every final decision (review first, safe to merge, request changes, superseded, or a tested merge order) comes with its evidence attached. Pull Guard never merges or closes anything by itself. It recommends, humans decide.
How we built it
Fingerprinting and immutable versions. Every PR is reduced to a canonical, patch-complete fingerprint: a SHA-256 hash over the full patch content of every changed file, plus filenames and renames. These fingerprints are the identity layer for the whole system. They drive cache keys and idempotency, so re-analyzing the same patch is free, and a force-push that changes one line anywhere produces a new identity and invalidates nothing else. Each analyzed state of a PR is stored as an immutable version record, which means every piece of evidence we produce can be traced back to the exact code it was produced from. Clustering and deduplication. Clustering works on two levels. First, structural overlap: files, symbols, and patch hashes are compared across the queue to find PRs touching the same subsystem. Second, semantic intent: the GPT-5.6 family reads each diff and its description and assesses whether two PRs are attempting the same change, even when the implementations look nothing alike. On top of that sits a supersession detector that classifies redundant pairs as exact duplicates, subsumed implementations (one PR does everything the other does, plus more), weaker test coverage on the same target, or partially obsoleted subsets. Detection is sticky: the evidence hash is recorded with the pair, so a force-push does not silently clear a redundancy finding, and the record auto-resolves when the canonical PR merges. Nothing is auto-closed; the maintainer gets one comparison instead of seven tabs. Claim extraction and adversarial tests. This is where Codex does the heavy lifting. First, a claim-extraction pass reads the immutable PR context and pulls out every verifiable claim the description makes, with strict provenance: each claim has to cite the source it came from, and a grounded retry pass handles vague or ungrounded output. Then, for each selected claim, Codex generates a candidate adversarial test designed to break that claim. Generated code never touches the control-plane checkout. It is stored as an immutable artifact and only applied inside an isolated runner worktree after the patch and command pass validation. Runners are disposable Docker containers started with --network none, empty required environments, and restricted mounts, and every execution is followed by an isolation audit that verifies the hardening was actually applied rather than merely planned. Grading is strict: a proof only counts if the test fails on the base code and passes on the PR, re-run across fresh sandboxes to catch flakiness. This is the part of the system that the GPT-5.6 family makes economically possible. Writing tests like this used to be senior-engineer work done under time pressure. Now it is seconds per claim, at a cost low enough to run against an entire queue, and the tests are consistently better targeted than what we wrote by hand. Merge order and interaction testing. Merging is sequencing, so Pull Guard treats the queue as a graph. For pairs of related PRs it schedules durable, ordered interaction runs: apply A then B, and B then A, in isolated worktrees, and record a compatibility verdict, the conflict stage if one fails, verification exit codes, and timing. The maintainer-facing Queue Plan is a read-only projection of that graph, so the safest path to main is visible at a glance. When a cluster genuinely needs changes from more than one PR, composite runs validate selected immutable versions together and repair real conflicts mechanically, again without writing to GitHub. Rebase flags surface stale PRs that will rot before they merge. Because pairwise testing grows quadratically, the decision funnel deliberately spends this scarce proof capacity only where the result can change a maintainer's decision. Control plane: Python/FastAPI backend, durable analysis queue with outbox events, runner recovery, stage-level progress tracking. Frontend: React + Vite dashboard with a Command Center, cluster comparison, decision workspace, Queue Plan, and funnel analytics. Challenges Runner reliability: sandboxes crash mid-test. We built queue recovery and retry semantics so a failed runner never silently loses evidence. Test quality: early generated tests were too easy to pass. Requiring fail-on-base and pass-on-PR, across fresh runs, is what makes a proof a proof. Interaction explosion: pairwise merge-order testing is $O(n^2)$; the funnel had to shrink $n$ aggressively before pairs were ever considered. Trust boundaries: every action stays a recommendation. Pull Guard never writes to GitHub. What’s next We plan to wrap Pull Guard’s verification pipeline in an agent-facing CLI. Coding agents will be able to run it against their own patches before opening a pull request, receive structured feedback on unsupported claims, failing adversarial tests, redundant implementations, and merge interactions, then revise their work and retry without waiting for human review. The goal is to turn Pull Guard into a verification loop agents can use autonomously: generate, prove, repair, and only submit once the patch meets the repository’s standards. Machine-readable output, stable exit codes, immutable run references, and configurable quality gates will make it usable from agent harnesses and CI systems without depending on the dashboard. Actions such as merging or closing pull requests will remain separately permissioned.
What we learned
When generation is cheap, verification becomes the expensive part, and verification only scales when it is staged: cheap filters first, deep proofs last. We also learned how much the frontier models changed what a small team can build. The GPT-5.6 family does real analytical work in this system: reading diffs, extracting claims with provenance, judging semantic intent, and writing adversarial tests that survive strict grading. Codex is not a convenience feature in Pull Guard; it is the reason per-PR proof is feasible at all. Finally, maintainers only trust an AI review system when the trust is structural: immutable versions, reproducible runs, visible evidence, and a strict rule that the system advises but never acts.
Pull Guard
A maintainer workspace for understanding a busy pull-request queue before it turns into a review bottleneck.
Evaluate it at the live demo: https://pullguard.divagr.com
Try it first
For evaluation, use pullguard.divagr.com. It is already connected to a prepared demo repository and shows the complete review flow. Local setup requires a GitHub App, OAuth credentials, a database, model access, and disposable runner infrastructure, so it is better suited to development than a quick product evaluation.
Pull Guard captures each PR at an exact base and head commit, reads the changed code and nearby context, groups genuinely related work, and gives maintainers a clear next step. It can run existing repository checks and, where a focused behaviour warrants it, add a private disposable verification layer. It never merges or changes GitHub on its own.
The problem
Coding agents made it cheap to generate plausible-looking pull requests in minutes. A repository that once received a handful of thoughtful PRs a week can now receive dozens of shallow, overlapping, or subtly incorrect changes — many describing the same idea, some hiding large unrelated churn, others passing their own tests while failing outside the narrow path the author checked.
The result is not just a bigger queue; it is a collapse in signal. Pull Guard exists to protect maintainer attention: turn a queue of 500 unknown pull requests into five trustworthy decisions.
How it works
Pull Guard operates around three actions.
1. Understand
- Change fingerprinting — fingerprints the actual transformation (files, symbols, imports, behaviour hints, test/protected/generated paths, dependencies, CI state), not just titles.
- Duplicate & flood detection — collapses overlapping submissions into one review target and detects bursts of shallow, highly similar PRs.
- Patch–claim alignment — flags where the patch does not match what the description promises.
- Contribution classification — distinguishes runtime, test-only, configuration, documentation, and extension work so expensive checks are used where they help.
2. Review and validate
- Source-grounded code review — traces changed symbols, call sites, tests, manifests, and CI configuration before retaining a finding.
- Repository validation — selects relevant existing tests, lint, type, or build checks and records what actually ran.
- Claim extraction — identifies a bounded observable behaviour only when a private proof would add useful information.
- Adversarial test generation — generates hidden tests designed to expose incomplete or misleading implementations (boundary, concurrency, malformed input, backwards-compatibility, and more).
- Self-verification — every generated test is checked against a clean baseline, re-run for reproducibility, validated against a deliberate regression (mutation), checked for isolation, and independently reviewed.
- Evidence grading — tests are graded
decisive,supporting,uncertain, orinvalid. Only validated evidence can justify blocking a PR. - Proof matrix — three fresh runs each for the base revision, the PR, and an intentionally broken copy, so a maintainer can see the test separates them.
3. Compile
- Queue-level analysis — detects dependencies, semantic conflicts Git cannot see, and changes made redundant by stronger submissions.
- Merge readiness — an evidence-led ledger (behaviour proof, patch alignment, CI, test coverage, change scope) with a plain-language recommendation.
- Combined-change checks — maintainers can test selected immutable PR versions together before considering a composite change.
Decision buckets
Each pull request lands in one maintainer-facing bucket, always with supporting evidence:
| Bucket | Meaning |
|---|---|
review_first | Worth human review ahead of the rest of the queue. |
safe_to_merge | Evidence supports merging; the maintainer still decides. |
request_changes | A validated problem needs fixing first. |
close_superseded | An equivalent or stronger change already exists. |
low_value_or_policy_violating | Noise, churn, or a contribution-rule violation. |
human_judgment | A product decision only a maintainer can make. |
Key features
- GitHub App intake — webhook ingestion with signature validation and duplicate-delivery protection; immutable PR versions bound to exact base/head SHAs (force-pushed versions become stale rather than silently changing evidence).
- Maintainer sign-in — GitHub OAuth, encrypted server-side token storage, and repository permission checks.
- Conservative related-work clustering — exact-patch duplicates are authoritative; optional semantic embedding retrieval precedes structural selection.
- Secure execution — rootless, network-disabled verification containers with read-only root, dropped capabilities, no-new-privileges, bounded resources, and redacted output; SHA-verified artifacts.
- Disposable Compute Engine runners (optional) — untrusted code runs on short-lived VMs that receive only scoped signed URLs; no GitHub, model, database, or ambient cloud credentials ever reach repository code.
- Durable audit trail — evidence, runner, model-invocation, and audit records are persisted.
See FEATURES.md for the available and planned capabilities.
Built with Codex and GPT-5.6 Luna
Codex accelerated Pull Guard's build substantially. It was used to explore the early codebase, turn a hackathon prototype into a deployable control plane, and iterate quickly on the product, runner, and maintainer experience. It was also especially effective at the otherwise slow operational work: shaping Cloud Run services and jobs, Cloud Build images, Cloud SQL migrations, Secret Manager configuration, runner artifacts, deployment scripts, and production debugging. That let the project move from local experiments to a live hosted demo without turning deployment work into a separate multi-day project.
In the product itself, GPT-5.6 Luna is the core intelligence layer. Luna performs the source-grounded code exploration, review and critique passes, then helps derive focused validation and private-proof candidates from the immutable PR context. Luna has been a strong fit for this workload: it provides the depth needed to follow unfamiliar code and reason across a diff, while keeping the price-to-performance practical enough to analyse a real queue rather than only hand-picked pull requests. Its output stays advisory: Pull Guard retains source citations, separates model analysis from executed evidence, and leaves GitHub decisions with the maintainer.
Roadmap: agent-facing CLI
The web workspace is the current maintainer surface. Next, Pull Guard will wrap the same immutable evidence and queue APIs in a CLI so coding agents and automation can inspect a repository, ask for the next review decision, retrieve evidence packets, and request a reanalysis without driving a browser. The CLI will preserve the same safety boundary: it can read, prepare, and explain; any GitHub write remains an explicit maintainer-authorized action.
Architecture
┌────────────────────────────┐
GitHub App ───▶ │ FastAPI control plane │ ───▶ React (Vite) frontend
webhooks/OAuth │ (pullguard/api) │ (served from frontend_dist)
└──────────────┬─────────────┘
│ SQLAlchemy
┌──────────────▼─────────────┐
│ PostgreSQL + pgvector │
└──────────────┬─────────────┘
│ queued work
┌──────────────▼─────────────┐
│ Analysis worker │ ───▶ Model stages (claims, tests,
│ (pullguard.orchestrator) │ review) + disposable runners
└─────────────────────────────┘
- Control plane — FastAPI app (
pullguard/api) serving the REST API and the built single-page frontend. - Worker —
python -m pullguard.orchestrator.worker; processes queued model and runner stages. - Database — PostgreSQL with
pgvectorfor semantic embeddings; schema managed by Alembic (alembic/). - Runners — local Docker executor for development; optional hardened Compute Engine VMs for production (see RUNNER_OPERATIONS.md).
Quick start (local)
Prerequisites: Docker (with Compose) for the full stack, or uv + Node 22 for running services manually.
Option A — full stack with Docker Compose
cp .env.example .env # fill in the required GitHub values
docker compose up --build
This starts Postgres, runs migrations, and launches the API and worker. The app is served at http://localhost:8000 (the API serves the built frontend).
Option B — run services manually
# Backend
uv sync
uv run alembic upgrade head
uv run uvicorn pullguard.api.main:app --host 0.0.0.0 --port 8000
# Frontend dev server (proxies /api/v1 and /auth to http://127.0.0.1:8000)
cd frontend
npm install
npm run dev # http://localhost:5173
Set PULL_GUARD_API_ORIGIN to point the Vite dev proxy at a different control
plane origin.
Deployment
Production runs on Google Cloud Run (Cloud SQL for Postgres, Artifact Registry for images, Secret Manager for secrets). See DEPLOYMENT.md for the full guide, including one-time GCP setup, the deploy script, database migrations, and the optional disposable Compute Engine runner.
The public instance of this repository is deployed at https://pullguard.divagr.com.
Configuration
Configuration is environment-driven (see .env.example). The
most important variables:
| Variable | Purpose |
|---|---|
PULL_GUARD_DATABASE_URL | PostgreSQL connection string. |
PULL_GUARD_GITHUB_APP_ID / ..._PRIVATE_KEY | GitHub App identity for webhooks. |
PULL_GUARD_GITHUB_WEBHOOK_SECRET | Validates incoming webhook signatures. |
PULL_GUARD_GITHUB_OAUTH_CLIENT_ID / ..._SECRET | Maintainer OAuth sign-in. |
PULL_GUARD_SESSION_SECRET | Derives at-rest OAuth-token encryption (≥32 chars). |
PULL_GUARD_OPENAI_API_KEY | Model stages (claims, tests, review). |
PULL_GUARD_RUNNER_BACKEND | local (default) or compute for disposable VMs. |
Development & tests
# Backend tests
uv run pytest # or: python -m unittest discover -s tests
# Frontend checks
cd frontend
npm run lint
npm run build
Project layout
pullguard/ FastAPI control plane, worker, and analysis pipeline
api/ HTTP API + response contracts
db/ SQLAlchemy models and session helpers
orchestrator/ Worker, queue, and outbox
runner/ Disposable runner integration
github/ GitHub App, OAuth, and ingestion
alembic/ Database migrations
frontend/ React + Vite single-page app
scripts/ Deployment and operations scripts (see scripts/README.md)
triage.py Legacy standalone CLI
The deployed product (this repository's pullguard/ control plane and
frontend/) supersedes the CLI for live, multi-repository use.
Safety & limits
- Advisory by default — Pull Guard never approves, merges, closes, or comments on a PR automatically. Write-side actions require explicit, typed maintainer approval and are fully logged.
- Untrusted code is sandboxed — generated tests and PR code run in disposable, network-disabled, resource-bounded environments that never receive repository, cloud, or model credentials.
- Missing evidence stays visible — an incomplete check is shown as incomplete, never fabricated as passing.
Contributing and licence
Pull Guard is released under the MIT License. Contributions are welcome; see CONTRIBUTING.md. For sensitive reports, follow SECURITY.md.
Analysis
View
Metric
- 152
Figures cover GitHub contributors during the hackathon window. A co-authored commit counts in full for each author, so per-member totals add up to more than the whole-team figures.
Technology
- CSSIn code
- FastAPIIn code
- HTMLIn code
- JavaScriptIn code
- PythonIn code
- ReactIn code
- Tailwind CSSIn code
- TypeScriptIn code
- Node.jsClaimed
- OpenAIClaimed
8 of 10 appear in the indexed code. 2 claimed on Devpost could not be matched to code, which may simply mean the tool leaves no trace in the repository.
AI coding agents
No AI coding agent signals were found in this repository.
Detected from committed agent config files and commit authorship. Absence of a signal is not proof an agent was unused.
Codebase size
Source size
2.5 MB
Source files
245
Counts recognized source files only; vendored directories, binaries and lockfiles are excluded, so this is smaller than the repository on disk.
Repository
divagr18/Pull-Guard
273 files · 2.9 MB · @ 8762d4d
Structure
Interface
21 files · 8%Screens, components and styles rendered to the user.
API & routing
3 files · 1%Request entry points: routes, handlers and controllers.
Application logic
137 files · 50%Domain rules, services and shared utilities.
+4 moreData & schema
3 files · 1%Schema definitions, migrations and data access.
Supporting
Layers are inferred from where files sit in the tree, not from reading the code. A project that names its directories unconventionally will read oddly here — open the file browser to check anything the diagram implies.
Languages
- Python80%
- TypeScript10%
- CSS5%
- YAML3%
- Markdown1%
- Shell0%
- Other (2)0%
Share of indexed source by file size. Binary and vendored files are excluded.
Dependencies
frontend/package.json
npm · 19- @tailwindcss/vite
- date-fns
- lucide-react
- react
- react-dom
- recharts
- tailwindcss
- +12 more
requirements.txt
pypi · 18- alembic
- cryptography
- fastapi
- google-cloud-compute
- google-cloud-storage
- httpx
- openai-codex
- pgvector
- psycopg[binary]
- pydantic
- python-dotenv
- PyYAML
- sentence-transformers
- sqlalchemy
- tree-sitter
- tree-sitter-javascript
- tree-sitter-typescript
- uvicorn
pyproject.toml
pypi · 16- alembic
- cryptography
- fastapi
- google-cloud-compute
- google-cloud-storage
- httpx
- pgvector
- psycopg[binary]
- pydantic
- python-dotenv
- PyYAML
- sqlalchemy
- tree-sitter
- tree-sitter-javascript
- tree-sitter-typescript
- uvicorn
Declared in the repository’s manifests at the indexed commit. A declared package is not proof it is used, and runtime dependencies are listed first.
This project’s features have not been analysed yet.
Export this project's context (description, README, evidence, key source files) to chat with an AI agent elsewhere.