Technical Requirements Document (TRD)
Specifies requirements for an AI platform that deduplicates and ranks pull requests and issues for open-source maintainers.
What this file does
Specifies requirements for an AI platform that deduplicates and ranks pull requests and issues for open-source maintainers.
When to use it
- Designing a system to detect duplicate PRs and issues at scale
- Building a multi-signal ranking engine for competing PRs
- Implementing vision alignment checks against project roadmap documents
- Creating a maintainer dashboard for triage and cluster management
Assumes this stack
Technical Requirements Document (TRD)
PReview -- AI-Powered PR & Issue Intelligence Platform for Open-Source Maintainers
Version: 1.1 Date: 2026-02-16 Origin: Peter Steinberger's tweet requesting AI tooling for PR/Issue deduplication and triage on OpenClaw (180K+ stars, 3100+ open PRs)
1. Problem Statement
Open-source maintainers of high-velocity repositories face an existential workflow crisis:
| Metric | Reality |
|---|---|
| PR growth rate (OpenClaw) | 400+ new PRs/day |
| Legitimate AI-generated PRs | ~10% (source) |
| Maintainer review capacity | ~50-80 PRs/day (generous) |
| Duplicate/overlapping PRs | Estimated 30-60% on viral repos |
Core pain points (from steipete's tweet + thread + HN discussion):
- Duplicate PRs/Issues -- Multiple contributors solve the same problem independently. No tooling surfaces this at scale.
- Quality signal detection -- Among N duplicate PRs, identifying the best one requires deep code review, not just surface matching.
- Vision drift -- PRs that don't align with project goals waste review cycles. Maintainers need automated alignment checks against a "vision document."
- Cognitive overload -- The review trust model is broken when PR authors (human or AI) may not understand their own submissions.
- Abandoned PRs -- AI-generated slop PRs are frequently abandoned after submission, clogging the queue.
steipete's exact requirements (from tweet thread):
"I need AI that scans every PR and Issue and de-dupes. It should also detect which PR is the best based on various signals (so really also deep review). Ideally have a vision document and mark/reject PRs that stray too far. Can't be fully automated -- but even assisting would help."
2. Project Scope
2.1 In Scope
| Capability | Description |
|---|---|
| PR Deduplication | Semantic clustering of PRs that address the same issue/feature |
| Issue Deduplication | Detection and linking of duplicate/related issues |
| PR Quality Ranking | Multi-signal scoring to rank competing PRs solving the same problem |
| Vision Alignment Check | Compare PR intent against a maintainer-defined project vision/roadmap document |
| Automated Triage Labels | Auto-classification: bug fix, feature, refactor, docs, test, chore |
| PR Staleness Detection | Flag abandoned, stale, or low-engagement PRs |
| Maintainer Dashboard | Web UI for reviewing clusters, rankings, and triage decisions |
| GitHub Integration | Native GitHub App + GitHub Actions workflow |
| API | REST + webhooks for third-party integrations |
2.2 Out of Scope (v1)
- Automated merge decisions (always human-in-the-loop for merge, no exceptions)
- Non-GitHub platforms (GitLab, Bitbucket -- deferred to v2)
- Inline code-review comments and repository-level code suggestions (covered by existing tools like CodeRabbit, PR-Agent)
- CI/CD pipeline integration beyond status checks
- Auto-generated code fixes or refactoring suggestions
3. Use Cases
UC-1: Duplicate PR Detection & Clustering
Actor: Maintainer Trigger: New PR is opened Flow:
- System receives webhook for new PR
- Extracts PR metadata: title, description, changed files, diff hunks, linked issues
- Generates semantic embedding of PR intent (not just code similarity)
- Compares against all open PRs using ANN (Approximate Nearest Neighbor) index
- If similarity > configurable threshold, creates a PR Cluster
- Posts a comment on the PR: "This PR appears related to #X, #Y, #Z. Cluster: [link to dashboard]"
- Labels PR with
duplicate-candidateorcluster:ABC
Edge Cases:
- PRs that solve the same issue differently (same intent, different approach) -- must cluster these
- PRs that partially overlap (touches same files but different scope) -- mark as
related, notduplicate - New PR matches PRs across two existing clusters -- triggers cluster merge evaluation (see Section 4.2.3)
UC-2: Best-PR Ranking Within a Cluster
Actor: Maintainer reviewing a cluster Trigger: Cluster has 2+ PRs Flow:
- For each PR in cluster, compute quality signals:
- Code quality score: lint compliance, test coverage delta, complexity metrics
- Diff cleanliness: minimal changes, no unrelated modifications, proper commit hygiene
- Test inclusion: does the PR add/modify tests?
- Description quality: clear problem statement, solution explanation, linked issue
- Author engagement: current-PR activity (replies to reviews, iterations, follow-ups) and contributor history
- Responsiveness: time-to-respond to review comments
- Abandonment risk: probability that the PR will be abandoned based on behavioral signals (see Section 4.2.4)
- Weighted composite score displayed in dashboard
- Maintainer sees ranked list with drill-down into each signal
- One-click action: "Approve as primary", "Request changes", "Close as duplicate"
UC-3: Vision Alignment Check
Actor: Maintainer (setup), System (continuous) Trigger: New PR opened, or maintainer updates vision doc Flow:
- Maintainer uploads/links a Vision Document (markdown, project roadmap, CONTRIBUTING.md, or free-text goals)
- System chunks and embeds the vision document into the vector store
- On each new PR, system computes alignment score: how well does this PR's intent match stated project goals?
- PRs with low alignment get labeled
needs-alignment-reviewand a comment explaining the mismatch - Maintainer can configure auto-close for PRs below a threshold (opt-in, dangerous)
UC-4: Issue Deduplication
Actor: Contributor opens issue Trigger: New issue created/edited Flow:
- System embeds issue title + body
- Searches existing open issues for semantic duplicates
- If duplicate found: comment with link to original, apply
duplicatelabel - If related (not exact duplicate): comment with "See also: #X" cross-reference
UC-5: Stale & Abandoned PR Detection
Actor: System (scheduled) Trigger: Cron job (daily) Flow:
- Scan all open PRs for:
- No author activity in N days (configurable, default: 14)
- Unresolved review comments with no response
- Merge conflicts unresolved for N days
- CI failures unaddressed
- Apply a single
stalelabel. Staleness stage (warning, stale, abandoned) is tracked as metadata in the database and displayed on the dashboard, not as separate labels. - Post comment with grace period before auto-close
- Exclude PRs with
do-not-closeor other exempt labels (configurable)
UC-6: Maintainer Dashboard
Actor: Maintainer Flow:
- Overview: total open PRs, clusters, duplicates detected, PRs triaged today
- Cluster view: grouped PRs with rankings, diffs, and signal breakdowns
- Vision alignment heatmap: which areas of the codebase are getting PRs vs. what the vision prioritizes
- Triage queue: PRs sorted by priority (needs review > needs-alignment > stale)
- Bulk actions: close all duplicates in cluster, assign labels, request changes
4. Technical Architecture
4.1 High-Level Architecture
+------------------------------------------------------------------+
| GitHub Platform |
| +----------+ +----------+ +----------+ +----------------+ |
| | PRs | | Issues | | Webhooks | | GitHub API | |
| +----+-----+ +----+-----+ +----+-----+ +-------+--------+ |
+-------|--------------|--------------|--------------|--------------+
| | | |
v v v v
+------------------------------------------------------------------+
| PReview Ingestion Layer |
| +--------------+ +--------------+ +-----------------------+ |
| | Webhook | | Batch Sync | | Rate-Limited GitHub | |
| | Receiver | | Worker | | Client | |
| +------+-------+ +------+-------+ +-----------+-----------+ |
+---------|-----------------|-----------------------|---------------+
| | |
v v v
+------------------------------------------------------------------+
| Message Queue (BullMQ + Redis) |
+----------------------------+-------------------------------------+
|
+----------------+----------------+
v v v
+----------------+ +--------------+ +------------------+
| Embedding | | Analysis | | Vision |
| Pipeline | | Pipeline | | Alignment Engine |
| | | | | |
| - Intent | | - Code qual | | - Vision doc |
| extraction | | - Test cov | | embedding |
| - Diff-aware | | - Author sig | | - Alignment |
| fallback | | - Abandon | | scoring |
| - Semantic | | risk | | - Drift alerts |
| embedding | | - Staleness | | |
+-------+--------+ +------+-------+ +--------+---------+
| | |
v v v
+------------------------------------------------------------------+
| Core Intelligence Layer |
| +------------------+ +------------------+ +---------------+ |
| | Dedup Engine | | Ranking Engine | | Triage Engine | |
| | (ANN + LLM judge) | | (Multi-signal) | | (Rule + ML) | |
| +------------------+ +------------------+ +---------------+ |
+----------------------------+-------------------------------------+
|
+----------------+----------------+
v v v
+----------------+ +--------------+ +------------------+
| PostgreSQL 17 | | pgvector | | Action Dispatcher|
| (metadata, | | (HNSW index, | | (GitHub comments,|
| clusters, | | embeddings) | | labels, status |
| scores, | | | | checks) |
| audit log) | | | | |
+----------------+ +--------------+ +--------+---------+
|
v
+----------------+
| Web Dashboard |
| (Next.js 15) |
+----------------+
4.2 Component Breakdown
4.2.1 Ingestion Layer
- Webhook Receiver: Fastify server handling GitHub
pull_request.*,issues.*,issue_comment.*,pull_request_review.*events - Batch Sync Worker: On first install, backfills all existing open PRs/issues via GitHub REST API with pagination and rate-limit respect
- Rate-Limited GitHub Client: Octokit wrapper with token rotation, retry logic, conditional requests (ETags), and secondary rate limit handling
4.2.2 Embedding Pipeline
- Three-stage embedding strategy (addresses HN criticism that "naive embeddings don't work"):
- Intent Extraction (LLM): Use a fast model (Claude Haiku 4.5 / GPT-4o-mini) to summarize each PR into a structured intent from title, body, and file list:
{ "problem": "Fix race condition in connection pool", "approach": "Add mutex lock around pool.acquire()", "files_changed_summary": "src/pool.ts: added lock, tests/pool.test.ts: new test", "category": "bug_fix", "scope": "core/networking" } - Diff-Aware Fallback: If the description quality score is below a threshold (empty body, generic description, or auto-generated template text), the system extracts intent from the actual diff hunks instead. This catches PRs where the description is missing or misleading -- common with AI-generated slop.
- Semantic Embedding: Embed the structured intent (not raw diff) using
text-embedding-3-large(3072 dimensions)
- Intent Extraction (LLM): Use a fast model (Claude Haiku 4.5 / GPT-4o-mini) to summarize each PR into a structured intent from title, body, and file list:
- Why three stages: Raw diffs are noisy. Two PRs fixing the same bug with different approaches will have very different diffs but near-identical intents. The LLM extraction normalizes this. The diff-aware fallback ensures PRs with poor descriptions still get meaningful embeddings.
4.2.3 Deduplication Engine
- ANN Index: pgvector with HNSW index for sub-100ms nearest-neighbor lookup
- Adaptive threshold tiers: Default thresholds are provided, but the system calibrates per-repo during initial sync by computing the baseline similarity distribution across all open PRs. Thresholds are then set at percentile-based boundaries relative to that distribution. This prevents a repo with 50 PRs from using the same thresholds as one with 5000.
- Default thresholds (used until calibration completes):
> 0.92similarity: High confidence duplicate (auto-label, auto-comment)0.80 - 0.92: Probable duplicate (flag for review, suggest cluster)0.65 - 0.80: Related (cross-reference only)< 0.65: Distinct (no action)
- Calibrated thresholds are stored per-repo in the
repositories.configcolumn and can be manually overridden via.github/preview.yml
- Default thresholds (used until calibration completes):
- LLM Judge (second pass): For high-similarity pairs, invoke an LLM to confirm: "Are these two PRs solving the same problem? If so, which is better and why?" This addresses the O(n^2) concern by only invoking LLM on ANN-filtered candidates.
- Incremental updates: New PRs only compare against existing index entries, not full pairwise recompute
- Cluster merging: When a new PR matches PRs in two different existing clusters above the probable-duplicate threshold, the system proposes a cluster merge. Merges are auto-applied if both clusters have fewer than 5 members; otherwise, they are flagged for maintainer approval on the dashboard.
4.2.4 PR Quality Ranking Engine
Signal weights (configurable per repo):
| Signal | Weight | Source |
|---|---|---|
| Code quality (lint, complexity) | 0.15 | Static analysis (ESLint, Ruff, etc.) via CI status |
| Test coverage delta | 0.20 | CI coverage reports |
| Diff minimality | 0.10 | Lines changed vs. problem scope |
| Description quality | 0.10 | LLM assessment |
| Author engagement | 0.10 | Current-PR activity (replies, iterations) + contributor history |
| Responsiveness to reviews | 0.10 | Time-to-reply on review comments |
| Abandonment risk (inverse) | 0.10 | Behavioral signals (see below) |
| Vision alignment score | 0.15 | Cosine similarity to vision doc |
Abandonment Risk Signals (replaces "AI-generation detection" -- the problem is abandonment, not authorship):
- No follow-up activity after submission (no replies to comments, no force-pushes)
- Single-commit PRs with large diffs and no iteration
- Generic PR descriptions (auto-generated template text with no customization)
- Account age < 30 days with no prior activity on the repository
- Uniform commit messages ("Update file.py", "Fix issue")
- Unresolved review comments with no author response after 72 hours
Note: The ranking engine does not penalize a PR for being AI-generated. An AI-generated PR that is actively maintained, iterated on, and responsive to reviews scores the same as a human-authored PR with equal quality. The signal is engagement and follow-through, not authorship.
4.2.5 Vision Alignment Engine
- Maintainer provides one or more "vision documents":
ROADMAP.md,VISION.md,CONTRIBUTING.md, or free-text in dashboard - Documents are chunked (by section/heading) and embedded into the vector store with metadata tracking the source document and chunk position
- Each PR's intent embedding is compared against vision chunks
- Alignment score: weighted cosine similarity across top-K vision chunks
- Misalignment report: LLM generates a 2-sentence explanation of why a PR doesn't align (e.g., "This PR adds a dark mode feature, but the current roadmap prioritizes API stability and performance")
- Re-scoring on vision update: When a maintainer updates a vision document, all open PRs are re-scored against the new vision. This is enqueued as a background job with lower priority than real-time webhook processing.
4.2.6 Action Dispatcher
All actions are configurable and auditable:
| Action | Default | Configurable |
|---|---|---|
| Post duplicate comment | ON | Yes |
Apply duplicate-candidate label | ON | Yes |
| Auto-close confirmed duplicates | OFF | Yes (opt-in) |
| Post vision misalignment comment | ON | Yes |
| Auto-close misaligned PRs | OFF | Yes (opt-in) |
| Apply triage labels | ON | Yes |
| Post staleness warnings | ON | Yes |
| Auto-close stale PRs | OFF | Yes (opt-in) |
5. Data Model
-- Core entities
CREATE TABLE repositories (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
github_id BIGINT UNIQUE NOT NULL,
full_name TEXT NOT NULL, -- "owner/repo"
vision_doc TEXT, -- raw vision document text
config JSONB DEFAULT '{}', -- per-repo config overrides (includes calibrated thresholds)
installed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE TABLE clusters (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
repo_id UUID NOT NULL REFERENCES repositories(id) ON DELETE CASCADE,
cluster_type TEXT NOT NULL CHECK (cluster_type IN ('pr', 'issue')),
summary TEXT, -- LLM-generated cluster summary
status TEXT NOT NULL DEFAULT 'open' CHECK (status IN ('open', 'resolved', 'dismissed')),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE TABLE pull_requests (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
repo_id UUID NOT NULL REFERENCES repositories(id) ON DELETE CASCADE,
github_number INT NOT NULL,
github_id BIGINT UNIQUE NOT NULL,
title TEXT NOT NULL,
body TEXT,
author_login TEXT NOT NULL,
author_id BIGINT NOT NULL,
state TEXT NOT NULL CHECK (state IN ('open', 'closed', 'merged')),
files_changed TEXT[],
diff_stats JSONB, -- {additions, deletions, changed_files}
intent_summary JSONB, -- LLM-extracted structured intent
embedding VECTOR(3072), -- semantic embedding
quality_score FLOAT,
abandon_risk FLOAT, -- abandonment risk probability (0-1)
vision_score FLOAT, -- alignment to vision doc (0-1)
staleness_stage TEXT DEFAULT 'active' CHECK (staleness_stage IN ('active', 'warning', 'stale', 'abandoned')),
created_at TIMESTAMPTZ NOT NULL,
updated_at TIMESTAMPTZ NOT NULL,
analyzed_at TIMESTAMPTZ,
UNIQUE(repo_id, github_number)
);
CREATE TABLE issues (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
repo_id UUID NOT NULL REFERENCES repositories(id) ON DELETE CASCADE,
github_number INT NOT NULL,
github_id BIGINT UNIQUE NOT NULL,
title TEXT NOT NULL,
body TEXT,
labels TEXT[],
embedding VECTOR(3072),
created_at TIMESTAMPTZ NOT NULL,
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
analyzed_at TIMESTAMPTZ,
UNIQUE(repo_id, github_number)
);
-- Cluster membership (replaces circular FK between pull_requests/clusters)
CREATE TABLE cluster_members (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
cluster_id UUID NOT NULL REFERENCES clusters(id) ON DELETE CASCADE,
item_type TEXT NOT NULL CHECK (item_type IN ('pr', 'issue')),
item_id UUID NOT NULL,
rank INT, -- position within cluster (1 = best)
similarity FLOAT, -- similarity score to cluster centroid
added_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE(cluster_id, item_id)
);
-- Vision document chunks for alignment scoring
CREATE TABLE vision_chunks (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
repo_id UUID NOT NULL REFERENCES repositories(id) ON DELETE CASCADE,
source_file TEXT, -- "ROADMAP.md", "CONTRIBUTING.md", or "inline"
chunk_index INT NOT NULL, -- position within source document
content TEXT NOT NULL, -- raw chunk text
embedding VECTOR(3072),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- Audit trail for all automated actions
CREATE TABLE actions_log (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
repo_id UUID NOT NULL REFERENCES repositories(id) ON DELETE CASCADE,
target_type TEXT NOT NULL CHECK (target_type IN ('pr', 'issue')),
target_number INT NOT NULL,
action_type TEXT NOT NULL CHECK (action_type IN ('comment', 'label', 'close', 'status_check', 'cluster_assign')),
payload JSONB,
executed_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- Indexes
CREATE INDEX idx_pr_embedding ON pull_requests USING hnsw (embedding vector_cosine_ops);
CREATE INDEX idx_issue_embedding ON issues USING hnsw (embedding vector_cosine_ops);
CREATE INDEX idx_vision_embedding ON vision_chunks USING hnsw (embedding vector_cosine_ops);
CREATE INDEX idx_cluster_members_cluster ON cluster_members(cluster_id);
CREATE INDEX idx_cluster_members_item ON cluster_members(item_type, item_id);
CREATE INDEX idx_pr_repo_state ON pull_requests(repo_id, state);
CREATE INDEX idx_pr_staleness ON pull_requests(repo_id, staleness_stage) WHERE state = 'open';
CREATE INDEX idx_issues_repo ON issues(repo_id);
CREATE INDEX idx_actions_repo_target ON actions_log(repo_id, target_type, target_number);
CREATE INDEX idx_vision_chunks_repo ON vision_chunks(repo_id);
CREATE INDEX idx_clusters_repo_status ON clusters(repo_id, status);
6. Tech Stack
| Layer | Technology | Rationale |
|---|---|---|
| Runtime | Node.js 22 (TypeScript, strict mode) | GitHub ecosystem native, Octokit first-class support |
| Web Framework | Fastify | Low overhead for webhook-heavy workload |
| Queue | BullMQ + Redis | Reliable job processing with retries, rate limiting, priority queues |
| Database | PostgreSQL 17 + pgvector | Single DB for relational + vector storage, reduces operational complexity. pgvector with HNSW is sufficient for sub-100K vectors. |
| ORM / Query Builder | Drizzle ORM | Type-safe queries, migration management, lightweight |
| Embedding Model | OpenAI text-embedding-3-large (3072 dimensions) | Best-in-class retrieval performance. Single model to avoid cross-model similarity drift. |
| LLM (Intent Extraction) | Claude Haiku 4.5 | Fast, cheap, structured output via tool use |
| LLM (Judge/Ranking) | Claude Sonnet 4.5 | Nuanced code understanding for quality ranking |
| Dashboard | Next.js 15 + Tailwind CSS + shadcn/ui | Rapid UI development, SSR for dashboard |
| Auth | GitHub OAuth (GitHub App installation flow) | Zero-friction for target users |
| Monorepo | Turborepo | Shared types/config across API and dashboard |
| Deployment | Docker, docker compose for self-hosted. Railway / Fly.io for SaaS. | Flexible deployment model |
| Monitoring | OpenTelemetry + Grafana | Observability for webhook processing, LLM latency, queue depth |
6.1 Monorepo Structure
preview/
packages/
core/ # Shared types, constants, config schema validation
api/ # Fastify backend, webhook handler, BullMQ workers
dashboard/ # Next.js 15 frontend
github-action/ # Lightweight GitHub Action wrapper
db/ # Drizzle schema, migrations, seed scripts
docker/
docker-compose.yml
Dockerfile.api
Dockerfile.dashboard
turbo.json
package.json
tsconfig.base.json
7. API Specification
7.1 Authentication
All REST API endpoints require authentication via one of:
- GitHub App installation token: For programmatic access and GitHub Action usage. Validated against the GitHub App's installation ID for the target repository.
- Session cookie: For dashboard users authenticated via GitHub OAuth flow. Session is stored server-side in Redis with a 24-hour TTL.
The webhook endpoint (/api/webhooks/github) authenticates via X-Hub-Signature-256 HMAC validation against the app's webhook secret.
7.2 Rate Limiting
All REST API endpoints are rate-limited per installation:
- Standard tier: 100 requests/minute
- Pro/Team tier: 500 requests/minute
- Self-hosted: Configurable, defaults to unlimited
Rate limit headers are included in all responses: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset.
7.3 Pagination
All list endpoints use cursor-based pagination with the following query parameters:
cursor: Opaque cursor string from the previous responselimit: Number of items per page (default: 50, max: 100)
Response format:
{
"data": [...],
"pagination": {
"next_cursor": "eyJpZCI6...",
"has_more": true
}
}
7.4 Webhook Endpoint (Incoming)
POST /api/webhooks/github
Headers: X-GitHub-Event, X-Hub-Signature-256
Validates HMAC signature, enqueues processing job, returns 202 Accepted immediately.
7.5 REST API (Dashboard + Integrations)
GET /api/v1/repos/:owner/:repo/clusters # List PR/Issue clusters
GET /api/v1/repos/:owner/:repo/clusters/:id # Cluster detail with ranked members
POST /api/v1/repos/:owner/:repo/clusters/:id/resolve # Mark cluster resolved
DELETE /api/v1/repos/:owner/:repo/clusters/:id # Dismiss cluster
GET /api/v1/repos/:owner/:repo/prs # List PRs with scores
GET /api/v1/repos/:owner/:repo/prs/:number/analysis # Full PR analysis breakdown
DELETE /api/v1/repos/:owner/:repo/prs/:number/analysis # Clear analysis, trigger re-analyze
GET /api/v1/repos/:owner/:repo/issues # List issues with dedup status
POST /api/v1/repos/:owner/:repo/vision # Upload/update vision document
GET /api/v1/repos/:owner/:repo/vision # Get current vision document and chunks
DELETE /api/v1/repos/:owner/:repo/vision # Remove vision document
GET /api/v1/repos/:owner/:repo/stats # Dashboard statistics
POST /api/v1/repos/:owner/:repo/resync # Trigger full re-analysis (idempotent: deduplicates against in-progress jobs by repo_id)
PUT /api/v1/repos/:owner/:repo/config # Update repo configuration
GET /api/v1/repos/:owner/:repo/config # Get current configuration
GET /api/v1/repos/:owner/:repo/actions # Audit log of all automated actions
7.6 Webhook Notifications (Outgoing)
POST {user_configured_url}
Headers: X-PReview-Event, X-PReview-Signature (HMAC)
Payload: { event: "cluster_created" | "duplicate_detected" | "vision_drift" | "stale_pr", data: {...} }
8. Scalability & Performance
8.1 Target Performance
| Operation | Target Latency | Strategy |
|---|---|---|
| Webhook receipt to job enqueued | < 200ms | Immediate 202 ack, async processing |
| PR analysis (embedding + scoring) | < 30s | Parallel LLM calls, cached embeddings |
| ANN similarity search | < 100ms | HNSW index on pgvector |
| Full cluster recompute (1000 PRs) | < 5 min | Incremental, not full pairwise |
| Dashboard page load | < 500ms | SSR + query optimization |
8.2 Scaling Strategy
- Horizontal worker scaling: BullMQ workers scale independently; add workers when queue depth exceeds threshold
- Embedding cache: Once computed, embeddings are immutable. PR body changes trigger re-embed.
- LLM cost control: Intent extraction only on new/updated PRs; judge invocations only on ANN-filtered pairs (reduces O(n^2) to O(n * k) where k = avg cluster size)
8.3 Cost Model
Per 1000 PRs analyzed (initial analysis):
| Operation | Estimated Cost |
|---|---|
| Intent extraction (Haiku, ~500 tokens/PR) | ~$0.50 |
| Embeddings (3072-dim, ~500 tokens/PR) | ~$0.30 |
| Judge calls (Sonnet, ~50 comparisons at 1K tokens) | ~$2.00 |
| Total | ~$2.80 |
Re-analysis costs (must be budgeted separately):
| Trigger | Scope | Estimated Cost per 1000 PRs |
|---|---|---|
| Vision document update | Re-score all open PRs (no re-embed) | ~$0.20 |
| Embedding model upgrade | Re-embed + re-cluster all PRs | ~$3.50 |
| Threshold recalibration | Re-cluster only (no re-embed, no LLM) | ~$0.00 |
9. Deployment Models
9.1 SaaS (Hosted)
- GitHub App installed from marketplace
- Freemium: free for public repos with < 500 open PRs, paid tiers for larger repos / private repos
- Multi-tenant, isolated per-repo data
9.2 Self-Hosted (Open Source)
- Single
docker compose upwith PostgreSQL + Redis + app - Bring-your-own LLM API keys (OpenAI, Anthropic, or local models via Ollama)
- Configuration via
.github/preview.ymlin repo root
9.3 GitHub Action (Lightweight)
- For repos that want dedup/triage without a full deployment
- Runs on
pull_requestandissuestriggers - Uses GitHub Actions cache for embedding persistence
- Limited to per-run analysis (no persistent cluster state)
10. Configuration Schema
# .github/preview.yml
preview:
version: 1
# Vision document(s) for alignment checking
vision:
files:
- ROADMAP.md
- CONTRIBUTING.md
text: |
Focus on API stability and performance.
No new UI features until v3.0.
# Deduplication settings
dedup:
pr_threshold: 0.85 # similarity score for duplicate flag (overrides calibrated value)
issue_threshold: 0.88
auto_close_duplicates: false
exclude_labels: ["do-not-close", "wip"]
# Quality ranking weights
# Must sum to 1.0. If they do not, the system normalizes them proportionally
# and logs a warning. Omitted fields use the default weight.
ranking:
code_quality: 0.15
test_coverage: 0.20
diff_minimality: 0.10
description_quality: 0.10
author_engagement: 0.10
responsiveness: 0.10
abandonment_risk: 0.10
vision_alignment: 0.15
# Staleness rules
staleness:
warning_after_days: 14
stale_after_days: 30
close_after_days: 60
exempt_labels: ["long-running", "blocked", "do-not-close"]
# Abandonment risk detection
abandonment_detection:
enabled: true
flag_threshold: 0.75 # probability above this triggers flag
auto_label: true
# Notifications
notifications:
webhook_url: https://your-slack-webhook.example.com
webhook_secret: ${PREVIEW_WEBHOOK_SECRET} # HMAC signing secret for outgoing webhooks
events: [cluster_created, vision_drift, stale_pr]
Validation rules:
rankingweights must be non-negative. If they do not sum to 1.0, the system auto-normalizes and logs a warning.- Omitted
rankingfields inherit default values before normalization. stalenessday values must satisfy:warning_after_days < stale_after_days < close_after_days.dedupthreshold values must be between 0.0 and 1.0.- Invalid configuration is rejected on save via the API. For
.github/preview.yml, invalid config is reported as a commit status check failure with a descriptive error message.
11. Competitive Landscape & Differentiation
| Tool | Scope | Key Limitation | PReview Advantage |
|---|---|---|---|
| ai-duplicate-detector | Issue dedup only | No PR support, 100 issues/run cap, no ranking | Full PR+Issue dedup, ranking, vision alignment |
| Dosu | Issue triage | No PR dedup, no code-level analysis | Deep PR analysis with diff understanding |
| trIAge | Issue classification | No PR quality ranking | Multi-signal PR ranking engine |
| PR-Agent (Qodo) | PR review | No dedup, no clustering, no vision alignment | Complementary -- PReview triages, PR-Agent reviews |
| CodeRabbit | PR review | No dedup, no maintainer dashboard | Different focus -- PReview is triage, not review |
| GitHub native (proposed) | TBD | Not shipped yet, likely basic | Purpose-built, open-source, extensible |
PReview's unique value: It is the only tool that combines deduplication + quality ranking + vision alignment + maintainer dashboard in a single platform. It does not replace code review tools -- it ensures maintainers review the right PRs.
12. Commercialization Strategy
12.1 Open-Source Core (AGPLv3)
- Full dedup engine, ranking, triage, GitHub Action
- Self-hosted deployment with BYO API keys
- Community-maintained, accepting contributions
12.2 Paid SaaS Tiers
| Tier | Price | Includes |
|---|---|---|
| Free | $0 | Public repos, < 500 open PRs, 3 repos |
| Pro | $49/mo | Private repos, < 5000 open PRs, 20 repos, Slack/Discord notifications |
| Team | $149/mo | Unlimited repos, team dashboard, SSO, priority queue, custom ranking weights |
| Enterprise | Custom | On-prem deployment, dedicated support, custom LLM integration, audit logs |
12.3 Revenue Expansion
- Marketplace integrations: Jira, Linear, Notion sync for cluster management
- Analytics: PR velocity trends, contributor quality scores, vision drift over time
- API access: Charge for high-volume API usage beyond included tiers
13. Implementation Phases
Phase 1: Infrastructure & Ingestion (Weeks 1-3)
- Monorepo setup (Turborepo, TypeScript strict, ESLint, Prettier)
- CI/CD pipeline (GitHub Actions: lint, type-check, test, build)
- Database schema and migrations (Drizzle ORM, pgvector extension)
- GitHub App scaffold with webhook receiver (Fastify, signature validation)
- PR/Issue ingestion pipeline with batch sync worker
- Rate-limited GitHub client (Octokit wrapper with token rotation, ETags)
- BullMQ job queue setup with Redis
Phase 2: Core Intelligence (Weeks 4-6)
- Intent extraction LLM pipeline (structured output via Claude Haiku)
- Diff-aware fallback for low-quality descriptions
- Embedding generation and pgvector HNSW storage
- ANN-based deduplication with default thresholds
- LLM Judge for duplicate confirmation (Claude Sonnet)
- Cluster creation and membership management
- GitHub comment/label action dispatcher
Phase 3: Advanced Features (Weeks 7-9)
- PR quality ranking engine (all 8 signals)
- Abandonment risk scoring
- Vision document ingestion, chunking, and embedding
- Vision alignment scoring and misalignment reports
- Per-repo threshold calibration during initial sync
- Staleness detection cron job
- Configuration loading from
.github/preview.yml - Configuration validation and commit status reporting
Phase 4: Dashboard & Polish (Weeks 10-13)
- Next.js dashboard with GitHub OAuth authentication
- Cluster visualization and management UI
- Triage queue with bulk actions
- Vision alignment heatmap
- Outgoing webhook notifications
- Monitoring and alerting (OpenTelemetry)
- Documentation site and onboarding flow
- GitHub Action wrapper for lightweight usage
Phase 5: Scale & Launch (Weeks 14-16)
- Multi-tenant SaaS deployment
- Billing integration (Stripe)
- API rate limiting and usage metering
- Performance optimization for repos with 5000+ open PRs
- Public launch and GitHub Marketplace listing
14. Success Metrics
| Metric | Target (6 months post-launch) |
|---|---|
| Repos installed | 500+ |
| PRs analyzed | 100K+ |
| Duplicate detection precision | > 90% |
| Duplicate detection recall | > 75% |
| Maintainer time saved per week (surveyed) | > 5 hours |
| False positive rate (incorrect duplicate flag) | < 5% |
| Mean time from PR open to triage label | < 5 minutes |
| Dashboard MAU | 200+ |
15. Risk Register
| Risk | Impact | Likelihood | Mitigation |
|---|---|---|---|
| LLM hallucinations mislabel PRs | High | Medium | LLM Judge as second pass; human-in-the-loop for destructive actions; confidence scores displayed |
| GitHub API rate limits hit on large repos | High | High | Token rotation, conditional requests (ETags), aggressive caching, batch sync during off-peak |
| Embedding model changes break similarity thresholds | Medium | Low | Version-pin embeddings; re-embed on model change with migration script; per-repo threshold recalibration |
| Maintainer trust erosion from false positives | High | Medium | Conservative default thresholds; all auto-close actions OFF by default; clear audit trail |
| LLM cost spikes on viral repos | Medium | Medium | Cost caps per repo; intent extraction caching; tiered LLM usage (Haiku first, Sonnet only for judge) |
| GitHub ships native solution | High | Medium | Open-source moat; deeper features (vision alignment, ranking); platform-agnostic v2 |
16. Open Questions for Implementation
- Embedding model benchmarking: Run benchmarks comparing
text-embedding-3-largevs open-source alternatives (e.g.,nomic-embed-text) on a PR dedup dataset. Validate that the chosen model gives best precision/recall at acceptable cost. - Cluster merging strategy: Validate the "auto-merge below 5 members, flag above" heuristic against real-world cluster data. Tune the merge threshold.
- Cross-repo dedup: For monorepo or multi-repo projects, should dedup work across repositories? Deferred to v2 but needs data model consideration now.
- Diff-level analysis: Should the system compare actual code diffs (AST-level) in addition to intent embeddings for higher accuracy? Evaluate cost/benefit.
- Feedback loop: How should maintainer actions (accepting/rejecting dedup suggestions) feed back into per-repo threshold calibration? Design the feedback data model.
This TRD is the blueprint for PReview -- a tool that addresses a real, validated problem (3100+ PRs on OpenClaw, GitHub considering kill-switches for AI PRs). No existing tool addresses the full scope of deduplication + ranking + vision alignment. Build it open-source, monetize the hosted version.
What's inside
6 sections covering problem statement, scope, 6 use cases, architecture with 6 components, data model with 7 tables and 10 indexes
Change this for your project
- Replace
Sauhard74/PReviewwith your own repository name - Replace
OpenClawreferences with your target repository - Replace
text-embedding-3-largewith your chosen embedding model - Replace
Claude Haiku 4.5 / GPT-4o-miniwith your LLM model
Where it goes
Keep in docs/ or alongside the feature. Agents read it to implement against a defined contract.
Worth borrowing
- Three-stage embedding strategy (LLM intent extraction, diff-aware fallback, semantic embedding) to handle noisy PR descriptions
- Adaptive similarity thresholds calibrated per-repo based on baseline distribution, with manual override via config file
- Abandonment risk signals based on behavioral patterns rather than AI-generation detection
Related Documents
GPU Selection Guide for Large Language Models (LLMs)
Guides GPU selection for LLM inference, fine-tuning, and training by mapping model sizes, precision levels, and budgets to VRAM requirements.
Community AI Agent Skills Discovery Sources
Catalogs 50+ platforms, repositories, directories, and communities for discovering and sharing AI agent skills across multiple coding tools.
ReleaseKit - Technical Requirements Document
Specifies a Go library and CLI for release automation with conventional commit parsing, validation checks, and workflow orchestration.
api_llm Specification
Defines a workspace of thin HTTP API clients for major LLM providers with no abstraction layer and explicit developer control.