Back to .md Directory

Technical Requirements Document (TRD)

Specifies requirements for an AI platform that deduplicates and ranks pull requests and issues for open-source maintainers.

May 2, 2026
0 downloads
1 views
ai workflow
View source

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

PostgreSQL 17pgvectorNext.js 15FastifyBullMQ + RedisGitHub API (Octokit)

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:

MetricReality
PR growth rate (OpenClaw)400+ new PRs/day
Legitimate AI-generated PRs~10% (source)
Maintainer review capacity~50-80 PRs/day (generous)
Duplicate/overlapping PRsEstimated 30-60% on viral repos

Core pain points (from steipete's tweet + thread + HN discussion):

  1. Duplicate PRs/Issues -- Multiple contributors solve the same problem independently. No tooling surfaces this at scale.
  2. Quality signal detection -- Among N duplicate PRs, identifying the best one requires deep code review, not just surface matching.
  3. Vision drift -- PRs that don't align with project goals waste review cycles. Maintainers need automated alignment checks against a "vision document."
  4. Cognitive overload -- The review trust model is broken when PR authors (human or AI) may not understand their own submissions.
  5. 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

CapabilityDescription
PR DeduplicationSemantic clustering of PRs that address the same issue/feature
Issue DeduplicationDetection and linking of duplicate/related issues
PR Quality RankingMulti-signal scoring to rank competing PRs solving the same problem
Vision Alignment CheckCompare PR intent against a maintainer-defined project vision/roadmap document
Automated Triage LabelsAuto-classification: bug fix, feature, refactor, docs, test, chore
PR Staleness DetectionFlag abandoned, stale, or low-engagement PRs
Maintainer DashboardWeb UI for reviewing clusters, rankings, and triage decisions
GitHub IntegrationNative GitHub App + GitHub Actions workflow
APIREST + 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:

  1. System receives webhook for new PR
  2. Extracts PR metadata: title, description, changed files, diff hunks, linked issues
  3. Generates semantic embedding of PR intent (not just code similarity)
  4. Compares against all open PRs using ANN (Approximate Nearest Neighbor) index
  5. If similarity > configurable threshold, creates a PR Cluster
  6. Posts a comment on the PR: "This PR appears related to #X, #Y, #Z. Cluster: [link to dashboard]"
  7. Labels PR with duplicate-candidate or cluster: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, not duplicate
  • 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:

  1. 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)
  2. Weighted composite score displayed in dashboard
  3. Maintainer sees ranked list with drill-down into each signal
  4. 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:

  1. Maintainer uploads/links a Vision Document (markdown, project roadmap, CONTRIBUTING.md, or free-text goals)
  2. System chunks and embeds the vision document into the vector store
  3. On each new PR, system computes alignment score: how well does this PR's intent match stated project goals?
  4. PRs with low alignment get labeled needs-alignment-review and a comment explaining the mismatch
  5. 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:

  1. System embeds issue title + body
  2. Searches existing open issues for semantic duplicates
  3. If duplicate found: comment with link to original, apply duplicate label
  4. 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:

  1. 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
  2. Apply a single stale label. Staleness stage (warning, stale, abandoned) is tracked as metadata in the database and displayed on the dashboard, not as separate labels.
  3. Post comment with grace period before auto-close
  4. Exclude PRs with do-not-close or other exempt labels (configurable)

UC-6: Maintainer Dashboard

Actor: Maintainer Flow:

  1. Overview: total open PRs, clusters, duplicates detected, PRs triaged today
  2. Cluster view: grouped PRs with rankings, diffs, and signal breakdowns
  3. Vision alignment heatmap: which areas of the codebase are getting PRs vs. what the vision prioritizes
  4. Triage queue: PRs sorted by priority (needs review > needs-alignment > stale)
  5. 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"):
    1. 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"
      }
      
    2. 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.
    3. Semantic Embedding: Embed the structured intent (not raw diff) using text-embedding-3-large (3072 dimensions)
  • 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.92 similarity: 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.config column and can be manually overridden via .github/preview.yml
  • 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):

SignalWeightSource
Code quality (lint, complexity)0.15Static analysis (ESLint, Ruff, etc.) via CI status
Test coverage delta0.20CI coverage reports
Diff minimality0.10Lines changed vs. problem scope
Description quality0.10LLM assessment
Author engagement0.10Current-PR activity (replies, iterations) + contributor history
Responsiveness to reviews0.10Time-to-reply on review comments
Abandonment risk (inverse)0.10Behavioral signals (see below)
Vision alignment score0.15Cosine 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:

ActionDefaultConfigurable
Post duplicate commentONYes
Apply duplicate-candidate labelONYes
Auto-close confirmed duplicatesOFFYes (opt-in)
Post vision misalignment commentONYes
Auto-close misaligned PRsOFFYes (opt-in)
Apply triage labelsONYes
Post staleness warningsONYes
Auto-close stale PRsOFFYes (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

LayerTechnologyRationale
RuntimeNode.js 22 (TypeScript, strict mode)GitHub ecosystem native, Octokit first-class support
Web FrameworkFastifyLow overhead for webhook-heavy workload
QueueBullMQ + RedisReliable job processing with retries, rate limiting, priority queues
DatabasePostgreSQL 17 + pgvectorSingle DB for relational + vector storage, reduces operational complexity. pgvector with HNSW is sufficient for sub-100K vectors.
ORM / Query BuilderDrizzle ORMType-safe queries, migration management, lightweight
Embedding ModelOpenAI 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.5Fast, cheap, structured output via tool use
LLM (Judge/Ranking)Claude Sonnet 4.5Nuanced code understanding for quality ranking
DashboardNext.js 15 + Tailwind CSS + shadcn/uiRapid UI development, SSR for dashboard
AuthGitHub OAuth (GitHub App installation flow)Zero-friction for target users
MonorepoTurborepoShared types/config across API and dashboard
DeploymentDocker, docker compose for self-hosted. Railway / Fly.io for SaaS.Flexible deployment model
MonitoringOpenTelemetry + GrafanaObservability 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 response
  • limit: 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

OperationTarget LatencyStrategy
Webhook receipt to job enqueued< 200msImmediate 202 ack, async processing
PR analysis (embedding + scoring)< 30sParallel LLM calls, cached embeddings
ANN similarity search< 100msHNSW index on pgvector
Full cluster recompute (1000 PRs)< 5 minIncremental, not full pairwise
Dashboard page load< 500msSSR + 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):

OperationEstimated 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):

TriggerScopeEstimated Cost per 1000 PRs
Vision document updateRe-score all open PRs (no re-embed)~$0.20
Embedding model upgradeRe-embed + re-cluster all PRs~$3.50
Threshold recalibrationRe-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 up with PostgreSQL + Redis + app
  • Bring-your-own LLM API keys (OpenAI, Anthropic, or local models via Ollama)
  • Configuration via .github/preview.yml in repo root

9.3 GitHub Action (Lightweight)

  • For repos that want dedup/triage without a full deployment
  • Runs on pull_request and issues triggers
  • 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:

  • ranking weights must be non-negative. If they do not sum to 1.0, the system auto-normalizes and logs a warning.
  • Omitted ranking fields inherit default values before normalization.
  • staleness day values must satisfy: warning_after_days < stale_after_days < close_after_days.
  • dedup threshold 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

ToolScopeKey LimitationPReview Advantage
ai-duplicate-detectorIssue dedup onlyNo PR support, 100 issues/run cap, no rankingFull PR+Issue dedup, ranking, vision alignment
DosuIssue triageNo PR dedup, no code-level analysisDeep PR analysis with diff understanding
trIAgeIssue classificationNo PR quality rankingMulti-signal PR ranking engine
PR-Agent (Qodo)PR reviewNo dedup, no clustering, no vision alignmentComplementary -- PReview triages, PR-Agent reviews
CodeRabbitPR reviewNo dedup, no maintainer dashboardDifferent focus -- PReview is triage, not review
GitHub native (proposed)TBDNot shipped yet, likely basicPurpose-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

TierPriceIncludes
Free$0Public repos, < 500 open PRs, 3 repos
Pro$49/moPrivate repos, < 5000 open PRs, 20 repos, Slack/Discord notifications
Team$149/moUnlimited repos, team dashboard, SSO, priority queue, custom ranking weights
EnterpriseCustomOn-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

MetricTarget (6 months post-launch)
Repos installed500+
PRs analyzed100K+
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 MAU200+

15. Risk Register

RiskImpactLikelihoodMitigation
LLM hallucinations mislabel PRsHighMediumLLM Judge as second pass; human-in-the-loop for destructive actions; confidence scores displayed
GitHub API rate limits hit on large reposHighHighToken rotation, conditional requests (ETags), aggressive caching, batch sync during off-peak
Embedding model changes break similarity thresholdsMediumLowVersion-pin embeddings; re-embed on model change with migration script; per-repo threshold recalibration
Maintainer trust erosion from false positivesHighMediumConservative default thresholds; all auto-close actions OFF by default; clear audit trail
LLM cost spikes on viral reposMediumMediumCost caps per repo; intent extraction caching; tiered LLM usage (Haiku first, Sonnet only for judge)
GitHub ships native solutionHighMediumOpen-source moat; deeper features (vision alignment, ranking); platform-agnostic v2

16. Open Questions for Implementation

  1. Embedding model benchmarking: Run benchmarks comparing text-embedding-3-large vs 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.
  2. Cluster merging strategy: Validate the "auto-merge below 5 members, flag above" heuristic against real-world cluster data. Tune the merge threshold.
  3. Cross-repo dedup: For monorepo or multi-repo projects, should dedup work across repositories? Deferred to v2 but needs data model consideration now.
  4. Diff-level analysis: Should the system compare actual code diffs (AST-level) in addition to intent embeddings for higher accuracy? Evaluate cost/benefit.
  5. 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/PReview with your own repository name
  • Replace OpenClaw references with your target repository
  • Replace text-embedding-3-large with your chosen embedding model
  • Replace Claude Haiku 4.5 / GPT-4o-mini with 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