Esc
↑↓ to move↵ to openEsc to close

DocsGetting started

How it works

The path of one pull request, from webhook to ranked queue.

Every pull request crosses four services. Each boundary is a place where the failure mode changes, which is why they are separate processes rather than modules of one.

One pull request through Riffle's four servicesDiagram
Mermaid source
%% caption: One pull request through Riffle's four services
graph LR
  gh[GitHub] -->|webhook| intake["intake<br/>dedupe by delivery ID"]
  intake -->|PrEvent| scorer
  scorer --> app
  app --> queue[Review queue]
  scorer -.->|explain| explainer
  explainer -.->|sentence, or null| scorer

One pull request, end to end

StepWhereWhat happensBudget
1intakeVerifies the webhook signature, deduplicates by delivery ID, publishes a PrEvent10 s, GitHub's limit
2scorerExtracts features, runs the global model plus the repository layer, writes a ScoreResultNever lost
3explainerTurns the feature vector and band into one sentenceMay time out
4appPlaces the PR in its band and posts the explanationThe only human surface

Why the boundaries fall where they do

Intake must never be slow. GitHub allows ten seconds for a webhook response, so intake does four things and nothing else: verify, deduplicate, publish, return 200.

Scoring must never be lost. An unranked pull request is a broken promise, so scoring is idempotent: the same delivery ID can never produce two scores.

Explanation is allowed to fail. The explainer runs an LLM behind a hard timeout with a deterministic template fallback. When it is slow or down, the explanation field is null and the rank is still valid.

What each pull request produces

json
{
  "delivery_id": "8f2c…a91",
  "pr_number": 2841,
  "risk_score": 0.81,
  "rank_band": "senior_recommended",
  "model_version": "v7",
  "explanation": "Touches a path this repository has reverted twice this quarter."
}

The full shape is on Contracts.