allelix 2.5.1__tar.gz → 2.5.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. {allelix-2.5.1/allelix.egg-info → allelix-2.5.2}/PKG-INFO +42 -32
  2. {allelix-2.5.1 → allelix-2.5.2}/README.md +41 -31
  3. {allelix-2.5.1 → allelix-2.5.2}/allelix/annotators/__init__.py +12 -5
  4. {allelix-2.5.1 → allelix-2.5.2}/allelix/annotators/base.py +17 -3
  5. {allelix-2.5.1 → allelix-2.5.2}/allelix/annotators/clinvar.py +73 -7
  6. {allelix-2.5.1 → allelix-2.5.2}/allelix/annotators/gnomad.py +26 -0
  7. {allelix-2.5.1 → allelix-2.5.2}/allelix/annotators/pharmgkb.py +11 -1
  8. allelix-2.5.2/allelix/annotators/reference_fallback.py +209 -0
  9. {allelix-2.5.1 → allelix-2.5.2}/allelix/annotators/rsid_identity.py +29 -6
  10. {allelix-2.5.1 → allelix-2.5.2}/allelix/annotators/snpedia.py +12 -2
  11. allelix-2.5.2/allelix/attribution.py +99 -0
  12. {allelix-2.5.1 → allelix-2.5.2}/allelix/cli/__init__.py +11 -3
  13. {allelix-2.5.1 → allelix-2.5.2}/allelix/cli/_helpers.py +149 -22
  14. {allelix-2.5.1 → allelix-2.5.2}/allelix/cli/_options.py +39 -11
  15. {allelix-2.5.1 → allelix-2.5.2}/allelix/cli/analyze.py +1 -0
  16. {allelix-2.5.1 → allelix-2.5.2}/allelix/cli/config.py +15 -19
  17. {allelix-2.5.1 → allelix-2.5.2}/allelix/cli/db.py +55 -12
  18. {allelix-2.5.1 → allelix-2.5.2}/allelix/cli/utility.py +375 -61
  19. {allelix-2.5.1 → allelix-2.5.2}/allelix/config.py +30 -6
  20. {allelix-2.5.1 → allelix-2.5.2}/allelix/data/high_value_snps.yaml +6 -0
  21. {allelix-2.5.1 → allelix-2.5.2}/allelix/databases/__init__.py +12 -2
  22. {allelix-2.5.1 → allelix-2.5.2}/allelix/databases/manager.py +192 -37
  23. {allelix-2.5.1 → allelix-2.5.2}/allelix/exporters/plink.py +65 -0
  24. {allelix-2.5.1 → allelix-2.5.2}/allelix/models.py +4 -0
  25. {allelix-2.5.1 → allelix-2.5.2}/allelix/parsers/_helpers.py +56 -1
  26. {allelix-2.5.1 → allelix-2.5.2}/allelix/parsers/twentythreeandme.py +37 -10
  27. {allelix-2.5.1 → allelix-2.5.2}/allelix/parsers/vcf.py +208 -24
  28. {allelix-2.5.1 → allelix-2.5.2}/allelix/reports/__init__.py +63 -6
  29. {allelix-2.5.1 → allelix-2.5.2}/allelix/reports/_pipeline.py +284 -54
  30. {allelix-2.5.1 → allelix-2.5.2}/allelix/reports/build_text.py +27 -1
  31. {allelix-2.5.1 → allelix-2.5.2}/allelix/reports/clinvar_text.py +50 -0
  32. {allelix-2.5.1 → allelix-2.5.2}/allelix/reports/diff.py +107 -46
  33. {allelix-2.5.1 → allelix-2.5.2}/allelix/reports/high_value.py +96 -0
  34. {allelix-2.5.1 → allelix-2.5.2}/allelix/reports/html.py +61 -6
  35. {allelix-2.5.1 → allelix-2.5.2}/allelix/reports/json_report.py +25 -3
  36. {allelix-2.5.1 → allelix-2.5.2}/allelix/reports/terminal.py +2 -1
  37. {allelix-2.5.1 → allelix-2.5.2}/allelix/reports/vcf.py +143 -43
  38. {allelix-2.5.1 → allelix-2.5.2}/allelix/utils/build_detect.py +15 -4
  39. {allelix-2.5.1 → allelix-2.5.2/allelix.egg-info}/PKG-INFO +42 -32
  40. {allelix-2.5.1 → allelix-2.5.2}/allelix.egg-info/SOURCES.txt +2 -0
  41. {allelix-2.5.1 → allelix-2.5.2}/pyproject.toml +3 -3
  42. {allelix-2.5.1 → allelix-2.5.2}/LICENSE +0 -0
  43. {allelix-2.5.1 → allelix-2.5.2}/MANIFEST.in +0 -0
  44. {allelix-2.5.1 → allelix-2.5.2}/allelix/__init__.py +0 -0
  45. {allelix-2.5.1 → allelix-2.5.2}/allelix/annotators/_resolution.py +0 -0
  46. {allelix-2.5.1 → allelix-2.5.2}/allelix/annotators/alphamissense.py +0 -0
  47. {allelix-2.5.1 → allelix-2.5.2}/allelix/annotators/cadd.py +0 -0
  48. {allelix-2.5.1 → allelix-2.5.2}/allelix/annotators/gwas.py +0 -0
  49. {allelix-2.5.1 → allelix-2.5.2}/allelix/cli/focused.py +0 -0
  50. {allelix-2.5.1 → allelix-2.5.2}/allelix/compare.py +0 -0
  51. {allelix-2.5.1 → allelix-2.5.2}/allelix/data/__init__.py +0 -0
  52. {allelix-2.5.1 → allelix-2.5.2}/allelix/data/clinvar_clnsig_snapshot.yaml +0 -0
  53. {allelix-2.5.1 → allelix-2.5.2}/allelix/databases/_versions.py +0 -0
  54. {allelix-2.5.1 → allelix-2.5.2}/allelix/databases/alphamissense_loader.py +0 -0
  55. {allelix-2.5.1 → allelix-2.5.2}/allelix/databases/cache_state.py +0 -0
  56. {allelix-2.5.1 → allelix-2.5.2}/allelix/databases/cadd_loader.py +0 -0
  57. {allelix-2.5.1 → allelix-2.5.2}/allelix/databases/cpic_loader.py +0 -0
  58. {allelix-2.5.1 → allelix-2.5.2}/allelix/databases/gnomad_loader.py +0 -0
  59. {allelix-2.5.1 → allelix-2.5.2}/allelix/databases/gwas_loader.py +0 -0
  60. {allelix-2.5.1 → allelix-2.5.2}/allelix/databases/loader_utils.py +0 -0
  61. {allelix-2.5.1 → allelix-2.5.2}/allelix/databases/pharmgkb_loader.py +0 -0
  62. {allelix-2.5.1 → allelix-2.5.2}/allelix/databases/rsid_identity_loader.py +0 -0
  63. {allelix-2.5.1 → allelix-2.5.2}/allelix/databases/schema.py +0 -0
  64. {allelix-2.5.1 → allelix-2.5.2}/allelix/databases/snpedia_loader.py +0 -0
  65. {allelix-2.5.1 → allelix-2.5.2}/allelix/databases/snpedia_parser.py +0 -0
  66. {allelix-2.5.1 → allelix-2.5.2}/allelix/exporters/__init__.py +0 -0
  67. {allelix-2.5.1 → allelix-2.5.2}/allelix/parsers/__init__.py +0 -0
  68. {allelix-2.5.1 → allelix-2.5.2}/allelix/parsers/ancestrydna.py +0 -0
  69. {allelix-2.5.1 → allelix-2.5.2}/allelix/parsers/base.py +0 -0
  70. {allelix-2.5.1 → allelix-2.5.2}/allelix/parsers/decodeme.py +0 -0
  71. {allelix-2.5.1 → allelix-2.5.2}/allelix/parsers/ftdna.py +0 -0
  72. {allelix-2.5.1 → allelix-2.5.2}/allelix/parsers/ftdna_famfinder.py +0 -0
  73. {allelix-2.5.1 → allelix-2.5.2}/allelix/parsers/ftdna_illumina.py +0 -0
  74. {allelix-2.5.1 → allelix-2.5.2}/allelix/parsers/genesforgood.py +0 -0
  75. {allelix-2.5.1 → allelix-2.5.2}/allelix/parsers/livingdna.py +0 -0
  76. {allelix-2.5.1 → allelix-2.5.2}/allelix/parsers/myhappygenes.py +0 -0
  77. {allelix-2.5.1 → allelix-2.5.2}/allelix/parsers/myheritage.py +0 -0
  78. {allelix-2.5.1 → allelix-2.5.2}/allelix/py.typed +0 -0
  79. {allelix-2.5.1 → allelix-2.5.2}/allelix/reports/_sources.py +0 -0
  80. {allelix-2.5.1 → allelix-2.5.2}/allelix/reports/association_text.py +0 -0
  81. {allelix-2.5.1 → allelix-2.5.2}/allelix/reports/methylation.py +0 -0
  82. {allelix-2.5.1 → allelix-2.5.2}/allelix/utils/__init__.py +0 -0
  83. {allelix-2.5.1 → allelix-2.5.2}/allelix/utils/allele.py +0 -0
  84. {allelix-2.5.1 → allelix-2.5.2}/allelix/utils/plausibility.py +0 -0
  85. {allelix-2.5.1 → allelix-2.5.2}/allelix.egg-info/dependency_links.txt +0 -0
  86. {allelix-2.5.1 → allelix-2.5.2}/allelix.egg-info/entry_points.txt +0 -0
  87. {allelix-2.5.1 → allelix-2.5.2}/allelix.egg-info/requires.txt +0 -0
  88. {allelix-2.5.1 → allelix-2.5.2}/allelix.egg-info/top_level.txt +0 -0
  89. {allelix-2.5.1 → allelix-2.5.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: allelix
3
- Version: 2.5.1
3
+ Version: 2.5.2
4
4
  Summary: Open-source genotype analysis toolkit. Format-agnostic ingestion, database-agnostic annotation, offline-first.
5
5
  Author: Allelix
6
6
  Maintainer-email: dial481 <dial481@users.noreply.github.com>
@@ -105,32 +105,34 @@ See [Development](#development) for source installs and running tests, [Managing
105
105
  | Format | Status | Notes |
106
106
  |---|---|---|
107
107
  | MyHappyGenes (Tempus) | ✓ | Tab-delimited, 5 columns. **Build is auto-detected** — real-world MHG exports mislabel the header as "build 37.1" while shipping GRCh38 coordinates. Allelix decides the build from position data and warns on header/data disagreement (ADR-0021, ADR-0044). See [docs/formats.md](https://github.com/allelix/allelix/blob/main/docs/formats.md#myhappygenes-tempus). |
108
- | 23andMe | ✓ | Tab-delimited, 4 columns, concatenated genotype. Reads build 36, 37 or 38; the header's build is used when the known-SNP positions agree. I-prefixed probe IDs passed through; they and `D`/`I` indel calls are not matched against ClinVar, and the report counts them (ADR-0051). |
108
+ | 23andMe | ✓ | Tab-delimited, 4 columns, concatenated genotype; a copy a spreadsheet re-saved as CSV (quoted or not) or with spaces is read too ([docs/formats.md](https://github.com/allelix/allelix/blob/main/docs/formats.md#23andme)). Reads build 36, 37 or 38; the header's build is used when the known-SNP positions agree. I-prefixed probe IDs passed through; they and `D`/`I` indel calls are not matched against ClinVar, and the report counts them (ADR-0051). A `D`/`I` call at a high-value rsID, or at one ClinVar classifies Pathogenic or Likely pathogenic, is also named as not checked (gene, variant name, rsID and call) on the console, in HTML and in JSON. |
109
109
  | Genes for Good | ✓ | The 23andMe-style `.txt` from a Genes for Good download (23andMe's columns under a PLINK banner). The imputed text file (about 7.7 million rows) and the imputed VCF are labelled imputed in every report; the measured files are not. The download's VCFs are read as VCF; see [docs/formats.md](https://github.com/allelix/allelix/blob/main/docs/formats.md#genes-for-good). |
110
110
  | AncestryDNA | ✓ | Tab-delimited, 5 columns. Chromosome mapping: 23→X, 24→Y, 25→X (PAR), 26→MT. V1 and V2 chip layouts. |
111
- | Family Tree DNA | ✓ | CSV, double-quoted fields, concatenated genotype. Build 37 default when known-SNP positions are present but do not decide; with none at all and no build line, the run stops (ADR-0044). |
111
+ | Family Tree DNA | ✓ | CSV, double-quoted fields, concatenated genotype. Build 37 default when known-SNP positions are present but do not decide (subject to the stops and the GRCh36 exception in [How the genome build is decided](#how-the-genome-build-is-decided-and-why-a-run-can-stop)); with none at all and no build line, the run stops (ADR-0044). |
112
112
  | MyHeritage DNA | ✓ | CSV, same structure as FTDNA. Detected by "MyHeritage" in the file's preamble. Handles double-double-quoted fields and files re-saved through a spreadsheet (padded rows, signature not on line 1, tab-delimited copies). Two exports: the **array** export (pre-2026 kits) and the **low-pass WGS** export (kits from January 2026; genotypes imputed from about 2x coverage), which the report labels as imputed and whose `##reference=build37` line is used as the header build. See [docs/formats.md](https://github.com/allelix/allelix/blob/main/docs/formats.md#myheritage). |
113
113
  | Living DNA | ✓ | Tab-delimited despite `.csv` extension. Handles AX-, AFFX-prefixed and CHR:POS positional SNP IDs. |
114
114
  | deCODEme | ✓ | `deCODEme_scan.csv` (deCODE genetics, service closed 2013): comma-separated, a per-row `Strand` column; minus-strand calls are complemented to the plus strand. Build 36: decided from known-SNP positions, then run with position lookups off (ADR-0025). See [docs/formats.md](https://github.com/allelix/allelix/blob/main/docs/formats.md#decodeme). |
115
- | FTDNA Illumina raw | ✓ | Tab-delimited variant of the FTDNA export (distinct from the CSV format above). `RSID/CHROMOSOME/POSITION/RESULT` columns. Build 37 default when known-SNP positions are present but do not decide. |
116
- | FTDNA FamFinder | ✓ | Third FTDNA file shape: tab-delimited with **separate `ALLELE1` / `ALLELE2` columns** instead of a concatenated `RESULT`. Detected by the `famfinder` substring in the file header plus the 5-column canonical header. Build 37 default when known-SNP positions are present but do not decide. |
117
- | VCF / gVCF | ✓ | REF/ALT encoding, `0/1` genotype notation. Plain VCF: absence at a position means reference. gVCF: explicit reference blocks (lines with `<NON_REF>` ALT and `END=` INFO) are not annotated — they match nothing in any annotation database — but they are read at high-value positions: a block with low genotype quality or no reads there makes the position a no-call, and so does a gVCF position no record covers. High-value no-calls list the reason (for example `low_gq_block`, `no_coverage`, `filtered`, `low_gq_call`). A plain VCF has no reference blocks, so a position without a record is reported as unknown rather than as reference or as a no-call. Multi-sample files require `--sample <ID>`. Rows the variant caller rejected (FILTER other than `PASS` or `.`) are skipped and counted in the report; `--keep-filtered` keeps them. A call with genotype quality below `--min-gq` (default 20) or with no reads (DP 0) is reported as a no-call. The spanning-deletion allele `*` is treated as symbolic: such calls are not assessed, and the report counts them and lists their positions. A half call (`./1`) is assessed on its called allele by ClinVar and the GWAS Catalog and shown as `T/.` with zygosity "Half call"; ClinPGx and SNPedia, which need both alleles, skip it. Malformed rows, rows without a genotype, genotypes naming an allele the record lacks and genotypes naming a symbolic allele such as `<NON_REF>` beside a real ALT are counted in the report; a no-call written `./.` stays a no-call (ADR-0047). A VCF that holds only structural-variant or copy-number records (such as a provider's separate `sv` / `cnv` file) is rejected with a message naming the file to use instead; see [docs/formats.md](https://github.com/allelix/allelix/blob/main/docs/formats.md#sequencingcom-30x-whole-genome) for per-provider deliverables. A VCF whose header declares an imputation tool's fields (Minimac, Beagle, GLIMPSE) is labelled imputed in every report; see [docs/formats.md](https://github.com/allelix/allelix/blob/main/docs/formats.md#imputed-vcfs). Streams via stdlib; `.vcf.gz` handled transparently. Optional `pip install allelix[vcf-index]` enables pysam-backed tabix random access for fast `extract --snps` on huge VCFs. |
115
+ | FTDNA Illumina raw | ✓ | Tab-delimited variant of the FTDNA export (distinct from the CSV format above). `RSID/CHROMOSOME/POSITION/RESULT` columns. Build 37 default when known-SNP positions are present but do not decide (subject to the stops and the GRCh36 exception in [How the genome build is decided](#how-the-genome-build-is-decided-and-why-a-run-can-stop)). |
116
+ | FTDNA FamFinder | ✓ | Third FTDNA file shape: tab-delimited with **separate `ALLELE1` / `ALLELE2` columns** instead of a concatenated `RESULT`. Detected by the `famfinder` substring in the file header plus the 5-column canonical header. Build 37 default when known-SNP positions are present but do not decide (subject to the stops and the GRCh36 exception in [How the genome build is decided](#how-the-genome-build-is-decided-and-why-a-run-can-stop)). |
117
+ | VCF / gVCF | ✓ | REF/ALT encoding, `0/1` genotype notation. A homozygous-reference call (`0/0`) or a gVCF reference block (lines with `<NON_REF>` ALT and `END=` INFO) carries no variant allele, so it usually matches nothing. The exception is a site where the reference genome itself carries a ClinVar allele (Factor V Leiden on GRCh37, for example): there a `0/0` call, or a reference block that passes the genotype-quality gate, is assessed as homozygous for that allele. See [docs/formats.md, Reference calls in VCFs](https://github.com/allelix/allelix/blob/main/docs/formats.md#reference-calls-in-vcfs) (ADR-0039, ADR-0059). At high-value positions, a block with low genotype quality or no reads makes the position a no-call, and so does a gVCF position no record covers. High-value no-calls list the reason (for example `low_gq_block`, `no_coverage`, `filtered`, `low_gq_call`). A record counts for a high-value or panel rsID when it is at one of the rsID's identity placements with that placement's REF, whatever its ID or ALT; an rsID the identity table does not place is matched by ID (ADR-0055). A plain VCF has no reference blocks, so a position without a record is reported as unknown rather than as reference or as a no-call; at such a ClinVar site the report counts the positions no record covers, and names the high-value ones (such as CYP3A5*3 rs776746 on GRCh37) as not checked. Multi-sample files require `--sample <ID>`. Rows the variant caller rejected (FILTER other than `PASS` or `.`) are skipped and counted in the report; `--keep-filtered` keeps them. A call with genotype quality below `--min-gq` (default 20) or with no reads (DP 0) is reported as a no-call. The spanning-deletion allele `*` is treated as symbolic: such calls are not assessed, and the report counts them and lists their positions. A half call (`./1`) is assessed on its called allele by ClinVar and the GWAS Catalog and shown as `T/.` with zygosity "Half call"; ClinPGx and SNPedia, which need both alleles, skip it. Malformed rows, rows without a genotype, genotypes naming an allele the record lacks and genotypes naming a symbolic allele such as `<NON_REF>` beside a real ALT are counted in the report; a no-call written `./.` stays a no-call (ADR-0047). A VCF that holds only structural-variant or copy-number records (such as a provider's separate `sv` / `cnv` file) is rejected with a message naming the file to use instead; see [docs/formats.md](https://github.com/allelix/allelix/blob/main/docs/formats.md#sequencingcom-30x-whole-genome) for per-provider deliverables. A VCF whose header declares an imputation tool's fields (Minimac, Beagle, GLIMPSE) is labelled imputed in every report; see [docs/formats.md](https://github.com/allelix/allelix/blob/main/docs/formats.md#imputed-vcfs). Streams via stdlib; `.vcf.gz` handled transparently. Optional `pip install allelix[vcf-index]` enables pysam-backed tabix random access for fast `extract --snps` on huge VCFs. |
118
118
 
119
119
  Files can be passed as delivered: gzipped (Family Tree DNA's `.csv.gz`, for example), or a zip holding one genotype file. A zip with several files, a password or an unusual compression stops with a message to unzip it first. Windows, Linux and old-Mac line endings are read; a leading byte order mark or blank line is ignored.
120
120
 
121
- If Allelix does not recognise a file, use the original file from your testing company. `--format <name>` forces a reader at your own risk; every report then carries a warning (see [docs/formats.md, Forcing a format](https://github.com/allelix/allelix/blob/main/docs/formats.md#forcing-a-format)).
121
+ If Allelix does not recognise a file, use the original file from your testing company. `--format <name>` forces a reader at your own risk; when that reader does not recognise the file, every report carries a warning (see [docs/formats.md, Forcing a format](https://github.com/allelix/allelix/blob/main/docs/formats.md#forcing-a-format)).
122
122
 
123
123
  Adding a new format means adding one file to `allelix/parsers/` and registering an instance in the `PARSERS` list in `allelix/parsers/__init__.py`.
124
124
 
125
125
  ### Roadmap
126
126
 
127
+ Shipped releases are listed for history; the [Changelog](https://github.com/allelix/allelix/blob/main/CHANGELOG.md) has each release's changes.
128
+
127
129
  | Release | Theme |
128
130
  |---|---|
129
- | **v2.2** | Custom-panel UX improvements, methylation panel retune + citations, ClinVar source switch (VCF → `variant_summary.txt`), CLI cleanups (`db clean` / `db path`). |
130
- | **v2.3** | Annotated VCF output plus v2.3.1 correctness hardening: cross-build enrichment guards, GWAS effect-type disambiguation, conservative build confidence, SNPedia orientation handling, and strict slow-suite drift verification. |
131
- | **v2.4** | Correctness hardening: cross-build rsID identity table with explicit allele translation, plausibility flag for genotyping-artifact detection, VCF quality gate honouring FILTER and genotype quality, unified rsID-recovery step across all commands, and parser/format hygiene. |
132
- | **v2.5** | ClinVar's own classification caps each row; ClinVar variants without an rsID; VCF calls counted, not dropped; read-only caches and named sources in every report; zip input; Genes for Good and deCODEme; imputed input labelled. |
133
- | **Later** | Variant consequences, CNVs and repeat expansions, gnomAD genomes, supplemental genotype-file merging, ancestry estimation, and PRS support. These are directional themes rather than release commitments. |
131
+ | **v2.2** (shipped) | Custom-panel UX improvements, methylation panel retune + citations, ClinVar source switch (VCF → `variant_summary.txt`), CLI cleanups (`db clean` / `db path`). |
132
+ | **v2.3** (shipped) | Annotated VCF output plus v2.3.1 correctness hardening: cross-build enrichment guards, GWAS effect-type disambiguation, conservative build confidence, SNPedia orientation handling, and strict slow-suite drift verification. |
133
+ | **v2.4** (shipped) | Correctness hardening: cross-build rsID identity table with explicit allele translation, plausibility flag for genotyping-artifact detection, VCF quality gate honouring FILTER and genotype quality, unified rsID-recovery step across all commands, and parser/format hygiene. |
134
+ | **v2.5** (shipped) | ClinVar's own classification caps each row; ClinVar variants without an rsID; VCF calls counted, not dropped; read-only caches and named sources in every report; zip input; Genes for Good and deCODEme; imputed input labelled. |
135
+ | **Later** (planned) | Variant consequences, CNVs and repeat expansions, gnomAD genomes, supplemental genotype-file merging, ancestry estimation, and PRS support. These are directional themes rather than release commitments. |
134
136
 
135
137
  Issues and milestones are tracked at [github.com/allelix/allelix/issues](https://github.com/allelix/allelix/issues).
136
138
 
@@ -138,15 +140,15 @@ Issues and milestones are tracked at [github.com/allelix/allelix/issues](https:/
138
140
 
139
141
  | Database | Status | Notes |
140
142
  |---|---|---|
141
- | ClinVar (GRCh37 + GRCh38) | ✓ | Public domain (NCBI). SNVs + indels + multi-allelic sites. **Both builds cached**; `analyze` dispatches by the decided build (ADR-0044). Rows are matched by chromosome, position, REF and ALT, never by rsID alone, and reference-allele records (REF = ALT) are never reported (ADR-0039). Carrier rule (ADR-0007) requires the user to carry the ALT allele. Indel-anchor protection (ADR-0011) prevents single-base array readouts from matching anchor-base indels. Array calls ClinVar cannot match are counted in the report: vendor probes without an rsID at a locus ClinVar classifies as Pathogenic or Likely pathogenic, and `D`/`I` calls (ADR-0051). ClinVar variants without an rsID are matched for VCF input only, where REF comes from the file (ADR-0046). A VCF allele written with bases it shares with REF (`GTT>GT,G`, `CA>TA`) is trimmed per carried allele before matching, and a VCF homozygous deletion to one base matches; indels are not left-aligned, so one the file places at a different position from ClinVar's still does not match (ADR-0047). |
142
- | ClinPGx (formerly PharmGKB) | ✓ | CC BY-SA 4.0. Clinical annotations only — single-rsid SNVs; star alleles and haplotypes deferred (ADR-0009). **Primary non-finding filter is the ClinVar REF carrier rule (ADR-0023):** if ClinVar publishes a single-base REF for the rsid and the user is homozygous for it, the row is suppressed. CPIC's `(rsid, base) → function_class` join (ADR-0020) survives as a secondary tier for rsids ClinVar doesn't catalog. Earlier prose tiers (ADR-0013, ADR-0017, ADR-0018) are superseded. |
143
+ | ClinVar (GRCh37 + GRCh38) | ✓ | Public domain (NCBI). SNVs + indels + multi-allelic sites. **Both builds cached**; `analyze` dispatches by the decided build (ADR-0044). Rows are matched by chromosome, position, REF and ALT, never by rsID alone. A record written REF = ALT, where this build's reference itself carries the record's allele (Factor V Leiden on GRCh37, or a record for the normal allele), is reported for a user who carries that allele; Pathogenic and Likely pathogenic classifications of a normal allele are dropped (ADR-0039). Carrier rule (ADR-0007) requires the user to carry the ALT allele. Indel-anchor protection (ADR-0011) prevents single-base array readouts from matching anchor-base indels. Array calls ClinVar cannot match are counted in the report: vendor probes without an rsID at a locus ClinVar classifies as Pathogenic or Likely pathogenic, and `D`/`I` calls (ADR-0051). A `D`/`I` call at a high-value or Pathogenic / Likely pathogenic rsID is named as not checked, not only counted; it is still not interpreted. Variants that ClinVar's Conflicting or Benign classification scored down (ADR-0045) are named with their submitter split. ClinVar variants without an rsID are matched for VCF input only, where REF comes from the file (ADR-0046). A VCF allele written with bases it shares with REF (`GTT>GT,G`, `CA>TA`) is trimmed per carried allele before matching, by ClinPGx, SNPedia and the GWAS Catalog as well as ClinVar, and a VCF homozygous deletion to one base matches; indels are not left-aligned, so one the file places at a different position from ClinVar's still does not match (ADR-0047). |
144
+ | ClinPGx (formerly PharmGKB) | ✓ | CC BY-SA 4.0. Clinical annotations only — single-rsid SNVs; star alleles and haplotypes deferred (ADR-0009). **Primary non-finding filter is the ClinVar REF carrier rule (ADR-0023):** where ClinVar for the input's build is installed and places the rsID at the call's position, a user homozygous for the normal allele ClinVar resolves there is not reported. That is usually ClinVar's single-base REF. The exception is a site where this build's reference itself carries ClinVar's variant allele (a REF = ALT row, F5 rs6025 on GRCh37): there the normal allele is the other build's REF, so a TT call is reported as a carrier and CC is the non-finding. Where that normal allele cannot be told, nothing is suppressed. Without that cache, only sites where the two builds' references differ (F5 rs6025) are decided: by the other build's ClinVar, else by gnomAD's major allele; if neither can tell, the row shows. Every other site is then not suppressed. CPIC's `(rsid, base) → function_class` join (ADR-0020) survives as a secondary tier for rsids ClinVar doesn't catalog. Earlier prose tiers (ADR-0013, ADR-0017, ADR-0018) are superseded. |
143
145
  | CPIC (per-allele function table) | ✓ | Internal data source for the ClinPGx filter. Fetched from `api.cpicpgx.org` at `db update` time. Used to populate the `pharmgkb_allele_function` table — not surfaced to end users as its own annotator. |
144
146
  | SNPedia | ✓ | CC BY-NC-SA 3.0 US. Pre-built cache downloaded via `db update` (~216K wiki pages, ~105K genotype rows). The cache carries SNPedia's orientation metadata: minus-strand alleles are complemented before matching. Single-base genotypes at rsIDs are then checked against the site's forward alleles in the rsID identity table: a page's stated orientation must put the genotype among them, an unknown orientation is read directly or else complemented, and A/T and C/G sites abstain. Sites that abstain are counted in the report. If the database is absent, analysis runs without it. For commercial use, pass `--exclude-snpedia`. The cache can also be rebuilt from source via `scripts/scrape_snpedia.py` + `scripts/parse_snpedia.py`. |
145
147
  | GWAS Catalog | ✓ | Public domain (EBI/NHGRI). Trait–SNP associations with p-values and effect sizes. Carrier rule (ADR-0007) requires the user to carry the risk allele. The reported allele is the study's effect (risk) allele, which can be the reference allele (ADR-0039). P-value magnitude scoring maps continuous p-values to the 0–10 scale; only a confidence-interval field that positively identifies a bare odds ratio receives the odds-ratio modifier. Beta coefficients and ambiguous or unit-bearing effects receive no modifier (ADR-0038). Unknown-risk-allele entries fire on rsID match alone but are capped at 3.0. Risk alleles are read on the forward strand against the site's alleles in the rsID identity table: as written, else complemented; A/T and C/G sites and rsIDs the table does not place at the call abstain, and the report counts them. Every row says whether the effect allele raises or lowers the odds or the measured value (JSON `effect_direction`). GWAS rows must meet the larger of `--min-magnitude` and `--gwas-min-magnitude` (default 9.0); the must-include rsIDs skip the GWAS floor (ADR-0024). |
146
148
  | gnomAD | ✓ | ODbL v1.0. **GRCh38-only enrichment annotator** — adds population allele frequency context to existing annotations. Shows how common each variant is in the general population (~57M exome variants with an rsID, from 730K individuals). A pathogenic variant that 35% of people carry reads very differently from one seen in 0.001%. Pre-built cache downloaded via `db update` (~7 GB on disk). GRCh37 input is enriched only through the rsID identity table's explicit allele translation (ADR-0040); gnomAD's own rsID resolution stays GRCh38-only, and GRCh36 input receives nothing. Use `--no-gnomad` to skip. |
147
149
  | AlphaMissense | ✓ | CC BY 4.0. **GRCh38-only enrichment annotator** — adds DeepMind's protein-structure-based pathogenicity predictions to existing annotations. Scores 71M missense variants on a 0–1 scale: <0.34 = likely benign, >0.564 = likely pathogenic. Complements ClinVar's expert classifications with computational predictions — especially valuable for variants ClinVar hasn't reviewed yet. Pre-built cache downloaded via `db update` (~8GB on disk). GRCh37 input is enriched only through the rsID identity table (ADR-0040); GRCh36 input receives nothing. Use `--no-alphamissense` to skip. |
148
150
  | CADD | ✓ | LicenseRef-CADD (non-commercial). **GRCh38-only enrichment annotator** — adds PHRED-scaled deleteriousness scores from CADD v1.7. **Opt-in**, disabled by default; GRCh37 input is scored only through the rsID identity table (ADR-0040) and GRCh36 input never; see [CADD modes](#cadd-modes) below for details. |
149
- | dbSNP rsID identity | ✓ | Public domain (NCBI dbSNP). Not an annotator: a cross-build table of each annotator rsID's GRCh37 and GRCh38 placement and alleles (ADR-0040). Supplies the reference allele for GRCh37 and GRCh38 array data, recovers rsIDs for rsID-less VCFs on both builds, and translates a GRCh37 allele to its GRCh38 coordinates so gnomAD, AlphaMissense and CADD can be looked up. Strand-inverted loci are translated explicitly; palindromic sites (A/T, C/G, or a site whose dbSNP alleles include all four bases) and alleles that became the GRCh38 reference abstain. Pre-built cache downloaded via `db update` (~4.9M rows covering ~3.0M rsIDs, from dbSNP b157). |
151
+ | dbSNP rsID identity | ✓ | Public domain (NCBI dbSNP). Not an annotator: a cross-build table of each annotator rsID's GRCh37 and GRCh38 placement and alleles (ADR-0040). Supplies the reference allele for GRCh37 and GRCh38 array data, recovers rsIDs for rsID-less VCFs on both builds, and translates a GRCh37 allele to its GRCh38 coordinates so gnomAD, AlphaMissense and CADD can be looked up. Strand-inverted loci are translated explicitly. A row the table flags palindromic (its dbSNP allele set equals its own complement: A/T, C/G, or all four bases) abstains where the GRCh37 and GRCh38 reference bases differ, and translates as written where they are equal; rows the table does not flag translate by their alleles. Alleles that became the GRCh38 reference abstain. Pre-built cache downloaded via `db update` (~4.9M rows covering ~3.0M rsIDs, from dbSNP b157). |
150
152
 
151
153
  ### CADD modes
152
154
 
@@ -157,26 +159,26 @@ CADD ranks how deleterious any single-nucleotide variant is using 100+ annotatio
157
159
  Two modes:
158
160
 
159
161
  - **Cache mode** (default): pre-built ~5.8 GB SQLite cache (~120M GRCh38 variant keys). Covers nearly every GRCh38 position allelix can annotate from its other databases (gnomAD, AlphaMissense, ClinVar). GRCh37 input is scored only after translation through the rsID identity table; GRCh36 input never is.
160
- - **Full mode** (`options.cadd_full = true`): tabix queries against the complete CADD v1.7 file via pysam (GRCh38 only). Adds coverage for novel or private variants that appear only in WGS/WES data and are not in any pre-computed database. Requires `pip install allelix[cadd]` for the pysam dependency.
162
+ - **Full mode** (`options.cadd_full = true`): tabix queries against the complete CADD v1.7 file via pysam (GRCh38 only). It scores variants the cache lacks, such as novel or private variants in WGS/WES data. CADD adds scores to findings from the other sources; it never makes a finding of its own, so a variant no other source reports gains nothing. Requires `pip install allelix[cadd]` for the pysam dependency, and CADD's files, downloaded separately from [cadd.gs.washington.edu/download](https://cadd.gs.washington.edu/download) (`db update --cadd` installs only the SQLite cache): `whole_genome_SNVs.tsv.gz` (about 81 GB) and its `.tbi` index in the data directory, plus, optionally for indels, `gnomad.genomes.r4.0.indel.tsv.gz` (about 1.2 GB) and its `.tbi`.
161
163
 
162
164
  If your input is a genotyping chip file, cache mode is normally all you need. GRCh37 chip files are scored through the rsID identity table; GRCh36 files are not scored.
163
165
 
164
166
  ### Build coverage asymmetry (GRCh37 vs GRCh38)
165
167
 
166
- ClinVar dispatches per build and ships with both GRCh37 and GRCh38 caches. gnomAD v4.1 exomes, AlphaMissense, and CADD v1.7 are GRCh38 datasets. Every enrichment lookup is made at GRCh38 coordinates by chromosome, position, reference and alternate allele, never by rsID. For GRCh37 input, the cross-build rsID identity table (ADR-0040) translates the allele the user carries at its GRCh37 position into the matching GRCh38 allele, written explicitly on the GRCh38 forward strand where the reference strand was inverted between assemblies. Palindromic sites (A/T, C/G, or all four bases in dbSNP, whose strand cannot be told from sequence) and alleles that are the GRCh38 reference receive no values rather than a guess. Without the identity table installed, GRCh37 analyses receive no gnomAD, AlphaMissense or CADD values; GRCh36 analyses never do.
168
+ ClinVar dispatches per build and ships with both GRCh37 and GRCh38 caches. gnomAD v4.1 exomes, AlphaMissense, and CADD v1.7 are GRCh38 datasets. Every enrichment lookup is made at GRCh38 coordinates by chromosome, position, reference and alternate allele, never by rsID. For GRCh37 input, the cross-build rsID identity table (ADR-0040) translates the allele the user carries at its GRCh37 position into the matching GRCh38 allele, written explicitly on the GRCh38 forward strand where the reference strand was inverted between assemblies. A row the identity table flags palindromic (A/T, C/G, or all four bases in dbSNP, whose strand cannot be told from sequence) whose reference base differs between the builds, and an allele that is the GRCh38 reference, receive no values rather than a guess. Without the identity table installed, GRCh37 analyses receive no gnomAD, AlphaMissense or CADD values; GRCh36 analyses never do.
167
169
 
168
170
  Position-based rsID recovery for rsID-less VCFs uses ClinVar (on the selected build), the identity table (on GRCh37 and GRCh38), and gnomAD (GRCh38 only). The same recovery step runs for `analyze`, `stats`, `extract` and `export plink`, so high-value warnings, extracted genotypes and PLINK allele coding all see the recovered rsIDs (ADR-0043). The identity table covers the rsIDs the annotators hold, so an rsID-less GRCh37 VCF now recovers ClinPGx, GWAS Catalog and SNPedia rsIDs as well as ClinVar ones. If you convert between assemblies, re-normalize REF/ALT as part of the conversion and pass `--build` explicitly when the file does not carry a trustworthy build signal.
169
171
 
170
- ### Known ClinPGx limitation: reference-genotype rows where ClinVar and CPIC both lack data
172
+ ### Known ClinPGx limitation: normal-genotype rows without usable normal-allele evidence
171
173
 
172
- ADR-0022 + ADR-0023: a tiny residual of ClinPGx rows may appear in reports even when the user is homozygous reference. ClinPGx publishes one annotation per genotype including the reference homozygote, and for the reference-homozygote row to be suppressed Allelix needs structured data on the variant from either:
174
+ ADR-0022 + ADR-0023: a tiny residual of ClinPGx rows may appear in reports even when the user carries the normal genotype. ClinPGx publishes one annotation per genotype including the normal homozygote, and for that row to be suppressed Allelix needs usable evidence of the normal allele from one of:
173
175
 
174
- - **ClinVar's REF allele** (the primary filter — see ADR-0023). Covers any rsID ClinVar catalogs.
176
+ - **ClinVar's normal allele** (the primary filter — see ADR-0023): usually its single-base REF, or the other build's REF where this build's reference carries ClinVar's variant allele (F5 rs6025 on GRCh37). Covers an rsID that ClinVar for the input's build places at the call's position. Without that cache, only a site whose reference differs between the builds is decided (by the other build's ClinVar, else gnomAD's major allele), and anything else shows.
175
177
  - **CPIC's per-allele function table** (the secondary fallback — see ADR-0020). Covers rsIDs CPIC has classified.
176
178
 
177
- For the rare rsID where ClinPGx has an annotation but *neither* ClinVar nor CPIC has data, the row emits. These are identifiable by a homozygous-reference genotype combined with "decreased risk," "may have a typical response," or similar comparative language. They are an upstream data gap, not an Allelix bug — we surface them honestly rather than hide them behind a curated exclusion list (which would recreate the maintenance trap the v0.5–v0.7 prose filters were trying to escape).
179
+ So without ClinVar or CPIC data, gnomAD can still decide a site whose reference differs between the builds, at that placement only. For the rare rsID where ClinPGx has an annotation and none of these gives a usable normal allele (no ClinVar or CPIC data, and no decision from the other build's ClinVar or gnomAD, which abstains on a tie, an unknown frequency or an unlisted site), the row emits: Allelix shows it rather than guess. These are identifiable by a homozygous-reference genotype combined with "decreased risk," "may have a typical response," or similar comparative language. They are an upstream data gap, not an Allelix bug — we surface them honestly rather than hide them behind a curated exclusion list (which would recreate the maintenance trap the v0.5–v0.7 prose filters were trying to escape).
178
180
 
179
- The CFTR × ivacaftor leak (~30+ rows on real data, pre-v0.7.3) is fixed by the ADR-0023 ClinVar REF check: CPIC's CFTR vocabulary (`"ivacaftor responsive"`) doesn't match the four-class enum the secondary tier expects, but ClinVar publishes REF for every CFTR rsID, so the primary tier catches them universally.
181
+ The CFTR × ivacaftor leak (~30+ rows on real data, pre-v0.7.3) is fixed by the ADR-0023 ClinVar REF check: CPIC's CFTR vocabulary (`"ivacaftor responsive"`) doesn't match the four-class enum the secondary tier expects, but ClinVar publishes REF for the CFTR rsIDs, so with ClinVar for the input's build installed the primary tier catches them.
180
182
 
181
183
  ### Known ClinVar upstream data quality issues
182
184
 
@@ -254,7 +256,7 @@ The HTML report shows a ⚠ icon next to a flagged genotype, with the reason on
254
256
 
255
257
  A call is not assessed when gnomAD has no frequency for the allele. Allelix's gnomAD cache covers exome sites with an rsID, so a missing value says nothing about rarity. Calls are also not assessed when the gnomAD database is skipped. Homozygous calls on X are not assessed because a male's single X reads as homozygous; heterozygous X calls are. A call the file itself declares as one copy (a one-letter array call such as 23andMe's male X calls, or a one-index VCF genotype) is shown as Hemizygous and assessed as one copy. Y and mitochondrial calls are not assessed. Confirm a flagged call with a clinical-grade test before relying on it.
256
258
 
257
- When three or more variants on chromosomes 1 to 22 are called homozygous for an allele ClinVar classifies as Pathogenic or Likely pathogenic, and are rare in gnomAD or have no gnomAD frequency, the report warns at the top. This is counted before any display filter, so `--min-magnitude` cannot hide it (ADR-0050).
259
+ When three or more variants on chromosomes 1 to 22 are called homozygous for an allele ClinVar classifies as Pathogenic or Likely pathogenic, and are rare in gnomAD or were looked up there and are absent, the report warns at the top. A call gnomAD was not asked about (gnomAD not installed, or a GRCh37 allele the identity table cannot translate) does not count. This is counted before any display filter, so `--min-magnitude` cannot hide it (ADR-0050).
258
260
 
259
261
  ### Database sizes and download times
260
262
 
@@ -285,6 +287,7 @@ Allelix keeps its caches in `~/.local/share/allelix/`, or `$XDG_DATA_HOME/alleli
285
287
  ├── cadd.sqlite ~5.8 GB (only if you opted into --cadd)
286
288
  ├── clinvar.GRCh37.sqlite ~1.2 GB
287
289
  ├── clinvar.GRCh38.sqlite ~1.2 GB
290
+ ├── clinvar-build.log each ClinVar build's source hashes and counts
288
291
  ├── gwas_catalog_associations.tsv ~745 MB
289
292
  ├── rsid_identity.sqlite ~457 MB
290
293
  ├── gwas.sqlite ~202 MB
@@ -323,7 +326,7 @@ du -sh "$ALLELIX_DATA"
323
326
  allelix db path --check
324
327
  ```
325
328
 
326
- **The one thing worth backing up: `config.toml`.** It lives inside the resolved data directory (typically `~/.local/share/allelix/config.toml`) and stores your license assertions (commercial-mode toggle, CADD opt-in confirmation) and per-source enable/disable settings. It's the only non-reproducible state in the install. The caches will rebuild on the next `db update`; `config.toml` won't. `allelix db clean` always preserves it.
329
+ **The one thing worth backing up: `config.toml`.** It lives inside the resolved data directory (typically `~/.local/share/allelix/config.toml`) and stores your license assertions (commercial-mode toggle, CADD opt-in confirmation) and per-source enable/disable settings. The caches will rebuild on the next `db update`; `config.toml` won't. `allelix db clean` always preserves it. `clinvar-build.log` (the source hashes and counts of past ClinVar builds) is not reproducible either, and `db clean` removes it: copy it elsewhere first if you want to keep that history.
327
330
 
328
331
  To fully uninstall allelix and reclaim everything:
329
332
 
@@ -422,11 +425,11 @@ The banner says where the build came from, for example `Build: GRCh38 (from the
422
425
  - a VCF has no contig lengths, no assembly and too few known-SNP positions;
423
426
  - a chip states no build and carries none of the known-SNP positions at all (for example an X-chromosome-only export, since they are all on autosomes);
424
427
  - a file states a build but carries none of the known-SNP positions to check it (for example an mtDNA-only export, or a small panel VCF without contig lengths);
425
- - the file has known-SNP positions but none of them sits at any supported build's coordinate, whether or not it states a build (contig lengths, when a VCF declares them, still decide);
428
+ - the file has known-SNP positions but none of them sits at any supported build's coordinate, whether or not it states a build (contig lengths, when a VCF declares them, still decide). Choosing a build is not the fix here: the message says the positions cannot be trusted on any build and asks you to check that this is the original file from the testing company. If you force a build anyway with `--build`, the run goes ahead but prints a **Build warning** naming the evidence (0 of N known-SNP positions match), and every report keeps it (JSON `report_warnings` code `build_override_unmatched`);
426
429
  - contig lengths decided, but the known-SNP positions met while annotating point to the other build (for example a lifted-over file that kept its old header). The message says the annotation pass ran; nothing is written;
427
430
  - `--build grch37` or `--build grch38` was given, but the file's contig lengths, or three or more known-SNP positions with an 80% majority, say the other build. The message names both builds. Contrary positions that fall short of that (fewer than three, or under an 80% majority) only print a **Build warning**, and so does a tie that leaves out the forced build. `--build grch36` is never checked, because it turns position-keyed lookups off.
428
431
 
429
- The message names the evidence and asks you to say which build the file uses: `--build grch37` or `--build grch38`, or `--build grch36` for an old GRCh36 / hg18 file, which then runs with position-keyed lookups off (see [`docs/grch36-liftover.md`](https://github.com/allelix/allelix/blob/main/docs/grch36-liftover.md)). For `compare`, use `--build1` / `--build2`. A stop leaves no report, annotated VCF, diff, PLINK files or extract table behind. A forced build is recorded as such in every report.
432
+ Otherwise the message names the evidence and asks you to say which build the file uses: `--build grch37` or `--build grch38`, or `--build grch36` for an old GRCh36 / hg18 file, which then runs with position-keyed lookups off (see [`docs/grch36-liftover.md`](https://github.com/allelix/allelix/blob/main/docs/grch36-liftover.md)). For `compare`, use `--build1` / `--build2`. A stop leaves no report, annotated VCF, diff, PLINK files or extract table behind. A forced build is recorded as such in every report.
430
433
 
431
434
  ### Keeping input and output files safe
432
435
 
@@ -435,13 +438,19 @@ must differ from the input, including symlink and hard-link aliases. These
435
438
  checks run before cache setup or output creation. Inputs that alias the former
436
439
  fixed report or PLINK `.tmp` path are also refused; unrelated old temp files
437
440
  are left untouched. Annotated VCF still refuses any existing output at the CLI.
441
+ Two outputs of one run must name different files too: `--output` and
442
+ `--vcf-out`, and PLINK's `.bed`, `.bim` and `.fam`, are refused before any work
443
+ when they resolve to one file (another spelling, a symlink, a linked folder or a
444
+ hardlink).
438
445
 
439
446
  Writers use unique temporary files in the destination directory and publish
440
447
  only complete, closed content. Failure before publication preserves previous
441
448
  outputs. PLINK stages all three files first, then publishes each atomically;
442
449
  a later publication failure can leave complete new files next to unchanged old
443
450
  ones. Existing ordinary permission bits are retained when replacement is
444
- permitted; new staging files start private. Ownership, ACLs, extended attributes,
451
+ permitted; new staging files start private, and a new output file gets the mode
452
+ your umask gives any new file (`0666 & ~umask`, so `0644` under `022`).
453
+ Ownership, ACLs, extended attributes,
445
454
  crash durability and concurrent path swaps are outside this guarantee.
446
455
 
447
456
  ### Is my database stale? How fresh is the data?
@@ -478,13 +487,13 @@ Notable load-bearing ADRs:
478
487
  - **ADR-0042 — VCF quality gate.** The variant caller's FILTER and genotype quality are honoured, low-quality reference blocks are no-calls, and every exclusion is counted in the report.
479
488
  - **ADR-0043 — One rsID-recovery step.** Every command that needs rsIDs recovers them for rsID-less input through the same shared step; build detection votes by position instead.
480
489
  - **ADR-0044 — Evidence before labels.** The build is decided before annotation from contig lengths or whole-file known-SNP positions; evidence that contradicts the label, or no usable evidence, stops the run instead of producing a report on the wrong build.
481
- - **ADR-0045: ClinVar's own classification.** A ClinVar row is capped by ClinVar's aggregate classification of the variant (Conflicting, Benign, Likely benign), with the submitter split shown; outranked P/LP submissions are kept by ClinVar's tiered rule where the aggregate is not a disease call.
490
+ - **ADR-0045: ClinVar's own classification.** A ClinVar row is capped by ClinVar's aggregate classification of the variant (Conflicting, Benign, Likely benign), with the submitter split shown, and the variants scored down are named on the console, in HTML and in JSON (`clinvar.scored_down_variants`); outranked P/LP submissions are kept by ClinVar's tiered rule where the aggregate is not a disease call.
482
491
  - **ADR-0046: ClinVar variants without an rsID.** Kept in the cache and matched for VCF input only, by locus and the file's REF; reports name such a finding by `chrom:pos ref>alt`.
483
- - **ADR-0047: VCF calls no longer dropped silently.** Untrimmed alleles are trimmed per carried allele, half calls are assessed by the sources that need only a carried allele, VCF homozygous deletions to one base match ClinVar, and dropped calls are counted with their reason (a genotype on a gVCF reference block's own `<NON_REF>` is not checked).
492
+ - **ADR-0047: VCF calls no longer dropped silently.** Untrimmed alleles are trimmed per carried allele (by every annotator since 2.5.2), half calls are assessed by the sources that need only a carried allele, VCF homozygous deletions to one base match ClinVar, and dropped calls are counted with their reason (a genotype on a gVCF reference block's own `<NON_REF>` is not checked).
484
493
  - **ADR-0048: Caches are read-only at analyze time.** With `--no-update`, `analyze` writes nothing to the data directory; every cache opens read-only, installs in rollback-journal mode, and a skipped source is named with its reason (missing, outdated, unreadable, damaged, or WAL mode from an older Allelix). Local migrations run in the update step.
485
494
  - **ADR-0049: Every report names its sources.** JSON `sources` gives every registered source's part in the run (used, ready but not used, or skipped with a reason); `report_warnings` carries report-level warnings, starting with ClinVar not used; the annotated VCF names skipped sources.
486
495
  - **ADR-0050: Zygosity codes and haploid calls.** JSON zygosity and plausibility are codes, a call the file declares haploid is shown as hemizygous, "not assessed" carries a reason, and three rare homozygous P/LP calls give a report warning.
487
- - **ADR-0051: Array calls ClinVar cannot match are counted.** Vendor probes at P/LP loci and `D`/`I` calls are counted in the report, and panel coverage puts each requested rsID in one of four states.
496
+ - **ADR-0051: Array calls ClinVar cannot match are counted.** Vendor probes at P/LP loci and `D`/`I` calls are counted in the report; since 2.5.2, a `D`/`I` call at a high-value or P/LP rsID is also named as not checked (JSON `high_value_unchecked` and `report_warnings` code `di_calls_not_checked`), and panel coverage puts each requested rsID in one of four states.
488
497
  - **ADR-0052: Zero-star P/LP on a common allele.** A ClinVar P/LP row without assertion criteria whose allele has a gnomAD frequency of 5% or more is scored down, marked and counted.
489
498
 
490
499
  Release history: see [`CHANGELOG.md`](https://github.com/allelix/allelix/blob/main/CHANGELOG.md).
@@ -500,12 +509,13 @@ pre-commit install --hook-type pre-commit
500
509
 
501
510
  ruff check .
502
511
  ruff format --check .
503
- pytest
512
+ pytest -m "not slow and not integration" # the fast suite CI runs
513
+ pytest # every test, including slow and integration
504
514
  ```
505
515
 
506
516
  The pre-commit hook enforces `ruff check` + `ruff format --check`. If a commit is blocked, fix the underlying problem rather than skipping the hook.
507
517
 
508
- For the full real-data release-validation battery — every parser format, VCF/gVCF, PLINK round-trip, edge cases, the GIAB/1000G corpus — see [`test_data/FULL_TEST_PROTOCOL.md`](https://github.com/allelix/allelix/blob/main/test_data/FULL_TEST_PROTOCOL.md). That document is the project's real-data testing story; running `pytest` covers the fast suite that gates every commit.
518
+ For the full real-data release-validation battery — every parser format, VCF/gVCF, PLINK round-trip, edge cases, the GIAB/1000G corpus — see [`test_data/FULL_TEST_PROTOCOL.md`](https://github.com/allelix/allelix/blob/main/test_data/FULL_TEST_PROTOCOL.md). That document is the project's real-data testing story; `pytest -m "not slow and not integration"` is the fast suite that gates every commit, as in CI.
509
519
 
510
520
  ## License
511
521
 
@@ -65,32 +65,34 @@ See [Development](#development) for source installs and running tests, [Managing
65
65
  | Format | Status | Notes |
66
66
  |---|---|---|
67
67
  | MyHappyGenes (Tempus) | ✓ | Tab-delimited, 5 columns. **Build is auto-detected** — real-world MHG exports mislabel the header as "build 37.1" while shipping GRCh38 coordinates. Allelix decides the build from position data and warns on header/data disagreement (ADR-0021, ADR-0044). See [docs/formats.md](https://github.com/allelix/allelix/blob/main/docs/formats.md#myhappygenes-tempus). |
68
- | 23andMe | ✓ | Tab-delimited, 4 columns, concatenated genotype. Reads build 36, 37 or 38; the header's build is used when the known-SNP positions agree. I-prefixed probe IDs passed through; they and `D`/`I` indel calls are not matched against ClinVar, and the report counts them (ADR-0051). |
68
+ | 23andMe | ✓ | Tab-delimited, 4 columns, concatenated genotype; a copy a spreadsheet re-saved as CSV (quoted or not) or with spaces is read too ([docs/formats.md](https://github.com/allelix/allelix/blob/main/docs/formats.md#23andme)). Reads build 36, 37 or 38; the header's build is used when the known-SNP positions agree. I-prefixed probe IDs passed through; they and `D`/`I` indel calls are not matched against ClinVar, and the report counts them (ADR-0051). A `D`/`I` call at a high-value rsID, or at one ClinVar classifies Pathogenic or Likely pathogenic, is also named as not checked (gene, variant name, rsID and call) on the console, in HTML and in JSON. |
69
69
  | Genes for Good | ✓ | The 23andMe-style `.txt` from a Genes for Good download (23andMe's columns under a PLINK banner). The imputed text file (about 7.7 million rows) and the imputed VCF are labelled imputed in every report; the measured files are not. The download's VCFs are read as VCF; see [docs/formats.md](https://github.com/allelix/allelix/blob/main/docs/formats.md#genes-for-good). |
70
70
  | AncestryDNA | ✓ | Tab-delimited, 5 columns. Chromosome mapping: 23→X, 24→Y, 25→X (PAR), 26→MT. V1 and V2 chip layouts. |
71
- | Family Tree DNA | ✓ | CSV, double-quoted fields, concatenated genotype. Build 37 default when known-SNP positions are present but do not decide; with none at all and no build line, the run stops (ADR-0044). |
71
+ | Family Tree DNA | ✓ | CSV, double-quoted fields, concatenated genotype. Build 37 default when known-SNP positions are present but do not decide (subject to the stops and the GRCh36 exception in [How the genome build is decided](#how-the-genome-build-is-decided-and-why-a-run-can-stop)); with none at all and no build line, the run stops (ADR-0044). |
72
72
  | MyHeritage DNA | ✓ | CSV, same structure as FTDNA. Detected by "MyHeritage" in the file's preamble. Handles double-double-quoted fields and files re-saved through a spreadsheet (padded rows, signature not on line 1, tab-delimited copies). Two exports: the **array** export (pre-2026 kits) and the **low-pass WGS** export (kits from January 2026; genotypes imputed from about 2x coverage), which the report labels as imputed and whose `##reference=build37` line is used as the header build. See [docs/formats.md](https://github.com/allelix/allelix/blob/main/docs/formats.md#myheritage). |
73
73
  | Living DNA | ✓ | Tab-delimited despite `.csv` extension. Handles AX-, AFFX-prefixed and CHR:POS positional SNP IDs. |
74
74
  | deCODEme | ✓ | `deCODEme_scan.csv` (deCODE genetics, service closed 2013): comma-separated, a per-row `Strand` column; minus-strand calls are complemented to the plus strand. Build 36: decided from known-SNP positions, then run with position lookups off (ADR-0025). See [docs/formats.md](https://github.com/allelix/allelix/blob/main/docs/formats.md#decodeme). |
75
- | FTDNA Illumina raw | ✓ | Tab-delimited variant of the FTDNA export (distinct from the CSV format above). `RSID/CHROMOSOME/POSITION/RESULT` columns. Build 37 default when known-SNP positions are present but do not decide. |
76
- | FTDNA FamFinder | ✓ | Third FTDNA file shape: tab-delimited with **separate `ALLELE1` / `ALLELE2` columns** instead of a concatenated `RESULT`. Detected by the `famfinder` substring in the file header plus the 5-column canonical header. Build 37 default when known-SNP positions are present but do not decide. |
77
- | VCF / gVCF | ✓ | REF/ALT encoding, `0/1` genotype notation. Plain VCF: absence at a position means reference. gVCF: explicit reference blocks (lines with `<NON_REF>` ALT and `END=` INFO) are not annotated — they match nothing in any annotation database — but they are read at high-value positions: a block with low genotype quality or no reads there makes the position a no-call, and so does a gVCF position no record covers. High-value no-calls list the reason (for example `low_gq_block`, `no_coverage`, `filtered`, `low_gq_call`). A plain VCF has no reference blocks, so a position without a record is reported as unknown rather than as reference or as a no-call. Multi-sample files require `--sample <ID>`. Rows the variant caller rejected (FILTER other than `PASS` or `.`) are skipped and counted in the report; `--keep-filtered` keeps them. A call with genotype quality below `--min-gq` (default 20) or with no reads (DP 0) is reported as a no-call. The spanning-deletion allele `*` is treated as symbolic: such calls are not assessed, and the report counts them and lists their positions. A half call (`./1`) is assessed on its called allele by ClinVar and the GWAS Catalog and shown as `T/.` with zygosity "Half call"; ClinPGx and SNPedia, which need both alleles, skip it. Malformed rows, rows without a genotype, genotypes naming an allele the record lacks and genotypes naming a symbolic allele such as `<NON_REF>` beside a real ALT are counted in the report; a no-call written `./.` stays a no-call (ADR-0047). A VCF that holds only structural-variant or copy-number records (such as a provider's separate `sv` / `cnv` file) is rejected with a message naming the file to use instead; see [docs/formats.md](https://github.com/allelix/allelix/blob/main/docs/formats.md#sequencingcom-30x-whole-genome) for per-provider deliverables. A VCF whose header declares an imputation tool's fields (Minimac, Beagle, GLIMPSE) is labelled imputed in every report; see [docs/formats.md](https://github.com/allelix/allelix/blob/main/docs/formats.md#imputed-vcfs). Streams via stdlib; `.vcf.gz` handled transparently. Optional `pip install allelix[vcf-index]` enables pysam-backed tabix random access for fast `extract --snps` on huge VCFs. |
75
+ | FTDNA Illumina raw | ✓ | Tab-delimited variant of the FTDNA export (distinct from the CSV format above). `RSID/CHROMOSOME/POSITION/RESULT` columns. Build 37 default when known-SNP positions are present but do not decide (subject to the stops and the GRCh36 exception in [How the genome build is decided](#how-the-genome-build-is-decided-and-why-a-run-can-stop)). |
76
+ | FTDNA FamFinder | ✓ | Third FTDNA file shape: tab-delimited with **separate `ALLELE1` / `ALLELE2` columns** instead of a concatenated `RESULT`. Detected by the `famfinder` substring in the file header plus the 5-column canonical header. Build 37 default when known-SNP positions are present but do not decide (subject to the stops and the GRCh36 exception in [How the genome build is decided](#how-the-genome-build-is-decided-and-why-a-run-can-stop)). |
77
+ | VCF / gVCF | ✓ | REF/ALT encoding, `0/1` genotype notation. A homozygous-reference call (`0/0`) or a gVCF reference block (lines with `<NON_REF>` ALT and `END=` INFO) carries no variant allele, so it usually matches nothing. The exception is a site where the reference genome itself carries a ClinVar allele (Factor V Leiden on GRCh37, for example): there a `0/0` call, or a reference block that passes the genotype-quality gate, is assessed as homozygous for that allele. See [docs/formats.md, Reference calls in VCFs](https://github.com/allelix/allelix/blob/main/docs/formats.md#reference-calls-in-vcfs) (ADR-0039, ADR-0059). At high-value positions, a block with low genotype quality or no reads makes the position a no-call, and so does a gVCF position no record covers. High-value no-calls list the reason (for example `low_gq_block`, `no_coverage`, `filtered`, `low_gq_call`). A record counts for a high-value or panel rsID when it is at one of the rsID's identity placements with that placement's REF, whatever its ID or ALT; an rsID the identity table does not place is matched by ID (ADR-0055). A plain VCF has no reference blocks, so a position without a record is reported as unknown rather than as reference or as a no-call; at such a ClinVar site the report counts the positions no record covers, and names the high-value ones (such as CYP3A5*3 rs776746 on GRCh37) as not checked. Multi-sample files require `--sample <ID>`. Rows the variant caller rejected (FILTER other than `PASS` or `.`) are skipped and counted in the report; `--keep-filtered` keeps them. A call with genotype quality below `--min-gq` (default 20) or with no reads (DP 0) is reported as a no-call. The spanning-deletion allele `*` is treated as symbolic: such calls are not assessed, and the report counts them and lists their positions. A half call (`./1`) is assessed on its called allele by ClinVar and the GWAS Catalog and shown as `T/.` with zygosity "Half call"; ClinPGx and SNPedia, which need both alleles, skip it. Malformed rows, rows without a genotype, genotypes naming an allele the record lacks and genotypes naming a symbolic allele such as `<NON_REF>` beside a real ALT are counted in the report; a no-call written `./.` stays a no-call (ADR-0047). A VCF that holds only structural-variant or copy-number records (such as a provider's separate `sv` / `cnv` file) is rejected with a message naming the file to use instead; see [docs/formats.md](https://github.com/allelix/allelix/blob/main/docs/formats.md#sequencingcom-30x-whole-genome) for per-provider deliverables. A VCF whose header declares an imputation tool's fields (Minimac, Beagle, GLIMPSE) is labelled imputed in every report; see [docs/formats.md](https://github.com/allelix/allelix/blob/main/docs/formats.md#imputed-vcfs). Streams via stdlib; `.vcf.gz` handled transparently. Optional `pip install allelix[vcf-index]` enables pysam-backed tabix random access for fast `extract --snps` on huge VCFs. |
78
78
 
79
79
  Files can be passed as delivered: gzipped (Family Tree DNA's `.csv.gz`, for example), or a zip holding one genotype file. A zip with several files, a password or an unusual compression stops with a message to unzip it first. Windows, Linux and old-Mac line endings are read; a leading byte order mark or blank line is ignored.
80
80
 
81
- If Allelix does not recognise a file, use the original file from your testing company. `--format <name>` forces a reader at your own risk; every report then carries a warning (see [docs/formats.md, Forcing a format](https://github.com/allelix/allelix/blob/main/docs/formats.md#forcing-a-format)).
81
+ If Allelix does not recognise a file, use the original file from your testing company. `--format <name>` forces a reader at your own risk; when that reader does not recognise the file, every report carries a warning (see [docs/formats.md, Forcing a format](https://github.com/allelix/allelix/blob/main/docs/formats.md#forcing-a-format)).
82
82
 
83
83
  Adding a new format means adding one file to `allelix/parsers/` and registering an instance in the `PARSERS` list in `allelix/parsers/__init__.py`.
84
84
 
85
85
  ### Roadmap
86
86
 
87
+ Shipped releases are listed for history; the [Changelog](https://github.com/allelix/allelix/blob/main/CHANGELOG.md) has each release's changes.
88
+
87
89
  | Release | Theme |
88
90
  |---|---|
89
- | **v2.2** | Custom-panel UX improvements, methylation panel retune + citations, ClinVar source switch (VCF → `variant_summary.txt`), CLI cleanups (`db clean` / `db path`). |
90
- | **v2.3** | Annotated VCF output plus v2.3.1 correctness hardening: cross-build enrichment guards, GWAS effect-type disambiguation, conservative build confidence, SNPedia orientation handling, and strict slow-suite drift verification. |
91
- | **v2.4** | Correctness hardening: cross-build rsID identity table with explicit allele translation, plausibility flag for genotyping-artifact detection, VCF quality gate honouring FILTER and genotype quality, unified rsID-recovery step across all commands, and parser/format hygiene. |
92
- | **v2.5** | ClinVar's own classification caps each row; ClinVar variants without an rsID; VCF calls counted, not dropped; read-only caches and named sources in every report; zip input; Genes for Good and deCODEme; imputed input labelled. |
93
- | **Later** | Variant consequences, CNVs and repeat expansions, gnomAD genomes, supplemental genotype-file merging, ancestry estimation, and PRS support. These are directional themes rather than release commitments. |
91
+ | **v2.2** (shipped) | Custom-panel UX improvements, methylation panel retune + citations, ClinVar source switch (VCF → `variant_summary.txt`), CLI cleanups (`db clean` / `db path`). |
92
+ | **v2.3** (shipped) | Annotated VCF output plus v2.3.1 correctness hardening: cross-build enrichment guards, GWAS effect-type disambiguation, conservative build confidence, SNPedia orientation handling, and strict slow-suite drift verification. |
93
+ | **v2.4** (shipped) | Correctness hardening: cross-build rsID identity table with explicit allele translation, plausibility flag for genotyping-artifact detection, VCF quality gate honouring FILTER and genotype quality, unified rsID-recovery step across all commands, and parser/format hygiene. |
94
+ | **v2.5** (shipped) | ClinVar's own classification caps each row; ClinVar variants without an rsID; VCF calls counted, not dropped; read-only caches and named sources in every report; zip input; Genes for Good and deCODEme; imputed input labelled. |
95
+ | **Later** (planned) | Variant consequences, CNVs and repeat expansions, gnomAD genomes, supplemental genotype-file merging, ancestry estimation, and PRS support. These are directional themes rather than release commitments. |
94
96
 
95
97
  Issues and milestones are tracked at [github.com/allelix/allelix/issues](https://github.com/allelix/allelix/issues).
96
98
 
@@ -98,15 +100,15 @@ Issues and milestones are tracked at [github.com/allelix/allelix/issues](https:/
98
100
 
99
101
  | Database | Status | Notes |
100
102
  |---|---|---|
101
- | ClinVar (GRCh37 + GRCh38) | ✓ | Public domain (NCBI). SNVs + indels + multi-allelic sites. **Both builds cached**; `analyze` dispatches by the decided build (ADR-0044). Rows are matched by chromosome, position, REF and ALT, never by rsID alone, and reference-allele records (REF = ALT) are never reported (ADR-0039). Carrier rule (ADR-0007) requires the user to carry the ALT allele. Indel-anchor protection (ADR-0011) prevents single-base array readouts from matching anchor-base indels. Array calls ClinVar cannot match are counted in the report: vendor probes without an rsID at a locus ClinVar classifies as Pathogenic or Likely pathogenic, and `D`/`I` calls (ADR-0051). ClinVar variants without an rsID are matched for VCF input only, where REF comes from the file (ADR-0046). A VCF allele written with bases it shares with REF (`GTT>GT,G`, `CA>TA`) is trimmed per carried allele before matching, and a VCF homozygous deletion to one base matches; indels are not left-aligned, so one the file places at a different position from ClinVar's still does not match (ADR-0047). |
102
- | ClinPGx (formerly PharmGKB) | ✓ | CC BY-SA 4.0. Clinical annotations only — single-rsid SNVs; star alleles and haplotypes deferred (ADR-0009). **Primary non-finding filter is the ClinVar REF carrier rule (ADR-0023):** if ClinVar publishes a single-base REF for the rsid and the user is homozygous for it, the row is suppressed. CPIC's `(rsid, base) → function_class` join (ADR-0020) survives as a secondary tier for rsids ClinVar doesn't catalog. Earlier prose tiers (ADR-0013, ADR-0017, ADR-0018) are superseded. |
103
+ | ClinVar (GRCh37 + GRCh38) | ✓ | Public domain (NCBI). SNVs + indels + multi-allelic sites. **Both builds cached**; `analyze` dispatches by the decided build (ADR-0044). Rows are matched by chromosome, position, REF and ALT, never by rsID alone. A record written REF = ALT, where this build's reference itself carries the record's allele (Factor V Leiden on GRCh37, or a record for the normal allele), is reported for a user who carries that allele; Pathogenic and Likely pathogenic classifications of a normal allele are dropped (ADR-0039). Carrier rule (ADR-0007) requires the user to carry the ALT allele. Indel-anchor protection (ADR-0011) prevents single-base array readouts from matching anchor-base indels. Array calls ClinVar cannot match are counted in the report: vendor probes without an rsID at a locus ClinVar classifies as Pathogenic or Likely pathogenic, and `D`/`I` calls (ADR-0051). A `D`/`I` call at a high-value or Pathogenic / Likely pathogenic rsID is named as not checked, not only counted; it is still not interpreted. Variants that ClinVar's Conflicting or Benign classification scored down (ADR-0045) are named with their submitter split. ClinVar variants without an rsID are matched for VCF input only, where REF comes from the file (ADR-0046). A VCF allele written with bases it shares with REF (`GTT>GT,G`, `CA>TA`) is trimmed per carried allele before matching, by ClinPGx, SNPedia and the GWAS Catalog as well as ClinVar, and a VCF homozygous deletion to one base matches; indels are not left-aligned, so one the file places at a different position from ClinVar's still does not match (ADR-0047). |
104
+ | ClinPGx (formerly PharmGKB) | ✓ | CC BY-SA 4.0. Clinical annotations only — single-rsid SNVs; star alleles and haplotypes deferred (ADR-0009). **Primary non-finding filter is the ClinVar REF carrier rule (ADR-0023):** where ClinVar for the input's build is installed and places the rsID at the call's position, a user homozygous for the normal allele ClinVar resolves there is not reported. That is usually ClinVar's single-base REF. The exception is a site where this build's reference itself carries ClinVar's variant allele (a REF = ALT row, F5 rs6025 on GRCh37): there the normal allele is the other build's REF, so a TT call is reported as a carrier and CC is the non-finding. Where that normal allele cannot be told, nothing is suppressed. Without that cache, only sites where the two builds' references differ (F5 rs6025) are decided: by the other build's ClinVar, else by gnomAD's major allele; if neither can tell, the row shows. Every other site is then not suppressed. CPIC's `(rsid, base) → function_class` join (ADR-0020) survives as a secondary tier for rsids ClinVar doesn't catalog. Earlier prose tiers (ADR-0013, ADR-0017, ADR-0018) are superseded. |
103
105
  | CPIC (per-allele function table) | ✓ | Internal data source for the ClinPGx filter. Fetched from `api.cpicpgx.org` at `db update` time. Used to populate the `pharmgkb_allele_function` table — not surfaced to end users as its own annotator. |
104
106
  | SNPedia | ✓ | CC BY-NC-SA 3.0 US. Pre-built cache downloaded via `db update` (~216K wiki pages, ~105K genotype rows). The cache carries SNPedia's orientation metadata: minus-strand alleles are complemented before matching. Single-base genotypes at rsIDs are then checked against the site's forward alleles in the rsID identity table: a page's stated orientation must put the genotype among them, an unknown orientation is read directly or else complemented, and A/T and C/G sites abstain. Sites that abstain are counted in the report. If the database is absent, analysis runs without it. For commercial use, pass `--exclude-snpedia`. The cache can also be rebuilt from source via `scripts/scrape_snpedia.py` + `scripts/parse_snpedia.py`. |
105
107
  | GWAS Catalog | ✓ | Public domain (EBI/NHGRI). Trait–SNP associations with p-values and effect sizes. Carrier rule (ADR-0007) requires the user to carry the risk allele. The reported allele is the study's effect (risk) allele, which can be the reference allele (ADR-0039). P-value magnitude scoring maps continuous p-values to the 0–10 scale; only a confidence-interval field that positively identifies a bare odds ratio receives the odds-ratio modifier. Beta coefficients and ambiguous or unit-bearing effects receive no modifier (ADR-0038). Unknown-risk-allele entries fire on rsID match alone but are capped at 3.0. Risk alleles are read on the forward strand against the site's alleles in the rsID identity table: as written, else complemented; A/T and C/G sites and rsIDs the table does not place at the call abstain, and the report counts them. Every row says whether the effect allele raises or lowers the odds or the measured value (JSON `effect_direction`). GWAS rows must meet the larger of `--min-magnitude` and `--gwas-min-magnitude` (default 9.0); the must-include rsIDs skip the GWAS floor (ADR-0024). |
106
108
  | gnomAD | ✓ | ODbL v1.0. **GRCh38-only enrichment annotator** — adds population allele frequency context to existing annotations. Shows how common each variant is in the general population (~57M exome variants with an rsID, from 730K individuals). A pathogenic variant that 35% of people carry reads very differently from one seen in 0.001%. Pre-built cache downloaded via `db update` (~7 GB on disk). GRCh37 input is enriched only through the rsID identity table's explicit allele translation (ADR-0040); gnomAD's own rsID resolution stays GRCh38-only, and GRCh36 input receives nothing. Use `--no-gnomad` to skip. |
107
109
  | AlphaMissense | ✓ | CC BY 4.0. **GRCh38-only enrichment annotator** — adds DeepMind's protein-structure-based pathogenicity predictions to existing annotations. Scores 71M missense variants on a 0–1 scale: <0.34 = likely benign, >0.564 = likely pathogenic. Complements ClinVar's expert classifications with computational predictions — especially valuable for variants ClinVar hasn't reviewed yet. Pre-built cache downloaded via `db update` (~8GB on disk). GRCh37 input is enriched only through the rsID identity table (ADR-0040); GRCh36 input receives nothing. Use `--no-alphamissense` to skip. |
108
110
  | CADD | ✓ | LicenseRef-CADD (non-commercial). **GRCh38-only enrichment annotator** — adds PHRED-scaled deleteriousness scores from CADD v1.7. **Opt-in**, disabled by default; GRCh37 input is scored only through the rsID identity table (ADR-0040) and GRCh36 input never; see [CADD modes](#cadd-modes) below for details. |
109
- | dbSNP rsID identity | ✓ | Public domain (NCBI dbSNP). Not an annotator: a cross-build table of each annotator rsID's GRCh37 and GRCh38 placement and alleles (ADR-0040). Supplies the reference allele for GRCh37 and GRCh38 array data, recovers rsIDs for rsID-less VCFs on both builds, and translates a GRCh37 allele to its GRCh38 coordinates so gnomAD, AlphaMissense and CADD can be looked up. Strand-inverted loci are translated explicitly; palindromic sites (A/T, C/G, or a site whose dbSNP alleles include all four bases) and alleles that became the GRCh38 reference abstain. Pre-built cache downloaded via `db update` (~4.9M rows covering ~3.0M rsIDs, from dbSNP b157). |
111
+ | dbSNP rsID identity | ✓ | Public domain (NCBI dbSNP). Not an annotator: a cross-build table of each annotator rsID's GRCh37 and GRCh38 placement and alleles (ADR-0040). Supplies the reference allele for GRCh37 and GRCh38 array data, recovers rsIDs for rsID-less VCFs on both builds, and translates a GRCh37 allele to its GRCh38 coordinates so gnomAD, AlphaMissense and CADD can be looked up. Strand-inverted loci are translated explicitly. A row the table flags palindromic (its dbSNP allele set equals its own complement: A/T, C/G, or all four bases) abstains where the GRCh37 and GRCh38 reference bases differ, and translates as written where they are equal; rows the table does not flag translate by their alleles. Alleles that became the GRCh38 reference abstain. Pre-built cache downloaded via `db update` (~4.9M rows covering ~3.0M rsIDs, from dbSNP b157). |
110
112
 
111
113
  ### CADD modes
112
114
 
@@ -117,26 +119,26 @@ CADD ranks how deleterious any single-nucleotide variant is using 100+ annotatio
117
119
  Two modes:
118
120
 
119
121
  - **Cache mode** (default): pre-built ~5.8 GB SQLite cache (~120M GRCh38 variant keys). Covers nearly every GRCh38 position allelix can annotate from its other databases (gnomAD, AlphaMissense, ClinVar). GRCh37 input is scored only after translation through the rsID identity table; GRCh36 input never is.
120
- - **Full mode** (`options.cadd_full = true`): tabix queries against the complete CADD v1.7 file via pysam (GRCh38 only). Adds coverage for novel or private variants that appear only in WGS/WES data and are not in any pre-computed database. Requires `pip install allelix[cadd]` for the pysam dependency.
122
+ - **Full mode** (`options.cadd_full = true`): tabix queries against the complete CADD v1.7 file via pysam (GRCh38 only). It scores variants the cache lacks, such as novel or private variants in WGS/WES data. CADD adds scores to findings from the other sources; it never makes a finding of its own, so a variant no other source reports gains nothing. Requires `pip install allelix[cadd]` for the pysam dependency, and CADD's files, downloaded separately from [cadd.gs.washington.edu/download](https://cadd.gs.washington.edu/download) (`db update --cadd` installs only the SQLite cache): `whole_genome_SNVs.tsv.gz` (about 81 GB) and its `.tbi` index in the data directory, plus, optionally for indels, `gnomad.genomes.r4.0.indel.tsv.gz` (about 1.2 GB) and its `.tbi`.
121
123
 
122
124
  If your input is a genotyping chip file, cache mode is normally all you need. GRCh37 chip files are scored through the rsID identity table; GRCh36 files are not scored.
123
125
 
124
126
  ### Build coverage asymmetry (GRCh37 vs GRCh38)
125
127
 
126
- ClinVar dispatches per build and ships with both GRCh37 and GRCh38 caches. gnomAD v4.1 exomes, AlphaMissense, and CADD v1.7 are GRCh38 datasets. Every enrichment lookup is made at GRCh38 coordinates by chromosome, position, reference and alternate allele, never by rsID. For GRCh37 input, the cross-build rsID identity table (ADR-0040) translates the allele the user carries at its GRCh37 position into the matching GRCh38 allele, written explicitly on the GRCh38 forward strand where the reference strand was inverted between assemblies. Palindromic sites (A/T, C/G, or all four bases in dbSNP, whose strand cannot be told from sequence) and alleles that are the GRCh38 reference receive no values rather than a guess. Without the identity table installed, GRCh37 analyses receive no gnomAD, AlphaMissense or CADD values; GRCh36 analyses never do.
128
+ ClinVar dispatches per build and ships with both GRCh37 and GRCh38 caches. gnomAD v4.1 exomes, AlphaMissense, and CADD v1.7 are GRCh38 datasets. Every enrichment lookup is made at GRCh38 coordinates by chromosome, position, reference and alternate allele, never by rsID. For GRCh37 input, the cross-build rsID identity table (ADR-0040) translates the allele the user carries at its GRCh37 position into the matching GRCh38 allele, written explicitly on the GRCh38 forward strand where the reference strand was inverted between assemblies. A row the identity table flags palindromic (A/T, C/G, or all four bases in dbSNP, whose strand cannot be told from sequence) whose reference base differs between the builds, and an allele that is the GRCh38 reference, receive no values rather than a guess. Without the identity table installed, GRCh37 analyses receive no gnomAD, AlphaMissense or CADD values; GRCh36 analyses never do.
127
129
 
128
130
  Position-based rsID recovery for rsID-less VCFs uses ClinVar (on the selected build), the identity table (on GRCh37 and GRCh38), and gnomAD (GRCh38 only). The same recovery step runs for `analyze`, `stats`, `extract` and `export plink`, so high-value warnings, extracted genotypes and PLINK allele coding all see the recovered rsIDs (ADR-0043). The identity table covers the rsIDs the annotators hold, so an rsID-less GRCh37 VCF now recovers ClinPGx, GWAS Catalog and SNPedia rsIDs as well as ClinVar ones. If you convert between assemblies, re-normalize REF/ALT as part of the conversion and pass `--build` explicitly when the file does not carry a trustworthy build signal.
129
131
 
130
- ### Known ClinPGx limitation: reference-genotype rows where ClinVar and CPIC both lack data
132
+ ### Known ClinPGx limitation: normal-genotype rows without usable normal-allele evidence
131
133
 
132
- ADR-0022 + ADR-0023: a tiny residual of ClinPGx rows may appear in reports even when the user is homozygous reference. ClinPGx publishes one annotation per genotype including the reference homozygote, and for the reference-homozygote row to be suppressed Allelix needs structured data on the variant from either:
134
+ ADR-0022 + ADR-0023: a tiny residual of ClinPGx rows may appear in reports even when the user carries the normal genotype. ClinPGx publishes one annotation per genotype including the normal homozygote, and for that row to be suppressed Allelix needs usable evidence of the normal allele from one of:
133
135
 
134
- - **ClinVar's REF allele** (the primary filter — see ADR-0023). Covers any rsID ClinVar catalogs.
136
+ - **ClinVar's normal allele** (the primary filter — see ADR-0023): usually its single-base REF, or the other build's REF where this build's reference carries ClinVar's variant allele (F5 rs6025 on GRCh37). Covers an rsID that ClinVar for the input's build places at the call's position. Without that cache, only a site whose reference differs between the builds is decided (by the other build's ClinVar, else gnomAD's major allele), and anything else shows.
135
137
  - **CPIC's per-allele function table** (the secondary fallback — see ADR-0020). Covers rsIDs CPIC has classified.
136
138
 
137
- For the rare rsID where ClinPGx has an annotation but *neither* ClinVar nor CPIC has data, the row emits. These are identifiable by a homozygous-reference genotype combined with "decreased risk," "may have a typical response," or similar comparative language. They are an upstream data gap, not an Allelix bug — we surface them honestly rather than hide them behind a curated exclusion list (which would recreate the maintenance trap the v0.5–v0.7 prose filters were trying to escape).
139
+ So without ClinVar or CPIC data, gnomAD can still decide a site whose reference differs between the builds, at that placement only. For the rare rsID where ClinPGx has an annotation and none of these gives a usable normal allele (no ClinVar or CPIC data, and no decision from the other build's ClinVar or gnomAD, which abstains on a tie, an unknown frequency or an unlisted site), the row emits: Allelix shows it rather than guess. These are identifiable by a homozygous-reference genotype combined with "decreased risk," "may have a typical response," or similar comparative language. They are an upstream data gap, not an Allelix bug — we surface them honestly rather than hide them behind a curated exclusion list (which would recreate the maintenance trap the v0.5–v0.7 prose filters were trying to escape).
138
140
 
139
- The CFTR × ivacaftor leak (~30+ rows on real data, pre-v0.7.3) is fixed by the ADR-0023 ClinVar REF check: CPIC's CFTR vocabulary (`"ivacaftor responsive"`) doesn't match the four-class enum the secondary tier expects, but ClinVar publishes REF for every CFTR rsID, so the primary tier catches them universally.
141
+ The CFTR × ivacaftor leak (~30+ rows on real data, pre-v0.7.3) is fixed by the ADR-0023 ClinVar REF check: CPIC's CFTR vocabulary (`"ivacaftor responsive"`) doesn't match the four-class enum the secondary tier expects, but ClinVar publishes REF for the CFTR rsIDs, so with ClinVar for the input's build installed the primary tier catches them.
140
142
 
141
143
  ### Known ClinVar upstream data quality issues
142
144
 
@@ -214,7 +216,7 @@ The HTML report shows a ⚠ icon next to a flagged genotype, with the reason on
214
216
 
215
217
  A call is not assessed when gnomAD has no frequency for the allele. Allelix's gnomAD cache covers exome sites with an rsID, so a missing value says nothing about rarity. Calls are also not assessed when the gnomAD database is skipped. Homozygous calls on X are not assessed because a male's single X reads as homozygous; heterozygous X calls are. A call the file itself declares as one copy (a one-letter array call such as 23andMe's male X calls, or a one-index VCF genotype) is shown as Hemizygous and assessed as one copy. Y and mitochondrial calls are not assessed. Confirm a flagged call with a clinical-grade test before relying on it.
216
218
 
217
- When three or more variants on chromosomes 1 to 22 are called homozygous for an allele ClinVar classifies as Pathogenic or Likely pathogenic, and are rare in gnomAD or have no gnomAD frequency, the report warns at the top. This is counted before any display filter, so `--min-magnitude` cannot hide it (ADR-0050).
219
+ When three or more variants on chromosomes 1 to 22 are called homozygous for an allele ClinVar classifies as Pathogenic or Likely pathogenic, and are rare in gnomAD or were looked up there and are absent, the report warns at the top. A call gnomAD was not asked about (gnomAD not installed, or a GRCh37 allele the identity table cannot translate) does not count. This is counted before any display filter, so `--min-magnitude` cannot hide it (ADR-0050).
218
220
 
219
221
  ### Database sizes and download times
220
222
 
@@ -245,6 +247,7 @@ Allelix keeps its caches in `~/.local/share/allelix/`, or `$XDG_DATA_HOME/alleli
245
247
  ├── cadd.sqlite ~5.8 GB (only if you opted into --cadd)
246
248
  ├── clinvar.GRCh37.sqlite ~1.2 GB
247
249
  ├── clinvar.GRCh38.sqlite ~1.2 GB
250
+ ├── clinvar-build.log each ClinVar build's source hashes and counts
248
251
  ├── gwas_catalog_associations.tsv ~745 MB
249
252
  ├── rsid_identity.sqlite ~457 MB
250
253
  ├── gwas.sqlite ~202 MB
@@ -283,7 +286,7 @@ du -sh "$ALLELIX_DATA"
283
286
  allelix db path --check
284
287
  ```
285
288
 
286
- **The one thing worth backing up: `config.toml`.** It lives inside the resolved data directory (typically `~/.local/share/allelix/config.toml`) and stores your license assertions (commercial-mode toggle, CADD opt-in confirmation) and per-source enable/disable settings. It's the only non-reproducible state in the install. The caches will rebuild on the next `db update`; `config.toml` won't. `allelix db clean` always preserves it.
289
+ **The one thing worth backing up: `config.toml`.** It lives inside the resolved data directory (typically `~/.local/share/allelix/config.toml`) and stores your license assertions (commercial-mode toggle, CADD opt-in confirmation) and per-source enable/disable settings. The caches will rebuild on the next `db update`; `config.toml` won't. `allelix db clean` always preserves it. `clinvar-build.log` (the source hashes and counts of past ClinVar builds) is not reproducible either, and `db clean` removes it: copy it elsewhere first if you want to keep that history.
287
290
 
288
291
  To fully uninstall allelix and reclaim everything:
289
292
 
@@ -382,11 +385,11 @@ The banner says where the build came from, for example `Build: GRCh38 (from the
382
385
  - a VCF has no contig lengths, no assembly and too few known-SNP positions;
383
386
  - a chip states no build and carries none of the known-SNP positions at all (for example an X-chromosome-only export, since they are all on autosomes);
384
387
  - a file states a build but carries none of the known-SNP positions to check it (for example an mtDNA-only export, or a small panel VCF without contig lengths);
385
- - the file has known-SNP positions but none of them sits at any supported build's coordinate, whether or not it states a build (contig lengths, when a VCF declares them, still decide);
388
+ - the file has known-SNP positions but none of them sits at any supported build's coordinate, whether or not it states a build (contig lengths, when a VCF declares them, still decide). Choosing a build is not the fix here: the message says the positions cannot be trusted on any build and asks you to check that this is the original file from the testing company. If you force a build anyway with `--build`, the run goes ahead but prints a **Build warning** naming the evidence (0 of N known-SNP positions match), and every report keeps it (JSON `report_warnings` code `build_override_unmatched`);
386
389
  - contig lengths decided, but the known-SNP positions met while annotating point to the other build (for example a lifted-over file that kept its old header). The message says the annotation pass ran; nothing is written;
387
390
  - `--build grch37` or `--build grch38` was given, but the file's contig lengths, or three or more known-SNP positions with an 80% majority, say the other build. The message names both builds. Contrary positions that fall short of that (fewer than three, or under an 80% majority) only print a **Build warning**, and so does a tie that leaves out the forced build. `--build grch36` is never checked, because it turns position-keyed lookups off.
388
391
 
389
- The message names the evidence and asks you to say which build the file uses: `--build grch37` or `--build grch38`, or `--build grch36` for an old GRCh36 / hg18 file, which then runs with position-keyed lookups off (see [`docs/grch36-liftover.md`](https://github.com/allelix/allelix/blob/main/docs/grch36-liftover.md)). For `compare`, use `--build1` / `--build2`. A stop leaves no report, annotated VCF, diff, PLINK files or extract table behind. A forced build is recorded as such in every report.
392
+ Otherwise the message names the evidence and asks you to say which build the file uses: `--build grch37` or `--build grch38`, or `--build grch36` for an old GRCh36 / hg18 file, which then runs with position-keyed lookups off (see [`docs/grch36-liftover.md`](https://github.com/allelix/allelix/blob/main/docs/grch36-liftover.md)). For `compare`, use `--build1` / `--build2`. A stop leaves no report, annotated VCF, diff, PLINK files or extract table behind. A forced build is recorded as such in every report.
390
393
 
391
394
  ### Keeping input and output files safe
392
395
 
@@ -395,13 +398,19 @@ must differ from the input, including symlink and hard-link aliases. These
395
398
  checks run before cache setup or output creation. Inputs that alias the former
396
399
  fixed report or PLINK `.tmp` path are also refused; unrelated old temp files
397
400
  are left untouched. Annotated VCF still refuses any existing output at the CLI.
401
+ Two outputs of one run must name different files too: `--output` and
402
+ `--vcf-out`, and PLINK's `.bed`, `.bim` and `.fam`, are refused before any work
403
+ when they resolve to one file (another spelling, a symlink, a linked folder or a
404
+ hardlink).
398
405
 
399
406
  Writers use unique temporary files in the destination directory and publish
400
407
  only complete, closed content. Failure before publication preserves previous
401
408
  outputs. PLINK stages all three files first, then publishes each atomically;
402
409
  a later publication failure can leave complete new files next to unchanged old
403
410
  ones. Existing ordinary permission bits are retained when replacement is
404
- permitted; new staging files start private. Ownership, ACLs, extended attributes,
411
+ permitted; new staging files start private, and a new output file gets the mode
412
+ your umask gives any new file (`0666 & ~umask`, so `0644` under `022`).
413
+ Ownership, ACLs, extended attributes,
405
414
  crash durability and concurrent path swaps are outside this guarantee.
406
415
 
407
416
  ### Is my database stale? How fresh is the data?
@@ -438,13 +447,13 @@ Notable load-bearing ADRs:
438
447
  - **ADR-0042 — VCF quality gate.** The variant caller's FILTER and genotype quality are honoured, low-quality reference blocks are no-calls, and every exclusion is counted in the report.
439
448
  - **ADR-0043 — One rsID-recovery step.** Every command that needs rsIDs recovers them for rsID-less input through the same shared step; build detection votes by position instead.
440
449
  - **ADR-0044 — Evidence before labels.** The build is decided before annotation from contig lengths or whole-file known-SNP positions; evidence that contradicts the label, or no usable evidence, stops the run instead of producing a report on the wrong build.
441
- - **ADR-0045: ClinVar's own classification.** A ClinVar row is capped by ClinVar's aggregate classification of the variant (Conflicting, Benign, Likely benign), with the submitter split shown; outranked P/LP submissions are kept by ClinVar's tiered rule where the aggregate is not a disease call.
450
+ - **ADR-0045: ClinVar's own classification.** A ClinVar row is capped by ClinVar's aggregate classification of the variant (Conflicting, Benign, Likely benign), with the submitter split shown, and the variants scored down are named on the console, in HTML and in JSON (`clinvar.scored_down_variants`); outranked P/LP submissions are kept by ClinVar's tiered rule where the aggregate is not a disease call.
442
451
  - **ADR-0046: ClinVar variants without an rsID.** Kept in the cache and matched for VCF input only, by locus and the file's REF; reports name such a finding by `chrom:pos ref>alt`.
443
- - **ADR-0047: VCF calls no longer dropped silently.** Untrimmed alleles are trimmed per carried allele, half calls are assessed by the sources that need only a carried allele, VCF homozygous deletions to one base match ClinVar, and dropped calls are counted with their reason (a genotype on a gVCF reference block's own `<NON_REF>` is not checked).
452
+ - **ADR-0047: VCF calls no longer dropped silently.** Untrimmed alleles are trimmed per carried allele (by every annotator since 2.5.2), half calls are assessed by the sources that need only a carried allele, VCF homozygous deletions to one base match ClinVar, and dropped calls are counted with their reason (a genotype on a gVCF reference block's own `<NON_REF>` is not checked).
444
453
  - **ADR-0048: Caches are read-only at analyze time.** With `--no-update`, `analyze` writes nothing to the data directory; every cache opens read-only, installs in rollback-journal mode, and a skipped source is named with its reason (missing, outdated, unreadable, damaged, or WAL mode from an older Allelix). Local migrations run in the update step.
445
454
  - **ADR-0049: Every report names its sources.** JSON `sources` gives every registered source's part in the run (used, ready but not used, or skipped with a reason); `report_warnings` carries report-level warnings, starting with ClinVar not used; the annotated VCF names skipped sources.
446
455
  - **ADR-0050: Zygosity codes and haploid calls.** JSON zygosity and plausibility are codes, a call the file declares haploid is shown as hemizygous, "not assessed" carries a reason, and three rare homozygous P/LP calls give a report warning.
447
- - **ADR-0051: Array calls ClinVar cannot match are counted.** Vendor probes at P/LP loci and `D`/`I` calls are counted in the report, and panel coverage puts each requested rsID in one of four states.
456
+ - **ADR-0051: Array calls ClinVar cannot match are counted.** Vendor probes at P/LP loci and `D`/`I` calls are counted in the report; since 2.5.2, a `D`/`I` call at a high-value or P/LP rsID is also named as not checked (JSON `high_value_unchecked` and `report_warnings` code `di_calls_not_checked`), and panel coverage puts each requested rsID in one of four states.
448
457
  - **ADR-0052: Zero-star P/LP on a common allele.** A ClinVar P/LP row without assertion criteria whose allele has a gnomAD frequency of 5% or more is scored down, marked and counted.
449
458
 
450
459
  Release history: see [`CHANGELOG.md`](https://github.com/allelix/allelix/blob/main/CHANGELOG.md).
@@ -460,12 +469,13 @@ pre-commit install --hook-type pre-commit
460
469
 
461
470
  ruff check .
462
471
  ruff format --check .
463
- pytest
472
+ pytest -m "not slow and not integration" # the fast suite CI runs
473
+ pytest # every test, including slow and integration
464
474
  ```
465
475
 
466
476
  The pre-commit hook enforces `ruff check` + `ruff format --check`. If a commit is blocked, fix the underlying problem rather than skipping the hook.
467
477
 
468
- For the full real-data release-validation battery — every parser format, VCF/gVCF, PLINK round-trip, edge cases, the GIAB/1000G corpus — see [`test_data/FULL_TEST_PROTOCOL.md`](https://github.com/allelix/allelix/blob/main/test_data/FULL_TEST_PROTOCOL.md). That document is the project's real-data testing story; running `pytest` covers the fast suite that gates every commit.
478
+ For the full real-data release-validation battery — every parser format, VCF/gVCF, PLINK round-trip, edge cases, the GIAB/1000G corpus — see [`test_data/FULL_TEST_PROTOCOL.md`](https://github.com/allelix/allelix/blob/main/test_data/FULL_TEST_PROTOCOL.md). That document is the project's real-data testing story; `pytest -m "not slow and not integration"` is the fast suite that gates every commit, as in CI.
469
479
 
470
480
  ## License
471
481
 
@@ -13,6 +13,7 @@ from allelix.annotators.clinvar import CLINVAR_SUPPORTED_BUILDS, ClinVarAnnotato
13
13
  from allelix.annotators.gnomad import GnomadAnnotator
14
14
  from allelix.annotators.gwas import GWASCatalogAnnotator
15
15
  from allelix.annotators.pharmgkb import PharmGKBAnnotator
16
+ from allelix.annotators.reference_fallback import ReferenceCarriedFallback
16
17
  from allelix.annotators.rsid_identity import RsidIdentityAnnotator
17
18
  from allelix.annotators.snpedia import SNPediaAnnotator
18
19
 
@@ -55,11 +56,17 @@ def get_annotators(
55
56
  include_benign=include_benign,
56
57
  other_build_refs=rsid_identity.normal_alleles_on_other_build,
57
58
  )
59
+ gnomad = GnomadAnnotator(data_dir)
60
+ # ClinVar's REF where its cache for the call's build is readable; at a
61
+ # reference-carried site without it, the other build's ClinVar or
62
+ # gnomAD's major allele, else nothing (2.5.2).
63
+ references = ReferenceCarriedFallback(clinvar, rsid_identity, gnomad)
58
64
  pharmgkb = PharmGKBAnnotator(
59
65
  data_dir,
60
- clinvar_ref_provider=clinvar.reference_at,
66
+ clinvar_ref_provider=references.reference_at,
61
67
  palindromic_provider=rsid_identity.palindromic_rsids,
62
- normal_allele_provider=clinvar.normal_allele_for,
68
+ normal_allele_provider=references.normal_allele_for,
69
+ reference_prefetch=references.prefetch,
63
70
  )
64
71
  gwas = GWASCatalogAnnotator(
65
72
  data_dir,
@@ -68,11 +75,11 @@ def get_annotators(
68
75
  )
69
76
  snpedia = SNPediaAnnotator(
70
77
  data_dir,
71
- clinvar_ref_provider=clinvar.reference_at,
72
- normal_allele_provider=clinvar.normal_allele_for,
78
+ clinvar_ref_provider=references.reference_at,
79
+ normal_allele_provider=references.normal_allele_for,
73
80
  forward_site_provider=rsid_identity.forward_sites,
81
+ reference_prefetch=references.prefetch,
74
82
  )
75
- gnomad = GnomadAnnotator(data_dir)
76
83
  alphamissense = AlphaMissenseAnnotator(data_dir)
77
84
  cadd = CaddAnnotator(data_dir, full_mode=cadd_full)
78
85
  return [clinvar, pharmgkb, gwas, snpedia, gnomad, alphamissense, cadd, rsid_identity]