Minor release. Focus: the ADR-0035 model change — a single coordinated Variant + Annotation redesign that closes the structural follow-ups deferred in v2.0.1 (per-Annotation allele tracking, structured GWAS fields, strand-aware carrier matching). One JSON schema_version bump (4 → 5) covers the whole change. No cache invalidation — existing v2.0.2 caches stay current on upgrade.
Upgrade
pip install --upgrade allelix
No cache invalidation. v2.1.0 ships zero schema or interpreter changes — the ClinVar interpreter version stays at 2 (set in v2.0.1). Existing v2.0.2 caches remain current; only the standard allelix db update freshness check runs.
Highlights
Variant.reffield — the reference allele now travels with the variant. VCF parsers populatereffrom the REF column at parse time. Array parsers (MyHappyGenes, 23andMe, AncestryDNA, FTDNA, MyHeritage, LivingDNA) getrefpopulated at pipeline batch-flush time via a gnomAD-backedrsid → forward-REFlookup. Required by downstream consumers (strand-aware carrier matching, plausibility flagging in v2.3, the future ACMG engine) to identify which side of the user's allele pair is the risk allele.Nonemeans "cannot resolve" — consumers degrade gracefully rather than guess.- Per-Annotation
altthreading extended from ClinVar to every annotator that has a per-row allele. TheAnnotation.altfield has existed since v1.4.1 but until this release only ClinVar populated it. v2.0.1's wrong-allele safety fix removed theMAX()enrichment fallback that could have stamped a different alt's gnomAD / AlphaMissense / CADD score onto an annotation; this release closes the loop by routing the per-row alt through. GWAS Catalog: the catalog's per-association risk allele threads asaltwhen present. SNPedia + ClinPGx: alt is derived from the user's diploid call againstVariant.ref. Result on array inputs: SNPedia and ClinPGx rows now get allele-specific gnomAD / AlphaMissense / CADD enrichment where v2.0.x left them with empty alt (and therefore no enrichment) on the same data. The v2.0.1 wrong-allele safety invariant is preserved — when alt can't be cleanly identified, the row continues to skip enrichment rather than guess. - Structured GWAS fields on
Annotation:trait,p_value,phecode. The v2.0.1 fix to inconsistent(PheCodedelimiter strings was a minimal patch; this release closes the structural half by promoting the values themselves out of the rendered description prose into typedAnnotationfields. The GWAS annotator extracts PheCode at the ingestion boundary and rebuilds the rendered description from the structured parts. Rollup / diff / future consumers read the structured fields directly — no more regex on prose for GWAS trait or p-value. - Strand-aware carrier matching. ClinVar and ClinPGx carrier matching now accepts complement-strand reads of the same variant: a heterozygous carrier of an ALT scored on the forward strand and the same biology read on the coding strand produce identical annotation sets. The strand-flip path activates only when
Variant.refconfirms reverse orientation (variant.ref == complement(source_ref)) AND the source(ref, alt)pair is non-palindromic (A/T and C/G pairs cannot be disambiguated from sequence alone — the documented ADR-0010 limitation). For standard forward-normalized inputs (every shipped parser produces these),variant.ref == source_refand the strand-flip path stays inert — direct membership handles carrier matching as before. The capability activates on genuinely reverse-stranded inputs (future supplemental file support, non-standard alignments, lab coding-strand exports). The v2.0.1 multi-allelic wrong-allele safety invariant is preserved: whenvariant.refdisagrees with both the source ref and its complement, the strand-flip path abstains and direct-match-only applies. - JSON
schema_versionbumped 4 → 5. Additive only — every v4 field is unchanged in v5; v4 consumers reading v5 output continue to succeed. The new fields areannotation.trait,annotation.p_value,annotation.phecode.Variant.refis internal model state and is not emitted in JSON. The diff loader accepts both v4 and v5 baselines so existing reports diff cleanly against new ones.
Known limitations
Carried forward from v2.0.1 / v2.0.2 — the structural cleanup landed in this release, not new debt.
- ClinVar per-(classification, condition) pairing. Multi-SCV variants display the primary aggregate classification plus the union of associated conditions; per-(classification, condition) 1:1 pairing requires the structured
submission_summary.txt.gzexport and is tracked for a future minor release. - Annotator finalizer cleanup. v2.0.2 stack-managed the enrichment annotators inside the analysis pipeline; full
Annotator.__del__removal pending audit of residual ResourceWarnings from code paths outside the main pipeline. - PharmGKB → ClinPGx rebrand. PharmGKB officially rebranded to ClinPGx in July 2025 (redirects live). The allelix-internal annotator name, cache filename, and source string remain
pharmgkbfor backward compatibility; the rename bundles with the next schema-breaking change so users rebuild caches once.
Added
Variant.ref: str | None. New optional field on theVariantdataclass carrying the reference allele. VCF parsers populate from the REF column. Array parsers leave itNoneat parse time; the pipeline resolves it at batch-flush via gnomAD'sbulk_resolve_coordinates(rsid)lookup when the gnomAD cache is ready.Annotation.trait,Annotation.p_value,Annotation.phecode. Structured GWAS fields promoted out of the rendered description prose. Default to empty /Noneon non-GWAS rows.
Changed
- Per-Annotation
altpopulated by every annotator that has a per-row allele. ClinVar was already populating it (since v1.4.1); GWAS / SNPedia / ClinPGx now populate too. Allele-specific gnomAD / AlphaMissense / CADD enrichment now fires on these rows whenVariant.refis known — VCF inputs always, array inputs whenever gnomAD's cache is ready (the standard installation). - GWAS rollup reads structured fields directly. The three description-parsing helpers (
_gwas_base_trait,_gwas_phecode_parent,_gwas_p_value) and the shared_PHECODE_DELIMconstant from v2.0.1 are deleted; rollup keys offa.trait,a.phecode,a.p_valuedirectly. - ClinVar and ClinPGx carrier matching is strand-aware. Complement-strand reads of the same biology now produce identical annotation sets to forward-strand reads. Forward-normalized inputs (the production case for every shipped parser) hit the same direct-match path as v2.0.x — no carrier-count changes on standard array or VCF inputs. The strand-flip extension activates on genuinely reverse-stranded inputs.
- JSON
schema_version4 → 5. Single bump covering the additive fields (annotation.trait,annotation.p_value,annotation.phecode). Diff loader accepts both v4 and v5 baselines.
Fixed
- GWAS structural follow-up to the v2.0.1 "inconsistent PheCode delimiter" patch. The shared-delimiter constant was a minimal v2.0.1 patch; this release does the structural cleanup the v2.0.1 entry deferred — trait / p-value / PheCode live as typed
Annotationfields, rollup / diff / consumers read them directly, the description-parsing helpers are gone. - Per-Annotation alt structural follow-up to the v2.0.1 "wrong-allele MAX-fallback" suppress-half. v2.0.1 removed the unsafe fallback; this release restores allele-specific enrichment on rsID-bearing input rows by routing the per-row alt onto the annotation — the v2.0.1 deferred structural-half.
- Strand-naive carrier matching. ClinVar and ClinPGx carrier matching now accepts complement-strand reads when
Variant.refconfirms orientation, with palindrome and multi-allelic safety guards.
Verification
Full pytest: 1576 pass / 0 skip / 0 warnings, 94.25% coverage. Ruff clean. plink2 v2.0.0-a.6.5 installed so the no-silent-skips invariant from v2.0.2 was exercised end-to-end.
Real-data battery (FULL_TEST_PROTOCOL.md Section 19), expected results:
- Carrier counts on standard array inputs: unchanged from v2.0.2 (forward arrays hit direct-match; strand-flip is inert per design)
- Wrong-allele safety invariant: 100% allele-direct CADD (v2.0.2's 107/107 + 113/113 must hold)
- SNPedia / ClinPGx annotation enrichment on array inputs: now allele-specific where v2.0.2 left them alt-less