prATC (PR Air Traffic Control) is a self-hostable system for large-scale pull request triage and merge planning.
- Current release line:
0.2.x - Current direction: evidence-backed PR review workflows, merged/open duplicate detection, and safer operator-facing review output
- Default API port:
7400(reserved prATC range:7400-7500)
- CHANGELOG.md — release history
- ROADMAP.md — upcoming priorities
- docs/plans/2026-04-09-pratc-v1-3-review-engine-design.md — v1.3 review engine design
- CLI Analysis: Analyze, cluster, graph, and plan merges for GitHub repositories
- Web Dashboard: ATC overview, triage inbox, dependency graphs, and merge planning
- ML Clustering: Python service for PR clustering and duplicate detection
- SQLite Cache: Incremental GitHub sync with persisted state
- Omni Batch Planning: Select PRs by ID ranges and boolean expressions via selector syntax
- Rate-Limit Aware: Built-in retry and budget management
- Review Pipeline: Advisory analyzers for security, reliability, performance, and quality review output
# Build everything
make verify-env
make build
make test
# Run with Docker Compose
docker-compose --profile local-ml up
docker-compose --profile minimax-light upAnalyze pull requests for a repository.
pratc analyze --repo=owner/repo --format=jsonOutput includes: repo, generatedAt, counts, clusters, duplicates, overlaps, conflicts, stalenessSignals.
Cluster pull requests by similarity.
pratc cluster --repo=owner/repo --format=jsonOutput includes: repo, generatedAt, model, thresholds, clusters.
Generate dependency/conflict graphs.
pratc graph --repo=owner/repo --format=dot # Default DOT output
pratc graph --repo=owner/repo --format=json # JSON outputGenerate a ranked merge plan. Dry-run by default (no changes executed).
pratc plan --repo=owner/repo --target=20
pratc plan --repo=owner/repo --target=20 --mode=combination
pratc plan --repo=owner/repo --target=20 --include-bots
pratc plan --repo=owner/repo --target=20 --dry-run=false # Execute modeFlags:
--target: Number of PRs to include (default: 20)--mode: Formula mode -combination(default),permutation,with_replacement--dry-run: Plan only, do not execute (default:true, always true if not explicitly set)--include-bots: Include bot PRs in merge plan (default:false)
Start the API server.
pratc serve --port=7400
pratc serve --port=7400 --repo=owner/repo # Default repo for APISync repository metadata and refs.
pratc sync --repo=owner/repo
pratc sync --repo=owner/repo --watch --interval=5mQuery the audit log.
pratc audit --limit=20 --format=jsonGET /healthz- Health checkGET /api/health- Health check (alias)
GET /api/settings?repo=- Get settingsPOST /api/settings- Set settingDELETE /api/settings?scope=&repo=&key=- Delete settingGET /api/settings/export?scope=&repo=- Export as YAMLPOST /api/settings/import?scope=&repo=- Import YAML
Legacy routes (query string repo):
GET /analyze?repo=- Analyze PRsGET /cluster?repo=- Cluster PRsGET /graph?repo=&format=- Graph PRs (add&format=dotfor DOT output)GET /plan?repo=&target=&mode=- Plan PRs
RESTful routes (/api/repos/:owner/:repo/...):
GET /api/repos/:owner/:repo/analyzeGET /api/repos/:owner/:repo/clusterGET /api/repos/:owner/:repo/graphGET /api/repos/:owner/:repo/planor/api/repos/:owner/:repo/plansGET /api/repos/:owner/:repo/plan/omni- Omni batch plan with selector
POST /api/repos/:owner/:repo/sync- Trigger syncGET /api/repos/:owner/:repo/sync/stream- Stream sync progress
When calling plan endpoints, these query parameters are supported:
| Parameter | Type | Default | Description |
|---|---|---|---|
target |
int | 20 | Number of PRs to include |
mode |
string | combination |
Formula mode: combination, permutation, with_replacement |
cluster_id |
string | - | Filter to specific cluster |
exclude_conflicts |
bool | false | Exclude conflicting PRs |
stale_score_threshold |
float | 0.0 | Staleness threshold (0-1) |
candidate_pool_cap |
int | 100 | Max candidate pool size (1-500) |
score_min |
float | 0.0 | Minimum PR score (0-100) |
dry_run |
bool | true | Plan only, do not execute |
Omni mode lets you select specific PRs by ID using a selector expression. This is useful when you already know which PRs you want to plan against, bypassing the normal scoring and filtering pipeline.
GET /api/repos/:owner/:repo/plan/omni
| Parameter | Type | Default | Description |
|---|---|---|---|
selector |
string | (required) | PR selector expression (see syntax below) |
target |
int | 20 | Number of PRs to select from the first stage |
stage_size |
int | 64 | Maximum PRs per processing stage |
Selectors use a simple expression language to reference PRs by number.
Grammar:
expr → orExpr
orExpr → andExpr (OR andExpr)*
andExpr → term (AND term)*
term → '(' expr ')' | atomic
atomic → ID | RANGE
- ID: A single PR number (e.g.,
123) - RANGE: Two IDs separated by a hyphen, inclusive (e.g.,
100-200) - AND: Intersection of two sets (binds tighter than OR)
- OR: Union of two sets (loosest precedence)
- Parentheses: Override default precedence
# Single PR
curl "http://localhost:7400/api/repos/owner/repo/plan/omni?selector=42"
# Range of PRs (inclusive)
curl "http://localhost:7400/api/repos/owner/repo/plan/omni?selector=1-100"
# Multiple ranges combined with AND (intersection)
# Selects PRs present in both ranges: {100, 101, ..., 150}
curl "http://localhost:7400/api/repos/owner/repo/plan/omni?selector=50-150+AND+100-200"
# Note: URL-encode spaces as + or %20
# Multiple ranges combined with OR (union)
# Selects PRs from either range
curl "http://localhost:7400/api/repos/owner/repo/plan/omni?selector=1-50+OR+200-250"
# Grouping with parentheses
# Selects intersection of (1-100 OR 300-400) with 50-150
curl "http://localhost:7400/api/repos/owner/repo/plan/omni?selector=(1-100+OR+300-400)+AND+50-150"
# Result: {50, 51, ..., 100}
# With custom stage size and target
curl "http://localhost:7400/api/repos/owner/repo/plan/omni?selector=1-200&target=10&stage_size=32"{
"repo": "owner/repo",
"generatedAt": "2026-04-02T12:00:00Z",
"selector": "1-100",
"mode": "omni_batch",
"stageCount": 2,
"stages": [
{ "stage": 1, "stageSize": 64, "matched": 64, "selected": 20 },
{ "stage": 2, "stageSize": 64, "matched": 37, "selected": 0 }
],
"selected": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20],
"ordering": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
}- stages: The matched PRs are divided into batches of
stage_size. Only the first stage populates theselectedlist, up totargetPRs. - selected: PR IDs chosen from the first stage (limited by
target). - ordering: Merge ordering for the selected PRs.
Omni mode works alongside the standard analyze and config workflow:
# 1. Analyze the repo to understand the PR landscape
pratc analyze --repo=owner/repo --format=json
# 2. Configure planning settings
pratc config set --scope=repo --repo=owner/repo planning.target 20
# 3. Use omni mode for targeted planning on a specific PR range
curl "http://localhost:7400/api/repos/owner/repo/plan/omni?selector=50-150"prATC provides a dual dashboard system for monitoring sync operations and managing pull requests in real time. Both dashboards share the same data and update simultaneously.
A lightweight terminal-based dashboard perfect for server environments and quick checks.
Quick Start:
# Start the TUI dashboard
pratc monitor
# Or start server with monitoring enabled
pratc serve --port=7400 --monitorFeatures:
- Three-zone layout: Jobs panel, Timeline visualization, Rate Limit status
- Console logs: Full-width log viewer with last 1,000 entries
- Keyboard controls: Navigate with arrow keys, Tab to switch zones
- Real-time updates: Sync job progress updates every 2 seconds
- Color-coded status: Cyan (active), Green (success), Amber (warning), Red (critical)
Keyboard Shortcuts:
| Key | Action |
|---|---|
Tab |
Switch between zones |
↑/↓ |
Navigate jobs or scroll logs |
←/→ |
Scroll timeline |
Enter |
View job details |
p |
Pause monitoring |
r |
Resume monitoring |
s |
Restart sync |
q or ? |
Quit or toggle help |
A responsive browser-based interface ideal for daily monitoring and team visibility.
Quick Start:
# Terminal 1: Start the API server
pratc serve --port=7400
# Terminal 2: Start the web dashboard (dev mode)
cd web && bun run devThen open http://localhost:3000/monitor in your browser.
Features:
- Responsive design: Adapts to desktop, tablet, and mobile screens
- Interactive panels: Click to expand job details, drag to scroll timeline
- Auto-refresh: Jobs update every 10 seconds, rate limit every 30 seconds
- Log filtering: Filter by level (Error, Warning, Info, Debug)
- Search functionality: Find specific events in console logs
Layout by Screen Size:
- Desktop (>1439px): Three-column grid (Jobs, Timeline, Rate Limit)
- Tablet (768px-1439px): Two-column grid
- Mobile (<768px): Single column, stacked vertically
Both dashboards provide:
| Feature | Description |
|---|---|
| Sync Jobs | Active sync operations with progress percentage and status |
| Timeline | Activity visualization over time (15-minute intervals) |
| Rate Limit | GitHub API budget status with remaining requests and reset time |
| Console | Real-time system logs with color-coded severity levels |
| Pause/Resume | Manual control when rate limit is critical |
- Dashboard User Guide - Complete guide for TUI and Web dashboards
- Monitor API Docs - WebSocket API for real-time updates
Screenshots showing the TUI three-zone layout and Web dashboard responsive design would appear here.
- TUI Dashboard: Terminal showing Jobs, Timeline, and Rate Limit panels with console logs below
- Web Dashboard: Browser interface with interactive cards, timeline visualization, and rate limit gauge
Two profiles are available for different deployment scenarios:
Full local ML stack with Python clustering service.
docker-compose --profile local-ml upLightweight profile without local ML (uses external APIs).
docker-compose --profile minimax-light upNote: The minimax-light profile replaces the previous openrouter-light naming.
cmd/pratc/ # CLI entrypoints
internal/ # Go packages
cmd/ # CLI command implementations
app/ # Service layer
cache/ # SQLite persistence
github/ # GitHub client
filter/ # Pre-filter pipeline
formula/ # Combinatorial formula engine
graph/ # Dependency graph engine
planner/ # Merge planning
settings/ # Settings management
sync/ # Background sync
types/ # Shared types
ml-service/ # Python ML service
web/ # TypeScript Next.js dashboard
fixtures/ # Test fixtures
Environment variables:
| Variable | Description |
|---|---|
PRATC_PORT |
API server port (default: 7400) |
PRATC_DB_PATH |
SQLite database path |
PRATC_SETTINGS_DB |
Settings database path |
PRATC_ANALYSIS_BACKEND |
Analysis backend: local or remote |
GITHUB_TOKEN |
GitHub API token |
# Go tests
go test -race -v ./...
# Python tests
uv run pytest -v
# Web tests
bun run test
# All tests
make test0- Success1- Runtime failure2- Invalid arguments
- Similarity > 0.90: duplicate
- Similarity 0.70-0.90: overlapping
MIT