Manual · genetic-variant-triage
Open the console →

When a genetic test says “we don’t know”

Roughly half of the missense variants in ClinVar are filed as uncertain — real mutations in real patients that nobody has been able to call harmful or harmless. This is a manual for a tool that reconciles an AI model’s opinion against that curated record, and — the part that matters — grades how much structural evidence actually stands behind each call.

The problem

A person is tested for a hereditary cancer risk. The lab sequences the relevant genes, finds a single letter of DNA that differs from the reference, and reports back. Sometimes the report says pathogenic. Sometimes benign. Very often it says neither.

That third answer is a variant of uncertain significance — a VUS. It does not mean the mutation is harmless. It means no one has assembled enough evidence to say either way. The patient gets a result that cannot inform a decision: no basis for surveillance, no basis for surgery, no basis for testing relatives.

Uncertainty is the default because certainty is expensive. Calling a variant requires seeing it in enough families, or running a functional assay, or finding it in the literature. Sequencing has raced ahead of that work. Every genome sequenced finds new variants faster than anyone can characterise them, and the rare ones — the ones most likely to matter — are by definition the ones seen least often.

Across the eight genes this tool loads, ClinVar records 14,359 missense variants as uncertain or conflicting. Each one is somebody’s inconclusive test result.

The idea

A machine-learning model can produce a prediction for every possible mutation in every human protein, including all the ones ClinVar is stuck on. That is useful and dangerous in equal measure: useful because it covers the gap, dangerous because a prediction presented with the same confidence as a curated clinical judgement invites people to treat them as equivalent.

The tool does three things, in order:

  1. Reconcile. Line the model’s prediction up against ClinVar’s assertion for the same variant. Where ClinVar says uncertain and the model is confident, that is a candidate for a second look.
  2. Grade the evidence. A prediction about a protein depends on knowing the protein’s shape. Sometimes that shape has been physically determined in a laboratory; sometimes it is itself a prediction that the predicting model is unsure about. Those are not the same, and the tool refuses to display them as though they were.
  3. Hand it to a human. The output is a ranked worklist of candidates for expert review, with the reasoning attached. It is not a diagnosis and never claims to be.

That third point is a constraint enforced in code, not just a disclaimer. A test in the suite fails if any review outcome in the vocabulary reads like a clinical call — endorsed and rejected are permitted; anything matching pathogenic or benign is not.

Where the data comes from

Five public sources, none requiring an API key, none requiring a GPU. Nothing here is folded, inferred or trained locally — every model output consumed has already been computed and published by someone else.

AlphaFold DB structure + pLDDT AlphaMissense variant scores ClinVar clinical assertions Open Targets disease + drugs RCSB PDB solved structures per accession byte-range fetch ontology 23 typed tables provenance on every row dataframes rules pure functions no I/O, no database decided classes variant console which are misclassified target dossier is the gene worth pursuing exported as static files — the browser only renders
Everything upstream of the rules layer is fetching and typing. Everything downstream is rendering. The rules in the middle touch no database and no user interface, which is why the entire frontend was replaced once without a single rule changing.

ClinVar

US National Center for Biotechnology Information

A public archive where clinical laboratories, hospitals and expert panels deposit what they have concluded about specific variants. It is accumulated human judgement, and it carries its own confidence signal: a star rating from zero (an assertion with no stated criteria) to four (a professional practice guideline).

Its limit: coverage is uneven and the uncertain pile is enormous. Of the uncertain variants this tool loads, 80% carry only one star — a single submitter, no corroboration. The tool separates those out rather than counting them alongside expert-panel calls.

AlphaMissense

Google DeepMind · CC BY-NC-SA 4.0

A model that scores every possible single-amino-acid substitution in the human proteome — roughly 71 million of them — for how likely it is to break the protein. It publishes a number from 0 to 1 and a class: likely benign, ambiguous, or likely pathogenic.

Its limit: it predicts a molecular effect, not a clinical one, and its class thresholds were calibrated on ClinVar — which constrains what agreement with ClinVar is allowed to prove. Both points are unpacked in §11.

AlphaFold Protein Structure Database

EMBL-EBI & Google DeepMind

Predicted three-dimensional structures for essentially every known protein, free to download. Critically it also publishes pLDDT — a per-residue score, 0 to 100, in which the model states how much it trusts its own answer at that specific position.

Its limit: a predicted structure is a single static snapshot. Real proteins move, and low confidence sometimes means the region has no fixed shape at all rather than that the model failed.

RCSB Protein Data Bank

Research Collaboratory for Structural Bioinformatics

The archive of structures determined experimentally — by X-ray crystallography, NMR or cryo-electron microscopy. Each entry records which stretch of the protein it actually resolved.

Its limit: coverage is domain-shaped, never whole-protein. The pieces that crystallise are the rigid, well-behaved ones; floppy regions are systematically under-represented.

Open Targets Platform

EMBL-EBI, Wellcome Sanger Institute and partners

Aggregated evidence linking genes to diseases, scored and split by the kind of evidence — genetic association, literature co-mention, known drugs, somatic mutation. It also carries tractability assessments, the drugs known to act on a target, and the clinical trials they appear in.

Its limit: a high overall score can be driven entirely by literature volume. The split matters more than the headline, which is why the dossier shows the components rather than the total.

Probing these APIs before designing anything paid for itself immediately. Open Targets already aggregates ChEMBL drug mechanisms and clinical trials, so two planned connectors were deleted before they were written — including the one dependency that had been returning server errors during testing.

How a machine can score a mutation

Both AlphaFold and AlphaMissense rest on one idea: evolution has already run the experiment, on a scale no laboratory could match.

Every protein you have exists in thousands of other species, each version tweaked over hundreds of millions of years. If a position tolerates many different amino acids across all of them, changing it is probably survivable — organisms have been trying that for aeons and are still here. If a position is identical in everything from humans to yeast, selection has been ruthlessly deleting whatever changed it.

the same stretch of one protein, in five species human mouse chicken zebrafish yeast L V C G S T A Q L I C G T T A Q M V C G N S A K L V C A D T A R I L C G P S A H never changes in 1.5 billion years a substitution here is a red flag changes freely a substitution here is probably tolerated a second signal: what moves together if a change at one position keeps being accompanied by a compensating change at another, the two are probably touching in 3D — even when they sit far apart in the sequence. Enough such constraints and the fold falls out. That is AlphaFold. Reading the same alignment for “how surprising is this letter here”, with the structure as context, is AlphaMissense.
The conserved column on the left is doing structural or functional work — selection has removed every organism that changed it. The variable column is not. This single comparison, run across the whole proteome, is most of where a pathogenicity score comes from.

From alignment to a number

AlphaMissense is built on AlphaFold’s architecture and starts from its weights, then adds two things. First, a language model over proteins: mask out an amino acid and learn to predict what belongs there, given the surrounding sequence, the evolutionary alignment, and the structural context AlphaFold provides. That yields a measure of surprise — a mutation placing a highly improbable amino acid at a position is a red flag, and the model knows whether that position is buried in the protein’s core, in a binding site, or dangling on a loop.

Second, weak labels from human population data. Variants common across human and primate populations stand in for “harmless”; variants conspicuously absent stand in for “harmful”, on the reasoning that selection has been quietly removing the bad ones. Note what is not in that training signal: ClinVar’s clinical labels. That omission is what makes comparing the two meaningful rather than circular — with one important qualification in §11.

What pLDDT actually is

When AlphaFold predicts a structure it also predicts, for each residue, how close its own answer is likely to be to the truth. That is pLDDT: above 90 is very high, 70 to 90 confident, 50 to 70 low, below 50 very low. It is the model’s own statement of doubt, and the tool leans on it heavily — but not naively, for reasons that take up the whole of §6.

The triage matrix

Two opinions arrive for each variant: what ClinVar’s curators concluded, and what the model predicts. The matrix is the entire product — everything else in the system exists to fill it in honestly or to render it.

Rows: what ClinVar says. Columns: what AlphaMissense predicts.
likely pathogenic ambiguous likely benign
uncertain / conflicting reclass_upgrade the reason the tool exists remains_uncertain the model cannot help here reclass_downgrade possible relief for a patient
pathogenic concordant model agrees with the curator model_uninformative discordant model contradicts a clinical call
benign discordant model contradicts a clinical call model_uninformative concordant
no assertion novel_candidate ClinVar has never seen it unasserted unasserted
other axis not_triaged ClinVar’s claim is about drug response or risk modification, not pathogenicity — a different question entirely

Needs a human · Possible downgrade · No signal

The matrix is built by taking every combination of the two vocabularies, so a pair cannot silently fall through to a default. A test asserts that completeness — which is how the last two rows came to exist at all. Both were missing from the original design and only surfaced when the data was examined rather than assumed.

An asymmetry worth knowing. Upgrade candidates cluster on residues with solid structural evidence; downgrades cluster in poorly-resolved, disordered regions — because that is where benign variation lives. A downgrade is therefore structurally less supportable than an upgrade, and the interface must not present the two with equal weight.

Structural confidence

The first design of this tool gated everything on pLDDT: trust the structural reading above 70, distrust it below. That was wrong, and the variant that proved it is the most consequential one in the dataset.

EGFR L858R is the classic activating mutation in non-small-cell lung cancer — the mutation that a whole generation of targeted therapies was built around. Its pLDDT is 51.2. A pLDDT-only gate would have quietly demoted it.

AlphaFold is unconfident there for a real reason: L858 sits in the kinase activation loop, a stretch that genuinely moves. Low confidence was not failure, it was an accurate report about a flexible region. And that same region has been crystallised many times over, because it is exactly where drugs bind.

pLDDT 100 0 70 solved ectodomain · 1IVO kinase domain · 1M14 L858R pLDDT 51.2 — below the cut but inside a solved region 25 400 646 1022 1210 residue position along EGFR C-terminal tail: unconfident and unsolved
Two independent signals, deliberately kept apart. The line is what the prediction thinks of itself; the bands are what laboratories have actually determined. They disagree at residue 858 — and where they disagree, the experiment wins.

So the binary gate became three ordered tiers:

TierMeaningHow it is decided
experimentally solved A laboratory has physically determined this residue’s position. The residue falls inside a span some PDB entry resolved.
confidently predicted No experiment, but AlphaFold is sure of itself here. pLDDT ≥ 70 and no experimental coverage.
predicted, unreliable A guess about terrain the model itself doubts. pLDDT < 70 and no experimental coverage.

Experimental coverage overrides pLDDT rather than being averaged with it. A solved residue is solved regardless of what the prediction thought.

What the structure shows that the sequence cannot

A fair challenge to any 3D view in a tool like this is that it is decoration. Mostly it would be: the triage decision is driven by the ClinVar class, the model score, the star rating and the evidence tier, every one of which is in the table. The sequence profile already shows variant burden and structural confidence in a form you can read positions off. A rotating ribbon adds nothing to that.

There is exactly one thing it can do that a one-dimensional view structurally cannot: show which residues are close together in space. Positions far apart along the chain can be touching in the folded protein, so a set of damaging variants that looks scattered in sequence can be a single cluster around a functional site — which is the difference between a coincidence and a mechanism.

So the console states that as a finding rather than leaving it to be admired. Selecting PTEN R130G reports 17 likely-pathogenic residues within 8 Å, nine of them more than ten positions away in the sequence, and lights those contacts up in the structure. R130 is the catalytic arginine of the phosphatase motif; the cluster around it is the active site. No profile could have told you that.

The distances are computed from the coordinates the viewer has already loaded, so the finding costs no extra data. Alpha-carbon separation is a coarse proxy for contact — real side chains reach further than their backbone — and 8 Å is the conventional cutoff on that measure, applied uniformly rather than tuned per protein.

A second thing this corrected

The tiers also overturned an assumption about BRCA1. Only 17.6% of that protein has ever been solved — it is largely disordered, and the original expectation was that the structural layer would be useless for it. In fact 73% of its reclassification candidates sit on solved residues, because pathogenic missense variation clusters in the folded domains where function lives, and those are exactly the parts that crystallise.

The lesson generalises: a protein’s average confidence is the wrong statistic. What matters is the confidence at the specific residues you are making claims about.

Building a worklist

About a quarter of predictions land in an actionable class — far too many to review. Priority narrows that to a queue a curator could actually work through, using rules rather than a score.

A variant reaches high priority only when all three hold:

  • it sits on an experimentally solved residue;
  • ClinVar’s assertion carries two stars or more — more than one submitter stands behind it;
  • the model’s score is near a rail (≥ 0.9 or ≤ 0.1), not merely over the line.

This is deliberately not a weighted composite. A single number cannot be argued with and hides which evidence carried it; a curator asking “why is this at the top of my queue?” gets the actual reasons back, because each variant carries them.

Applied to eight genes, that takes 13,247 actionable variants down to 1,023 at high priority — out of 55,177 predictions.

The second question

Knowing a gene carries damaging variants raises an obvious follow-up: could it be drugged? The dossier answers that from the same ontology, across five banded dimensions — genetic evidence, structural readiness, binding site, chemical matter, and variant burden — and derives a readable archetype rather than a score.

GeneGeneticPocketDrugs SolvedArchetype
EGFRstrongyes 8287.4% validated druggable target
SCN1Astrongno 72100% validated druggable target
CFTRstrongyes 13100% validated druggable target
TP53strongno 9100% genetically validated, clinically emerging
KRASstrongno 3100% validated druggable target
MLH1strongno 082.5% genetically validated, chemically unexplored
PTENstrongno 0100% genetically validated, chemically unexplored
BRCA1strongno 017.6% genetically validated, chemically unexplored

KRAS is the row that rewards a second look. Open Targets reports no pocket — and it has three approved drugs. Both are true: KRAS was called undruggable for forty years because its surface is smooth, and the inhibitors that finally worked bind a cryptic pocket that only opens in one conformation of one mutant. A tool that resolved the contradiction by picking a side would have destroyed the most interesting thing on the row.

BRCA1 is the instructive row. The genetic evidence tying it to breast cancer is about as strong as biology gets — and it has no pocket, no ligand and no drugs. That is not a gap in the data; it is the real state of the world for a tumour suppressor, where the therapeutic strategy has to be indirect. A tool that quietly hid the empty column would be lying by omission.

Every one of BRCA1’s twenty experimental structures covers only residues 1646–1859 — the BRCT domain. That single column is the 17.6% solved figure from §6, visible directly.

How it is built

An ontology in DuckDB, a pure rules layer over it, and a static site generated from both. A handful of decisions did most of the work.

Objects, not one wide table

A genomic variant, a per-transcript prediction and a clinical assertion are three different facts from three sources with three refresh cadences. They are three tables, joined by explicit edges. Flattening them would have destroyed the distinction the product depends on — and the data insisted on it: ClinVar reports consequences aggregated across all transcripts (48 of them for BRCA1), so one variant can be harmless on the canonical transcript and a missense change on another.

Provenance is a column, not a comment

Every row points at the retrieval that produced it — source, URL, version, timestamp, payload hash. Any number in the interface can be traced back to the request that fetched it.

No bulk downloads

NCBI serves the 193 MB ClinVar archive at roughly 101 KB/s, and opening more connections does not help. But the file is block-compressed with an index, and the server honours byte ranges — so the tool reads the index, works out which bytes cover the gene, and fetches only those. Four genes: about 1.5 MB and 30 seconds, instead of 193 MB and half an hour.

One place per threshold

The pLDDT cut of 70 and AlphaMissense’s 0.34 / 0.564 class boundaries exist in exactly one place each, in Python. The browser receives decided classes as integer codes and never re-derives a boundary. An earlier version had the browser recomputing them — two implementations of the same science in two languages, which is a disagreement waiting to happen.

The rules touch nothing

The rules layer imports no database, no HTTP client and no interface code, and an automated check enforces it. That is what allowed the entire frontend to be replaced — a Streamlit application swapped for a static site — without one rule changing, and with the test suite still passing throughout.

Decisions and the record

When a reviewer reaches a conclusion, that judgement becomes data, and it is held to the same standards as everything else.

  • Append and supersede, never overwrite. Revising a decision writes a new record and marks the old one superseded. Both stay on the record, and the earlier rationale is never edited.
  • No decision without a rationale. An empty justification is refused outright — a conclusion nobody can review is not a conclusion.
  • Blocked actions explain themselves. A control that refuses names the reason: this variant’s class is not reviewable, or this dossier rests on too few sources.
  • Decisions record the data build they were made against. If ClinVar or AlphaMissense is refreshed underneath, earlier judgements are flagged as stale rather than presented as current.

Stated plainly in the interface, not just here: the log lives in browser storage. It records who claims to have decided what. That is attribution, not authentication, and it is not tamper-proof. Exporting the log is what makes it durable.

What it does not do

Agreement with ClinVar is not independent validation

Where ClinVar is confident, the model agrees about 95% of the time on pathogenic calls and 87% on benign ones. That sounds like validation. It is not — or not entirely. AlphaMissense’s class thresholds were themselves chosen to hit roughly 90% precision on ClinVar. Some of the agreement is therefore true by construction.

The defensible claim is narrower and still useful: the model behaves on these genes as its published calibration predicts, so its scores can be used to prioritise uncertain variants for expert attention. The tool must never claim to have validated AlphaMissense, and this manual will not either.

Molecular effect is not clinical outcome

The model predicts whether a mutation breaks a protein. Whether a broken protein makes a particular person ill depends on penetrance, the other copy of the gene, tissue context, modifier genes and environment — none of which is modelled anywhere in this system. That gap is exactly why the output is a queue for experts rather than a verdict.

Coverage

  • Single-letter protein-coding changes only — no insertions, deletions, splice-site or regulatory variants.
  • The canonical transcript only, because that is all AlphaMissense publishes. Variants that are missense on some other transcript appear as having no prediction, which is honest rather than silent.
  • Eight genes are loaded. Adding one is a single edit and a re-export, but proteins longer than about 2,700 residues will fail loudly on import — AlphaFold splits those into fragments and the schema assumes one. The constraint was written to break rather than silently drop rows.

Licensing

AlphaMissense data is CC BY-NC-SA 4.0: non-commercial, attribution required, and share-alike travels with any derivative of the data. Research and portfolio use are fine; a commercial product is not.

Running it

No GPU, no API keys, no accounts.

uv run python ingest.py          # build the ontology from public sources (~3 min)
uv run python export_site.py     # generate the static site
uv run python serve.py           # http://localhost:8080

And to check that it still tells the truth:

uv run --with pytest pytest -q                                   # core rules, schema, integration
node --test tests/decisions.test.mjs tests/view-model.test.mjs   # governance and view-model rules
uv run python audit_site.py                                      # every displayed number, re-derived

That last one exists because an exported site is a second copy of the truth, and a second copy can drift. It recomputes every headline figure straight from the database and fails if the site disagrees.

The deployed site is a snapshot, rebuilt weekly

The published console never calls an upstream API at view time, which is why it cannot be broken by an outage, a rate limit or a schema change — and equally why reloading the page fetches nothing new. It reads files that were generated at build time.

A scheduled workflow rebuilds the whole thing every Thursday: it re-fetches every source, regenerates the site, and runs the full test suite, both phase validators and the audit before deploying. A build that breaks an invariant leaves the previous deployment live rather than shipping something wrong. The header states both the build date and which ClinVar and Open Targets releases it stands on, because a build date alone can look reassuring while resting on a months-old release.

ClinVar is the source that actually moves — it publishes weekly, and reclassification is exactly the event this tool cares about. New RCSB depositions matter too, since a structure solved this week can promote a residue from predicted to experimentally solved and change an evidence tier.

Recorded decisions survive that honestly. Each one stores the data build it was made against, so after a rebuild an earlier judgement is flagged as stale rather than quietly presented as current.