# Project export: Pull Guard

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: OpenAI Build Week
- Tagline: Pull Guard fingerprints and deduplicates PRs, proves every claim with generated adversarial tests, checks optimal merge orders, and ranks the queue so maintainers review only what's safe to merge.
- Devpost: https://devpost.com/software/pull-guard
- GitHub: https://github.com/divagr18/Pull-Guard
- Demo: https://pullguard.divagr.com/
- Video: https://www.youtube.com/embed/G-lfle9lpuY?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 1 GitHub contributor(s) — div18 (152 commits)

## Devpost submission (written by the team)

### 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.

## README (from the GitHub repository)

# 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>
>
> **Source:** <https://github.com/divagr18/Pull-Guard>

## Try it first

For evaluation, use **[pullguard.divagr.com](https://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`,
  or `invalid`. 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](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

```text
                 ┌────────────────────────────┐
 GitHub App ───▶ │  FastAPI control plane      │ ───▶ React (Vite) frontend
 webhooks/OAuth  │  (pullguard/api)            │      (served from frontend_dist)
                 └──────────────┬─────────────┘
                                │ SQLAlchemy
                 ┌──────────────▼─────────────┐
                 │  PostgreSQL + pgvector      │
                 └──────────────┬─────────────┘
                                │ queued work
                 ┌──────────────▼─────────────┐
                 │  Analysis worker            │ ───▶ Model

[README truncated for size]

## Detected evidence (automated analysis)

Indexed codebase: 245 recognized source files, 2602 KB.
- CSS (language) — detected in the code
- FastAPI (technology) — detected in the code
- HTML (language) — detected in the code
- JavaScript (language) — detected in the code
- Python (language) — detected in the code
- React (technology) — detected in the code
- Tailwind CSS (technology) — detected in the code
- TypeScript (language) — detected in the code
- Node.js (technology) — claimed on Devpost, not found in the code
- OpenAI (technology) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (120 of 266)

```
.dockerignore
.env.example
.gcloudignore
.gitignore
alembic.ini
alembic/env.py
alembic/script.py.mako
alembic/versions/20260714_0001_control_plane.py
alembic/versions/20260714_0002_pr_version_state.py
alembic/versions/20260714_0003_outbox_events.py
alembic/versions/20260714_0004_phase2_fast_path.py
alembic/versions/20260714_0005_semantic_embeddings.py
alembic/versions/20260715_0006_maintainer_sessions.py
alembic/versions/20260715_0007_runner_jobs.py
alembic/versions/20260715_0008_proof_records.py
alembic/versions/20260715_0009_independent_reviews.py
alembic/versions/20260715_0010_compute_runner_callbacks.py
alembic/versions/20260716_0011_interaction_runs.py
alembic/versions/20260717_0012_composite_plans.py
alembic/versions/20260717_0013_composite_validations.py
alembic/versions/20260717_0014_bridge_proposals.py
alembic/versions/20260717_0015_composite_publications.py
alembic/versions/20260717_0016_queue_intelligence.py
alembic/versions/20260718_0017_flood_closures.py
alembic/versions/20260718_0018_evidence_policy_feedback.py
alembic/versions/20260718_0019_ast_edits_value_assessments.py
alembic/versions/20260718_0020_repo_baselines_graph_snapshots.py
alembic/versions/20260719_0019_decision_funnel.py
alembic/versions/20260719_0020_maintainer_pr_actions.py
alembic/versions/20260719_0021_runner_reconciliation_lease.py
alembic/versions/20260719_0022_review_policy.py
alembic/versions/20260720_0023_public_observe_repositories.py
alembic/versions/20260720_0024_merge_scanner_and_funnel_heads.py
alembic/versions/20260720_0025_live_progress_and_triage_ranking.py
alembic/versions/20260720_0026_contribution_dispositions.py
alembic/versions/20260720_0027_analysis_run_input_snapshots.py
alembic/versions/20260721_0028_flood_wave_evidence.py
alembic/versions/20260721_0030_rebase_flags.py
alembic/versions/20260721_0031_supersession_records.py
alembic/versions/20260721_0032_enforce_mode.py
alembic/versions/20260721_0033_analysis_work_items.py
alembic/versions/20260721_0034_code_intelligence.py
alembic/versions/20260721_0035_semantic_cluster_provenance.py
cloudbuild-runner-agent.yaml
CONTRIBUTING.md
DEPLOYMENT.md
docker-compose.yml
Dockerfile
FEATURES.md
frontend/.gitignore
frontend/eslint.config.js
frontend/index.html
frontend/package.json
frontend/README.md
frontend/src/App.tsx
frontend/src/components/AllPullRequests.tsx
frontend/src/components/CheckStatus.tsx
frontend/src/components/Clusters.tsx
frontend/src/components/CommandCenter.tsx
frontend/src/components/CompositeFlow.tsx
frontend/src/components/EnforceActivity.tsx
frontend/src/components/Evidence.tsx
frontend/src/components/FloodWave.tsx
frontend/src/components/FunnelAnalytics.tsx
frontend/src/components/Header.tsx
frontend/src/components/Inbox.tsx
frontend/src/components/LiveAnalysis.tsx
frontend/src/components/QueuePlan.tsx
frontend/src/components/ReviewPolicy.tsx
frontend/src/components/RunActivity.tsx
frontend/src/components/Sidebar.tsx
frontend/src/components/Supersessions.tsx
frontend/src/components/ui.tsx
frontend/src/components/ValueAssessment.tsx
frontend/src/components/Workspace.tsx
frontend/src/index.css
frontend/src/lib/copy.ts
frontend/src/lib/queueFilters.ts
frontend/src/lib/types.ts
frontend/src/main.tsx
frontend/src/theme.ts
frontend/tsconfig.app.json
frontend/tsconfig.json
frontend/tsconfig.node.json
frontend/vite.config.ts
LICENSE
pullguard/__init__.py
pullguard/analysis_errors.py
pullguard/analysis_progress.py
pullguard/analysis_work.py
pullguard/anomaly.py
pullguard/api/__init__.py
pullguard/api/contracts.py
pullguard/api/main.py
pullguard/artifacts.py
pullguard/ast_edits.py
pullguard/audit.py
pullguard/bridges.py
pullguard/candidate_tests.py
pullguard/ci_causality.py
pullguard/claim_verification.py
pullguard/claims.py
pullguard/code_intelligence.py
pullguard/composites.py
pullguard/contribution_categories.py
pullguard/db/__init__.py
pullguard/db/models.py
pullguard/db/session.py
pullguard/decision_buckets.py
pullguard/decision_engine.py
pullguard/decision_funnel.py
pullguard/docs_value.py
pullguard/domain.py
pullguard/enforce/__init__.py
pullguard/enforce/policy.py
pullguard/enforce/scheduler.py
pullguard/enforce/spam_classifier.py
pullguard/enforce/triggers.py
pullguard/evidence_packets.py
pullguard/fast_path.py
[146 more files omitted for size]
```

### Dependencies

- frontend/package.json: @eslint/js@^10.0.1, @tailwindcss/vite@^4.3.1, @types/node@^24.12.3, @types/react@^19.2.14, @types/react-dom@^19.2.3, @vitejs/plugin-react@^6.0.1, date-fns@^4.4.0, eslint@^10.3.0, eslint-plugin-react-hooks@^7.1.1, eslint-plugin-react-refresh@^0.5.2, globals@^17.6.0, lucide-react@^1.18.0, react@^19.2.6, react-dom@^19.2.6, recharts@^3.9.2, tailwindcss@^4.3.1, typescript@~6.0.2, typescript-eslint@^8.59.2, vite@^8.0.12
- pyproject.toml: alembic@>=1.14.0,<2.0.0, cryptography@>=43.0.0,<46.0.0, fastapi@>=0.115.0,<1.0.0, google-cloud-compute@>=1.18.0,<2.0.0, google-cloud-storage@>=2.18.0,<3.0.0, httpx@>=0.27.0,<0.28.0, pgvector@>=0.3.0,<0.5.0, psycopg[binary]@>=3.2.0,<4.0.0, pydantic@>=2.0.0,<3.0.0, python-dotenv@>=1.0.0,<2.0.0, PyYAML@>=6.0.0,<7.0.0, sqlalchemy@>=2.0.0,<3.0.0, tree-sitter@>=0.25.0,<0.26.0, tree-sitter-javascript@>=0.25.0,<0.26.0, tree-sitter-typescript@>=0.23.0,<0.24.0, uvicorn@>=0.30.0,<1.0.0
- requirements.txt: alembic@>=1.14.0,<2.0.0, cryptography@>=43.0.0,<46.0.0, fastapi@>=0.115.0,<1.0.0, google-cloud-compute@>=1.18.0,<2.0.0, google-cloud-storage@>=2.18.0,<3.0.0, httpx@>=0.27.0,<0.28.0, openai-codex, pgvector@>=0.3.0,<0.5.0, psycopg[binary]@>=3.2.0,<4.0.0, pydantic@>=2.0.0,<3.0.0, python-dotenv@>=1.0.0,<2.0.0, PyYAML@>=6.0.0,<7.0.0, sentence-transformers@>=5.0.0, sqlalchemy@>=2.0.0,<3.0.0, tree-sitter@>=0.25.0,<0.26.0, tree-sitter-javascript@>=0.25.0,<0.26.0, tree-sitter-typescript@>=0.23.0,<0.24.0, uvicorn@>=0.30.0,<1.0.0

### Recent commits (newest first)

- Expand Codex and Luna acknowledgements
- Update public repository references
- Document Codex and Luna usage
- Document agent-facing CLI roadmap
- Increase bounded repair output budget
- Polish public release documentation
- Prepare public release and streamline review flow
- Requeue validation after stale supersession skip
- Continue worker when durable analysis remains
- Recover analysis skipped by stale supersession
- Fix PowerShell deployment job arguments
- Fix queue recovery and add production deploy script
- Add ready to review queue filter
- Harden analysis pipeline and runner recovery
- complete queue control plane and review workflow
- Classify self-contained plugin contributions clearly
- Render completed no-test screening in workspace
- Explain completed empty claim screens
- Re-screen recategorized decision funnel members
- Recover queue refresh races and grounded claim gaps

## Key source files (fetched from GitHub, selected and truncated for size)

### SECURITY.md

```markdown
# Security policy

Do not report vulnerabilities in public issues. Email the repository owner or
use GitHub's private security-advisory flow with a concise description,
reproduction steps, and impact.

Runner isolation, artifact access, GitHub App permissions, OAuth/session
handling, and model-prompt data exposure are high-priority reports.

```

### CONTRIBUTING.md

```markdown
# Contributing to Pull Guard

Use Python 3.11 or 3.12 with [uv](https://docs.astral.sh/uv/), and Node 22 for
the frontend.

```bash
uv sync
uv run pytest
cd frontend && npm install && npm run build
```

Keep pull requests focused and include tests for changed behaviour. Never commit
secrets, production credentials, private runner artifacts, generated adversarial
tests, or raw model prompts.

```

### requirements.txt

```
sentence-transformers>=5.0.0
tree-sitter>=0.25.0,<0.26.0
tree-sitter-javascript>=0.25.0,<0.26.0
tree-sitter-typescript>=0.23.0,<0.24.0
pgvector>=0.3.0,<0.5.0
python-dotenv>=1.0.0,<2.0.0
PyYAML>=6.0.0,<7.0.0
openai-codex
pydantic>=2.0.0,<3.0.0
fastapi>=0.115.0,<1.0.0
uvicorn>=0.30.0,<1.0.0
# Starlette 0.36's TestClient still passes the removed ``app=`` argument.
# Keep local and CI installs on the compatible side until FastAPI/Starlette is
# upgraded together.
httpx>=0.27.0,<0.28.0
cryptography>=43.0.0,<46.0.0
sqlalchemy>=2.0.0,<3.0.0
psycopg[binary]>=3.2.0,<4.0.0
alembic>=1.14.0,<2.0.0
google-cloud-storage>=2.18.0,<3.0.0
google-cloud-compute>=1.18.0,<2.0.0

```

### Dockerfile

```
# syntax=docker/dockerfile:1.7
FROM node:22-alpine AS frontend-build
WORKDIR /web
COPY frontend/package.json frontend/package-lock.json ./
RUN npm ci
COPY frontend/ ./
RUN npm run build

FROM python:3.12-slim
COPY --from=ghcr.io/astral-sh/uv:0.11.28 /uv /uvx /bin/
WORKDIR /app
ENV PYTHONDONTWRITEBYTECODE=1 PYTHONUNBUFFERED=1 UV_COMPILE_BYTECODE=1 UV_LINK_MODE=copy UV_NO_DEV=1 UV_PYTHON_DOWNLOADS=0 PATH="/app/.venv/bin:$PATH"
COPY pyproject.toml uv.lock ./
# gcloud builds submit currently invokes a Docker driver without BuildKit cache
# mounts. uv still resolves and installs substantially faster than pip; its
# temporary cache is discarded so it does not inflate the runtime image.
RUN uv sync --locked --no-install-project --no-cache
COPY . ./
COPY --from=frontend-build /web/dist ./frontend_dist
CMD ["uvicorn", "pullguard.api.main:app", "--host", "0.0.0.0", "--port", "8080"]

```

### pyproject.toml

```
[project]
name = "pullguard"
version = "0.1.0"
description = "Evidence-backed pull request triage control plane"
requires-python = ">=3.11,<3.13"
dependencies = [
    "alembic>=1.14.0,<2.0.0",
    "cryptography>=43.0.0,<46.0.0",
    "fastapi>=0.115.0,<1.0.0",
    "google-cloud-storage>=2.18.0,<3.0.0",
    "google-cloud-compute>=1.18.0,<2.0.0",
    "httpx>=0.27.0,<0.28.0",
    "pgvector>=0.3.0,<0.5.0",
    "psycopg[binary]>=3.2.0,<4.0.0",
    "pydantic>=2.0.0,<3.0.0",
    "python-dotenv>=1.0.0,<2.0.0",
    "PyYAML>=6.0.0,<7.0.0",
    "sqlalchemy>=2.0.0,<3.0.0",
    "tree-sitter>=0.25.0,<0.26.0",
    "tree-sitter-javascript>=0.25.0,<0.26.0",
    "tree-sitter-typescript>=0.23.0,<0.24.0",
    "uvicorn>=0.30.0,<1.0.0",
]

[tool.uv]
package = false

[dependency-groups]
dev = [
    "pytest>=9.0.0,<10.0.0",
]

[tool.pytest.ini_options]
testpaths = ["tests"]
norecursedirs = ["fixtures"]
pythonpath = ["."]

```

### docker-compose.yml

```yaml
services:
  postgres:
    image: postgres:16-alpine
    environment:
      POSTGRES_DB: pullguard
      POSTGRES_USER: pullguard
      POSTGRES_PASSWORD: pullguard-dev-only
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U pullguard -d pullguard"]
      interval: 5s
      timeout: 3s
      retries: 20
    volumes: ["postgres-data:/var/lib/postgresql/data"]
  migrate:
    build: .
    environment: &pullguard-env
      PULL_GUARD_ENV: development
      PULL_GUARD_DATABASE_URL: postgresql+psycopg://pullguard:pullguard-dev-only@postgres:5432/pullguard
      PULL_GUARD_ARTIFACT_ROOT: /artifacts
      PULL_GUARD_GITHUB_WEBHOOK_SECRET: ${PULL_GUARD_GITHUB_WEBHOOK_SECRET:?set in .env}
      PULL_GUARD_GITHUB_APP_ID: ${PULL_GUARD_GITHUB_APP_ID:?set in .env}
      PULL_GUARD_GITHUB_APP_PRIVATE_KEY: ${PULL_GUARD_GITHUB_APP_PRIVATE_KEY:?set in .env}
    command: ["alembic", "upgrade", "head"]
    depends_on:
      postgres: { condition: service_healthy }
  api:
    build: .
    ports: ["8000:8080"]
    environment: *pullguard-env
    volumes: ["./.pullguard/artifacts:/artifacts"]
    depends_on:
      migrate: { condition: service_completed_successfully }
  worker:
    build: .
    environment: *pullguard-env
    volumes: ["./.pullguard/artifacts:/artifacts"]
    command: ["python", "-m", "pullguard.orchestrator.worker"]
    depends_on:
      migrate: { condition: service_completed_successfully }
volumes:
  postgres-data:

```

### frontend/package.json

```
{
  "name": "frontend",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "tsc -b && vite build",
    "lint": "eslint .",
    "preview": "vite preview"
  },
  "dependencies": {
    "@tailwindcss/vite": "^4.3.1",
    "date-fns": "^4.4.0",
    "lucide-react": "^1.18.0",
    "react": "^19.2.6",
    "react-dom": "^19.2.6",
    "recharts": "^3.9.2",
    "tailwindcss": "^4.3.1"
  },
  "devDependencies": {
    "@eslint/js": "^10.0.1",
    "@types/node": "^24.12.3",
    "@types/react": "^19.2.14",
    "@types/react-dom": "^19.2.3",
    "@vitejs/plugin-react": "^6.0.1",
    "eslint": "^10.3.0",
    "eslint-plugin-react-hooks": "^7.1.1",
    "eslint-plugin-react-refresh": "^0.5.2",
    "globals": "^17.6.0",
    "typescript": "~6.0.2",
    "typescript-eslint": "^8.59.2",
    "vite": "^8.0.12"
  }
}

```

### tests/fixtures/runner_node/base/package.json

```
{"scripts":{"test":"node --eval \"if (2 + 2 !== 4) process.exit(1); console.log('node-base-ok')\""}}

```

### tests/fixtures/runner_node/pull_request/package.json

```
{"scripts":{"test":"node --eval \"if ('pull guard'.replace(' ', '-') !== 'pull-guard') process.exit(1); console.log('node-pr-ok')\""}}

```

### frontend/src/main.tsx

```typescript
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App.tsx'
import { applyTheme, getInitialTheme } from './theme'

// Set the theme before first paint so the app never flashes the wrong palette.
applyTheme(getInitialTheme())

createRoot(document.getElementById('root')!).render(
  <StrictMode>
    <App />
  </StrictMode>,
)

```

[230 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]