allelix 2.4.1__tar.gz → 2.5.0__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 (90) hide show
  1. {allelix-2.4.1/allelix.egg-info → allelix-2.5.0}/PKG-INFO +67 -39
  2. {allelix-2.4.1 → allelix-2.5.0}/README.md +66 -38
  3. {allelix-2.4.1 → allelix-2.5.0}/allelix/annotators/__init__.py +20 -5
  4. {allelix-2.4.1 → allelix-2.5.0}/allelix/annotators/_resolution.py +17 -10
  5. {allelix-2.4.1 → allelix-2.5.0}/allelix/annotators/alphamissense.py +17 -6
  6. {allelix-2.4.1 → allelix-2.5.0}/allelix/annotators/base.py +89 -5
  7. {allelix-2.4.1 → allelix-2.5.0}/allelix/annotators/cadd.py +27 -10
  8. {allelix-2.4.1 → allelix-2.5.0}/allelix/annotators/clinvar.py +523 -109
  9. {allelix-2.4.1 → allelix-2.5.0}/allelix/annotators/gnomad.py +45 -62
  10. {allelix-2.4.1 → allelix-2.5.0}/allelix/annotators/gwas.py +231 -183
  11. {allelix-2.4.1 → allelix-2.5.0}/allelix/annotators/pharmgkb.py +68 -39
  12. {allelix-2.4.1 → allelix-2.5.0}/allelix/annotators/rsid_identity.py +111 -17
  13. allelix-2.5.0/allelix/annotators/snpedia.py +542 -0
  14. {allelix-2.4.1 → allelix-2.5.0}/allelix/cli/__init__.py +17 -1
  15. {allelix-2.4.1 → allelix-2.5.0}/allelix/cli/_helpers.py +267 -38
  16. {allelix-2.4.1 → allelix-2.5.0}/allelix/cli/_options.py +17 -6
  17. {allelix-2.4.1 → allelix-2.5.0}/allelix/cli/analyze.py +2 -2
  18. {allelix-2.4.1 → allelix-2.5.0}/allelix/cli/config.py +4 -4
  19. {allelix-2.4.1 → allelix-2.5.0}/allelix/cli/db.py +34 -5
  20. {allelix-2.4.1 → allelix-2.5.0}/allelix/cli/utility.py +19 -9
  21. {allelix-2.4.1 → allelix-2.5.0}/allelix/config.py +14 -4
  22. {allelix-2.4.1 → allelix-2.5.0}/allelix/databases/_versions.py +18 -1
  23. allelix-2.5.0/allelix/databases/cache_state.py +161 -0
  24. {allelix-2.4.1 → allelix-2.5.0}/allelix/databases/gwas_loader.py +3 -1
  25. {allelix-2.4.1 → allelix-2.5.0}/allelix/databases/loader_utils.py +35 -0
  26. {allelix-2.4.1 → allelix-2.5.0}/allelix/databases/manager.py +487 -87
  27. {allelix-2.4.1 → allelix-2.5.0}/allelix/databases/pharmgkb_loader.py +5 -2
  28. {allelix-2.4.1 → allelix-2.5.0}/allelix/databases/schema.py +15 -1
  29. {allelix-2.4.1 → allelix-2.5.0}/allelix/databases/snpedia_parser.py +11 -0
  30. {allelix-2.4.1 → allelix-2.5.0}/allelix/exporters/plink.py +47 -49
  31. {allelix-2.4.1 → allelix-2.5.0}/allelix/models.py +198 -11
  32. {allelix-2.4.1 → allelix-2.5.0}/allelix/parsers/__init__.py +25 -4
  33. allelix-2.5.0/allelix/parsers/_helpers.py +549 -0
  34. {allelix-2.4.1 → allelix-2.5.0}/allelix/parsers/ancestrydna.py +17 -13
  35. {allelix-2.4.1 → allelix-2.5.0}/allelix/parsers/base.py +39 -2
  36. allelix-2.5.0/allelix/parsers/decodeme.py +116 -0
  37. {allelix-2.4.1 → allelix-2.5.0}/allelix/parsers/ftdna.py +16 -8
  38. {allelix-2.4.1 → allelix-2.5.0}/allelix/parsers/ftdna_famfinder.py +16 -7
  39. {allelix-2.4.1 → allelix-2.5.0}/allelix/parsers/ftdna_illumina.py +11 -4
  40. allelix-2.5.0/allelix/parsers/genesforgood.py +78 -0
  41. {allelix-2.4.1 → allelix-2.5.0}/allelix/parsers/livingdna.py +19 -11
  42. {allelix-2.4.1 → allelix-2.5.0}/allelix/parsers/myhappygenes.py +7 -6
  43. {allelix-2.4.1 → allelix-2.5.0}/allelix/parsers/myheritage.py +13 -9
  44. {allelix-2.4.1 → allelix-2.5.0}/allelix/parsers/twentythreeandme.py +37 -37
  45. {allelix-2.4.1 → allelix-2.5.0}/allelix/parsers/vcf.py +194 -30
  46. allelix-2.5.0/allelix/reports/__init__.py +227 -0
  47. {allelix-2.4.1 → allelix-2.5.0}/allelix/reports/_pipeline.py +596 -147
  48. allelix-2.5.0/allelix/reports/_sources.py +146 -0
  49. allelix-2.5.0/allelix/reports/association_text.py +99 -0
  50. {allelix-2.4.1 → allelix-2.5.0}/allelix/reports/build_text.py +44 -10
  51. allelix-2.5.0/allelix/reports/clinvar_text.py +137 -0
  52. {allelix-2.4.1 → allelix-2.5.0}/allelix/reports/diff.py +6 -4
  53. {allelix-2.4.1 → allelix-2.5.0}/allelix/reports/html.py +427 -148
  54. {allelix-2.4.1 → allelix-2.5.0}/allelix/reports/json_report.py +98 -12
  55. {allelix-2.4.1 → allelix-2.5.0}/allelix/reports/terminal.py +47 -28
  56. {allelix-2.4.1 → allelix-2.5.0}/allelix/reports/vcf.py +17 -10
  57. {allelix-2.4.1 → allelix-2.5.0}/allelix/utils/allele.py +63 -2
  58. {allelix-2.4.1 → allelix-2.5.0}/allelix/utils/build_detect.py +120 -1
  59. {allelix-2.4.1 → allelix-2.5.0}/allelix/utils/plausibility.py +39 -17
  60. {allelix-2.4.1 → allelix-2.5.0/allelix.egg-info}/PKG-INFO +67 -39
  61. {allelix-2.4.1 → allelix-2.5.0}/allelix.egg-info/SOURCES.txt +6 -0
  62. {allelix-2.4.1 → allelix-2.5.0}/pyproject.toml +1 -1
  63. allelix-2.4.1/allelix/annotators/snpedia.py +0 -441
  64. allelix-2.4.1/allelix/parsers/_helpers.py +0 -120
  65. allelix-2.4.1/allelix/reports/__init__.py +0 -108
  66. {allelix-2.4.1 → allelix-2.5.0}/LICENSE +0 -0
  67. {allelix-2.4.1 → allelix-2.5.0}/MANIFEST.in +0 -0
  68. {allelix-2.4.1 → allelix-2.5.0}/allelix/__init__.py +0 -0
  69. {allelix-2.4.1 → allelix-2.5.0}/allelix/cli/focused.py +0 -0
  70. {allelix-2.4.1 → allelix-2.5.0}/allelix/compare.py +0 -0
  71. {allelix-2.4.1 → allelix-2.5.0}/allelix/data/__init__.py +0 -0
  72. {allelix-2.4.1 → allelix-2.5.0}/allelix/data/clinvar_clnsig_snapshot.yaml +0 -0
  73. {allelix-2.4.1 → allelix-2.5.0}/allelix/data/high_value_snps.yaml +0 -0
  74. {allelix-2.4.1 → allelix-2.5.0}/allelix/databases/__init__.py +0 -0
  75. {allelix-2.4.1 → allelix-2.5.0}/allelix/databases/alphamissense_loader.py +0 -0
  76. {allelix-2.4.1 → allelix-2.5.0}/allelix/databases/cadd_loader.py +0 -0
  77. {allelix-2.4.1 → allelix-2.5.0}/allelix/databases/cpic_loader.py +0 -0
  78. {allelix-2.4.1 → allelix-2.5.0}/allelix/databases/gnomad_loader.py +0 -0
  79. {allelix-2.4.1 → allelix-2.5.0}/allelix/databases/rsid_identity_loader.py +0 -0
  80. {allelix-2.4.1 → allelix-2.5.0}/allelix/databases/snpedia_loader.py +0 -0
  81. {allelix-2.4.1 → allelix-2.5.0}/allelix/exporters/__init__.py +0 -0
  82. {allelix-2.4.1 → allelix-2.5.0}/allelix/py.typed +0 -0
  83. {allelix-2.4.1 → allelix-2.5.0}/allelix/reports/high_value.py +0 -0
  84. {allelix-2.4.1 → allelix-2.5.0}/allelix/reports/methylation.py +0 -0
  85. {allelix-2.4.1 → allelix-2.5.0}/allelix/utils/__init__.py +0 -0
  86. {allelix-2.4.1 → allelix-2.5.0}/allelix.egg-info/dependency_links.txt +0 -0
  87. {allelix-2.4.1 → allelix-2.5.0}/allelix.egg-info/entry_points.txt +0 -0
  88. {allelix-2.4.1 → allelix-2.5.0}/allelix.egg-info/requires.txt +0 -0
  89. {allelix-2.4.1 → allelix-2.5.0}/allelix.egg-info/top_level.txt +0 -0
  90. {allelix-2.4.1 → allelix-2.5.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: allelix
3
- Version: 2.4.1
3
+ Version: 2.5.0
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>
@@ -55,19 +55,19 @@ Dynamic: license-file
55
55
 
56
56
  # Allelix
57
57
 
58
- > **Status:** Production. Nine parser formats (including VCF + gVCF), four annotators (ClinVar + ClinPGx + GWAS Catalog + SNPedia), three GRCh38 enrichment sources (gnomAD + AlphaMissense + CADD) reached from GRCh37 input through a cross-build rsID identity table, licensable-source gating for commercial users, dual-build ClinVar caches (GRCh37 + GRCh38), HTML/JSON/terminal reports and annotated VCF output for pipeline integration (v2.3.0+), methylation + pharmacogenomics focused commands, report diffing, persistent config with commercial-mode safety switch. The genome build is decided before annotation from the evidence the file carries: VCF contig lengths, or known-SNP positions from the whole file. Evidence that contradicts the file's label, or a VCF with no usable evidence, stops the run and asks for `--build` instead of producing a report on the wrong build; a tentative GRCh36 signal fails closed to GRCh36 because no modern position cache is safe for it (ADR-0025, ADR-0037, ADR-0044). No regex on prose anywhere in production. The [Changelog](https://github.com/allelix/allelix/blob/main/CHANGELOG.md) tracks every release.
58
+ > **Status:** Production. Reads eleven file formats, including VCF and gVCF. Reports what ClinVar, ClinPGx, the GWAS Catalog and SNPedia say about your variants, with frequencies and scores from gnomAD, AlphaMissense and CADD. It decides the genome build from the file itself and stops rather than guess. HTML, JSON, terminal or annotated VCF. Everything runs on your machine. The [Changelog](https://github.com/allelix/allelix/blob/main/CHANGELOG.md) tracks every release.
59
59
 
60
60
  ## Quickstart
61
61
 
62
- **Requires Python 3.11 or newer.** On Python 3.10 or older, `pip install allelix` succeeds but the first command fails with `ImportError: cannot import name 'UTC' from 'datetime'` — that's the symptom; the fix is to install on 3.11+. Check with `python --version`.
62
+ **Requires Python 3.11 or newer.** On Python 3.10 or older pip cannot install this release (it may fall back to an old one, which then fails with `ImportError: cannot import name 'UTC' from 'datetime'`); install on 3.11+. Check with `python --version`.
63
63
 
64
64
  ```bash
65
65
  pip install allelix
66
66
 
67
- # Download reference databases (~18 GB default install; ~24 GB if you opt into CADD).
67
+ # Download reference databases (~19 GB default install; ~25 GB if you opt into CADD).
68
68
  # Use --no-gnomad / --no-alphamissense to skip the large ones.
69
- # CADD is opt-in: download with `allelix db update --cadd`, then pass
70
- # `--cadd` to an analysis or set `sources.cadd = true` persistently.
69
+ # CADD is opt-in: download with `allelix db update --cadd`, then enable it
70
+ # with `allelix config set sources.cadd true`.
71
71
  allelix db update
72
72
 
73
73
  # Analyze a genotype file
@@ -83,8 +83,10 @@ allelix analyze trio.vcf.gz --sample HG002 --output report.html
83
83
  # skipped and counted; calls with GQ below 20 or no reads become no-calls.
84
84
  allelix analyze your_wgs.vcf.gz --keep-filtered --min-gq 10 --output report.html
85
85
 
86
- # Filter to a custom panel (rsIDs + gene names, one per line; '#' comments and blank lines ignored)
86
+ # Filter to a custom panel (rsIDs and gene names, one per line; a # starts a comment, on its own line or after an entry; blank lines are ignored)
87
87
  allelix analyze your_genotype_file.txt --filter-file my_panel.txt --output report.html
88
+ # Panel coverage puts each requested rsID in one state: annotated, typed (no annotation),
89
+ # no call, or not on chip ("not in file" for a VCF)
88
90
 
89
91
  # Annotated VCF output — VCF-in / VCF-out for pipeline integration
90
92
  # Writes a valid VCF with annotations as INFO fields + full provenance in the header.
@@ -100,14 +102,20 @@ See [Development](#development) for source installs and running tests, [Managing
100
102
  | Format | Status | Notes |
101
103
  |---|---|---|
102
104
  | 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). |
103
- | 23andMe | ✓ | Tab-delimited, 4 columns, concatenated genotype. Supports build 36/37/38 from header. I-prefixed probe IDs passed through. |
105
+ | 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). |
106
+ | 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). |
104
107
  | AncestryDNA | ✓ | Tab-delimited, 5 columns. Chromosome mapping: 23→X, 24→Y, 25→X (PAR), 26→MT. V1 and V2 chip layouts. |
105
108
  | 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). |
106
109
  | 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). |
107
110
  | Living DNA | ✓ | Tab-delimited despite `.csv` extension. Handles AX-, AFFX-prefixed and CHR:POS positional SNP IDs. |
111
+ | 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). |
108
112
  | 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. |
109
113
  | 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. |
110
- | 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. 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. 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. |
114
+ | 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
+
116
+ 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.
117
+
118
+ 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)).
111
119
 
112
120
  Adding a new format means adding one file to `allelix/parsers/` and registering an instance in the `PARSERS` list in `allelix/parsers/__init__.py`.
113
121
 
@@ -115,23 +123,24 @@ Adding a new format means adding one file to `allelix/parsers/` and registering
115
123
 
116
124
  | Release | Theme |
117
125
  |---|---|
118
- | **v2.2** | Annotation model expansion (cross-source conflict surfacing, risk-allele display, structured MTAG flag), custom-panel UX improvements, methylation panel retune + citations, ClinVar source switch (VCF → `variant_summary.txt`), CLI cleanups (`db clean` / `db path`). |
126
+ | **v2.2** | Custom-panel UX improvements, methylation panel retune + citations, ClinVar source switch (VCF → `variant_summary.txt`), CLI cleanups (`db clean` / `db path`). |
119
127
  | **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. |
120
128
  | **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. |
129
+ | **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. |
121
130
  | **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. |
122
131
 
123
- Issues and milestones are tracked at [github.com/allelix/allelix/issues](https://github.com/allelix/allelix/issues). Annotator-level strand-aware carrier matching shipped in v2.1.0's ADR-0035 cluster.
132
+ Issues and milestones are tracked at [github.com/allelix/allelix/issues](https://github.com/allelix/allelix/issues).
124
133
 
125
134
  ## Supported Databases
126
135
 
127
136
  | Database | Status | Notes |
128
137
  |---|---|---|
129
- | 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. |
138
+ | 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). |
130
139
  | 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. |
131
140
  | 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. |
132
- | 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, while unknown-orientation palindromic genotypes abstain rather than risk a false match. 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`. |
133
- | 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. GWAS risk-allele strand normalization remains deferred. |
134
- | 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 (~16M exome variants 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. |
141
+ | 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`. |
142
+ | 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). |
143
+ | 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. |
135
144
  | 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. |
136
145
  | 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. |
137
146
  | 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). |
@@ -140,7 +149,7 @@ Issues and milestones are tracked at [github.com/allelix/allelix/issues](https:/
140
149
 
141
150
  CADD ranks how deleterious any single-nucleotide variant is using 100+ annotation tracks (coding, non-coding, regulatory). PHRED 10 = top 10% most deleterious, 20 = top 1%, 30 = top 0.1%.
142
151
 
143
- **Opt-in**: download the cache with `allelix db update --cadd`. Then pass `--cadd` to an analysis for a one-run override, or enable it persistently with `allelix config set sources.cadd true`. Downloading the cache alone does not change persistent configuration. Use `--no-cadd` to skip enrichment for a single run when it is enabled in config.
152
+ **Opt-in**: download the cache with `allelix db update --cadd`, then enable it with `allelix config set sources.cadd true`. Downloading the cache alone does not change persistent configuration. Use `--no-cadd` to skip enrichment for a single run when it is enabled in config.
144
153
 
145
154
  Two modes:
146
155
 
@@ -168,13 +177,11 @@ The CFTR × ivacaftor leak (~30+ rows on real data, pre-v0.7.3) is fixed by the
168
177
 
169
178
  ### Known ClinVar upstream data quality issues
170
179
 
171
- Two ClinVar rows in real-world reports are known upstream artifacts, not Allelix bugs:
180
+ One ClinVar row in real-world reports is a known upstream artifact, not an Allelix bug:
172
181
 
173
182
  - **PKD1 rs199476100 GG (Pathogenic/Likely pathogenic, magnitude 8.5).** This is a stop-gained variant with a gnomAD frequency of 0.0005% (7 observations in 1.38 million chromosomes). Homozygosity for this variant is biologically implausible — PKD1 is autosomal dominant and the nonsense variant would be embryonic-lethal or devastating in homozygous state. The chip genotyping call is almost certainly a probe artifact. The code correctly reports what ClinVar says and what the chip reads; the error is upstream of Allelix. The row is now marked implausible (see [Plausibility flag](#plausibility-flag)) but is still shown.
174
183
 
175
- - **IL10 rs1800896 CT (Pathogenic, magnitude 9.0).** This is a common polymorphism (MAF ~20–40%) in the IL-10 promoter. ClinVar's Pathogenic classification comes from a single submitter for hepatitis C susceptibility; another submission classifies the allele as "Uncertain risk allele" for leprosy susceptibility. Allelix's per-SCV loader keeps each submitter's significance paired with its own condition, so it does not create cross-submission combinations. The remaining concern is the upstream strength of a single-submitter classification on a common variant. Future work: review-status weighting could down-weight that evidence.
176
-
177
- Neither issue affects Allelix's filter logic. Both are inherent to ClinVar's aggregation model and the limitations of array-based genotyping chips.
184
+ It does not affect Allelix's filter logic. It comes from the limits of array-based genotyping chips.
178
185
 
179
186
  ## Regulatory Posture
180
187
 
@@ -204,7 +211,7 @@ to manage.
204
211
 
205
212
  ## Configuration
206
213
 
207
- Allelix stores persistent configuration in `config.toml` (in the data directory, default `~/.local/share/allelix/`). A default config is created on first run.
214
+ Allelix stores persistent configuration in `config.toml` (in the data directory, default `~/.local/share/allelix/`). A default config is created on first run, except by `analyze --no-update`, which writes nothing to the data directory (ADR-0048).
208
215
 
209
216
  ```bash
210
217
  # View current config (annotated with license notes)
@@ -228,7 +235,7 @@ allelix config set plausibility.homozygous_max_af 0.0001
228
235
  allelix config set plausibility.heterozygous_max_af 0
229
236
  ```
230
237
 
231
- CLI flags (`--no-gnomad`, `--no-alphamissense`, `--no-cadd`, `--exclude-snpedia`, `--cadd`) override the config for a single run. The config sets the baseline; flags override per-invocation.
238
+ CLI flags (`--no-gnomad`, `--no-alphamissense`, `--no-cadd`, `--exclude-snpedia`) turn a source off for a single run. The config sets the baseline; flags override per-invocation.
232
239
 
233
240
  ### Plausibility flag
234
241
 
@@ -240,9 +247,11 @@ Genotyping arrays sometimes miscall very rare variants, most visibly as a homozy
240
247
  | Heterozygous | at or below `plausibility.heterozygous_max_af` (default 0: gnomAD has the site but no carriers) | **suspicious** |
241
248
  | Either | otherwise | plausible |
242
249
 
243
- The HTML report shows a ⚠ icon next to a flagged genotype, with the reason on hover and in the detail panel. The terminal table marks implausible calls with ⚠ and suspicious ones with ?. JSON reports carry `plausibility` on every annotation (`plausible`, `suspicious`, `implausible`, or empty when not assessed).
250
+ The HTML report shows a ⚠ icon next to a flagged genotype, with the reason on hover and in the detail panel, and a muted ○ with the reason when the call was not assessed. The terminal table marks implausible calls with ⚠, suspicious ones with ? and not-assessed ones with ○. JSON reports carry `plausibility` on every annotation (`plausible`, `suspicious`, `implausible` or `not_assessed`) and, when not assessed, `plausibility_reason` (`no_frequency`, `gnomad_not_run`, `chromosome`, `half_call` or `no_allele`; ADR-0050).
251
+
252
+ 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.
244
253
 
245
- 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. Y and mitochondrial calls are not assessed. Confirm a flagged call with a clinical-grade test before relying on it.
254
+ 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).
246
255
 
247
256
  ### Database sizes and download times
248
257
 
@@ -250,13 +259,13 @@ Not all databases are equal in size. `allelix db update` downloads them all by d
250
259
 
251
260
  | Database | On disk | Download time | What it adds |
252
261
  |---|---|---|---|
253
- | ClinVar (GRCh37 + GRCh38) | ~1.6 GB | 1–2 min | Core clinical variant classifications. Required. |
262
+ | ClinVar (GRCh37 + GRCh38) | ~2.4 GB | 1–2 min, then up to about 20 min to build both caches | Core clinical variant classifications. Required. |
254
263
  | ClinPGx + CPIC | ~6MB | seconds | Drug-gene interactions. |
255
264
  | GWAS Catalog | ~950 MB | 1–2 min | Trait-SNP associations from genome-wide studies. |
256
265
  | gnomAD | ~7 GB | 5–15 min | Population allele frequencies (how common is this variant?). |
257
266
  | AlphaMissense | ~8GB | 5–15 min | Missense pathogenicity predictions (how likely to break protein function?). |
258
267
  | dbSNP rsID identity | ~460 MB | ~1 min | Cross-build placements: reference allele for array data, rsID recovery on both builds, GRCh37 enrichment translation. |
259
- | CADD (opt-in) | ~6 GB | 5–15 min | Variant deleteriousness scores (how damaging is this variant?). Download with `db update --cadd`; enable per analysis or in config. |
268
+ | CADD (opt-in) | ~6 GB | 5–15 min | Variant deleteriousness scores (how damaging is this variant?). Download with `db update --cadd`; enable in config. |
260
269
 
261
270
  gnomAD and AlphaMissense are the largest but add the most interpretive context. gnomAD answers "is this variant rare or common?" — a pathogenic variant carried by 35% of the population reads very differently from one seen in 3 people. AlphaMissense answers "does this missense change likely damage the protein?" — especially valuable for the thousands of variants ClinVar hasn't reviewed yet.
262
271
 
@@ -264,15 +273,15 @@ To skip either during download: `allelix db update --no-gnomad --no-alphamissens
264
273
 
265
274
  ### Managing your data
266
275
 
267
- Allelix stores reference database caches at `~/.local/share/allelix/` (Linux/macOS) or the XDG-equivalent on other platforms. Default install (no CADD) is ~18 GB on disk; with the CADD opt-in it's ~24 GB.
276
+ Allelix keeps its caches in `~/.local/share/allelix/`, or `$XDG_DATA_HOME/allelix` when `XDG_DATA_HOME` is set. To use another folder, set `ALLELIX_DATA_DIR` or pass `--data-dir`. Default install (no CADD) is ~19 GB on disk; with the CADD opt-in it's ~25 GB.
268
277
 
269
278
  ```
270
279
  ~/.local/share/allelix/
271
280
  ├── alphamissense.sqlite ~7.8 GB
272
281
  ├── gnomad.sqlite ~7.1 GB
273
282
  ├── cadd.sqlite ~5.8 GB (only if you opted into --cadd)
274
- ├── clinvar.GRCh37.sqlite ~800 MB
275
- ├── clinvar.GRCh38.sqlite ~800 MB
283
+ ├── clinvar.GRCh37.sqlite ~1.2 GB
284
+ ├── clinvar.GRCh38.sqlite ~1.2 GB
276
285
  ├── gwas_catalog_associations.tsv ~745 MB
277
286
  ├── rsid_identity.sqlite ~457 MB
278
287
  ├── gwas.sqlite ~202 MB
@@ -283,7 +292,7 @@ Allelix stores reference database caches at `~/.local/share/allelix/` (Linux/mac
283
292
  **The caches are disposable and re-downloadable.** Everything here was fetched by `allelix db update` and can be re-fetched at any time. There's no per-user state in the cache — your genotype files never enter this directory. Safe to delete to reclaim space (or to force a full refresh if you suspect a cache went stale):
284
293
 
285
294
  ```bash
286
- # Reclaim all ~18-24 GB, with size report and confirmation prompt
295
+ # Reclaim all ~19-25 GB, with size report and confirmation prompt
287
296
  allelix db clean
288
297
 
289
298
  # Preserve the CADD cache (avoids re-downloading the slowest one, ~5.8 GB)
@@ -336,7 +345,7 @@ Allelix source code is licensed under the **GNU Affero General Public License v3
336
345
  | gnomAD | gnomad.broadinstitute.org | ODbL v1.0 | Attribution required. GRCh38 population allele frequencies for context (GRCh37 input through the identity table); not a clinical annotator. Use `--no-gnomad` to omit. |
337
346
  | AlphaMissense | zenodo.org/records/10813168 | CC BY 4.0 | Attribution required. Cheng et al., Science 2023. GRCh38 missense variant pathogenicity predictions (GRCh37 input through the identity table). Use `--no-alphamissense` to omit. |
338
347
  | dbSNP (rsID identity table) | ncbi.nlm.nih.gov/snp | Public domain | No restrictions. Cross-build rsID placements derived from dbSNP; not surfaced as an annotator. |
339
- | CADD | cadd.gs.washington.edu | LicenseRef-CADD | Attribution required, **non-commercial by default**. Commercial licenses available from UW CoMotion. GRCh38 scores (GRCh37 input through the identity table); download with `allelix db update --cadd`, then enable per analysis with `--cadd` or persistently in config. Use `--no-cadd` to omit. |
348
+ | CADD | cadd.gs.washington.edu | LicenseRef-CADD | Attribution required, **non-commercial by default**. Commercial licenses available from UW CoMotion. GRCh38 scores (GRCh37 input through the identity table); download with `allelix db update --cadd`, then enable in config (`allelix config set sources.cadd true`). Use `--no-cadd` to omit it for one run. |
340
349
 
341
350
  **Commercial users:** When `license.commercial = true`, non-commercial sources are gated by a three-state permission model. SNPedia is permanently blocked (no commercial license is available). CADD is blocked by default but can be unlocked — the University of Washington offers commercial licenses at `https://els2.comotion.uw.edu/product/cadd-scores`; after purchasing, assert your license with `allelix config set license.cadd true` to re-enable CADD in commercial mode. All other databases (ClinVar, ClinPGx, GWAS Catalog, gnomAD, AlphaMissense) are compatible with commercial use. `allelix config show` displays the permission state for each source.
342
351
 
@@ -371,7 +380,7 @@ The v2.3.1 cache format also preserves each page's SNPedia orientation. Known mi
371
380
 
372
381
  ### `ImportError: cannot import name 'UTC' from 'datetime'`
373
382
 
374
- You're on Python 3.10 or older. Allelix requires 3.11+. `python --version` to check; upgrade and reinstall.
383
+ You're on Python 3.10 or older, where pip cannot install this release and may fall back to an old one. Allelix requires 3.11+. `python --version` to check; upgrade and reinstall.
375
384
 
376
385
  ### `allelix db update` failed partway through
377
386
 
@@ -381,15 +390,24 @@ If the failure is a network timeout on the same source repeatedly, the issue is
381
390
 
382
391
  ### `db status` shows an annotator as "not ready" but the cache files exist on disk
383
392
 
384
- Almost always a schema or interpreter version bump in a recent allelix release that invalidates the pre-bump cache. The fix is `allelix db update` — it'll re-ingest only the affected annotators (everything else signal-skips). See the relevant `CHANGELOG.md` entry for "Cache invalidation" notes per release.
393
+ `analyze` prints one line per skipped source with the reason and the fix (ADR-0048):
394
+
395
+ - **out of date:** almost always a schema or interpreter version bump in a recent allelix release that invalidates the pre-bump cache. The fix is `allelix db update`; it re-ingests only the affected annotators (everything else signal-skips). See the relevant `CHANGELOG.md` entry for "Cache invalidation" notes per release.
396
+ - **cannot read:** the file's permissions do not let you read it.
397
+ - **damaged:** the file is not an SQLite database, lacks a table, or its main table has no rows; `allelix db update` replaces that source only (`--force` would download every source again).
398
+ - **WAL mode from an older Allelix:** gnomAD, AlphaMissense and CADD caches installed by earlier releases are in WAL mode. Reading one leaves `-wal` and `-shm` files beside it, and in a directory you cannot write SQLite cannot read it at all, so `analyze --no-update` skips it. Run `allelix db update` once (with write access to the data directory); it converts them without downloading. `analyze` without `--no-update` converts them too.
399
+
400
+ The report records the same reasons: the JSON `sources` block lists every source with its part in the run, and the HTML lists the sources that could not be used. A report without ClinVar carries a warning at the top (ADR-0049).
401
+
402
+ With `--no-update`, `analyze` writes nothing to the data directory, so a data directory can be read-only.
385
403
 
386
404
  ### How the genome build is decided, and why a run can stop
387
405
 
388
406
  Before anything is annotated, Allelix decides the file's genome build from the evidence the file itself carries (ADR-0044):
389
407
 
390
- - **VCF contig lengths.** Every nuclear chromosome has a different length in GRCh37 and GRCh38, so the `##contig` lengths settle the build whatever the contig names look like (`chr1` or `1`). If chrM has the old hg19 length, mitochondrial records are not annotated and a note says so.
408
+ - **VCF contig lengths.** Every nuclear chromosome has a different length in GRCh37 and GRCh38, so the `##contig` lengths settle the build whatever the contig names look like (`chr1` or `1`). If chrM has the old hg19 length (16,571 bp), no mitochondrial record is annotated and a note says so, also when the build comes from `--build` or the file's assembly header. In a GRCh37 VCF, mitochondrial records named `chrM` or `M` (UCSC hg19 names) are left out, counted and noted, unless the file declares that name with the 16,569 bp reference length; `MT` and `chrMT` are read.
391
409
  - **Known-SNP positions across the whole file.** A set of well-known SNPs sits at different positions in each build. Allelix reads the whole file for them before annotating (a few seconds on a full chip). At least three concordant positions and an 80% majority make the evidence confident (ADR-0037). A VCF record without an rsID counts when it sits at such a position.
392
- - **The file's own label** (a build line in an array header, or a VCF `assembly=`) is used only when the evidence does not contradict it.
410
+ - **The file's own label** (a build line in an array header, or a VCF `assembly=`) is used only when the evidence does not contradict it and at least one known-SNP position is there to check it.
393
411
 
394
412
  The banner says where the build came from, for example `Build: GRCh38 (from the contig lengths: 24 contig length(s) match GRCh38)`. If confident positions contradict the label (real-world MHG / Tempus exports say 37 and ship 38), Allelix uses the positions and prints a **Build mismatch** notice. An unlabelled chip whose known-SNP positions do not decide uses its format's documented default, with a visible note, provided at least one of those positions matches a build and the build most of them match is the default (a few that disagree do not stop it); the note gives the counts. A tentative GRCh36 winner is the safety exception: Allelix uses GRCh36 so it cannot query a modern position cache (see [`docs/grch36-liftover.md`](https://github.com/allelix/allelix/blob/main/docs/grch36-liftover.md)).
395
413
 
@@ -400,8 +418,10 @@ The banner says where the build came from, for example `Build: GRCh38 (from the
400
418
  - the known-SNP positions are tied between builds and the build that would otherwise decide (the label, the format default, or the contig-length build) is not one of them. When it is one of them, Allelix proceeds on it and prints an "inconclusive" warning, also shown in the HTML report. Without contig lengths, a tie that includes GRCh36 instead takes the GRCh36 safety exception described above, with the same warning;
401
419
  - a VCF has no contig lengths, no assembly and too few known-SNP positions;
402
420
  - 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);
421
+ - 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);
403
422
  - 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);
404
- - 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.
423
+ - 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;
424
+ - `--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.
405
425
 
406
426
  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.
407
427
 
@@ -425,18 +445,26 @@ The "why" behind major design choices lives in [`docs/adr/`](https://github.com/
425
445
 
426
446
  Notable load-bearing ADRs:
427
447
 
428
- - **ADR-0016 — Data Classification Principle.** Classification reads structured fields only. Regex on prose is forbidden in production code.
429
- - **ADR-0020 — CPIC API as the per-allele function source.** The ClinPGx non-finding filter is a table join keyed on `(rsid, base) → clinicalfunctionalstatus`, sourced from CPIC's structured API. Supersedes the prose-extraction tiers from earlier versions (ADR-0017, ADR-0018).
430
448
  - **ADR-0007 — Genotype matching requires the user to carry the ALT allele.** Applies to ClinVar.
431
449
  - **ADR-0009 — ClinPGx matches the user's exact normalized diploid call.**
432
450
  - **ADR-0015 — Mock data generators are the contract.** Fixture shape must mirror real data shape; invariants tested.
451
+ - **ADR-0016 — Data Classification Principle.** Classification reads structured fields only. Regex on prose is forbidden in production code.
452
+ - **ADR-0020 — CPIC API as the per-allele function source.** The ClinPGx non-finding filter is a table join keyed on `(rsid, base) → clinicalfunctionalstatus`, sourced from CPIC's structured API. Supersedes the prose-extraction tiers from earlier versions (ADR-0017, ADR-0018).
433
453
  - **ADR-0036 — Annotated VCF output contract.** JSON and annotated VCF schemas evolve independently.
434
454
  - **ADR-0037 — Conservative position-based build confidence.** A winning build needs at least three matches and an 80% ratio; weaker signals remain diagnostic only.
435
455
  - **ADR-0038 — GWAS effect-modifier disambiguation.** Only positively identified odds ratios receive an effect-size modifier; beta and ambiguous effects abstain.
456
+ - **ADR-0041 — Plausibility flag.** Very rare homozygous calls and heterozygous calls with no gnomAD carriers are flagged for review, never suppressed.
436
457
  - **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.
437
458
  - **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.
438
459
  - **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.
439
- - **ADR-0041 — Plausibility flag.** Very rare homozygous calls and heterozygous calls with no gnomAD carriers are flagged for review, never suppressed.
460
+ - **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.
461
+ - **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`.
462
+ - **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).
463
+ - **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.
464
+ - **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.
465
+ - **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.
466
+ - **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.
467
+ - **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.
440
468
 
441
469
  Release history: see [`CHANGELOG.md`](https://github.com/allelix/allelix/blob/main/CHANGELOG.md).
442
470