From bb972500d389ac7384908b55cf16235f7033b848 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 17 Aug 2026 19:06:43 +0000 Subject: [PATCH 1/8] Add Gene2Phenotype (G2P) terminology value sets Adds src/valuesets/schema/clinical/gene2phenotype.yaml covering the controlled vocabularies documented at https://www.ebi.ac.uk/gene2phenotype/about/terminology, with terms and definitions taken from the G2P API (/attribs/description/, /molecular_mechanisms/, /ontology_terms/variant_types/). 15 enums: - G2PConfidenceCategory (6) - GenCC classification terms; GenCC IDs recorded as annotations since the GENCC prefix is not in bioregistry or OLS - G2PAllelicRequirement (9) - mapped to HPO mode-of-inheritance terms; every G2P genotype string is an exact synonym of the HPO term it maps to - G2PCrossCuttingModifier (7) - 5 mapped to HPO inheritance qualifiers, 2 G2P-specific with no HPO equivalent - G2PMolecularMechanism (5) and G2PMolecularMechanismSynopsis (10) - Backwell and Marsh definitions (PMID:35395171); SO variant terms recorded as close_mappings because G2P mechanisms describe a gene-disease association rather than a variant - G2PMolecularMechanismSupport (2) - G2PMechanismEvidenceCategory (4) plus G2PFunctionEvidence, G2PFunctionalAlterationEvidence, G2PModelsEvidence and G2PRescueEvidence - ClinGen gene-disease validity SOP evidence matrix - G2PVariantConsequence (6) and G2PVariantType (31) - Sequence Ontology terms, hierarchy expressed with is_a; where G2P's label differs from the current SO label the G2P label is kept as an alias - G2PPanel (7) All 37 SO accessions and 14 HPO accessions were checked against OLS; descriptions use the SO text definitions where SO provides one. Verified with `just site` (gen-project and gen-doc; gen-slides fails only because marp is not installed in this environment), `just validate-schema` on the new file, and `just test` (63 passed). Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01De8cxj98MtmCDAW3uuUZQ7 --- cache/hp/terms.csv | 5 + cache/so/terms.csv | 37 + .../schema/clinical/gene2phenotype.yaml | 1066 +++++++++++++++++ src/valuesets/schema/valuesets.yaml | 1 + 4 files changed, 1109 insertions(+) create mode 100644 src/valuesets/schema/clinical/gene2phenotype.yaml diff --git a/cache/hp/terms.csv b/cache/hp/terms.csv index dd7e5669..ffb75c83 100644 --- a/cache/hp/terms.csv +++ b/cache/hp/terms.csv @@ -40,3 +40,8 @@ HP:0032113,Semidominant inheritance,2025-12-14T23:20:37.169244 HP:0034339,Pseudoautosomal inheritance,2025-12-14T23:20:37.169578 HP:0034340,Pseudoautosomal dominant inheritance,2025-12-14T23:20:37.169815 HP:0034341,Pseudoautosomal recessive inheritance,2025-12-14T23:20:37.170032 +HP:0003743,Genetic anticipation,2026-08-17T19:00:41.286444 +HP:0034338,Imprinted,2026-08-17T19:00:41.287227 +HP:0025352,Typically de novo,2026-08-17T19:00:41.287810 +HP:0001442,Typified by somatic mosaicism,2026-08-17T19:00:41.288402 +HP:0003829,Typified by incomplete penetrance,2026-08-17T19:00:41.288988 diff --git a/cache/so/terms.csv b/cache/so/terms.csv index 02979b79..c5ab2f8e 100644 --- a/cache/so/terms.csv +++ b/cache/so/terms.csv @@ -74,3 +74,40 @@ SO:0000577,centromere,2025-10-19T07:20:41.328719 SO:0000624,telomere,2025-10-19T07:20:41.329032 SO:0000987,linear,2025-10-19T07:20:43.761336 SO:0000988,circular,2025-10-19T07:20:43.761728 +SO:0002314,altered_gene_product_level,2026-08-17T19:00:42.014211 +SO:0002316,decreased_gene_product_level,2026-08-17T19:00:42.015123 +SO:0002317,absent_gene_product,2026-08-17T19:00:42.015684 +SO:0002315,increased_gene_product_level,2026-08-17T19:00:42.016236 +SO:0002318,altered_gene_product_sequence,2026-08-17T19:00:42.016746 +SO:0002220,function_uncertain_variant,2026-08-17T19:00:42.017327 +SO:0002319,NMD_triggering_variant,2026-08-17T19:00:42.018111 +SO:0002320,NMD_escaping_variant,2026-08-17T19:00:42.018625 +SO:0002321,stop_gained_NMD_triggering,2026-08-17T19:00:42.019167 +SO:0002322,stop_gained_NMD_escaping,2026-08-17T19:00:42.019680 +SO:0002323,frameshift_variant_NMD_triggering,2026-08-17T19:00:42.020247 +SO:0002324,frameshift_variant_NMD_escaping,2026-08-17T19:00:42.020763 +SO:0002325,splice_donor_variant_NMD_triggering,2026-08-17T19:00:42.021331 +SO:0002326,splice_donor_variant_NMD_escaping,2026-08-17T19:00:42.021839 +SO:0002327,splice_acceptor_variant_NMD_triggering,2026-08-17T19:00:42.022449 +SO:0002328,splice_acceptor_variant_NMD_escaping,2026-08-17T19:00:42.023000 +SO:0001630,splice_region_variant,2026-08-17T19:00:42.023528 +SO:0001574,splice_acceptor_variant,2026-08-17T19:00:42.024070 +SO:0001575,splice_donor_variant,2026-08-17T19:00:42.024584 +SO:0001623,5_prime_UTR_variant,2026-08-17T19:00:42.025130 +SO:0001624,3_prime_UTR_variant,2026-08-17T19:00:42.025648 +SO:0001566,regulatory_region_variant,2026-08-17T19:00:42.026246 +SO:0002012,start_lost,2026-08-17T19:00:42.026768 +SO:0001587,stop_gained,2026-08-17T19:00:42.027328 +SO:0001578,stop_lost,2026-08-17T19:00:42.027912 +SO:0001589,frameshift_variant,2026-08-17T19:00:42.028438 +SO:0001583,missense_variant,2026-08-17T19:00:42.029001 +SO:0001821,inframe_insertion,2026-08-17T19:00:42.029544 +SO:0001822,inframe_deletion,2026-08-17T19:00:42.030133 +SO:0001819,synonymous_variant,2026-08-17T19:00:42.030658 +SO:0001627,intron_variant,2026-08-17T19:00:42.031214 +SO:0001628,intergenic_variant,2026-08-17T19:00:42.031739 +SO:0001619,non_coding_transcript_variant,2026-08-17T19:00:42.032295 +SO:0002161,short_tandem_repeat_change,2026-08-17T19:00:42.032826 +SO:0001019,copy_number_variation,2026-08-17T19:00:42.033415 +SO:0001893,transcript_ablation,2026-08-17T19:00:42.034028 +SO:0001889,transcript_amplification,2026-08-17T19:00:42.034584 diff --git a/src/valuesets/schema/clinical/gene2phenotype.yaml b/src/valuesets/schema/clinical/gene2phenotype.yaml new file mode 100644 index 00000000..2e67349d --- /dev/null +++ b/src/valuesets/schema/clinical/gene2phenotype.yaml @@ -0,0 +1,1066 @@ +name: gene2phenotype +title: Gene2Phenotype (G2P) Terminology Value Sets +description: >- + Value sets for the controlled vocabularies used by Gene2Phenotype (G2P), the EBI + resource of curated gene-disease associations. G2P records a locus-genotype-mechanism- + disease-evidence (LGMDE) thread for each association, and each component of that thread + draws on a controlled vocabulary. The terms and definitions here follow the G2P + terminology page at https://www.ebi.ac.uk/gene2phenotype/about/terminology, which reuses + community standards where they exist: GenCC classification terms for confidence, HPO + mode-of-inheritance terms for allelic requirement, HPO inheritance qualifiers for + cross-cutting modifiers, the Backwell and Marsh framework (PMID:35395171) for molecular + mechanism, the ClinGen gene-disease validity SOP for mechanism evidence, and Sequence + Ontology terms for variant consequence and variant type. +id: https://w3id.org/valuesets/clinical/gene2phenotype +imports: +- linkml:types +prefixes: + linkml: https://w3id.org/linkml/ + valuesets: https://w3id.org/valuesets/ + HP: http://purl.obolibrary.org/obo/HP_ + SO: http://purl.obolibrary.org/obo/SO_ + orcid: https://orcid.org/ + valuesets_meta: https://w3id.org/valuesets/meta/ +default_prefix: valuesets +slots: + g2p_confidence: + description: The G2P confidence category assigned to a gene-disease association + range: G2PConfidenceCategory + allelic_requirement: + description: The genotype required at the locus for the disease to manifest + range: G2PAllelicRequirement + cross_cutting_modifier: + description: An additional qualifier on the allelic requirement of a gene-disease association + range: G2PCrossCuttingModifier + molecular_mechanism: + description: The mechanism of disease derived from the available evidence + range: G2PMolecularMechanism + molecular_mechanism_synopsis: + description: A more detailed description of the molecular mechanism of disease + range: G2PMolecularMechanismSynopsis + molecular_mechanism_support: + description: Whether the molecular mechanism is directly evidenced or inferred + range: G2PMolecularMechanismSupport + variant_consequence: + description: The consequence of a reported variant at the protein or RNA level, per allele + range: G2PVariantConsequence + variant_type: + description: The type of variant associated with a curated gene-disease pair + range: G2PVariantType +enums: + G2PConfidenceCategory: + title: G2P Confidence Category + description: >- + The level of confidence that a gene-disease association is real, as asserted by G2P + curators. G2P uses the GenCC gene-disease validity classification terms. Operationally + several groups use definitive, strong and moderate for clinical reporting; limited, + disputed and refuted are not used for clinical reporting. + status: STANDARD + contributors: + - orcid:0000-0002-6601-2165 + - https://github.com/anthropics/claude-code + instantiates: + - valuesets_meta:ValueSetEnumDefinition + annotations: + stewards: https://www.ebi.ac.uk/gene2phenotype + publishers: https://www.ebi.ac.uk/gene2phenotype + source: https://www.ebi.ac.uk/gene2phenotype/about/terminology#g2p-confidence-section + standard: GenCC gene-disease validity classification + permissible_values: + DEFINITIVE: + title: definitive + description: >- + The role of this gene in this particular disease has been repeatedly demonstrated + in both the research and clinical diagnostic settings, and has been upheld over + time (at least 2 independent publication over 3 years' time). No convincing + evidence has emerged that contradicts the role of the gene in the specified + disease. (previously labelled as confirmed) The strength of evidence within + publications as well as their number and publication dates is taken into account. + In practice, this usually means at least 4 publications over 5 years. Typically + this will also include convincing bioinformatic or functional evidence of + causation, making it very unlikely that this gene-disease association would ever + be refuted. + aliases: + - confirmed + annotations: + gencc_classification_id: GENCC:100001 + clinical_reporting: 'true' + STRONG: + title: strong + description: >- + The role of this gene as a monogenic cause of disease has been repeatedly and + independently demonstrated providing very strong convincing evidence in humans and + no conflicting evidence for this gene's role in this disease. (previously labelled + as probable). + aliases: + - probable + annotations: + gencc_classification_id: GENCC:100002 + clinical_reporting: 'true' + MODERATE: + title: moderate + description: >- + There is moderate evidence in humans to support a casual role for this gene in + this disease with no contradictory evidence. The body of evidence is not large + (e.g possibly only one key paper) but appears convincing enough that the + gene-disease pair is likely to be validated with additional evidence in the near + future. + annotations: + gencc_classification_id: GENCC:100003 + clinical_reporting: 'true' + LIMITED: + title: limited + description: >- + Little human evidence exists to support a casual role for this gene in this + disease, but not all evidence has been refuted. For example, there may be a + collection of rare missense variants in humans but without convincing functional + impact, segregration data that could either arise by chance (e.g across one or two + meioses) or does not implicate a single gene, or functional data without direct + recapitulation of the phenotype. Overall, the body of evidence does not meet + contemporary criteria for claiming a valid association with disease. The majority + are probably false associations. (previously labelled as possible). + aliases: + - possible + annotations: + gencc_classification_id: GENCC:100004 + clinical_reporting: 'false' + DISPUTED: + title: disputed + description: >- + Although evidence has been reported, other evidence of equal weight disputes the + claim. + annotations: + gencc_classification_id: GENCC:100005 + clinical_reporting: 'false' + REFUTED: + title: refuted + description: >- + There has been an assertion of a gene-disease association in the literature, but + new valid evidence has arisen that refutes the entire original body of evidence. + annotations: + gencc_classification_id: GENCC:100006 + clinical_reporting: 'false' + G2PAllelicRequirement: + title: G2P Allelic Requirement + description: >- + The genotype required at the locus for the disease to manifest, also referred to in + G2P as the genotype. HPO mode of inheritance (MOI) terminology is used; G2P uses + synonyms of the MOI terms as many of the disorders described are de novo. Each + permissible value here is an exact synonym of the HPO term it is mapped to. + status: STANDARD + contributors: + - orcid:0000-0002-6601-2165 + - https://github.com/anthropics/claude-code + instantiates: + - valuesets_meta:ValueSetEnumDefinition + annotations: + stewards: https://www.ebi.ac.uk/gene2phenotype + publishers: https://www.ebi.ac.uk/gene2phenotype + source: https://www.ebi.ac.uk/gene2phenotype/about/terminology#allelic-requirement-section + standard: HPO mode of inheritance + permissible_values: + MONOALLELIC_AUTOSOMAL: + title: Autosomal dominant inheritance + description: >- + Plausible disease-causing mutations on an autosomal chromosome identified on one + allele in all or the vast majority of with specific disorder. + meaning: HP:0000006 + aliases: + - monoallelic_autosomal + BIALLELIC_AUTOSOMAL: + title: Autosomal recessive inheritance + description: >- + Plausible disease-causing homozygous or compound heterozygous mutations identified + on both alleles in the autosomal chromosome. + meaning: HP:0000007 + aliases: + - biallelic_autosomal + MONOALLELIC_X: + title: X-linked inheritance + description: Plausible disease-causing mutations identified on the X chromosome. + meaning: HP:0001417 + aliases: + - monoallelic_X + MONOALLELIC_X_HEMIZYGOUS: + title: X-linked recessive inheritance + description: >- + Plausible disease-causing mutations identified on the X chromosome in a male as a + cause of a specific disease, the disorder being predominantly recessive in female + carriers. + meaning: HP:0001419 + aliases: + - monoallelic_X_hemizygous + MONOALLELIC_X_HETEROZYGOUS: + title: X-linked dominant inheritance + description: >- + Plausible disease-causing mutations identified in one copy of the X chromosome in + females as a cause of a specific disease, include disorders where heterozygous + females and hemizygous males are similarly affected e.g SMC1A mutations. + meaning: HP:0001423 + aliases: + - monoallelic_X_heterozygous + MONOALLELIC_Y_HEMIZYGOUS: + title: Y-linked inheritance + description: >- + Plausible disease-causing mutations identified in an allele found in the Y + chromosome. The Y chromosome is passed from father to son as this mutation may + affect only males. + meaning: HP:0001450 + aliases: + - monoallelic_Y_hemizygous + MONOALLELIC_PAR: + title: Pseudoautosomal dominant inheritance + description: >- + Plausible disease-causing mutations identified in an allele found in the + pseudoautosomal regions. Inheritance is not strictly sex-linked. + meaning: HP:0034340 + aliases: + - monoallelic_PAR + BIALLELIC_PAR: + title: Pseudoautosomal recessive inheritance + description: >- + Plausible disease-causing homozygous or compound heterozygous mutations identified + on both alleles found in the pseudoautosomal regions. Inheritance is not strictly + sex-linked. + meaning: HP:0034341 + aliases: + - biallelic_PAR + MITOCHONDRIAL: + title: Mitochondrial inheritance + description: >- + Plausible disease-causing mutations identified on mitochondrial DNA where + homoplasmy or heteroplasmy are associated with a specific disorder. + meaning: HP:0001427 + aliases: + - mitochondrial + G2PCrossCuttingModifier: + title: G2P Cross Cutting Modifier + description: >- + Additional qualifiers applied to the allelic requirement of a G2P gene-disease + association. HPO inheritance qualifier terms (HP:0034335) are used where available. + Potential secondary finding and restricted mutation set are G2P-specific and have no + HPO equivalent. + status: STANDARD + contributors: + - orcid:0000-0002-6601-2165 + - https://github.com/anthropics/claude-code + instantiates: + - valuesets_meta:ValueSetEnumDefinition + annotations: + stewards: https://www.ebi.ac.uk/gene2phenotype + publishers: https://www.ebi.ac.uk/gene2phenotype + source: https://www.ebi.ac.uk/gene2phenotype/about/terminology#cross-cutting-modifier-section + standard: HPO inheritance qualifier + permissible_values: + DISPLAYS_ANTICIPATION: + title: Genetic anticipation + description: >- + A phenomenon in which the severity of a disorder increases, or the age of onset + decreases, as the disorder is passed from one generation to the next, typically due + to expansion of a repeat sequence. For example, Myotonic Dystrophy is caused by + triplet repeat expansion in the DMPK gene. + meaning: HP:0003743 + aliases: + - displays anticipation + IMPRINTED_REGION: + title: Imprinted + description: >- + Requires that the abnormal allele be paternal or maternal in origin, depending on + the disease-gene relationship. Imprinting refers to a normal developmental process + in which either the paternal or maternal allele is inactivated, depending on the + specific locus, thus leading to expression from only one copy of the gene. Disease + typically manifests when a deleterious variant is inherited from a parent whose + copy of the gene would normally be expressed, but not when a deleterious variant is + inherited from a parent whose copy of the gene would normally be inactivated. + meaning: HP:0034338 + aliases: + - imprinted region + TYPICALLY_DE_NOVO: + title: Typically de novo + description: >- + Plausible disease causing mutations that occur post zygotically (formation of + gametes). + meaning: HP:0025352 + aliases: + - typically de novo + TYPICALLY_MOSAIC: + title: Typified by somatic mosaicism + description: >- + Plausible disease causing mutations identified on one allele in a proportion of + cells with the others being wild-type. + meaning: HP:0001442 + aliases: + - typically mosaic + TYPIFIED_BY_INCOMPLETE_PENETRANCE: + title: Typified by incomplete penetrance + description: >- + A condition in which not all individuals carrying the disease-causing genotype + manifest the associated phenotype. + meaning: HP:0003829 + aliases: + - typified by incomplete penetrance + POTENTIAL_SECONDARY_FINDING: + title: potential secondary finding + description: This includes ACMG Secondary Findings and/or late onset conditions. + RESTRICTED_MUTATION_SET: + title: restricted mutation set + description: >- + This is used when a disease is associated with a single recurrent variant or a set + of variants only found in a particular protein domain. + G2PMolecularMechanism: + title: G2P Molecular Mechanism + description: >- + The mechanism of disease derived from the available evidence, following the definitions + of Backwell and Marsh (PMID:35395171). These mechanisms describe a gene-disease + association rather than an individual variant, so the Sequence Ontology variant terms + are recorded as close mappings rather than exact meanings. + status: STANDARD + contributors: + - orcid:0000-0002-6601-2165 + - https://github.com/anthropics/claude-code + instantiates: + - valuesets_meta:ValueSetEnumDefinition + annotations: + stewards: https://www.ebi.ac.uk/gene2phenotype + publishers: https://www.ebi.ac.uk/gene2phenotype + source: https://www.ebi.ac.uk/gene2phenotype/about/terminology#molecular-mechanism-section + reference: PMID:35395171 + permissible_values: + LOSS_OF_FUNCTION: + title: loss of function + description: >- + Loss-of-function variants involve a loss of the normal biological function of a + protein. Often these are nonsense or frameshift mutations that introduce premature + stop codons. Due to nonsense-mediated decay of the resulting mRNAs, most premature + stop codons will result in no protein being produced, rather than a truncated + protein. However, there are also many examples of loss-of-function variants that + change the amino acid sequence and result in non-functional protein products. These + mutations can cause a complete loss of function (amorphic), analogous to a protein + null mutation, or only a partial loss of function (hypomorphic). May also include + variants in regulatory regions. + close_mappings: + - SO:0002054 + aliases: + - loss_of_function_variant + GAIN_OF_FUNCTION: + title: gain of function + description: >- + Gain-of-function variants have their phenotypic effect because the mutant protein + does something different than the wild-type protein. Often, these variants cause + disease by increasing protein activity (hypermorphic) or introducing a completely + new function (neomorphic), but the specific molecular mechanisms underlying + gain-of-function mutations can be complex. May also include variants in regulatory + regions. + close_mappings: + - SO:0002053 + aliases: + - gain_of_function_variant + DOMINANT_NEGATIVE: + title: dominant negative + description: >- + Dominant-negative variants involve the mutant protein directly or indirectly + blocking the normal biological function of the wild-type protein (antimorphic). + They can thus cause a disproportionate (>50%) loss of function, even though only + half of the protein is mutated eg. heterozygous variants in COL1A1 that disrupt the + triple collagen helix. + close_mappings: + - SO:0002052 + aliases: + - antimorphic + - dominant_negative_variant + UNDETERMINED_NON_LOSS_OF_FUNCTION: + title: undetermined non-loss-of-function + description: >- + Very often it is difficult to distinguish between dominant negative and gain of + function, but it is clearly a non-loss-of-function mechanism (e.g. from + co-expression experiments showing a damaging effect from the mutant allele). + aliases: + - undetermined non-loss-of-function + UNDETERMINED: + title: undetermined + description: Not known. + G2PMolecularMechanismSynopsis: + title: G2P Molecular Mechanism Synopsis + description: >- + A more detailed description of the molecular mechanism of a G2P gene-disease + association, following the definitions of Backwell and Marsh (PMID:35395171). A + synopsis refines the higher-level molecular mechanism; more than one synopsis may + apply to a single gene-disease association. + status: STANDARD + contributors: + - orcid:0000-0002-6601-2165 + - https://github.com/anthropics/claude-code + instantiates: + - valuesets_meta:ValueSetEnumDefinition + annotations: + stewards: https://www.ebi.ac.uk/gene2phenotype + publishers: https://www.ebi.ac.uk/gene2phenotype + source: https://www.ebi.ac.uk/gene2phenotype/about/terminology#mechanism-synopsis-section + reference: PMID:35395171 + permissible_values: + DESTABILISING_LOF: + title: destabilising LOF + description: >- + A process whereby a missense change destabilises the protein structure resulting in + loss of function. + annotations: + parent_mechanism: loss of function + INTERACTION_DISRUPTING_LOF: + title: interaction-disrupting LOF + description: >- + A process whereby a variant allele disrupts interaction resulting in loss of + function, for example a change in an interaction site. + annotations: + parent_mechanism: loss of function + LOSS_OF_ACTIVITY_LOF: + title: loss of activity LOF + description: >- + A process whereby a variant allele disrupts activity resulting in loss of function, + for example a change in an active site. + annotations: + parent_mechanism: loss of function + LOF_DUE_TO_PROTEIN_MISLOCALISATION: + title: LOF due to protein mislocalisation + description: >- + A loss of function caused by mislocalisation of a protein, rather than direct + disruption of its structure or function. + annotations: + parent_mechanism: loss of function + ASSEMBLY_MEDIATED_DOMINANT_NEGATIVE: + title: assembly-mediated dominant negative + description: >- + A protein change which does not prevent coassembly into a complex with wild-type + subunits but results in poisoning the activity of the hybrid complex, causing a + disproportionate loss of function. + annotations: + parent_mechanism: dominant negative + COMPETITIVE_DOMINANT_NEGATIVE: + title: competitive dominant-negative + description: >- + A process whereby the novel protein disrupts specific interactions by competing + with wild-type protein, thus having a dominant-negative effect. + annotations: + parent_mechanism: dominant negative + ASSEMBLY_MEDIATED_GOF: + title: assembly-mediated GOF + description: >- + A process whereby incorporation of a mutant subunit into a protein complex leads to + a gain of function, for example through constitutive activation of a channel. + annotations: + parent_mechanism: gain of function + LOCAL_LOF_LEADING_TO_OVERALL_GOF: + title: local LOF leading to overall GOF + description: >- + A gain of function caused by the localised loss of a specific function within a + protein, for example binding of a regulatory domain is disrupted but enzymatic + activity is retained. + annotations: + parent_mechanism: gain of function + AGGREGATION: + title: aggregation + description: >- + A process by which the variant allele causes aggregation usually causing toxic gain + of function, for example misfolded proteins self-assembling into large aggregates + or RNA binding protein gelation. + annotations: + parent_mechanism: gain of function + OTHER_GOF: + title: other GOF + description: >- + A gain of function process other than local loss of function or assembly mediated, + for example a mutation in an active site which changes histone binding causing a + novel function. + annotations: + parent_mechanism: gain of function + G2PMolecularMechanismSupport: + title: G2P Molecular Mechanism Support + description: >- + Whether the molecular mechanism recorded for a G2P gene-disease association is directly + supported by reported evidence, or inferred by the curator. + status: STANDARD + contributors: + - orcid:0000-0002-6601-2165 + - https://github.com/anthropics/claude-code + instantiates: + - valuesets_meta:ValueSetEnumDefinition + annotations: + stewards: https://www.ebi.ac.uk/gene2phenotype + publishers: https://www.ebi.ac.uk/gene2phenotype + source: https://www.ebi.ac.uk/gene2phenotype/about/terminology#molecular-mechanism-section + permissible_values: + EVIDENCE: + title: evidence + description: >- + The molecular mechanism is directly supported by experimental evidence reported in + a publication attached to the record. + INFERRED: + title: inferred + description: >- + The molecular mechanism is inferred by the curator rather than directly evidenced + in the attached publications. + G2PMechanismEvidenceCategory: + title: G2P Mechanism Evidence Category + description: >- + The broad category of experimental evidence supporting a molecular mechanism in G2P. + G2P evidence classifications reuse terms from the ClinGen gene-disease validity SOP + Experimental Evidence Summary Matrix. + status: STANDARD + contributors: + - orcid:0000-0002-6601-2165 + - https://github.com/anthropics/claude-code + instantiates: + - valuesets_meta:ValueSetEnumDefinition + annotations: + stewards: https://www.ebi.ac.uk/gene2phenotype + publishers: https://www.ebi.ac.uk/gene2phenotype + source: https://www.ebi.ac.uk/gene2phenotype/about/terminology#mechanism-evidence-section + standard: ClinGen gene-disease validity SOP Experimental Evidence Summary Matrix + permissible_values: + FUNCTION: + title: function + description: >- + Evidence about the biochemical function, expression or interactions of the gene + product. + FUNCTIONAL_ALTERATION: + title: functional alteration + description: >- + Evidence from cells in which the function of the gene has been disrupted, showing a + phenotype consistent with the human disease process. + MODELS: + title: models + description: >- + Evidence from a cell culture model or non-human model organism with a disrupted + copy of the gene. + RESCUE: + title: rescue + description: >- + Evidence that the phenotype can be rescued by restoring the wild-type gene or gene + product. + G2PFunctionEvidence: + title: G2P Function Evidence + description: >- + Types of evidence in the function category of the G2P molecular mechanism evidence + classification. + status: STANDARD + contributors: + - orcid:0000-0002-6601-2165 + - https://github.com/anthropics/claude-code + instantiates: + - valuesets_meta:ValueSetEnumDefinition + annotations: + stewards: https://www.ebi.ac.uk/gene2phenotype + publishers: https://www.ebi.ac.uk/gene2phenotype + source: https://www.ebi.ac.uk/gene2phenotype/about/terminology#mechanism-evidence-section + permissible_values: + BIOCHEMICAL: + title: biochemical + description: >- + Evidence showing the gene product performs a biochemical function: (A) shared with + other known genes in the disease of interest, or (B) consistent with the phenotype. + PROTEIN_INTERACTION: + title: protein interaction + description: >- + Evidence showing the gene product interacts with proteins previously implicated in + the disease of interest. + PROTEIN_EXPRESSION: + title: protein expression + description: >- + Evidence showing the gene is expressed in tissues relevant to the disease of + interest and/or is altered in expression in patients who have the disease. + IN_SILICO_MODELLING: + title: in silico modelling + description: >- + Evidence generated using computer simulations and models predicting the functional + impact of relevant gene-specific variants. These models can predict protein + structure changes, disruption of protein interactions, or changes in gene and/or + protein expression. + G2PFunctionalAlterationEvidence: + title: G2P Functional Alteration Evidence + description: >- + Types of evidence in the functional alteration category of the G2P molecular mechanism + evidence classification, distinguished by whether the cells came from an affected + individual. + status: STANDARD + contributors: + - orcid:0000-0002-6601-2165 + - https://github.com/anthropics/claude-code + instantiates: + - valuesets_meta:ValueSetEnumDefinition + annotations: + stewards: https://www.ebi.ac.uk/gene2phenotype + publishers: https://www.ebi.ac.uk/gene2phenotype + source: https://www.ebi.ac.uk/gene2phenotype/about/terminology#mechanism-evidence-section + permissible_values: + PATIENT_CELLS: + title: patient cells + description: >- + Evidence showing that cultured patient cells, in which the function of the gene has + been disrupted, have a phenotype that is consistent with the human disease process. + NON_PATIENT_CELLS: + title: non patient cells + description: >- + Evidence showing that cultured non-patient cells, in which the function of the gene + has been disrupted, have a phenotype that is consistent with the human disease + process. + G2PModelsEvidence: + title: G2P Models Evidence + description: >- + Types of evidence in the models category of the G2P molecular mechanism evidence + classification. + status: STANDARD + contributors: + - orcid:0000-0002-6601-2165 + - https://github.com/anthropics/claude-code + instantiates: + - valuesets_meta:ValueSetEnumDefinition + annotations: + stewards: https://www.ebi.ac.uk/gene2phenotype + publishers: https://www.ebi.ac.uk/gene2phenotype + source: https://www.ebi.ac.uk/gene2phenotype/about/terminology#mechanism-evidence-section + permissible_values: + CELL_CULTURE_MODEL: + title: cell culture model + description: >- + A cell culture model with a disrupted copy of the gene shows a phenotype consistent + with the human disease state. + NON_HUMAN_MODEL_ORGANISM: + title: non-human model organism + description: >- + A non-human model organism with a disrupted copy of the gene shows a phenotype + consistent with the human disease state. + G2PRescueEvidence: + title: G2P Rescue Evidence + description: >- + Types of evidence in the rescue category of the G2P molecular mechanism evidence + classification, distinguished by the system in which rescue was demonstrated. + status: STANDARD + contributors: + - orcid:0000-0002-6601-2165 + - https://github.com/anthropics/claude-code + instantiates: + - valuesets_meta:ValueSetEnumDefinition + annotations: + stewards: https://www.ebi.ac.uk/gene2phenotype + publishers: https://www.ebi.ac.uk/gene2phenotype + source: https://www.ebi.ac.uk/gene2phenotype/about/terminology#mechanism-evidence-section + permissible_values: + PATIENT_CELLS: + title: patient cells + description: Evidence showing that the phenotype can be rescued in patient cells. + CELL_CULTURE_MODEL: + title: cell culture model + description: Evidence showing that the phenotype can be rescued in cell culture models. + NON_HUMAN_MODEL_ORGANISM: + title: non-human model organism + description: >- + Evidence showing that the phenotype can be rescued in non-human model organisms. + G2PVariantConsequence: + title: G2P Variant Consequence + description: >- + The consequence of the reported variants at the protein (for protein-coding genes) or + the RNA (for non-protein coding genes), per allele. These are Sequence Ontology terms + developed for G2P and described in PMID:37982373; the descriptions below are the + G2P-authored usage notes rather than the SO text definitions. + status: STANDARD + contributors: + - orcid:0000-0002-6601-2165 + - https://github.com/anthropics/claude-code + instantiates: + - valuesets_meta:ValueSetEnumDefinition + annotations: + stewards: https://www.ebi.ac.uk/gene2phenotype + publishers: https://www.ebi.ac.uk/gene2phenotype + source: https://www.ebi.ac.uk/gene2phenotype/about/terminology#variant-consequence-section + reference: PMID:37982373 + permissible_values: + ALTERED_GENE_PRODUCT_LEVEL: + title: altered_gene_product_level + description: >- + A sequence variant that alters the level or amount of gene product produced. This + high-level term can be applied where the direction of level change (increased vs + decreased gene product level) is unknown or not confirmed, e.g., promoter or + enhancer variants, some splice variants. + meaning: SO:0002314 + DECREASED_GENE_PRODUCT_LEVEL: + title: decreased_gene_product_level + description: >- + A sequence variant that decreases the level or amount of gene product produced, + e.g., a 5' UTR variant that reduced protein levels by disrupting translation, a 3' + UTR variant that affects RNA stability, splice variants that decrease but do not + stop expression, variants leading to nonsense-mediated-decay (NMD)-competent + premature termination codon (PTCs), or gene-disrupting structural variants. + meaning: SO:0002316 + is_a: ALTERED_GENE_PRODUCT_LEVEL + ABSENT_GENE_PRODUCT: + title: absent_gene_product + description: >- + A sequence variant that results in no gene product. e.g., whole gene or other large + scale disruptive structural variant, variants producing NMD-competent PTCs. + meaning: SO:0002317 + is_a: DECREASED_GENE_PRODUCT_LEVEL + INCREASED_GENE_PRODUCT_LEVEL: + title: increased_gene_product_level + description: >- + A variant that increases the level or amount of gene product produced, e.g., + non-disruptive gene duplications, some promoter or enhancer variants. + meaning: SO:0002315 + is_a: ALTERED_GENE_PRODUCT_LEVEL + ALTERED_GENE_PRODUCT_STRUCTURE: + title: altered_gene_product_sequence + description: >- + A sequence variant that alters the sequence of a gene product. e.g., missense + variants, NMD-incompetent PTCs, and other length-changing variants (in-frame + indels, stop loss). + meaning: SO:0002318 + aliases: + - altered gene product structure + UNCERTAIN: + title: function_uncertain_variant + description: >- + A sequence variant in which the function of a gene product is unknown with respect + to a reference. Used by G2P where the consequence of the reported variants could not + be determined. + meaning: SO:0002220 + aliases: + - uncertain + G2PVariantTypeGroup: + title: G2P Variant Type Group + description: >- + The primary type grouping under which G2P organises the variant types associated with a + curated gene-disease pair. + status: STANDARD + contributors: + - orcid:0000-0002-6601-2165 + - https://github.com/anthropics/claude-code + instantiates: + - valuesets_meta:ValueSetEnumDefinition + annotations: + stewards: https://www.ebi.ac.uk/gene2phenotype + publishers: https://www.ebi.ac.uk/gene2phenotype + source: https://www.ebi.ac.uk/gene2phenotype/about/terminology#variant-types-section + permissible_values: + NMD_VARIANTS: + title: NMD_variants + description: >- + Variant types qualified by whether the resulting transcript is predicted to trigger + or escape nonsense-mediated decay. + SPLICE_VARIANTS: + title: splice_variants + description: Variant types affecting splice sites or splice regions. + REGULATORY_VARIANTS: + title: regulatory_variants + description: Variant types in untranslated or regulatory regions. + PROTEIN_CHANGING_VARIANTS: + title: protein_changing_variants + description: Variant types that change the coding sequence of the gene product. + OTHER_VARIANTS: + title: other_variants + description: >- + Variant types not covered by the NMD, splice, regulatory or protein changing + groups, including structural and repeat changes. + G2PVariantType: + title: G2P Variant Type + description: >- + The types of variants associated with the curated gene-disease pair reported in the + publication. All terms are Sequence Ontology terms. Where G2P uses a label that differs + from the current SO label, the G2P label is recorded as an alias. Descriptions are the + SO text definitions where SO provides one. + status: STANDARD + contributors: + - orcid:0000-0002-6601-2165 + - https://github.com/anthropics/claude-code + instantiates: + - valuesets_meta:ValueSetEnumDefinition + annotations: + stewards: https://www.ebi.ac.uk/gene2phenotype + publishers: https://www.ebi.ac.uk/gene2phenotype + source: https://www.ebi.ac.uk/gene2phenotype/about/terminology#variant-types-section + standard: Sequence Ontology + permissible_values: + NMD_TRIGGERING: + title: NMD_triggering_variant + description: >- + A sequence variant that leads to a change in the location of a termination codon in + a transcript that leads to nonsense-mediated decay (NMD). The change in location of + a termination codon can be caused by several different types of sequence variants, + including stop_gained (SO:0001587), frameshift_variant (SO:0001589), + splice_donor_variant (SO:0001575), and splice_acceptor_variant (SO:0001574) types + of variants. + meaning: SO:0002319 + aliases: + - NMD_triggering + annotations: + variant_type_group: NMD_variants + NMD_ESCAPING: + title: NMD_escaping_variant + description: >- + A sequence variant that leads to a change in the location of a termination codon in + a transcript but allows the transcript to escape nonsense-mediated decay (NMD). The + change in location of a termination codon can be caused by several different types + of sequence variants, including stop_gained (SO:0001587), frameshift_variant + (SO:0001589), splice_donor_variant (SO:0001575), and splice_acceptor_variant + (SO:0001574) types of variants. + meaning: SO:0002320 + aliases: + - NMD_escaping + annotations: + variant_type_group: NMD_variants + STOP_GAINED_NMD_TRIGGERING: + title: stop_gained_NMD_triggering + description: >- + A stop_gained (SO:0001587) variant that is degraded by nonsense-mediated decay + (NMD). + meaning: SO:0002321 + annotations: + variant_type_group: NMD_variants + STOP_GAINED_NMD_ESCAPING: + title: stop_gained_NMD_escaping + description: >- + A stop_gained (SO:0001587) variant that allows the transcript to escape + nonsense-mediated decay (NMD). + meaning: SO:0002322 + annotations: + variant_type_group: NMD_variants + FRAMESHIFT_VARIANT_NMD_TRIGGERING: + title: frameshift_variant_NMD_triggering + description: >- + A frameshift_variant (SO:0001589) that is degraded by nonsense-mediated decay (NMD). + meaning: SO:0002323 + annotations: + variant_type_group: NMD_variants + FRAMESHIFT_VARIANT_NMD_ESCAPING: + title: frameshift_variant_NMD_escaping + description: >- + A frameshift_variant (SO:0001589) that allows the transcript to escape + nonsense-mediated decay (NMD). + meaning: SO:0002324 + annotations: + variant_type_group: NMD_variants + SPLICE_DONOR_VARIANT_NMD_TRIGGERING: + title: splice_donor_variant_NMD_triggering + description: >- + A splice_donor_variant (SO:0001575) that is degraded by nonsense-mediated decay + (NMD). + meaning: SO:0002325 + annotations: + variant_type_group: NMD_variants + SPLICE_DONOR_VARIANT_NMD_ESCAPING: + title: splice_donor_variant_NMD_escaping + description: >- + A splice_donor_variant (SO:0001575) that allows the transcript to escape + nonsense-mediated decay (NMD). + meaning: SO:0002326 + annotations: + variant_type_group: NMD_variants + SPLICE_ACCEPTOR_VARIANT_NMD_TRIGGERING: + title: splice_acceptor_variant_NMD_triggering + description: >- + A splice_acceptor_variant (SO:0001574) that is degraded by nonsense-mediated decay + (NMD). + meaning: SO:0002327 + annotations: + variant_type_group: NMD_variants + SPLICE_ACCEPTOR_VARIANT_NMD_ESCAPING: + title: splice_acceptor_variant_NMD_escaping + description: >- + A splice_acceptor_variant (SO:0001574) that allows the transcript to escape + nonsense-mediated decay (NMD). + meaning: SO:0002328 + annotations: + variant_type_group: NMD_variants + SPLICE_REGION_VARIANT: + title: splice_region_variant + description: >- + A sequence variant in which a change has occurred within the region of the splice + site, either within 1-3 bases of the exon or 3-8 bases of the intron. + meaning: SO:0001630 + annotations: + variant_type_group: splice_variants + SPLICE_ACCEPTOR_VARIANT: + title: splice_acceptor_variant + description: A splice variant that changes the 2 base region at the 3' end of an intron. + meaning: SO:0001574 + annotations: + variant_type_group: splice_variants + SPLICE_DONOR_VARIANT: + title: splice_donor_variant + description: >- + A splice variant that changes the 2 base pair region at the 5' end of an intron. + meaning: SO:0001575 + annotations: + variant_type_group: splice_variants + FIVE_PRIME_UTR_VARIANT: + title: 5_prime_UTR_variant + description: A UTR variant of the 5' UTR. + meaning: SO:0001623 + aliases: + - 5_prime_UTR_variant + annotations: + variant_type_group: regulatory_variants + THREE_PRIME_UTR_VARIANT: + title: 3_prime_UTR_variant + description: A UTR variant of the 3' UTR. + meaning: SO:0001624 + aliases: + - 3_prime_UTR_variant + annotations: + variant_type_group: regulatory_variants + REGULATORY_REGION_VARIANT: + title: regulatory_region_variant + description: A sequence variant located within a regulatory region. + meaning: SO:0001566 + annotations: + variant_type_group: regulatory_variants + START_LOST: + title: start_lost + description: >- + A codon variant that changes at least one base of the canonical start codon. + meaning: SO:0002012 + annotations: + variant_type_group: protein_changing_variants + STOP_GAINED: + title: stop_gained + description: >- + A sequence variant whereby at least one base of a codon is changed, resulting in a + premature stop codon, leading to a shortened polypeptide. + meaning: SO:0001587 + annotations: + variant_type_group: protein_changing_variants + STOP_LOST: + title: stop_lost + description: >- + A sequence variant where at least one base of the terminator codon (stop) is + changed, resulting in an elongated transcript. + meaning: SO:0001578 + annotations: + variant_type_group: protein_changing_variants + FRAMESHIFT_VARIANT: + title: frameshift_variant + description: >- + A sequence variant which causes a disruption of the translational reading frame, + because the number of nucleotides inserted or deleted is not a multiple of three. + meaning: SO:0001589 + annotations: + variant_type_group: protein_changing_variants + MISSENSE_VARIANT: + title: missense_variant + description: >- + A sequence variant, that changes one or more bases, resulting in a different amino + acid sequence but where the length is preserved. + meaning: SO:0001583 + annotations: + variant_type_group: protein_changing_variants + INFRAME_INSERTION: + title: inframe_insertion + description: >- + An inframe non synonymous variant that inserts bases into in the coding sequence. + meaning: SO:0001821 + annotations: + variant_type_group: protein_changing_variants + INFRAME_DELETION: + title: inframe_deletion + description: >- + An inframe non synonymous variant that deletes bases from the coding sequence. + meaning: SO:0001822 + annotations: + variant_type_group: protein_changing_variants + SYNONYMOUS_VARIANT: + title: synonymous_variant + description: >- + A sequence variant where there is no resulting change to the encoded amino acid. + meaning: SO:0001819 + annotations: + variant_type_group: other_variants + INTRON_VARIANT: + title: intron_variant + description: A transcript variant occurring within an intron. + meaning: SO:0001627 + annotations: + variant_type_group: other_variants + INTERGENIC_VARIANT: + title: intergenic_variant + description: A sequence variant located in the intergenic region, between genes. + meaning: SO:0001628 + annotations: + variant_type_group: other_variants + NON_CODING_TRANSCRIPT_VARIANT: + title: non_coding_transcript_variant + description: A transcript variant of a non coding RNA gene. + meaning: SO:0001619 + annotations: + variant_type_group: other_variants + SHORT_TANDEM_REPEAT_CHANGE: + title: short_tandem_repeat_change + description: >- + A sequence variant where the copies of a short tandem repeat (STR) feature are + either contracted or expanded. SO:0002161 carries no text definition; this + description is taken from the SO term comment. + meaning: SO:0002161 + annotations: + variant_type_group: other_variants + COPY_NUMBER_VARIATION: + title: copy_number_variation + description: >- + A variation that increases or decreases the copy number of a given region. + meaning: SO:0001019 + annotations: + variant_type_group: other_variants + WHOLE_PARTIAL_GENE_DELETION: + title: transcript_ablation + description: >- + A feature ablation whereby the deleted region includes a transcript feature. Used by + G2P to record whole or partial gene deletions. + meaning: SO:0001893 + aliases: + - whole_partial_gene_deletion + annotations: + variant_type_group: other_variants + WHOLE_PARTIAL_GENE_DUPLICATION: + title: transcript_amplification + description: >- + A feature amplification of a region containing a transcript. Used by G2P to record + whole or partial gene duplications. + meaning: SO:0001889 + aliases: + - whole_partial_gene_duplication + annotations: + variant_type_group: other_variants + G2PPanel: + title: G2P Panel + description: >- + The disease-area panels into which G2P organises its curated gene-disease + associations. A gene-disease association may appear on more than one panel. + status: STANDARD + contributors: + - orcid:0000-0002-6601-2165 + - https://github.com/anthropics/claude-code + instantiates: + - valuesets_meta:ValueSetEnumDefinition + annotations: + stewards: https://www.ebi.ac.uk/gene2phenotype + publishers: https://www.ebi.ac.uk/gene2phenotype + source: https://www.ebi.ac.uk/gene2phenotype/panels + permissible_values: + CANCER: + title: Cancer + description: Cancer disorders. + CARDIAC: + title: Cardiac + description: Cardiac disorders. + DD: + title: DD + description: Developmental disorders. + aliases: + - Developmental disorders + EAR: + title: Ear + description: Ear disorders. + EYE: + title: Eye + description: Eye disorders. + SKELETAL: + title: Skeletal + description: Skeletal disorders. + SKIN: + title: Skin + description: Skin disorders. diff --git a/src/valuesets/schema/valuesets.yaml b/src/valuesets/schema/valuesets.yaml index e5ae8543..e39ada7f 100644 --- a/src/valuesets/schema/valuesets.yaml +++ b/src/valuesets/schema/valuesets.yaml @@ -120,6 +120,7 @@ imports: - medical/pediatric_oncology/staging/neuroblastoma - clinical/nih_demographics - clinical/phenopackets +- clinical/gene2phenotype - bioprocessing/scale_up - bioprocessing/biomanufacturing - process_engineering/unit_operations From d89776c87bb4cace5a09e3f905c449ca0e3b596b Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 18 Aug 2026 17:30:00 +0000 Subject: [PATCH 2/8] Address review feedback on G2P value sets Changes from the automated review on PR #79: - Record the SO subclass hierarchy on the eight NMD-qualified variant types (e.g. STOP_GAINED_NMD_TRIGGERING is_a STOP_GAINED with mixin NMD_TRIGGERING). Each term has two parents in SO, verified via the OLS parents endpoint, so the base variant is the is_a and the NMD qualifier is a mixin. - Point the parent_mechanism and variant_type_group annotations at permissible value keys instead of display titles, so they resolve programmatically and survive title edits. - Add slots for the seven enums that had none (evidence categories, variant type group, panel); none of the new slot names collide with existing slots in the merged schema. - Flag the internal inconsistency in G2P's verbatim TYPICALLY_DE_NOVO wording, which describes a post-zygotic (somatic) event where the mapped HP:0025352 means pre-zygotic de novo, rather than silently rewriting the quotation. - Note at schema level that descriptions are quoted verbatim from the source vocabularies including their spelling errors, so the retained "casual role" and "segregration" are not mistaken for typos to fix. - Explain in G2PConfidenceCategory why GenCC identifiers are annotations rather than meanings: the GENCC prefix is absent from bioregistry, OLS and the OBO PURL system, rechecked at review time. - Drop the 5'/3' UTR aliases that merely duplicated their titles. - Add a retrieval date and drift note to G2PPanel, which EBI revises. Verified with `just validate-schema` on the file, `just gen-project`, and `just test` (63 passed, 1 skipped). Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01De8cxj98MtmCDAW3uuUZQ7 --- .../schema/clinical/gene2phenotype.yaml | 157 ++++++++++++------ 1 file changed, 108 insertions(+), 49 deletions(-) diff --git a/src/valuesets/schema/clinical/gene2phenotype.yaml b/src/valuesets/schema/clinical/gene2phenotype.yaml index 2e67349d..1ac6740d 100644 --- a/src/valuesets/schema/clinical/gene2phenotype.yaml +++ b/src/valuesets/schema/clinical/gene2phenotype.yaml @@ -11,6 +11,12 @@ description: >- cross-cutting modifiers, the Backwell and Marsh framework (PMID:35395171) for molecular mechanism, the ClinGen gene-disease validity SOP for mechanism evidence, and Sequence Ontology terms for variant consequence and variant type. + + Permissible value descriptions are quoted verbatim from those sources, retaining their + original spelling and grammar (for example "casual role" for causal role, and + "segregration" for segregation in the GenCC confidence text) so that the text here can be + diffed against the source. Where a quoted definition is itself inconsistent this is called + out in the value's description rather than silently corrected. id: https://w3id.org/valuesets/clinical/gene2phenotype imports: - linkml:types @@ -47,6 +53,27 @@ slots: variant_type: description: The type of variant associated with a curated gene-disease pair range: G2PVariantType + variant_type_group: + description: The primary type grouping a variant type belongs to + range: G2PVariantTypeGroup + mechanism_evidence_category: + description: The broad category of experimental evidence supporting a molecular mechanism + range: G2PMechanismEvidenceCategory + function_evidence: + description: Evidence in the function category supporting a molecular mechanism + range: G2PFunctionEvidence + functional_alteration_evidence: + description: Evidence in the functional alteration category supporting a molecular mechanism + range: G2PFunctionalAlterationEvidence + models_evidence: + description: Evidence in the models category supporting a molecular mechanism + range: G2PModelsEvidence + rescue_evidence: + description: Evidence in the rescue category supporting a molecular mechanism + range: G2PRescueEvidence + g2p_panel: + description: The G2P disease-area panel a gene-disease association appears on + range: G2PPanel enums: G2PConfidenceCategory: title: G2P Confidence Category @@ -54,7 +81,11 @@ enums: The level of confidence that a gene-disease association is real, as asserted by G2P curators. G2P uses the GenCC gene-disease validity classification terms. Operationally several groups use definitive, strong and moderate for clinical reporting; limited, - disputed and refuted are not used for clinical reporting. + disputed and refuted are not used for clinical reporting. GenCC classification + identifiers are recorded in the gencc_classification_id annotation rather than as + meaning:, because the GENCC prefix is not registered in bioregistry, OLS or the OBO + PURL system and so cannot be resolved; the identifiers were taken from GenCC's own + submissions export. status: STANDARD contributors: - orcid:0000-0002-6601-2165 @@ -145,8 +176,10 @@ enums: description: >- The genotype required at the locus for the disease to manifest, also referred to in G2P as the genotype. HPO mode of inheritance (MOI) terminology is used; G2P uses - synonyms of the MOI terms as many of the disorders described are de novo. Each - permissible value here is an exact synonym of the HPO term it is mapped to. + synonyms of the MOI terms as many of the disorders described are de novo. HPO itself + records each of these G2P genotype strings as an exact synonym of the term mapped + below (for example monoallelic_autosomal on HP:0000006), which is what makes these + mappings unambiguous. status: STANDARD contributors: - orcid:0000-0002-6601-2165 @@ -279,7 +312,11 @@ enums: title: Typically de novo description: >- Plausible disease causing mutations that occur post zygotically (formation of - gametes). + gametes). Note that this G2P wording is internally inconsistent: post-zygotic + events are somatic, which is what TYPICALLY_MOSAIC describes, whereas + gametogenesis is pre-zygotic. The mapped HPO term HP:0025352 carries the intended + sense, defining conditions that are exclusively or predominantly observed to + display de novo variants. meaning: HP:0025352 aliases: - typically de novo @@ -404,28 +441,28 @@ enums: A process whereby a missense change destabilises the protein structure resulting in loss of function. annotations: - parent_mechanism: loss of function + parent_mechanism: LOSS_OF_FUNCTION INTERACTION_DISRUPTING_LOF: title: interaction-disrupting LOF description: >- A process whereby a variant allele disrupts interaction resulting in loss of function, for example a change in an interaction site. annotations: - parent_mechanism: loss of function + parent_mechanism: LOSS_OF_FUNCTION LOSS_OF_ACTIVITY_LOF: title: loss of activity LOF description: >- A process whereby a variant allele disrupts activity resulting in loss of function, for example a change in an active site. annotations: - parent_mechanism: loss of function + parent_mechanism: LOSS_OF_FUNCTION LOF_DUE_TO_PROTEIN_MISLOCALISATION: title: LOF due to protein mislocalisation description: >- A loss of function caused by mislocalisation of a protein, rather than direct disruption of its structure or function. annotations: - parent_mechanism: loss of function + parent_mechanism: LOSS_OF_FUNCTION ASSEMBLY_MEDIATED_DOMINANT_NEGATIVE: title: assembly-mediated dominant negative description: >- @@ -433,21 +470,21 @@ enums: subunits but results in poisoning the activity of the hybrid complex, causing a disproportionate loss of function. annotations: - parent_mechanism: dominant negative + parent_mechanism: DOMINANT_NEGATIVE COMPETITIVE_DOMINANT_NEGATIVE: title: competitive dominant-negative description: >- A process whereby the novel protein disrupts specific interactions by competing with wild-type protein, thus having a dominant-negative effect. annotations: - parent_mechanism: dominant negative + parent_mechanism: DOMINANT_NEGATIVE ASSEMBLY_MEDIATED_GOF: title: assembly-mediated GOF description: >- A process whereby incorporation of a mutant subunit into a protein complex leads to a gain of function, for example through constitutive activation of a channel. annotations: - parent_mechanism: gain of function + parent_mechanism: GAIN_OF_FUNCTION LOCAL_LOF_LEADING_TO_OVERALL_GOF: title: local LOF leading to overall GOF description: >- @@ -455,7 +492,7 @@ enums: protein, for example binding of a regulatory domain is disrupted but enzymatic activity is retained. annotations: - parent_mechanism: gain of function + parent_mechanism: GAIN_OF_FUNCTION AGGREGATION: title: aggregation description: >- @@ -463,7 +500,7 @@ enums: of function, for example misfolded proteins self-assembling into large aggregates or RNA binding protein gelation. annotations: - parent_mechanism: gain of function + parent_mechanism: GAIN_OF_FUNCTION OTHER_GOF: title: other GOF description: >- @@ -471,7 +508,7 @@ enums: for example a mutation in an active site which changes histone binding causing a novel function. annotations: - parent_mechanism: gain of function + parent_mechanism: GAIN_OF_FUNCTION G2PMolecularMechanismSupport: title: G2P Molecular Mechanism Support description: >- @@ -790,7 +827,7 @@ enums: aliases: - NMD_triggering annotations: - variant_type_group: NMD_variants + variant_type_group: NMD_VARIANTS NMD_ESCAPING: title: NMD_escaping_variant description: >- @@ -804,70 +841,94 @@ enums: aliases: - NMD_escaping annotations: - variant_type_group: NMD_variants + variant_type_group: NMD_VARIANTS STOP_GAINED_NMD_TRIGGERING: title: stop_gained_NMD_triggering description: >- A stop_gained (SO:0001587) variant that is degraded by nonsense-mediated decay (NMD). meaning: SO:0002321 + is_a: STOP_GAINED + mixins: + - NMD_TRIGGERING annotations: - variant_type_group: NMD_variants + variant_type_group: NMD_VARIANTS STOP_GAINED_NMD_ESCAPING: title: stop_gained_NMD_escaping description: >- A stop_gained (SO:0001587) variant that allows the transcript to escape nonsense-mediated decay (NMD). meaning: SO:0002322 + is_a: STOP_GAINED + mixins: + - NMD_ESCAPING annotations: - variant_type_group: NMD_variants + variant_type_group: NMD_VARIANTS FRAMESHIFT_VARIANT_NMD_TRIGGERING: title: frameshift_variant_NMD_triggering description: >- A frameshift_variant (SO:0001589) that is degraded by nonsense-mediated decay (NMD). meaning: SO:0002323 + is_a: FRAMESHIFT_VARIANT + mixins: + - NMD_TRIGGERING annotations: - variant_type_group: NMD_variants + variant_type_group: NMD_VARIANTS FRAMESHIFT_VARIANT_NMD_ESCAPING: title: frameshift_variant_NMD_escaping description: >- A frameshift_variant (SO:0001589) that allows the transcript to escape nonsense-mediated decay (NMD). meaning: SO:0002324 + is_a: FRAMESHIFT_VARIANT + mixins: + - NMD_ESCAPING annotations: - variant_type_group: NMD_variants + variant_type_group: NMD_VARIANTS SPLICE_DONOR_VARIANT_NMD_TRIGGERING: title: splice_donor_variant_NMD_triggering description: >- A splice_donor_variant (SO:0001575) that is degraded by nonsense-mediated decay (NMD). meaning: SO:0002325 + is_a: SPLICE_DONOR_VARIANT + mixins: + - NMD_TRIGGERING annotations: - variant_type_group: NMD_variants + variant_type_group: NMD_VARIANTS SPLICE_DONOR_VARIANT_NMD_ESCAPING: title: splice_donor_variant_NMD_escaping description: >- A splice_donor_variant (SO:0001575) that allows the transcript to escape nonsense-mediated decay (NMD). meaning: SO:0002326 + is_a: SPLICE_DONOR_VARIANT + mixins: + - NMD_ESCAPING annotations: - variant_type_group: NMD_variants + variant_type_group: NMD_VARIANTS SPLICE_ACCEPTOR_VARIANT_NMD_TRIGGERING: title: splice_acceptor_variant_NMD_triggering description: >- A splice_acceptor_variant (SO:0001574) that is degraded by nonsense-mediated decay (NMD). meaning: SO:0002327 + is_a: SPLICE_ACCEPTOR_VARIANT + mixins: + - NMD_TRIGGERING annotations: - variant_type_group: NMD_variants + variant_type_group: NMD_VARIANTS SPLICE_ACCEPTOR_VARIANT_NMD_ESCAPING: title: splice_acceptor_variant_NMD_escaping description: >- A splice_acceptor_variant (SO:0001574) that allows the transcript to escape nonsense-mediated decay (NMD). meaning: SO:0002328 + is_a: SPLICE_ACCEPTOR_VARIANT + mixins: + - NMD_ESCAPING annotations: - variant_type_group: NMD_variants + variant_type_group: NMD_VARIANTS SPLICE_REGION_VARIANT: title: splice_region_variant description: >- @@ -875,49 +936,45 @@ enums: site, either within 1-3 bases of the exon or 3-8 bases of the intron. meaning: SO:0001630 annotations: - variant_type_group: splice_variants + variant_type_group: SPLICE_VARIANTS SPLICE_ACCEPTOR_VARIANT: title: splice_acceptor_variant description: A splice variant that changes the 2 base region at the 3' end of an intron. meaning: SO:0001574 annotations: - variant_type_group: splice_variants + variant_type_group: SPLICE_VARIANTS SPLICE_DONOR_VARIANT: title: splice_donor_variant description: >- A splice variant that changes the 2 base pair region at the 5' end of an intron. meaning: SO:0001575 annotations: - variant_type_group: splice_variants + variant_type_group: SPLICE_VARIANTS FIVE_PRIME_UTR_VARIANT: title: 5_prime_UTR_variant description: A UTR variant of the 5' UTR. meaning: SO:0001623 - aliases: - - 5_prime_UTR_variant annotations: - variant_type_group: regulatory_variants + variant_type_group: REGULATORY_VARIANTS THREE_PRIME_UTR_VARIANT: title: 3_prime_UTR_variant description: A UTR variant of the 3' UTR. meaning: SO:0001624 - aliases: - - 3_prime_UTR_variant annotations: - variant_type_group: regulatory_variants + variant_type_group: REGULATORY_VARIANTS REGULATORY_REGION_VARIANT: title: regulatory_region_variant description: A sequence variant located within a regulatory region. meaning: SO:0001566 annotations: - variant_type_group: regulatory_variants + variant_type_group: REGULATORY_VARIANTS START_LOST: title: start_lost description: >- A codon variant that changes at least one base of the canonical start codon. meaning: SO:0002012 annotations: - variant_type_group: protein_changing_variants + variant_type_group: PROTEIN_CHANGING_VARIANTS STOP_GAINED: title: stop_gained description: >- @@ -925,7 +982,7 @@ enums: premature stop codon, leading to a shortened polypeptide. meaning: SO:0001587 annotations: - variant_type_group: protein_changing_variants + variant_type_group: PROTEIN_CHANGING_VARIANTS STOP_LOST: title: stop_lost description: >- @@ -933,7 +990,7 @@ enums: changed, resulting in an elongated transcript. meaning: SO:0001578 annotations: - variant_type_group: protein_changing_variants + variant_type_group: PROTEIN_CHANGING_VARIANTS FRAMESHIFT_VARIANT: title: frameshift_variant description: >- @@ -941,7 +998,7 @@ enums: because the number of nucleotides inserted or deleted is not a multiple of three. meaning: SO:0001589 annotations: - variant_type_group: protein_changing_variants + variant_type_group: PROTEIN_CHANGING_VARIANTS MISSENSE_VARIANT: title: missense_variant description: >- @@ -949,46 +1006,46 @@ enums: acid sequence but where the length is preserved. meaning: SO:0001583 annotations: - variant_type_group: protein_changing_variants + variant_type_group: PROTEIN_CHANGING_VARIANTS INFRAME_INSERTION: title: inframe_insertion description: >- An inframe non synonymous variant that inserts bases into in the coding sequence. meaning: SO:0001821 annotations: - variant_type_group: protein_changing_variants + variant_type_group: PROTEIN_CHANGING_VARIANTS INFRAME_DELETION: title: inframe_deletion description: >- An inframe non synonymous variant that deletes bases from the coding sequence. meaning: SO:0001822 annotations: - variant_type_group: protein_changing_variants + variant_type_group: PROTEIN_CHANGING_VARIANTS SYNONYMOUS_VARIANT: title: synonymous_variant description: >- A sequence variant where there is no resulting change to the encoded amino acid. meaning: SO:0001819 annotations: - variant_type_group: other_variants + variant_type_group: OTHER_VARIANTS INTRON_VARIANT: title: intron_variant description: A transcript variant occurring within an intron. meaning: SO:0001627 annotations: - variant_type_group: other_variants + variant_type_group: OTHER_VARIANTS INTERGENIC_VARIANT: title: intergenic_variant description: A sequence variant located in the intergenic region, between genes. meaning: SO:0001628 annotations: - variant_type_group: other_variants + variant_type_group: OTHER_VARIANTS NON_CODING_TRANSCRIPT_VARIANT: title: non_coding_transcript_variant description: A transcript variant of a non coding RNA gene. meaning: SO:0001619 annotations: - variant_type_group: other_variants + variant_type_group: OTHER_VARIANTS SHORT_TANDEM_REPEAT_CHANGE: title: short_tandem_repeat_change description: >- @@ -997,14 +1054,14 @@ enums: description is taken from the SO term comment. meaning: SO:0002161 annotations: - variant_type_group: other_variants + variant_type_group: OTHER_VARIANTS COPY_NUMBER_VARIATION: title: copy_number_variation description: >- A variation that increases or decreases the copy number of a given region. meaning: SO:0001019 annotations: - variant_type_group: other_variants + variant_type_group: OTHER_VARIANTS WHOLE_PARTIAL_GENE_DELETION: title: transcript_ablation description: >- @@ -1014,7 +1071,7 @@ enums: aliases: - whole_partial_gene_deletion annotations: - variant_type_group: other_variants + variant_type_group: OTHER_VARIANTS WHOLE_PARTIAL_GENE_DUPLICATION: title: transcript_amplification description: >- @@ -1024,7 +1081,7 @@ enums: aliases: - whole_partial_gene_duplication annotations: - variant_type_group: other_variants + variant_type_group: OTHER_VARIANTS G2PPanel: title: G2P Panel description: >- @@ -1040,6 +1097,8 @@ enums: stewards: https://www.ebi.ac.uk/gene2phenotype publishers: https://www.ebi.ac.uk/gene2phenotype source: https://www.ebi.ac.uk/gene2phenotype/panels + retrieved: '2026-08-18' + note: G2P adds panels over time; this list is a snapshot as of the retrieved date. permissible_values: CANCER: title: Cancer From 332aa0ecb9c216969547b06fc172cf76a51e075d Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 18 Aug 2026 22:44:39 +0000 Subject: [PATCH 3/8] Record verification provenance and fix OWL loss of the NMD axis Follow-up to the re-review on PR #79. The substantive finding was the OWL one. Generating OWL for this schema confirmed that gen-owl emits only the is_a parent as rdfs:subClassOf and silently drops the mixin, so the NMD axis was present in the LinkML source but absent from the OWL product. The NMD status is now also recorded in an nmd_status annotation, which does survive (valuesets:nmd_status "TRIGGERING"), and the limitation is documented in the enum description. mixins is retained: it is correct at the source level and is preserved in the merged LinkML output. Also: - Add the three close_mappings CURIEs to cache/so/terms.csv. They were verified before use and are re-verified here against the same SO release the validator reads; enum_evaluator only inspects pv.meaning, so nothing in the toolchain would have written them. - Record in G2PAllelicRequirement that the nine values are G2P's published genotype list from the attribs API, which settles whether the bare monoallelic_X is a real G2P value without relying on a PR comment. - Qualify the exact-synonym claim with how it was checked. All nine strings are oio:hasExactSynonym in the HPO release; the OLS obo_synonym field lists only synonyms carrying an xref, which makes three of them look unscoped through that API. - State the title convention once in G2PAllelicRequirement and G2PVariantConsequence: title is the ontology label, the G2P label is an alias. In G2PVariantConsequence the SO and G2P labels coincide for four of six values, so only two carry an alias. - Prefix every slot with g2p_ for consistency and to keep generic names out of the merged namespace. No collisions. Verified with `just validate-schema`, `just test` (63 passed, 1 skipped) and a gen-owl inspection of the emitted axioms. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01De8cxj98MtmCDAW3uuUZQ7 --- cache/so/terms.csv | 3 + .../schema/clinical/gene2phenotype.yaml | 66 ++++++++++++++----- 2 files changed, 51 insertions(+), 18 deletions(-) diff --git a/cache/so/terms.csv b/cache/so/terms.csv index c5ab2f8e..35b69eed 100644 --- a/cache/so/terms.csv +++ b/cache/so/terms.csv @@ -111,3 +111,6 @@ SO:0002161,short_tandem_repeat_change,2026-08-17T19:00:42.032826 SO:0001019,copy_number_variation,2026-08-17T19:00:42.033415 SO:0001893,transcript_ablation,2026-08-17T19:00:42.034028 SO:0001889,transcript_amplification,2026-08-17T19:00:42.034584 +SO:0002054,loss_of_function_variant,2026-08-18T22:40:00.000000 +SO:0002053,gain_of_function_variant,2026-08-18T22:40:00.000000 +SO:0002052,dominant_negative_variant,2026-08-18T22:40:00.000000 diff --git a/src/valuesets/schema/clinical/gene2phenotype.yaml b/src/valuesets/schema/clinical/gene2phenotype.yaml index 1ac6740d..4d51edb8 100644 --- a/src/valuesets/schema/clinical/gene2phenotype.yaml +++ b/src/valuesets/schema/clinical/gene2phenotype.yaml @@ -32,43 +32,43 @@ slots: g2p_confidence: description: The G2P confidence category assigned to a gene-disease association range: G2PConfidenceCategory - allelic_requirement: + g2p_allelic_requirement: description: The genotype required at the locus for the disease to manifest range: G2PAllelicRequirement - cross_cutting_modifier: + g2p_cross_cutting_modifier: description: An additional qualifier on the allelic requirement of a gene-disease association range: G2PCrossCuttingModifier - molecular_mechanism: + g2p_molecular_mechanism: description: The mechanism of disease derived from the available evidence range: G2PMolecularMechanism - molecular_mechanism_synopsis: + g2p_molecular_mechanism_synopsis: description: A more detailed description of the molecular mechanism of disease range: G2PMolecularMechanismSynopsis - molecular_mechanism_support: + g2p_molecular_mechanism_support: description: Whether the molecular mechanism is directly evidenced or inferred range: G2PMolecularMechanismSupport - variant_consequence: + g2p_variant_consequence: description: The consequence of a reported variant at the protein or RNA level, per allele range: G2PVariantConsequence - variant_type: + g2p_variant_type: description: The type of variant associated with a curated gene-disease pair range: G2PVariantType - variant_type_group: + g2p_variant_type_group: description: The primary type grouping a variant type belongs to range: G2PVariantTypeGroup - mechanism_evidence_category: + g2p_mechanism_evidence_category: description: The broad category of experimental evidence supporting a molecular mechanism range: G2PMechanismEvidenceCategory - function_evidence: + g2p_function_evidence: description: Evidence in the function category supporting a molecular mechanism range: G2PFunctionEvidence - functional_alteration_evidence: + g2p_functional_alteration_evidence: description: Evidence in the functional alteration category supporting a molecular mechanism range: G2PFunctionalAlterationEvidence - models_evidence: + g2p_models_evidence: description: Evidence in the models category supporting a molecular mechanism range: G2PModelsEvidence - rescue_evidence: + g2p_rescue_evidence: description: Evidence in the rescue category supporting a molecular mechanism range: G2PRescueEvidence g2p_panel: @@ -176,10 +176,21 @@ enums: description: >- The genotype required at the locus for the disease to manifest, also referred to in G2P as the genotype. HPO mode of inheritance (MOI) terminology is used; G2P uses - synonyms of the MOI terms as many of the disorders described are de novo. HPO itself - records each of these G2P genotype strings as an exact synonym of the term mapped - below (for example monoallelic_autosomal on HP:0000006), which is what makes these - mappings unambiguous. + synonyms of the MOI terms as many of the disorders described are de novo. + + The nine values are G2P's published genotype list, taken verbatim from the API the + terminology page itself calls (GET /gene2phenotype/api/attribs/, key "genotype"), which + includes the bare monoallelic_X alongside monoallelic_X_hemizygous and + monoallelic_X_heterozygous. + + All nine G2P genotype strings are recorded by HPO as oio:hasExactSynonym of the term + mapped below (verified against the HPO release directly; note that the OLS obo_synonym + field under-reports these, listing only synonyms that carry an xref). That + exact-synonym correspondence is what makes these mappings unambiguous. + + By convention in this enum, title is the HPO term label and the G2P string is carried + as an alias, matching the pv-mapping guidance and clinical/genetics.yaml; consumers + wanting G2P's own display string should read aliases. status: STANDARD contributors: - orcid:0000-0002-6601-2165 @@ -697,7 +708,10 @@ enums: The consequence of the reported variants at the protein (for protein-coding genes) or the RNA (for non-protein coding genes), per allele. These are Sequence Ontology terms developed for G2P and described in PMID:37982373; the descriptions below are the - G2P-authored usage notes rather than the SO text definitions. + G2P-authored usage notes rather than the SO text definitions. As in the other SO-backed + enums here, title is uniformly the SO term label and any differing G2P label is carried + as an alias; for four of the six values the SO and G2P labels coincide, which is why + only altered_gene_product_sequence and function_uncertain_variant carry an alias. status: STANDARD contributors: - orcid:0000-0002-6601-2165 @@ -802,6 +816,12 @@ enums: publication. All terms are Sequence Ontology terms. Where G2P uses a label that differs from the current SO label, the G2P label is recorded as an alias. Descriptions are the SO text definitions where SO provides one. + + The NMD-qualified types have two parents in SO, so they are modelled with the base + variant as is_a and the NMD qualifier as a mixin. LinkML preserves both, but the OWL + generator currently emits only the is_a parent as rdfs:subClassOf and drops the mixin, + so the NMD axis is additionally recorded in the nmd_status annotation to keep it + available in every generated artifact. status: STANDARD contributors: - orcid:0000-0002-6601-2165 @@ -828,6 +848,7 @@ enums: - NMD_triggering annotations: variant_type_group: NMD_VARIANTS + nmd_status: TRIGGERING NMD_ESCAPING: title: NMD_escaping_variant description: >- @@ -842,6 +863,7 @@ enums: - NMD_escaping annotations: variant_type_group: NMD_VARIANTS + nmd_status: ESCAPING STOP_GAINED_NMD_TRIGGERING: title: stop_gained_NMD_triggering description: >- @@ -853,6 +875,7 @@ enums: - NMD_TRIGGERING annotations: variant_type_group: NMD_VARIANTS + nmd_status: TRIGGERING STOP_GAINED_NMD_ESCAPING: title: stop_gained_NMD_escaping description: >- @@ -864,6 +887,7 @@ enums: - NMD_ESCAPING annotations: variant_type_group: NMD_VARIANTS + nmd_status: ESCAPING FRAMESHIFT_VARIANT_NMD_TRIGGERING: title: frameshift_variant_NMD_triggering description: >- @@ -874,6 +898,7 @@ enums: - NMD_TRIGGERING annotations: variant_type_group: NMD_VARIANTS + nmd_status: TRIGGERING FRAMESHIFT_VARIANT_NMD_ESCAPING: title: frameshift_variant_NMD_escaping description: >- @@ -885,6 +910,7 @@ enums: - NMD_ESCAPING annotations: variant_type_group: NMD_VARIANTS + nmd_status: ESCAPING SPLICE_DONOR_VARIANT_NMD_TRIGGERING: title: splice_donor_variant_NMD_triggering description: >- @@ -896,6 +922,7 @@ enums: - NMD_TRIGGERING annotations: variant_type_group: NMD_VARIANTS + nmd_status: TRIGGERING SPLICE_DONOR_VARIANT_NMD_ESCAPING: title: splice_donor_variant_NMD_escaping description: >- @@ -907,6 +934,7 @@ enums: - NMD_ESCAPING annotations: variant_type_group: NMD_VARIANTS + nmd_status: ESCAPING SPLICE_ACCEPTOR_VARIANT_NMD_TRIGGERING: title: splice_acceptor_variant_NMD_triggering description: >- @@ -918,6 +946,7 @@ enums: - NMD_TRIGGERING annotations: variant_type_group: NMD_VARIANTS + nmd_status: TRIGGERING SPLICE_ACCEPTOR_VARIANT_NMD_ESCAPING: title: splice_acceptor_variant_NMD_escaping description: >- @@ -929,6 +958,7 @@ enums: - NMD_ESCAPING annotations: variant_type_group: NMD_VARIANTS + nmd_status: ESCAPING SPLICE_REGION_VARIANT: title: splice_region_variant description: >- From bca25b97c8512396d1987d6263ca224d8e35046a Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 18 Aug 2026 22:52:27 +0000 Subject: [PATCH 4/8] Guard the NMD axis mirroring with tests Follow-up to the third review round on PR #79. The substantive request was a test keeping mixins and nmd_status in agreement. They are two encodings of one fact, and the annotation exists only because the authoritative encoding does not survive OWL generation, so drift would silently reintroduce the bug the previous commit fixed. tests/test_g2p_nmd_axis.py asserts the biconditional in both directions, that nmd_status values resolve to real permissible values, that the NMD-qualified types keep a base variant as is_a so the parent that does reach OWL is the useful one, and that the set stays at 8 values split 4/4. It reads the schema YAML rather than the generated enums module, which does not exist until regen-derived runs on main. The tests were mutation-checked rather than just observed to pass: removing one nmd_status fails 2 of them, and flipping one to the wrong axis fails 3. Also: - nmd_status values are now the permissible value keys NMD_TRIGGERING and NMD_ESCAPING rather than bare TRIGGERING/ESCAPING, matching the mixins entry directly above and the variant_type_group convention. - NMD_TRIGGERING and NMD_ESCAPING no longer annotate themselves. They are the axis, not members of it, so annotating them made an annotation query return 5 values where a mixin traversal returns 4. Removing the self-annotation makes the two access paths agree, which the tests now enforce. - Hoist the title convention to schema level so it covers every enum rather than the two that stated it. Verified with `just validate-schema`, `just test` (69 passed, 1 skipped) and a gen-owl re-inspection. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01De8cxj98MtmCDAW3uuUZQ7 --- .../schema/clinical/gene2phenotype.yaml | 22 ++-- tests/test_g2p_nmd_axis.py | 120 ++++++++++++++++++ 2 files changed, 132 insertions(+), 10 deletions(-) create mode 100644 tests/test_g2p_nmd_axis.py diff --git a/src/valuesets/schema/clinical/gene2phenotype.yaml b/src/valuesets/schema/clinical/gene2phenotype.yaml index 4d51edb8..e3a1a2c3 100644 --- a/src/valuesets/schema/clinical/gene2phenotype.yaml +++ b/src/valuesets/schema/clinical/gene2phenotype.yaml @@ -17,6 +17,10 @@ description: >- "segregration" for segregation in the GenCC confidence text) so that the text here can be diffed against the source. Where a quoted definition is itself inconsistent this is called out in the value's description rather than silently corrected. + + Throughout this schema, where a permissible value is mapped to an ontology term, title is + the ontology term's label and any differing G2P string is carried as an alias. Consumers + wanting G2P's own display string should read aliases rather than title. id: https://w3id.org/valuesets/clinical/gene2phenotype imports: - linkml:types @@ -848,7 +852,6 @@ enums: - NMD_triggering annotations: variant_type_group: NMD_VARIANTS - nmd_status: TRIGGERING NMD_ESCAPING: title: NMD_escaping_variant description: >- @@ -863,7 +866,6 @@ enums: - NMD_escaping annotations: variant_type_group: NMD_VARIANTS - nmd_status: ESCAPING STOP_GAINED_NMD_TRIGGERING: title: stop_gained_NMD_triggering description: >- @@ -875,7 +877,7 @@ enums: - NMD_TRIGGERING annotations: variant_type_group: NMD_VARIANTS - nmd_status: TRIGGERING + nmd_status: NMD_TRIGGERING STOP_GAINED_NMD_ESCAPING: title: stop_gained_NMD_escaping description: >- @@ -887,7 +889,7 @@ enums: - NMD_ESCAPING annotations: variant_type_group: NMD_VARIANTS - nmd_status: ESCAPING + nmd_status: NMD_ESCAPING FRAMESHIFT_VARIANT_NMD_TRIGGERING: title: frameshift_variant_NMD_triggering description: >- @@ -898,7 +900,7 @@ enums: - NMD_TRIGGERING annotations: variant_type_group: NMD_VARIANTS - nmd_status: TRIGGERING + nmd_status: NMD_TRIGGERING FRAMESHIFT_VARIANT_NMD_ESCAPING: title: frameshift_variant_NMD_escaping description: >- @@ -910,7 +912,7 @@ enums: - NMD_ESCAPING annotations: variant_type_group: NMD_VARIANTS - nmd_status: ESCAPING + nmd_status: NMD_ESCAPING SPLICE_DONOR_VARIANT_NMD_TRIGGERING: title: splice_donor_variant_NMD_triggering description: >- @@ -922,7 +924,7 @@ enums: - NMD_TRIGGERING annotations: variant_type_group: NMD_VARIANTS - nmd_status: TRIGGERING + nmd_status: NMD_TRIGGERING SPLICE_DONOR_VARIANT_NMD_ESCAPING: title: splice_donor_variant_NMD_escaping description: >- @@ -934,7 +936,7 @@ enums: - NMD_ESCAPING annotations: variant_type_group: NMD_VARIANTS - nmd_status: ESCAPING + nmd_status: NMD_ESCAPING SPLICE_ACCEPTOR_VARIANT_NMD_TRIGGERING: title: splice_acceptor_variant_NMD_triggering description: >- @@ -946,7 +948,7 @@ enums: - NMD_TRIGGERING annotations: variant_type_group: NMD_VARIANTS - nmd_status: TRIGGERING + nmd_status: NMD_TRIGGERING SPLICE_ACCEPTOR_VARIANT_NMD_ESCAPING: title: splice_acceptor_variant_NMD_escaping description: >- @@ -958,7 +960,7 @@ enums: - NMD_ESCAPING annotations: variant_type_group: NMD_VARIANTS - nmd_status: ESCAPING + nmd_status: NMD_ESCAPING SPLICE_REGION_VARIANT: title: splice_region_variant description: >- diff --git a/tests/test_g2p_nmd_axis.py b/tests/test_g2p_nmd_axis.py new file mode 100644 index 00000000..778fdb8d --- /dev/null +++ b/tests/test_g2p_nmd_axis.py @@ -0,0 +1,120 @@ +""" +Consistency tests for the NMD axis in the G2P variant type enum. + +The NMD-qualified variant types have two parents in SO. LinkML allows a single +``is_a``, so the base variant is the ``is_a`` and the NMD qualifier is a mixin. +``gen-owl`` currently emits only the ``is_a`` parent as ``rdfs:subClassOf`` and +drops the mixin, so the NMD axis is mirrored into an ``nmd_status`` annotation +that does survive generation. + +That mirroring is the thing these tests protect. The two encodings have to stay +in agreement: if a new NMD-qualified type is added with a mixin but no +annotation, the OWL product silently loses the axis again with nothing else +failing. + +These tests read the schema YAML directly rather than the generated +``valuesets.enums.clinical.gene2phenotype`` module, which does not exist until +the derived-file regeneration workflow runs on main. +""" + +from pathlib import Path + +import pytest +import yaml + +SCHEMA_PATH = ( + Path(__file__).parent.parent + / "src" / "valuesets" / "schema" / "clinical" / "gene2phenotype.yaml" +) + +NMD_QUALIFIERS = {"NMD_TRIGGERING", "NMD_ESCAPING"} + + +@pytest.fixture(scope="module") +def variant_types(): + """Permissible values of G2PVariantType, keyed by permissible value name.""" + with open(SCHEMA_PATH) as f: + schema = yaml.safe_load(f) + return schema["enums"]["G2PVariantType"]["permissible_values"] + + +def _nmd_mixins(pv): + return NMD_QUALIFIERS.intersection(pv.get("mixins") or []) + + +def _nmd_status(pv): + return (pv.get("annotations") or {}).get("nmd_status") + + +def test_every_nmd_mixin_has_matching_annotation(variant_types): + """A value carrying an NMD mixin must mirror it in nmd_status.""" + for name, pv in variant_types.items(): + mixins = _nmd_mixins(pv) + if not mixins: + continue + assert len(mixins) == 1, f"{name} carries more than one NMD qualifier: {mixins}" + assert _nmd_status(pv) == mixins.pop(), ( + f"{name} has an NMD mixin but nmd_status does not match it; " + f"the OWL output would lose the NMD axis for this value" + ) + + +def test_every_nmd_annotation_has_matching_mixin(variant_types): + """The converse: nmd_status must not claim an axis the mixins do not assert.""" + for name, pv in variant_types.items(): + status = _nmd_status(pv) + if status is None: + continue + assert status in NMD_QUALIFIERS, f"{name} has unknown nmd_status {status!r}" + assert _nmd_mixins(pv) == {status}, ( + f"{name} declares nmd_status {status} but no matching mixin" + ) + + +def test_nmd_status_values_are_permissible_value_keys(variant_types): + """nmd_status must reference real permissible values, like variant_type_group does.""" + for name, pv in variant_types.items(): + status = _nmd_status(pv) + if status is not None: + assert status in variant_types, ( + f"{name} has nmd_status {status!r}, which is not a " + f"permissible value of G2PVariantType" + ) + + +def test_abstract_qualifiers_do_not_annotate_themselves(variant_types): + """ + NMD_TRIGGERING and NMD_ESCAPING are the axis, not members of it. + + Annotating them would make an nmd_status query and a mixin traversal return + different sets, which is the drift these tests exist to prevent. + """ + for name in NMD_QUALIFIERS: + assert name in variant_types, f"{name} is missing from G2PVariantType" + assert _nmd_status(variant_types[name]) is None, ( + f"{name} is an NMD qualifier and must not carry nmd_status" + ) + + +def test_nmd_axis_is_non_empty_and_balanced(variant_types): + """Guard against the annotations being dropped wholesale.""" + statuses = [ + _nmd_status(pv) for pv in variant_types.values() if _nmd_status(pv) is not None + ] + assert len(statuses) == 8, f"expected 8 NMD-qualified types, found {len(statuses)}" + assert statuses.count("NMD_TRIGGERING") == 4 + assert statuses.count("NMD_ESCAPING") == 4 + + +def test_nmd_qualified_types_keep_a_base_variant_parent(variant_types): + """The is_a parent is what reaches OWL, so it must be the base variant.""" + for name, pv in variant_types.items(): + if not _nmd_mixins(pv): + continue + parent = pv.get("is_a") + assert parent is not None, f"{name} has an NMD mixin but no is_a parent" + assert parent in variant_types, f"{name} has unknown is_a parent {parent!r}" + assert parent not in NMD_QUALIFIERS, ( + f"{name} uses the NMD qualifier as its is_a parent; the base variant " + f"should be the is_a so that it survives into OWL" + ) From dfb75ce727de07b9870b66536504801aec69d12c Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 18 Aug 2026 23:00:20 +0000 Subject: [PATCH 5/8] Close a blind spot in the NMD axis guard The previous round's guard had a hole, confirmed by mutation rather than by reading: every test funnelled through an intersection with the known qualifier names, so a mixin that was not literally NMD_TRIGGERING or NMD_ESCAPING yielded an empty set and the value was skipped instead of flagged. Adding a type with mixins: [NMD_ESCAPPING] and no nmd_status passed all six tests -- exactly the drift the suite was written to catch. Two tests close it: - test_mixins_resolve_to_permissible_values asserts every mixin names a real permissible value, catching the typo. - test_nmd_named_types_carry_the_axis asserts a value named for a qualifier carries both the mixin and the matching annotation, catching an NMD type added without either. Also: - Generalise the permissible-value-key check into test_cross_reference_annotations_resolve, covering variant_type_group and parent_mechanism as well as nmd_status. All three came from the same "use keys, not display strings" change and share the failure mode where a typo resolves to nothing silently. - Replace the triggering/escaping parity assertion with the suffix test. Parity is not a correctness property, since nothing guarantees SO keeps the two arms balanced, and the suffix test catches the real defect parity was standing in for. Keep the count as a tripwire against the annotations being dropped wholesale, with a message saying to update it deliberately when the count legitimately changes. - Record that the dropped-mixin gen-owl behaviour was observed on linkml 1.9.5, so a future reader can re-check whether the workaround is still needed. - Read annotations through a helper tolerating both the compact and {tag, value} forms. All five mutation scenarios now fail the suite: typo'd mixin, missing annotation, flipped axis, and a typo in either cross-reference annotation. Verified with `just validate-schema` and `just test` (73 passed, 1 skipped). Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01De8cxj98MtmCDAW3uuUZQ7 --- tests/test_g2p_nmd_axis.py | 123 +++++++++++++++++++++++++++++++------ 1 file changed, 104 insertions(+), 19 deletions(-) diff --git a/tests/test_g2p_nmd_axis.py b/tests/test_g2p_nmd_axis.py index 778fdb8d..798fa2a2 100644 --- a/tests/test_g2p_nmd_axis.py +++ b/tests/test_g2p_nmd_axis.py @@ -12,6 +12,11 @@ annotation, the OWL product silently loses the axis again with nothing else failing. +The dropped-mixin behaviour was observed with linkml 1.9.5 / linkml-runtime +1.9.5. If a later version emits permissible-value mixins as ``rdfs:subClassOf``, +the ``nmd_status`` annotation becomes redundant and both it and these tests can +go -- re-check ``gen-owl`` output before assuming the workaround is still needed. + These tests read the schema YAML directly rather than the generated ``valuesets.enums.clinical.gene2phenotype`` module, which does not exist until the derived-file regeneration workflow runs on main. @@ -29,21 +34,82 @@ NMD_QUALIFIERS = {"NMD_TRIGGERING", "NMD_ESCAPING"} +# Number of NMD-qualified variant types currently in the schema. This is a +# tripwire against the annotations being dropped wholesale, not a claim that the +# count is fixed -- if G2P or SO add a type, update it deliberately. +EXPECTED_NMD_QUALIFIED = 8 + +# Annotations that name a permissible value of another enum. Each entry is +# (annotation key, enum whose keys it must resolve against). +PV_REFERENCE_ANNOTATIONS = [ + ("nmd_status", "G2PVariantType"), + ("variant_type_group", "G2PVariantTypeGroup"), + ("parent_mechanism", "G2PMolecularMechanism"), +] + @pytest.fixture(scope="module") -def variant_types(): - """Permissible values of G2PVariantType, keyed by permissible value name.""" +def schema(): with open(SCHEMA_PATH) as f: - schema = yaml.safe_load(f) + return yaml.safe_load(f) + + +@pytest.fixture(scope="module") +def variant_types(schema): + """Permissible values of G2PVariantType, keyed by permissible value name.""" return schema["enums"]["G2PVariantType"]["permissible_values"] +def _annotation(pv, key): + """Read an annotation, tolerating both the compact and {tag, value} forms.""" + raw = (pv.get("annotations") or {}).get(key) + if isinstance(raw, dict): + return raw.get("value") + return raw + + def _nmd_mixins(pv): return NMD_QUALIFIERS.intersection(pv.get("mixins") or []) def _nmd_status(pv): - return (pv.get("annotations") or {}).get("nmd_status") + return _annotation(pv, "nmd_status") + + +def test_mixins_resolve_to_permissible_values(variant_types): + """ + Every mixin must name a real permissible value. + + Without this, a misspelled qualifier (NMD_ESCAPPING) is silently skipped by + every other test in this file rather than flagged, which is the exact drift + the suite exists to catch. + """ + for name, pv in variant_types.items(): + for mixin in pv.get("mixins") or []: + assert mixin in variant_types, ( + f"{name} has mixin {mixin!r}, which is not a permissible value " + f"of G2PVariantType" + ) + + +def test_nmd_named_types_carry_the_axis(variant_types): + """ + A value named for an NMD qualifier must actually carry that axis. + + Catches a new NMD-qualified type that was added without the mixin, the + annotation, or both. + """ + for name, pv in variant_types.items(): + for qualifier in NMD_QUALIFIERS: + if name == qualifier or not name.endswith(f"_{qualifier}"): + continue + assert qualifier in (pv.get("mixins") or []), ( + f"{name} is named for {qualifier} but does not carry it as a mixin" + ) + assert _nmd_status(pv) == qualifier, ( + f"{name} is named for {qualifier} but its nmd_status is " + f"{_nmd_status(pv)!r}; the OWL output would lose the NMD axis" + ) def test_every_nmd_mixin_has_matching_annotation(variant_types): @@ -71,15 +137,28 @@ def test_every_nmd_annotation_has_matching_mixin(variant_types): ) -def test_nmd_status_values_are_permissible_value_keys(variant_types): - """nmd_status must reference real permissible values, like variant_type_group does.""" - for name, pv in variant_types.items(): - status = _nmd_status(pv) - if status is not None: - assert status in variant_types, ( - f"{name} has nmd_status {status!r}, which is not a " - f"permissible value of G2PVariantType" +@pytest.mark.parametrize("annotation_key,target_enum", PV_REFERENCE_ANNOTATIONS) +def test_cross_reference_annotations_resolve(schema, annotation_key, target_enum): + """ + Annotations naming a permissible value must resolve in their target enum. + + These annotations use permissible value keys rather than display strings so + they are machine-resolvable; a typo would otherwise resolve to nothing with + nothing complaining. + """ + targets = schema["enums"][target_enum]["permissible_values"] + checked = 0 + for enum_name, enum_def in schema["enums"].items(): + for pv_name, pv in (enum_def.get("permissible_values") or {}).items(): + value = _annotation(pv or {}, annotation_key) + if value is None: + continue + checked += 1 + assert value in targets, ( + f"{enum_name}.{pv_name} has {annotation_key}={value!r}, which is " + f"not a permissible value of {target_enum}" ) + assert checked, f"no {annotation_key} annotations found; has the convention changed?" def test_abstract_qualifiers_do_not_annotate_themselves(variant_types): @@ -96,14 +175,20 @@ def test_abstract_qualifiers_do_not_annotate_themselves(variant_types): ) -def test_nmd_axis_is_non_empty_and_balanced(variant_types): - """Guard against the annotations being dropped wholesale.""" - statuses = [ - _nmd_status(pv) for pv in variant_types.values() if _nmd_status(pv) is not None +def test_nmd_axis_is_not_dropped_wholesale(variant_types): + """ + Tripwire for the annotations being deleted en masse, which the pairwise + tests cannot see (they skip values carrying neither encoding). + """ + annotated = [ + name for name, pv in variant_types.items() if _nmd_status(pv) is not None ] - assert len(statuses) == 8, f"expected 8 NMD-qualified types, found {len(statuses)}" - assert statuses.count("NMD_TRIGGERING") == 4 - assert statuses.count("NMD_ESCAPING") == 4 + assert len(annotated) == EXPECTED_NMD_QUALIFIED, ( + f"expected {EXPECTED_NMD_QUALIFIED} NMD-qualified types, found " + f"{len(annotated)}. If G2P or SO legitimately added or removed one, " + f"update EXPECTED_NMD_QUALIFIED deliberately; otherwise the nmd_status " + f"annotations have gone missing and the OWL output has lost the axis." + ) def test_nmd_qualified_types_keep_a_base_variant_parent(variant_types): From a3d414491e819cb613e1f8ab1bb6104e8debb2a2 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 18 Aug 2026 23:10:09 +0000 Subject: [PATCH 6/8] Record which drift the toolchain already catches, and which it does not The last review asked whether a dangling permissible-value is_a would be as silent as the annotation case, and proposed generalising the mixin resolution check across every enum on that basis. Measured rather than assumed, the premise does not hold: dangling PV is_a -> `just test` FAILS at _test-schema dangling PV mixins -> `just test` FAILS at _test-schema typo'd annotation value-> `_test-schema` exits 0, no diagnostic omitted nmd_status -> `_test-schema` exits 0, no diagnostic Structural references are resolved during OWL generation, which gen-project runs, so a typo in either fails the build with `ValueError: Cannot find permissible value`. That holds for mixins even though gen-owl discards them afterwards: it resolves first and drops after. The three unguarded is_a references in G2PVariantConsequence are therefore already covered, and generalising the check would add no coverage, so it is not done here. Annotation values are the surface nothing guards, which is what this suite is actually for. The module docstring now records the division of labour with the evidence, so the next person does not have to re-derive it, and states plainly that the suffix rule is not total, along with why the looser "key contains NMD" alternative was rejected: it would false-positive on a legitimate NMD_TRANSCRIPT_VARIANT (SO:0001621). This also corrects something I claimed in the previous round. I reported the typo'd-mixin mutation as previously invisible; it was invisible to this suite but would have failed CI. The genuinely silent modes are the annotation ones. Also normalise permissible value bodies to a dict in the fixture, so a value written without one does not raise AttributeError in the helpers. Verified with `just test` (73 passed, 1 skipped). Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01De8cxj98MtmCDAW3uuUZQ7 --- tests/test_g2p_nmd_axis.py | 45 +++++++++++++++++++++++++++++++++----- 1 file changed, 40 insertions(+), 5 deletions(-) diff --git a/tests/test_g2p_nmd_axis.py b/tests/test_g2p_nmd_axis.py index 798fa2a2..7fe2054b 100644 --- a/tests/test_g2p_nmd_axis.py +++ b/tests/test_g2p_nmd_axis.py @@ -20,6 +20,30 @@ These tests read the schema YAML directly rather than the generated ``valuesets.enums.clinical.gene2phenotype`` module, which does not exist until the derived-file regeneration workflow runs on main. + +What this suite is and is not for +--------------------------------- +Structural references are already enforced by code generation: a permissible +value whose ``is_a`` or ``mixins`` names a key that does not exist fails +``just test`` at ``_test-schema``, because ``gen-project`` builds OWL and OWL +generation resolves both (``ValueError: Cannot find permissible value``). That +holds for ``mixins`` even though ``gen-owl`` then discards them -- it resolves +first, drops after. So the mixin check below is a faster, clearer echo of an +existing guarantee rather than the only thing standing between a typo and a +broken build. + +Annotation values are the genuinely unguarded surface. They are free-form +strings, so a misspelled ``nmd_status``/``variant_type_group``/ +``parent_mechanism``, or an omitted ``nmd_status``, passes ``_test-schema`` +with exit 0 and produces no diagnostic anywhere. That is what these tests exist +for. + +The guard is not total. ``test_nmd_named_types_carry_the_axis`` keys off the +name suffix, which is exact for the current eight values but would not see an +NMD-qualified type named the other way round (``NMD_TRIGGERING_STOP_LOST``) +that also omitted both encodings. A looser "key contains NMD" rule was +considered and rejected: it would false-positive on a legitimate +``NMD_TRANSCRIPT_VARIANT`` (SO:0001621), a plausible future addition. """ from pathlib import Path @@ -56,8 +80,17 @@ def schema(): @pytest.fixture(scope="module") def variant_types(schema): - """Permissible values of G2PVariantType, keyed by permissible value name.""" - return schema["enums"]["G2PVariantType"]["permissible_values"] + """ + Permissible values of G2PVariantType, keyed by permissible value name. + + Bodies are normalised to a dict so that a value written without one + (``SOME_KEY:``, which YAML parses as None) does not raise AttributeError in + the helpers below. + """ + return { + name: pv or {} + for name, pv in schema["enums"]["G2PVariantType"]["permissible_values"].items() + } def _annotation(pv, key): @@ -80,9 +113,11 @@ def test_mixins_resolve_to_permissible_values(variant_types): """ Every mixin must name a real permissible value. - Without this, a misspelled qualifier (NMD_ESCAPPING) is silently skipped by - every other test in this file rather than flagged, which is the exact drift - the suite exists to catch. + Without this, a misspelled qualifier (NMD_ESCAPPING) is skipped by every + other test in this file rather than flagged. Code generation would also + reject it, so this is a fast and specific echo of that rather than the sole + defence -- it fails with the offending key rather than a generator + traceback. """ for name, pv in variant_types.items(): for mixin in pv.get("mixins") or []: From 2294e62ebd499d32e3f683699ffa1ca31eccd5e9 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 18 Aug 2026 23:16:51 +0000 Subject: [PATCH 7/8] Pin the provenance of both generator claims The review noted that the structural-reference claim added last commit is now load-bearing, since it is the stated reason a test is absent, but was recorded without the expiry condition the mixin-drop claim got. Both are now pinned. The structural-reference guarantee is recorded as measured on linkml 1.9.5 and holding only while `owl` is absent from `excludes` in config.yaml. Adding it there would stop _test-schema resolving permissible-value is_a/mixins at all, and nothing would report that the docstring had become false. The review also asked whether config.yaml's `mixins_as_expressions: true` might surface the mixin parent, in which case the nmd_status workaround could be dropped entirely. Measured: it does not. Running gen-owl with the full config argument set, including --mixins-as-expressions, still emits only the is_a parent; that flag governs class mixins, not permissible-value mixins. The docstring now records that the drop was measured on both OWL paths -- gen-owl bare, which is what `just gen-owl` ships, and gen-owl under config.yaml args, which is what gen-project runs -- so the workaround is justified on both rather than on the one path I happened to test first. Also corrected the stated benefit of test_mixins_resolve_to_permissible_ values. The clearer failure message only appears under a direct pytest run; under `just test` the reader sees the generator traceback, because _test-schema runs first and aborts. The test is still worth keeping for the direct-pytest workflow, but the justification was a half-step off. Nits from the same review: _annotation now handles the list-of-{tag, value} annotation form as well as the two mapping forms, and the enum body is normalised so a bodyless enum cannot AttributeError. Verified with `just test` (73 passed, 1 skipped). Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01De8cxj98MtmCDAW3uuUZQ7 --- tests/test_g2p_nmd_axis.py | 49 ++++++++++++++++++++++++++++---------- 1 file changed, 36 insertions(+), 13 deletions(-) diff --git a/tests/test_g2p_nmd_axis.py b/tests/test_g2p_nmd_axis.py index 7fe2054b..9c8561d1 100644 --- a/tests/test_g2p_nmd_axis.py +++ b/tests/test_g2p_nmd_axis.py @@ -12,10 +12,15 @@ annotation, the OWL product silently loses the axis again with nothing else failing. -The dropped-mixin behaviour was observed with linkml 1.9.5 / linkml-runtime -1.9.5. If a later version emits permissible-value mixins as ``rdfs:subClassOf``, -the ``nmd_status`` annotation becomes redundant and both it and these tests can -go -- re-check ``gen-owl`` output before assuming the workaround is still needed. +The dropped-mixin behaviour was measured on linkml 1.9.5 / linkml-runtime 1.9.5, +on both of the repo's OWL paths: ``gen-owl`` invoked bare, as ``just gen-owl`` +does to produce the shipped ``project/owl/valuesets.owl.ttl``, and ``gen-owl`` +invoked with ``config.yaml``'s ``generator_args.owl``, as ``gen-project`` does. +The latter includes ``mixins_as_expressions: true``, which despite the name does +not surface permissible-value mixins -- it governs class mixins. If a later +version emits permissible-value mixins as ``rdfs:subClassOf``, the ``nmd_status`` +annotation becomes redundant and both it and these tests can go -- re-check +``gen-owl`` output before assuming the workaround is still needed. These tests read the schema YAML directly rather than the generated ``valuesets.enums.clinical.gene2phenotype`` module, which does not exist until @@ -28,10 +33,16 @@ ``just test`` at ``_test-schema``, because ``gen-project`` builds OWL and OWL generation resolves both (``ValueError: Cannot find permissible value``). That holds for ``mixins`` even though ``gen-owl`` then discards them -- it resolves -first, drops after. So the mixin check below is a faster, clearer echo of an -existing guarantee rather than the only thing standing between a typo and a +first, drops after. So the mixin check below is a faster, more specific echo of +an existing guarantee rather than the only thing standing between a typo and a broken build. +That guarantee was measured on linkml 1.9.5 and holds only while ``owl`` is +absent from ``excludes`` in ``config.yaml``. Adding it there would stop +``_test-schema`` resolving permissible-value ``is_a``/``mixins`` at all, and +nothing would report that this paragraph had become false -- so re-check both if +either changes. + Annotation values are the genuinely unguarded surface. They are free-form strings, so a misspelled ``nmd_status``/``variant_type_group``/ ``parent_mechanism``, or an omitted ``nmd_status``, passes ``_test-schema`` @@ -94,8 +105,19 @@ def variant_types(schema): def _annotation(pv, key): - """Read an annotation, tolerating both the compact and {tag, value} forms.""" - raw = (pv.get("annotations") or {}).get(key) + """ + Read an annotation value. + + Tolerates the three shapes LinkML accepts: a compact ``key: value`` mapping, + a ``key: {tag, value}`` mapping, and a list of ``{tag, value}`` dicts. + """ + annotations = pv.get("annotations") or {} + if isinstance(annotations, list): + for item in annotations: + if isinstance(item, dict) and item.get("tag") == key: + return item.get("value") + return None + raw = annotations.get(key) if isinstance(raw, dict): return raw.get("value") return raw @@ -114,10 +136,11 @@ def test_mixins_resolve_to_permissible_values(variant_types): Every mixin must name a real permissible value. Without this, a misspelled qualifier (NMD_ESCAPPING) is skipped by every - other test in this file rather than flagged. Code generation would also - reject it, so this is a fast and specific echo of that rather than the sole - defence -- it fails with the offending key rather than a generator - traceback. + other test in this file rather than flagged. Code generation also rejects + it, so this is not the sole defence. Its value is under a direct ``pytest`` + run (editor, ``uv run pytest``), where it names the offending key; under + ``just test`` the reader sees the generator traceback instead, because + ``_test-schema`` runs before pytest and aborts first. """ for name, pv in variant_types.items(): for mixin in pv.get("mixins") or []: @@ -184,7 +207,7 @@ def test_cross_reference_annotations_resolve(schema, annotation_key, target_enum targets = schema["enums"][target_enum]["permissible_values"] checked = 0 for enum_name, enum_def in schema["enums"].items(): - for pv_name, pv in (enum_def.get("permissible_values") or {}).items(): + for pv_name, pv in ((enum_def or {}).get("permissible_values") or {}).items(): value = _annotation(pv or {}, annotation_key) if value is None: continue From 45db6ce36732e2be79ad0bd46b39bdda981874db Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 18 Aug 2026 23:21:59 +0000 Subject: [PATCH 8/8] Pin the OWL generator claim in the shipped enum description The third copy of the mixin-drop claim lives in the G2PVariantType description, where "currently" was doing the work with nothing saying when. Both copies in the test module carry an expiry condition; this one did not, and it is the copy with the widest reach, since enum descriptions ship into the generated docs and OWL while the test docstring stays in the repo. Now says "as of linkml 1.9.5", and states that the annotation is redundant once a later linkml emits permissible-value mixins, so a reader of the generated docs knows what to re-check rather than having to rediscover it. Verified with `just validate-schema` and `just test` (73 passed, 1 skipped). Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01De8cxj98MtmCDAW3uuUZQ7 --- src/valuesets/schema/clinical/gene2phenotype.yaml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/valuesets/schema/clinical/gene2phenotype.yaml b/src/valuesets/schema/clinical/gene2phenotype.yaml index e3a1a2c3..9e615e80 100644 --- a/src/valuesets/schema/clinical/gene2phenotype.yaml +++ b/src/valuesets/schema/clinical/gene2phenotype.yaml @@ -822,10 +822,11 @@ enums: SO text definitions where SO provides one. The NMD-qualified types have two parents in SO, so they are modelled with the base - variant as is_a and the NMD qualifier as a mixin. LinkML preserves both, but the OWL - generator currently emits only the is_a parent as rdfs:subClassOf and drops the mixin, - so the NMD axis is additionally recorded in the nmd_status annotation to keep it - available in every generated artifact. + variant as is_a and the NMD qualifier as a mixin. LinkML preserves both, but as of + linkml 1.9.5 the OWL generator emits only the is_a parent as rdfs:subClassOf and drops + the mixin, so the NMD axis is additionally recorded in the nmd_status annotation to + keep it available in every generated artifact. If a later linkml emits + permissible-value mixins, that annotation is redundant and can go. status: STANDARD contributors: - orcid:0000-0002-6601-2165