{"slug": "what-if-pull-requests-had-an-explain-command", "title": "What If Pull Requests Had an Explain Command?", "summary": "PR Explain, an AI-assisted service that turns a GitHub Pull Request into an evidence-backed explanation, performs deterministic code analysis to build a structured change graph, collect evidence, and determine potential impact before an AI model narrates the findings. The service analyzes changed files, symbols, imports, call and symbol relationships, line-level evidence, and revision-to-revision changes, then posts the explanation back to the Pull Request as a GitHub comment. Its pipeline runs from a GitHub App webhook through a FastAPI API to a background worker that fetches the repository snapshot, analyzes the diff, parses symbols, builds the change graph, collects evidence, analyzes impact, and persists claims into an explanation packet.", "body_md": "**PR Explain** is an AI-assisted Pull Request explanation service that turns a GitHub Pull Request into an evidence-backed explanation of what changed, why it changed, and what parts of the codebase may be affected.\n\nThe core idea is simple:\n\n**PR Explain is like `SQL EXPLAIN` for Pull Requests.**\n\nInstead of asking an AI model to blindly read a Pull Request and guess what happened, PR Explain first performs deterministic code analysis, builds a structured change graph, collects evidence, and determines potential impact. The AI model then turns those facts into a human-readable explanation.\n\nIf you've used `EXPLAIN` or `EXPLAIN ANALYZE` in SQL, the easiest way to understand PR Explain is to think of it as the same concept applied to code changes.\n\nFor example:\n\n```\nEXPLAIN\nSELECT *\nFROM users\nWHERE email = 'alice@example.com';\n```\n\nThe database doesn't ask an AI model to guess what the query does.\n\nInstead, the database analyzes the query and produces a structured execution plan describing things such as:\n\n- Which tables are accessed\n- Which indexes may be used\n- How operations are connected\n- The expected execution strategy\n- Potentially expensive operations\n\nPR Explain applies the same philosophy to Pull Requests.\n\n```\nSQL Query\n    │\n    ▼\nQuery Planner\n    │\n    ▼\nExecution Plan\n    │\n    ▼\nHuman Understanding\nPull Request\n    │\n    ▼\nCode Change Analyzer\n    │\n    ▼\nChange Graph + Evidence + Impact\n    │\n    ▼\nAI Explanation\n    │\n    ▼\nHuman Understanding\n```\n\nThe important distinction is:\n\n**The AI is not the analyzer.**\n\nThe deterministic analysis pipeline is responsible for discovering what actually changed.\n\nThe AI model is responsible for explaining those discovered facts.\n\nA generic AI code-review system might look like:\n\n```\nPull Request\n     │\n     ▼\n    LLM\n     │\n     ▼\n\"Here's what I think changed...\"\n```\n\nPR Explain instead follows:\n\n```\nPull Request\n     │\n     ▼\nDeterministic Analysis\n     │\n     ├── Changed files\n     ├── Changed symbols\n     ├── Imports\n     ├── Calls\n     ├── Relationships\n     ├── Evidence\n     └── Impact\n              │\n              ▼\n      Explanation Packet\n              │\n              ▼\n          AI Model\n              │\n              ▼\n     Human-readable explanation\n```\n\nThis makes the AI model primarily a **narrator**, rather than the source of truth.\n\nFor example, if a Pull Request changes:\n\n```\nPaymentService.process_payment()\n        │\n        ├── PaymentRepository.create()\n        │\n        └── EventPublisher.publish()\n```\n\nPR Explain first determines those relationships from the repository and Pull Request.\n\nThe AI then receives the resulting evidence and can explain:\n\nThis change modifies payment processing and affects both persistence and event publishing. The new behavior therefore has potential impact on the payment data path as well as downstream consumers of the payment event.\n\nThe model is explaining relationships that the analysis pipeline has already established rather than inventing them.\n\nPR Explain analyzes Pull Requests and produces an explanation based on the actual repository contents and changes.\n\nThe analysis includes:\n\n- Changed files\n- Changed symbols\n- Functions and classes\n- Imports\n- Call relationships\n- Symbol relationships\n- Relevant files outside the diff\n- Line-level evidence\n- Potential impact\n- Claims about the change\n- Revision-to-revision changes\n\nThe resulting explanation can be viewed in the web application and posted back to the Pull Request as a GitHub comment.\n\nA Pull Request moves through the following pipeline:\n\n```\nGitHub Pull Request\n        │\n        ▼\nGitHub App Webhook\n        │\n        ▼\nFastAPI API\n        │\n        ▼\nBackground Job\n        │\n        ▼\nWorker\n        │\n        ├── Fetch repository snapshot\n        ├── Analyze diff\n        ├── Parse symbols\n        ├── Build change graph\n        ├── Collect evidence\n        ├── Analyze impact\n        └── Persist claims\n                │\n                ▼\n        Explanation Packet\n                │\n                ▼\n           AI Provider\n                │\n                ├───────────────┐\n                │               │\n                ▼               ▼\n             Ollama           OpenAI\n             Local            Production\n                │               │\n                └───────┬───────┘\n                        ▼\n                  Explanation\n                        │\n                        ▼\n                   PostgreSQL\n                    /       \\\n                   /         \\\n                  ▼           ▼\n             React UI     GitHub API\n                              │\n                              ▼\n                        PR Comment\n```\n\nPR Explain follows:\n\n**Deterministic analysis first. AI narration second.**\n\nThe deterministic pipeline creates a structured representation of the change.\n\nThe AI receives a bounded explanation packet containing those facts.\n\nThis separation provides several benefits:\n\n- Better grounding\n- Lower risk of hallucinated files or functions\n- Repeatable analysis\n- Inspectable evidence\n- Local/private AI inference\n- Ability to change AI providers without changing the analysis pipeline\n- AI failures do not destroy deterministic analysis\n\nThe GitHub App is the entry point into PR Explain.\n\nIt receives Pull Request webhook events and gives PR Explain permission to read repository contents and update Pull Request comments.\n\n| Permission | Access | Purpose | \n|---|---|---|\n| Metadata | Read | Repository and installation metadata | \n| Contents | Read | Read repository contents | \n| Pull requests | Write | Create/update PR explanation comments | \n\nPR Explain does not require GitHub Checks permissions and does not submit a Pull Request review score.\n\nYou need to create a GitHub App before PR Explain can receive Pull Request events.\n\nGitHub's official documentation:\n\nGitHub Apps Quickstart\n\nGo to:\n\n```\nGitHub\n  → Settings\n  → Developer settings\n  → GitHub Apps\n  → New GitHub App\n```\n\nFor an organization-owned application:\n\n```\nOrganization\n  → Settings\n  → Developer settings\n  → GitHub Apps\n  → New GitHub App\n```\n\nUse:\n\n```\nGitHub App name: PR Explain\n```\n\nConfigure the homepage URL to point to your deployed application or repository.\n\nEnable:\n\n```\nActive: Yes\n```\n\nSet the webhook URL to:\n\n```\nhttps://<your-public-host>/api/webhooks/github\n```\n\nFor local development, GitHub cannot directly reach `localhost`.\n\nUse a tunnel such as Cloudflare Tunnel:\n\n```\ncloudflared tunnel --url http://localhost:8000\n```\n\nIf the tunnel provides:\n\n```\nhttps://example.trycloudflare.com\n```\n\nconfigure:\n\n```\nhttps://example.trycloudflare.com/api/webhooks/github\n```\n\nas the GitHub App webhook URL.\n\nOnly the FastAPI API needs to be exposed.\n\n**Do not expose Ollama publicly.**\n\nGenerate a strong random secret and configure the same value in:\n\n```\nGITHUB_WEBHOOK_SECRET=your-secret\n```\n\nThe webhook secret is used to validate that incoming webhook requests originate from GitHub.\n\nNever commit the webhook secret to Git.\n\nSubscribe to:\n\n- `installation`\n- `installation_repositories`\n- `pull_request`\n\nFor Pull Requests, PR Explain handles:\n\n- `opened`\n- `reopened`\n- `synchronize`\n- `closed`\n\nThe `closed` event is received but does not trigger analysis.\n\nFrom the GitHub App settings:\n\n```\nPrivate keys\n  → Generate a private key\n```\n\nGitHub will download a `.pem` file.\n\nFor example:\n\n```\npr-explain.private-key.pem\n```\n\nKeep this file secure.\n\nGitHub documentation:\n\nManaging private keys for GitHub Apps\n\nConfigure:\n\n```\nGITHUB_APP_ID=123456\n\nGITHUB_APP_PRIVATE_KEY_FILE=/absolute/path/to/pr-explain.private-key.pem\n\nGITHUB_WEBHOOK_SECRET=your-webhook-secret\n```\n\nWhen using Docker Compose, the private key is mounted into the containers as:\n\n```\n/run/secrets/github-app.pem\n```\n\nThe application reads the private key from the mounted file.\n\nKeep this empty when using the file-based configuration:\n\n```\nGITHUB_APP_PRIVATE_KEY=\n```\n\nAfter creating the GitHub App:\n\n1. Open the GitHub App settings.\n2. Select **Install App** .\n3. Select the GitHub account or organization.\n4. Choose the repositories where PR Explain should operate.\n5. Complete the installation.\n\nFor security, use **Only select repositories** if PR Explain should only operate on specific repositories.\n\nInstall:\n\n- Docker Desktop\n- Git\n- GitHub account\n- GitHub App\n- Cloudflare Tunnel or another webhook tunnel\n\nDocker Compose runs the local application stack.\n\nThe local stack contains:\n\n```\nPostgreSQL\nOllama\nOllama model downloader\nFastAPI API\nBackground worker\nReact frontend\ngit clone https://github.com/rajatrao/PR_explain.git\n\ncd PR_explain\n```\n\nCreate your environment file:\n\n```\ncp .env.example .env\n```\n\nAt minimum, configure:\n\n```\nGITHUB_APP_ID=YOUR_APP_ID\n\nGITHUB_APP_PRIVATE_KEY_FILE=/absolute/path/to/pr-explain.private-key.pem\n\nGITHUB_WEBHOOK_SECRET=YOUR_WEBHOOK_SECRET\n```\n\nLocal development uses Ollama running in Docker.\n\nThe default provider is:\n\n```\nLLM_PROVIDER=ollama\n```\n\nThe default configuration is:\n\n```\nLLM_PROVIDER=ollama\n\nOLLAMA_BASE_URL=http://127.0.0.1:11434\n\nOLLAMA_MODEL=qwen3-coder:30b\n\nOLLAMA_TIMEOUT_MS=180000\n```\n\nThe first Docker Compose startup downloads the configured model into a persistent Ollama volume.\n\nThis allows the entire AI workflow to run locally without sending the explanation packet to a cloud AI provider.\n\nRun:\n\n```\ndocker compose up\n```\n\nThe local services are:\n\n| Service | Address | \n|---|---|\n| React frontend | `http://localhost:5173` | \n| FastAPI API | `http://localhost:8000` | \n| Ollama | `http://localhost:11434` | \n| PostgreSQL | `localhost:5432` | \n\nThe first startup may take some time because the Ollama model needs to be downloaded.\n\nStart a local webhook tunnel:\n\n```\ncloudflared tunnel --url http://localhost:8000\n```\n\nThen configure the GitHub App webhook:\n\n```\nhttps://<tunnel-host>/api/webhooks/github\n```\n\nFor example:\n\n```\nhttps://example.trycloudflare.com/api/webhooks/github\n```\n\nAgain:\n\n**Do not expose port `11434` publicly.**\n\nThe webhook tunnel should only point to the FastAPI API.\n\nMermaid flowchart: GitHub, Cloudflare Tunnel, FastAPI API, (\"PostgreSQL\"), Background Worker, Ollama Docker, qwen3-coder:30b, React UI\n\nThe important architectural property is:\n\n```\nGitHub\n   │\n   ▼\nFastAPI\n   │\n   ▼\nWorker\n   │\n   ▼\nOllama\n```\n\nThe FastAPI API does **not** directly perform model inference.\n\nThe worker owns the long-running analysis and AI interaction.\n\nThis keeps webhook processing fast and prevents model inference from blocking HTTP requests.\n\nProduction deployments can use a public AI model such as OpenAI instead of running Ollama.\n\nThe provider is selected through configuration.\n\n```\nLLM_PROVIDER=ollama\n```\n\nFlow:\n\n```\nWorker\n   │\n   ▼\nOllama\n   │\n   ▼\nLocal AI Model\nLLM_PROVIDER=openai\n```\n\nFlow:\n\n```\nWorker\n   │\n   ▼\nOpenAI API\n   │\n   ▼\nCloud AI Model\n```\n\nNo application code changes are required to switch between these configurations.\n\nFor production, configure:\n\n```\nLLM_PROVIDER=openai\n\nLLM_BASE_URL=https://api.openai.com/v1\n\nLLM_API_KEY=your-openai-api-key\n\nLLM_MODEL=your-model-name\n```\n\nFor example:\n\n```\nLLM_PROVIDER=openai\nLLM_BASE_URL=https://api.openai.com/v1\nLLM_API_KEY=...\nLLM_MODEL=...\n```\n\nOpenAI API documentation:\n\nOpenAI API Quickstart\n\nStore the API key using your deployment platform's secret manager.\n\nDo not commit:\n\n```\nLLM_API_KEY=...\n```\n\nto Git.\n\n|  | Local Development | Production | \n|---|---|---|\n| Provider | Ollama | OpenAI | \n| Runtime | Docker | Cloud API | \n| Model | `qwen3-coder:30b` | Configured cloud model | \n| Network | Local | Internet | \n| API key | Not required | Required | \n| Ollama | Required | Not required | \n| Application code | Same | Same | \n| Configuration | `LLM_PROVIDER=ollama` | `LLM_PROVIDER=openai` | \n\nThe AI provider is an explicit configuration choice.\n\nThere is no automatic fallback between providers.\n\nFor example:\n\n```\nLLM_PROVIDER=openai\n```\n\ndoes not silently fall back to Ollama.\n\nLikewise:\n\n```\nLLM_PROVIDER=ollama\n```\n\ndoes not automatically send data to a public AI provider.\n\nThis makes the data-flow and privacy decision explicit.\n\nA typical production deployment looks like:\n\nMermaid flowchart: GitHub, GitHub App, HTTPS / Load Balancer, FastAPI API, (\"PostgreSQL\"), Background Worker, OpenAI API, React Web App, Pull Request Comment\n\nThe production deployment can therefore be thought of as:\n\n```\n                        GitHub\n                           │\n                           │ Webhook\n                           ▼\n                  ┌─────────────────┐\n                  │   FastAPI API   │\n                  │                 │\n                  │ Webhooks / API  │\n                  └────────┬────────┘\n                           │\n                           ▼\n                  ┌─────────────────┐\n                  │   PostgreSQL    │\n                  │                 │\n                  │ Jobs / Analysis │\n                  │ Claims / Output │\n                  └────────┬────────┘\n                           │\n                           ▼\n                  ┌─────────────────┐\n                  │ Background      │\n                  │ Worker          │\n                  │                 │\n                  │ Analysis + AI   │\n                  └────────┬────────┘\n                           │\n                    Explanation\n                       Packet\n                           │\n                           ▼\n                  ┌─────────────────┐\n                  │    OpenAI API   │\n                  │                 │\n                  │   AI Model      │\n                  └─────────────────┘\n```\n\nOne of the most important properties of PR Explain is making the AI data path explicit.\n\n```\nGitHub\n   │\n   ▼\nSelf-hosted API\n   │\n   ▼\nSelf-hosted Worker\n   │\n   ├── Repository snapshot\n   ├── Pull Request diff\n   ├── Symbols\n   ├── Evidence\n   └── Explanation packet\n             │\n             ▼\n       Local Ollama\n```\n\nIn this mode, AI inference is performed locally on infrastructure you control.\n\n```\nGitHub\n   │\n   ▼\nSelf-hosted API\n   │\n   ▼\nSelf-hosted Worker\n   │\n   └── Explanation packet\n             │\n             ▼\n        OpenAI API\n```\n\nWhen:\n\n```\nLLM_PROVIDER=openai\n```\n\nthe configured explanation packet is sent to the cloud AI provider.\n\nThis is an explicit configuration decision.\n\nAI inference and repository analysis can take significantly longer than a normal HTTP request.\n\nThe webhook path therefore does not perform the complete analysis synchronously.\n\nInstead:\n\n```\nGitHub\n   │\n   ▼\nWebhook\n   │\n   ▼\nFastAPI\n   │\n   ▼\nCreate Job\n   │\n   ▼\nReturn\n```\n\nThe worker then performs:\n\n```\nJob\n │\n ├── Repository snapshot\n ├── Diff analysis\n ├── Symbol analysis\n ├── Change graph\n ├── Evidence\n ├── Impact\n ├── Explanation packet\n ├── AI inference\n └── GitHub comment\n```\n\nThis provides:\n\n- Fast webhook responses\n- Retryable background jobs\n- Better failure isolation\n- Long-running AI inference outside the HTTP request\n- Separation between API and processing workloads\n\nThe current processing pipeline is:\n\n```\nsnapshot_fetch\n      │\n      ▼\ndiff_analysis\n      │\n      ▼\nsymbol_analysis\n      │\n      ▼\nchange_graph\n      │\n      ▼\nevidence\n      │\n      ▼\nimpact\n      │\n      ▼\nclaims_persisted\n      │\n      ▼\nexplanation_packet_persisted\n      │\n      ▼\nexplanation\n      │\n      ▼\ncomment\n```\n\nThe deterministic analysis stage produces facts about the Pull Request.\n\nExamples include:\n\n```\nsrc/payment/service.py\nsrc/payment/repository.py\ntests/payment/test_service.py\nPaymentService.process_payment\nPaymentRepository.create\nPaymentEventPublisher.publish\nPaymentService.process_payment\n        │\n        ├── calls → PaymentRepository.create\n        │\n        └── calls → PaymentEventPublisher.publish\n```\n\nThe analysis can associate claims with the relevant source or diff evidence.\n\nFiles and symbols outside the direct diff can be identified when they are relevant to the change.\n\nThe AI model does not receive an unrestricted repository and is not expected to independently discover the entire change.\n\nInstead, the worker constructs a bounded explanation packet.\n\nConceptually:\n\n```\nExplanation Packet\n├── Pull Request metadata\n├── Changed files\n├── Changed symbols\n├── Relationships\n├── Evidence\n├── Impact\n├── Claims\n└── Relevant context\n```\n\nThe packet is intentionally bounded to prevent unnecessarily large model requests.\n\nThe AI model takes the explanation packet and turns it into human-readable language.\n\nConceptually:\n\n```\nChange Graph\n     +\nEvidence\n     +\nImpact\n     +\nClaims\n     │\n     ▼\nExplanation Packet\n     │\n     ▼\nAI Model\n     │\n     ▼\nHuman-readable Explanation\n```\n\nThe model should explain the supplied facts rather than invent unsupported relationships.\n\nPR Explain separates:\n\n```\nanalysis_status\nexplanation_status\ncomment_status\n```\n\nThis is important because deterministic analysis and AI narration are independent stages.\n\nFor example:\n\n```\nGitHub PR\n    │\n    ▼\nAnalysis\n    │\n    ▼\nClaims Stored\n    │\n    ▼\nAI Explanation\n    │\n    ├── SUCCESS\n    │\n    └── FAILURE\n```\n\nIf the AI provider fails:\n\n- Deterministic analysis is not discarded.\n- Claims remain stored.\n- Files and symbols remain available.\n- Explanation can be retried.\n- The Pull Request comment is not posted until explanation succeeds.\n\nSimilarly, if the GitHub comment update fails, the stored analysis and explanation remain available in the application.\n\nPR Explain maintains one conversation-style explanation comment per Pull Request.\n\nWhen a Pull Request receives a new commit:\n\n```\nNew Commit\n    │\n    ▼\nNew HEAD SHA\n    │\n    ▼\nNew Analysis\n    │\n    ▼\nNew Explanation\n    │\n    ▼\nExisting PR Comment Updated\n```\n\nThis avoids creating a new comment for every commit.\n\nThe application can also compare claim sets across different Pull Request head SHAs to expose revision deltas.\n\nThe React frontend provides two primary views.\n\nThe Explain view provides the high-level change story and diagram.\n\nIt answers:\n\nWhat changed and how does this change affect the system?\n\nThe Details view exposes the underlying evidence:\n\n- Changed files\n- Symbols\n- Call relationships\n- Line-level evidence\n- Tests\n- Relevant files outside the diff\n- API facts\n- Unknown facts\n- Analysis status\n- Explanation status\n\nBoth views are backed by the same stored analysis.\n\n```\nPR_explain/\n├── backend/\n│   └── app/\n│       ├── api/\n│       ├── analysis/\n│       ├── llm/\n│       ├── worker/\n│       └── ...\n│\n├── frontend/\n│   └── ...\n│\n├── fixtures/\n│   └── bench/\n│\n├── .env.example\n├── docker-compose.yml\n├── README.md\n└── .gitignore\n```\n\nMain runtime components:\n\n| Component | Responsibility | \n|---|---|\n| `backend` | API, GitHub integration, analysis and worker | \n| `frontend` | React web application | \n| `postgres` | Persistent application data | \n| `worker` | Background analysis and AI explanation | \n| `ollama` | Local AI inference | \n| `ollama-pull` | Downloads the configured Ollama model | \n| `fixtures` | Test and benchmark fixtures | \n\n```\nGITHUB_APP_ID=\nGITHUB_APP_PRIVATE_KEY_FILE=\nGITHUB_WEBHOOK_SECRET=\nGITHUB_API_URL=https://api.github.com\nLLM_PROVIDER=ollama\n\nOLLAMA_BASE_URL=http://127.0.0.1:11434\n\nOLLAMA_MODEL=qwen3-coder:30b\n\nOLLAMA_TIMEOUT_MS=180000\nLLM_PROVIDER=openai\n\nLLM_BASE_URL=https://api.openai.com/v1\n\nLLM_API_KEY=\n\nLLM_MODEL=\nDATABASE_URL=postgresql+psycopg://pr_explain:pr_explain@localhost:5432/pr_explain\n\nPOSTGRES_USER=pr_explain\nPOSTGRES_PASSWORD=pr_explain\nPOSTGRES_DB=pr_explain\nAPP_BASE_URL=http://localhost:5173\n\nCORS_ORIGINS=http://localhost:5173\nEXPLANATION_PACKET_CHAR_BUDGET=32000\n\nFANOUT_CAP=50\n\nMAX_CHANGED_SYMBOLS=80\n```\n\nUse `.env.example` as the source of truth for the current supported configuration.\n\nThe test suite can run without Ollama or GitHub.\n\n```\ncd backend\n\npython3 -m venv .venv\n\nsource .venv/bin/activate\n\npip install -e \".[dev]\"\n\npytest\n```\n\nTests cover areas including:\n\n- OAuth fixture claims\n- Citation validation\n- Worker failure behavior\n- Comment rendering\n\nThe test suite does not need to download an AI model or contact GitHub.\n\nFor lightweight local development:\n\n```\ncd backend\n\nDATABASE_URL=sqlite:///./dev.db \\\nPYTHONPATH=. \\\npython -m app.seed\n```\n\nStart FastAPI:\n\n```\nDATABASE_URL=sqlite:///./dev.db \\\nPYTHONPATH=. \\\nuvicorn app.api:app --port 8000\n```\n\nIn another terminal:\n\n```\ncd frontend\n\nnpm install\n\nnpm run dev\n```\n\nThen open the URL printed by the frontend development server.\n\nPR Explain includes a model benchmark for testing different AI providers and models against frozen explanation packets.\n\nRun:\n\n```\npython -m app.llm.bench\n```\n\nThe benchmark produces a local JSON report covering areas such as:\n\n- Grounding\n- Structure\n- Latency\n- Process memory\n\nThe benchmark is intended to evaluate the model/provider configuration.\n\nIt is **not** a Pull Request quality score.\n\nDo not commit:\n\n```\n.env\n*.pem\nGitHub App private keys\nGitHub webhook secrets\nLLM API keys\nDatabase passwords\n```\n\nThe GitHub App private key grants the application authentication capabilities as the GitHub App.\n\nKeep it outside source control and inject it into the application using a secure file or secret-management mechanism.\n\nFor local development, Ollama is bound to:\n\n```\n127.0.0.1:11434\n```\n\nDo not expose Ollama through your GitHub webhook tunnel.\n\nOnly the FastAPI API needs to be publicly reachable:\n\n```\nInternet\n    │\n    ▼\nFastAPI :8000\n```\n\nnot:\n\n```\nInternet\n    │\n    ▼\nOllama :11434\n```\n\nUse your deployment platform's secret manager for:\n\n```\nGITHUB_APP_PRIVATE_KEY\nGITHUB_WEBHOOK_SECRET\nLLM_API_KEY\nPOSTGRES_PASSWORD\n```\n\nDo not store production secrets in the Git repository.\n\nBefore deploying PR Explain:\n\n- Create the GitHub App\n- Configure Metadata → Read\n- Configure Contents → Read\n- Configure Pull requests → Write\n- Enable installation events\n- Enable installation repository events\n- Enable Pull Request events\n- Generate GitHub App private key\n- Store the private key securely\n-  Configure `GITHUB_APP_ID`\n-  Configure `GITHUB_WEBHOOK_SECRET`\n- Install the GitHub App on the required repositories\n- Deploy FastAPI with HTTPS\n- Configure the GitHub webhook URL\n- Deploy PostgreSQL\n- Deploy the background worker\n-  Configure `LLM_PROVIDER=openai`\n-  Configure `LLM_BASE_URL`\n-  Configure `LLM_API_KEY`\n-  Configure `LLM_MODEL`\n- Configure frontend production API origin\n- Configure CORS\n- Configure persistent database storage\n- Configure application logging and monitoring\n- Test a Pull Request from webhook through analysis to GitHub comment\n- Verify that Ollama is not publicly exposed\n- Verify that secrets are not present in logs\n\nFor production, use a real HTTPS server/application endpoint rather than a development webhook tunnel.\n\nSuppose a developer opens:\n\n```\nPR #42\n\nAdd payment event publishing\n```\n\nThe Pull Request modifies:\n\n```\nsrc/payment/service.py\nsrc/payment/events.py\ntests/payment/test_service.py\n```\n\nPR Explain receives:\n\n```\nGitHub Webhook\n      │\n      ▼\nFastAPI\n      │\n      ▼\nBackground Worker\n```\n\nThe worker analyzes the repository:\n\n```\nPaymentService.process_payment()\n          │\n          ├── PaymentRepository.create()\n          │\n          └── PaymentEventPublisher.publish()\n```\n\nIt collects:\n\n```\nFiles\nSymbols\nRelationships\nEvidence\nImpact\nClaims\n```\n\nIt creates:\n\n```\nExplanation Packet\n```\n\nThe configured AI provider then receives the packet.\n\n```\nExplanation Packet\n       │\n       ▼\nOllama\n       │\n       ▼\nLocal Model\nExplanation Packet\n       │\n       ▼\nOpenAI\n       │\n       ▼\nCloud Model\n```\n\nThe generated explanation is stored and posted to GitHub:\n\n```\nWorker\n   │\n   ├── PostgreSQL\n   │\n   └── GitHub API\n          │\n          ▼\n     PR #42 Comment\n```\n\nLike SQL `EXPLAIN`, PR Explain first produces a structured representation of what is happening.\n\nThe AI then explains that representation.\n\nThe system determines what changed before generating a natural-language explanation.\n\nThe model is a narrator over deterministic analysis.\n\nDevelopers can run the complete AI workflow locally using Docker and Ollama.\n\nNo cloud AI API is required for local inference.\n\nProduction can use OpenAI or another compatible provider through configuration without changing the core analysis pipeline.\n\nSwitching from:\n\n```\nLLM_PROVIDER=ollama\n```\n\nto:\n\n```\nLLM_PROVIDER=openai\n```\n\nis an explicit decision to move AI inference from local infrastructure to a cloud provider.\n\nAnalysis, explanation, and GitHub commenting are separate stages.\n\nA model failure should not destroy deterministic analysis.\n\nThe GitHub App requests only the repository permissions required by PR Explain.\n\nIf you remember only one thing about PR Explain, remember this:\n\n```\n                 SQL\n                  │\n                  ▼\n          ┌───────────────┐\n          │    EXPLAIN    │\n          └───────┬───────┘\n                  │\n                  ▼\n          Execution Plan\n                  │\n                  ▼\n         Human Understanding\n\n                 Code\n                  │\n                  ▼\n          ┌───────────────┐\n          │  PR EXPLAIN   │\n          └───────┬───────┘\n                  │\n                  ▼\n       Change Graph + Evidence\n                  │\n                  ▼\n             AI Model\n                  │\n                  ▼\n         Human Understanding\n```\n\n**PR Explain is `EXPLAIN` for Pull Requests.**\n\nIt doesn't just ask an AI:\n\n\"What does this PR do?\"\n\nIt first asks the codebase:\n\n\"What actually changed, what does it connect to, and what evidence supports that?\"\n\nThen it asks the AI:\n\n\"Now explain those facts to a human.\"\n\n- Support for scalable async processing of PR explain request\n- Improve explain notes to give more in-depth understanding of changes\n- Add more support for overall review experience\n\nMIT — for hackathon / research use.", "url": "https://wpnews.pro/news/what-if-pull-requests-had-an-explain-command", "canonical_source": "https://github.com/rajatrao/PR_explain", "published_at": "2026-10-06 22:14:22+00:00", "updated_at": "2026-10-06 22:19:36.421355+00:00", "lang": "en", "topics": ["ai-tools", "developer-tools", "ai-products", "artificial-intelligence"], "entities": ["PR Explain", "GitHub", "FastAPI"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/what-if-pull-requests-had-an-explain-command", "markdown": "https://wpnews.pro/news/what-if-pull-requests-had-an-explain-command.md", "text": "https://wpnews.pro/news/what-if-pull-requests-had-an-explain-command.txt", "jsonld": "https://wpnews.pro/news/what-if-pull-requests-had-an-explain-command.jsonld"}}