From 37a4e270ae01f691ab23437bac634c9ed31f8126 Mon Sep 17 00:00:00 2001 From: "marcin p. joachimiak" <4625870+realmarcin@users.noreply.github.com> Date: Fri, 14 Aug 2026 21:21:04 -0700 Subject: [PATCH] Drop the vacuous GO annotations, upgrade the ones that meant something (#182) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Decision on #182: drop the generic GO remaps. The #180 cleanup remapped obsolete GO ids to the nearest VALID term, which for a handful meant climbing to a near-root process. The result was 16 annotations of `GO:0008152 metabolic process` on records in a knowledge base about microbial communities, every one of which does metabolism. They were not all the same thing, so they are not all treated the same: 9 read `preferred_term: metabolic process` against `GO:0008152 metabolic process` — the annotation restates the term and the term restates the domain. Dropped; nothing is lost. 7 carried a real concept flattened onto the generic parent, and are re-grounded rather than deleted: organic substance catabolic process -> GO:0009056 catabolic process (5) nitrogen compound metabolic process -> GO:0071941 nitrogen cycle metabolic process (2) Deleting those seven would have thrown away curated claims about catabolism and nitrogen cycling to satisfy the letter of "drop the remaps". The precise terms a curator would want — GO:0006807 and GO:1901575 — are OBSOLETE in GO, checked with OAK, which is exactly why they were flattened in the first place. GO:0071941 was already used twice elsewhere in the KB, so this follows an existing convention rather than inventing one. `term` is `required: true` on BiologicalProcessDescriptor, so there is no option to keep an ungrounded `preferred_term`: dropping the annotation necessarily means dropping the whole descriptor. That is why upgrading beats deleting wherever a current term exists. GO:0044419 (interspecies interaction, 152 occurrences) is deliberately NOT touched. It came from the same obsolete-remap list but it is genuinely informative for this KB, and the replacement is better than the original. Gate added, mutation-checked by restoring a vacuous annotation to a real record. It keys on the identifier, never on `preferred_term == label` — a correctly grounded specific term also matches its own label, and that is good curation rather than a defect; a second test pins that. Co-Authored-By: Claude Opus 5 --- .../AMD_Acidophile_Heterotroph_Network.html | 4 +- ...eactor_DNRA_Destabilization_Community.html | 4 +- ...land_Seawater_Ion_Microcosm_Community.html | 8 +- ...e_Formaldehyde_Crossfeeding_Community.html | 15 -- .../ORNL_PMI_Populus_PD10_SynCom.html | 64 ------- ...Wetland_Sulfur_Carbon_Virus_Community.html | 4 +- ...thylotrophic_Methanogenesis_Community.html | 4 +- ...xygen_Sulfate_GHG_Microcosm_Community.html | 4 +- .../AMD_Acidophile_Heterotroph_Network.yaml | 4 +- ...eactor_DNRA_Destabilization_Community.yaml | 4 +- ...land_Seawater_Ion_Microcosm_Community.yaml | 8 +- ...e_Formaldehyde_Crossfeeding_Community.yaml | 4 - .../ORNL_PMI_Populus_PD10_SynCom.yaml | 32 ---- ...Wetland_Sulfur_Carbon_Virus_Community.yaml | 4 +- ...thylotrophic_Methanogenesis_Community.yaml | 4 +- ...xygen_Sulfate_GHG_Microcosm_Community.yaml | 4 +- tests/test_no_vacuous_go_annotations.py | 158 ++++++++++++++++++ 17 files changed, 186 insertions(+), 143 deletions(-) create mode 100644 tests/test_no_vacuous_go_annotations.py diff --git a/docs/communities/AMD_Acidophile_Heterotroph_Network.html b/docs/communities/AMD_Acidophile_Heterotroph_Network.html index bd18ff1c..dd12db44 100644 --- a/docs/communities/AMD_Acidophile_Heterotroph_Network.html +++ b/docs/communities/AMD_Acidophile_Heterotroph_Network.html @@ -841,8 +841,8 @@

Organic Carbon Scavenging and Remineralization

  • nitrogen compound metabolic process - (GO:0008152) + (GO:0071941)
  • diff --git a/docs/communities/Anammox_Bioreactor_DNRA_Destabilization_Community.html b/docs/communities/Anammox_Bioreactor_DNRA_Destabilization_Community.html index 4e4c5059..e921e3fa 100644 --- a/docs/communities/Anammox_Bioreactor_DNRA_Destabilization_Community.html +++ b/docs/communities/Anammox_Bioreactor_DNRA_Destabilization_Community.html @@ -604,8 +604,8 @@

    DNRA Competes with Anammox for Nitrogen Removal

  • nitrogen compound metabolic process - (GO:0008152) + (GO:0071941)
  • diff --git a/docs/communities/Coastal_Forested_Wetland_Seawater_Ion_Microcosm_Community.html b/docs/communities/Coastal_Forested_Wetland_Seawater_Ion_Microcosm_Community.html index 9d53dc19..e798cac7 100644 --- a/docs/communities/Coastal_Forested_Wetland_Seawater_Ion_Microcosm_Community.html +++ b/docs/communities/Coastal_Forested_Wetland_Seawater_Ion_Microcosm_Community.html @@ -667,8 +667,8 @@

    Non-Sulfate Seawater Ions Drive Community And Emission Responses

  • organic substance catabolic process - (GO:0008152) + (GO:0009056)
  • @@ -813,8 +813,8 @@

    Artificial Seawater Alters Carbon Nitrogen And Phosphorus Gene Response

  • organic substance catabolic process - (GO:0008152) + (GO:0009056)
  • diff --git a/docs/communities/Model_Lignocellulose_Formaldehyde_Crossfeeding_Community.html b/docs/communities/Model_Lignocellulose_Formaldehyde_Crossfeeding_Community.html index dc2cbbf5..d18f2356 100644 --- a/docs/communities/Model_Lignocellulose_Formaldehyde_Crossfeeding_Community.html +++ b/docs/communities/Model_Lignocellulose_Formaldehyde_Crossfeeding_Community.html @@ -751,17 +751,6 @@

    Methylotrophic Formaldehyde Mitigation

    -

    Biological Processes:

    - - @@ -1307,10 +1296,6 @@

    Evidence

    var processes = []; - - processes.push("metabolic process"); - - var evidenceCount = 1; nodes.push({ diff --git a/docs/communities/ORNL_PMI_Populus_PD10_SynCom.html b/docs/communities/ORNL_PMI_Populus_PD10_SynCom.html index 5f8a6732..961471fc 100644 --- a/docs/communities/ORNL_PMI_Populus_PD10_SynCom.html +++ b/docs/communities/ORNL_PMI_Populus_PD10_SynCom.html @@ -918,12 +918,6 @@

    Medium-Dependent Cross-Feeding

    class="term-link" target="_blank" rel="noopener noreferrer">GO:0044419) -
  • - metabolic process - (GO:0008152) -
  • - @@ -1028,12 +1022,6 @@

    Metabolite Exchange with Pantoea

    class="term-link" target="_blank" rel="noopener noreferrer">GO:0044419) -
  • - metabolic process - (GO:0008152) -
  • - @@ -1086,12 +1074,6 @@

    Cross-Feeding with Rhizobium

    class="term-link" target="_blank" rel="noopener noreferrer">GO:0044419) -
  • - metabolic process - (GO:0008152) -
  • - @@ -1144,12 +1126,6 @@

    Cross-Feeding with Bacillus

    class="term-link" target="_blank" rel="noopener noreferrer">GO:0044419) -
  • - metabolic process - (GO:0008152) -
  • - @@ -1202,12 +1178,6 @@

    Cross-Feeding with Caulobacter

    class="term-link" target="_blank" rel="noopener noreferrer">GO:0044419) -
  • - metabolic process - (GO:0008152) -
  • - @@ -1260,12 +1230,6 @@

    Cross-Feeding with Duganella

    class="term-link" target="_blank" rel="noopener noreferrer">GO:0044419) -
  • - metabolic process - (GO:0008152) -
  • - @@ -1318,12 +1282,6 @@

    Cross-Feeding with Streptomyces

    class="term-link" target="_blank" rel="noopener noreferrer">GO:0044419) -
  • - metabolic process - (GO:0008152) -
  • - @@ -1376,12 +1334,6 @@

    Cross-Feeding with Paraburkholderia

    class="term-link" target="_blank" rel="noopener noreferrer">GO:0044419) -
  • - metabolic process - (GO:0008152) -
  • - @@ -1780,8 +1732,6 @@

    Evidence

    processes.push("interspecies interaction between organisms"); - processes.push("metabolic process"); - var evidenceCount = 1; @@ -1858,8 +1808,6 @@

    Evidence

    processes.push("interspecies interaction between organisms"); - processes.push("metabolic process"); - var evidenceCount = 1; @@ -1898,8 +1846,6 @@

    Evidence

    processes.push("interspecies interaction between organisms"); - processes.push("metabolic process"); - var evidenceCount = 1; @@ -1938,8 +1884,6 @@

    Evidence

    processes.push("interspecies interaction between organisms"); - processes.push("metabolic process"); - var evidenceCount = 1; @@ -1978,8 +1922,6 @@

    Evidence

    processes.push("interspecies interaction between organisms"); - processes.push("metabolic process"); - var evidenceCount = 1; @@ -2018,8 +1960,6 @@

    Evidence

    processes.push("interspecies interaction between organisms"); - processes.push("metabolic process"); - var evidenceCount = 1; @@ -2058,8 +1998,6 @@

    Evidence

    processes.push("interspecies interaction between organisms"); - processes.push("metabolic process"); - var evidenceCount = 1; @@ -2098,8 +2036,6 @@

    Evidence

    processes.push("interspecies interaction between organisms"); - processes.push("metabolic process"); - var evidenceCount = 1; diff --git a/docs/communities/Prairie_Pothole_Wetland_Sulfur_Carbon_Virus_Community.html b/docs/communities/Prairie_Pothole_Wetland_Sulfur_Carbon_Virus_Community.html index 1aa1c2d3..8d8541f4 100644 --- a/docs/communities/Prairie_Pothole_Wetland_Sulfur_Carbon_Virus_Community.html +++ b/docs/communities/Prairie_Pothole_Wetland_Sulfur_Carbon_Virus_Community.html @@ -730,8 +730,8 @@

    Diverse Sulfate-Reduction Guild

  • organic substance catabolic process - (GO:0008152) + (GO:0009056)
  • diff --git a/docs/communities/Stordalen_Mire_Methylotrophic_Methanogenesis_Community.html b/docs/communities/Stordalen_Mire_Methylotrophic_Methanogenesis_Community.html index f18d9ce0..9f05682d 100644 --- a/docs/communities/Stordalen_Mire_Methylotrophic_Methanogenesis_Community.html +++ b/docs/communities/Stordalen_Mire_Methylotrophic_Methanogenesis_Community.html @@ -766,8 +766,8 @@

    Bacterial Anaerobic Methylotrophy

  • organic substance catabolic process - (GO:0008152) + (GO:0009056)
  • diff --git a/docs/communities/Wetland_Oxygen_Sulfate_GHG_Microcosm_Community.html b/docs/communities/Wetland_Oxygen_Sulfate_GHG_Microcosm_Community.html index 07788109..2bb3c606 100644 --- a/docs/communities/Wetland_Oxygen_Sulfate_GHG_Microcosm_Community.html +++ b/docs/communities/Wetland_Oxygen_Sulfate_GHG_Microcosm_Community.html @@ -853,8 +853,8 @@

    Lactate-Sulfate Carbon And Sulfur Subnetwork

  • organic substance catabolic process - (GO:0008152) + (GO:0009056)
  • diff --git a/kb/communities/AMD_Acidophile_Heterotroph_Network.yaml b/kb/communities/AMD_Acidophile_Heterotroph_Network.yaml index 86bd0430..a1f5f07c 100644 --- a/kb/communities/AMD_Acidophile_Heterotroph_Network.yaml +++ b/kb/communities/AMD_Acidophile_Heterotroph_Network.yaml @@ -344,8 +344,8 @@ ecological_interactions: label: aerobic respiration - preferred_term: nitrogen compound metabolic process term: - id: GO:0008152 - label: metabolic process + id: GO:0071941 + label: nitrogen cycle metabolic process evidence: - reference: doi:10.3389/fmicb.2015.00475 supports: SUPPORT diff --git a/kb/communities/Anammox_Bioreactor_DNRA_Destabilization_Community.yaml b/kb/communities/Anammox_Bioreactor_DNRA_Destabilization_Community.yaml index 1382e38b..3fa5e46d 100644 --- a/kb/communities/Anammox_Bioreactor_DNRA_Destabilization_Community.yaml +++ b/kb/communities/Anammox_Bioreactor_DNRA_Destabilization_Community.yaml @@ -164,8 +164,8 @@ ecological_interactions: biological_processes: - preferred_term: nitrogen compound metabolic process term: - id: GO:0008152 - label: metabolic process + id: GO:0071941 + label: nitrogen cycle metabolic process evidence: - reference: PMID:31980038 supports: SUPPORT diff --git a/kb/communities/Coastal_Forested_Wetland_Seawater_Ion_Microcosm_Community.yaml b/kb/communities/Coastal_Forested_Wetland_Seawater_Ion_Microcosm_Community.yaml index 07c19b8a..62505ac2 100644 --- a/kb/communities/Coastal_Forested_Wetland_Seawater_Ion_Microcosm_Community.yaml +++ b/kb/communities/Coastal_Forested_Wetland_Seawater_Ion_Microcosm_Community.yaml @@ -129,8 +129,8 @@ ecological_interactions: label: methanogenesis - preferred_term: organic substance catabolic process term: - id: GO:0008152 - label: metabolic process + id: GO:0009056 + label: catabolic process evidence: - reference: PMID:38628812 supports: SUPPORT @@ -198,8 +198,8 @@ ecological_interactions: biological_processes: - preferred_term: organic substance catabolic process term: - id: GO:0008152 - label: metabolic process + id: GO:0009056 + label: catabolic process evidence: - reference: PMID:38628812 supports: SUPPORT diff --git a/kb/communities/Model_Lignocellulose_Formaldehyde_Crossfeeding_Community.yaml b/kb/communities/Model_Lignocellulose_Formaldehyde_Crossfeeding_Community.yaml index ae57eb40..d2606535 100644 --- a/kb/communities/Model_Lignocellulose_Formaldehyde_Crossfeeding_Community.yaml +++ b/kb/communities/Model_Lignocellulose_Formaldehyde_Crossfeeding_Community.yaml @@ -236,10 +236,6 @@ ecological_interactions: id: CHEBI:16842 label: formaldehyde biological_processes: - - preferred_term: metabolic process - term: - id: GO:0008152 - label: metabolic process evidence: - reference: doi:10.3390/microorganisms9020321 supports: SUPPORT diff --git a/kb/communities/ORNL_PMI_Populus_PD10_SynCom.yaml b/kb/communities/ORNL_PMI_Populus_PD10_SynCom.yaml index ee26916c..f4f0a3e0 100644 --- a/kb/communities/ORNL_PMI_Populus_PD10_SynCom.yaml +++ b/kb/communities/ORNL_PMI_Populus_PD10_SynCom.yaml @@ -327,10 +327,6 @@ ecological_interactions: term: id: GO:0044419 label: biological process involved in interspecies interaction between organisms - - preferred_term: metabolic process - term: - id: GO:0008152 - label: metabolic process evidence: - reference: PMID:33995895 supports: SUPPORT @@ -381,10 +377,6 @@ ecological_interactions: term: id: GO:0044419 label: biological process involved in interspecies interaction between organisms - - preferred_term: metabolic process - term: - id: GO:0008152 - label: metabolic process evidence: - reference: PMID:33995895 supports: SUPPORT @@ -410,10 +402,6 @@ ecological_interactions: term: id: GO:0044419 label: biological process involved in interspecies interaction between organisms - - preferred_term: metabolic process - term: - id: GO:0008152 - label: metabolic process evidence: - reference: PMID:33995895 supports: SUPPORT @@ -439,10 +427,6 @@ ecological_interactions: term: id: GO:0044419 label: biological process involved in interspecies interaction between organisms - - preferred_term: metabolic process - term: - id: GO:0008152 - label: metabolic process evidence: - reference: PMID:33995895 supports: SUPPORT @@ -468,10 +452,6 @@ ecological_interactions: term: id: GO:0044419 label: biological process involved in interspecies interaction between organisms - - preferred_term: metabolic process - term: - id: GO:0008152 - label: metabolic process evidence: - reference: PMID:33995895 supports: SUPPORT @@ -497,10 +477,6 @@ ecological_interactions: term: id: GO:0044419 label: biological process involved in interspecies interaction between organisms - - preferred_term: metabolic process - term: - id: GO:0008152 - label: metabolic process evidence: - reference: PMID:33995895 supports: SUPPORT @@ -526,10 +502,6 @@ ecological_interactions: term: id: GO:0044419 label: biological process involved in interspecies interaction between organisms - - preferred_term: metabolic process - term: - id: GO:0008152 - label: metabolic process evidence: - reference: PMID:33995895 supports: SUPPORT @@ -555,10 +527,6 @@ ecological_interactions: term: id: GO:0044419 label: biological process involved in interspecies interaction between organisms - - preferred_term: metabolic process - term: - id: GO:0008152 - label: metabolic process evidence: - reference: PMID:33995895 supports: SUPPORT diff --git a/kb/communities/Prairie_Pothole_Wetland_Sulfur_Carbon_Virus_Community.yaml b/kb/communities/Prairie_Pothole_Wetland_Sulfur_Carbon_Virus_Community.yaml index 27f6c697..d912c7a9 100644 --- a/kb/communities/Prairie_Pothole_Wetland_Sulfur_Carbon_Virus_Community.yaml +++ b/kb/communities/Prairie_Pothole_Wetland_Sulfur_Carbon_Virus_Community.yaml @@ -165,8 +165,8 @@ ecological_interactions: label: dissimilatory sulfate reduction - preferred_term: organic substance catabolic process term: - id: GO:0008152 - label: metabolic process + id: GO:0009056 + label: catabolic process evidence: - reference: PMID:30086797 supports: SUPPORT diff --git a/kb/communities/Stordalen_Mire_Methylotrophic_Methanogenesis_Community.yaml b/kb/communities/Stordalen_Mire_Methylotrophic_Methanogenesis_Community.yaml index 277d5b72..35bc7029 100644 --- a/kb/communities/Stordalen_Mire_Methylotrophic_Methanogenesis_Community.yaml +++ b/kb/communities/Stordalen_Mire_Methylotrophic_Methanogenesis_Community.yaml @@ -189,8 +189,8 @@ ecological_interactions: biological_processes: - preferred_term: organic substance catabolic process term: - id: GO:0008152 - label: metabolic process + id: GO:0009056 + label: catabolic process evidence: - reference: PMID:38063415 supports: SUPPORT diff --git a/kb/communities/Wetland_Oxygen_Sulfate_GHG_Microcosm_Community.yaml b/kb/communities/Wetland_Oxygen_Sulfate_GHG_Microcosm_Community.yaml index 67237861..ca01e829 100644 --- a/kb/communities/Wetland_Oxygen_Sulfate_GHG_Microcosm_Community.yaml +++ b/kb/communities/Wetland_Oxygen_Sulfate_GHG_Microcosm_Community.yaml @@ -233,8 +233,8 @@ ecological_interactions: label: dissimilatory sulfate reduction - preferred_term: organic substance catabolic process term: - id: GO:0008152 - label: metabolic process + id: GO:0009056 + label: catabolic process evidence: - reference: PMID:38961111 supports: SUPPORT diff --git a/tests/test_no_vacuous_go_annotations.py b/tests/test_no_vacuous_go_annotations.py new file mode 100644 index 00000000..938572eb --- /dev/null +++ b/tests/test_no_vacuous_go_annotations.py @@ -0,0 +1,158 @@ +"""Some GO terms are true of every record here, so they annotate nothing (#182). + +The #180 id↔label cleanup remapped obsolete GO ids to their nearest *valid* +term. For a handful that meant climbing to a near-root process, and the result +was sixteen annotations reading + + - preferred_term: metabolic process + term: + id: GO:0008152 + label: metabolic process + +on records in a knowledge base about **microbial communities**, every one of +which does metabolism. Nine were exactly that and were dropped. Seven carried a +real concept in `preferred_term` that had been flattened onto the generic +parent, and were re-grounded rather than deleted: + + organic substance catabolic process -> GO:0009056 catabolic process + nitrogen compound metabolic process -> GO:0071941 nitrogen cycle metabolic process + +Both targets are current; the precise terms a curator would want +(`GO:0006807`, `GO:1901575`) are **obsolete in GO**, which is why they were +flattened in the first place. `GO:0071941` was already used twice elsewhere in +the KB, so this follows an existing convention rather than inventing one. + +`term` is `required: true` on `BiologicalProcessDescriptor`, so "drop the +annotation" necessarily means dropping the whole descriptor — there is no way to +keep an ungrounded `preferred_term`. That is why upgrading beats deleting +wherever a current term exists. +""" + +from __future__ import annotations + +import pathlib + +import pytest +import yaml + +REPO = pathlib.Path(__file__).parent.parent +COMMUNITIES = REPO / "kb/communities" + +# GO terms so close to the root of the biological-process branch that asserting +# them of a microbial community conveys nothing. Each needs a reason, so that +# adding one is a decision rather than a reflex. +_VACUOUS = { + "GO:0008152": "'metabolic process' — true of every organism in the KB", + "GO:0008150": "'biological_process' — the branch root", +} + + +def _walk(node, filename): + """Descriptors under `node`. Module-level so it does not close over the loop + variable — a nested closure here reads `path` from the enclosing scope at + *call* time, which for a generator is after the loop has moved on (ruff + B023). Harmless in this shape, wrong the moment the generator is not drained + immediately.""" + if isinstance(node, dict): + term = node.get("term") + if node.get("preferred_term") and isinstance(term, dict) and term.get("id"): + yield filename, node["preferred_term"], term["id"], term.get("label") + for value in node.values(): + yield from _walk(value, filename) + elif isinstance(node, list): + for value in node: + yield from _walk(value, filename) + + +def _descriptors(corpus: pathlib.Path | None = None): + """Every (file, preferred_term, id, label) biological-process descriptor.""" + for path in sorted((corpus or COMMUNITIES).glob("*.yaml")): + document = yaml.safe_load(path.read_text(encoding="utf-8")) or {} + yield from _walk(document, path.name) + + +def test_no_record_is_annotated_with_a_vacuous_process(): + """The gate.""" + offenders = [ + f"{name}: {preferred!r} -> {identifier} ({_VACUOUS[identifier]})" + for name, preferred, identifier, _label in _descriptors() + if identifier in _VACUOUS + ] + assert offenders == [], ( + "these annotations are true of every community in the KB and so " + "distinguish nothing (#182). Ground the concept in `preferred_term` to " + "a specific current GO term, or drop the descriptor:\n" + + "\n".join(f" {line}" for line in offenders) + ) + + +def test_the_upgraded_terms_are_present_and_correctly_labelled(): + """The seven that were re-grounded rather than deleted. + + Asserts the label travelled with the id: a remap that changed one and not + the other is the exact defect #180 existed to clear, and re-introducing it + here would be ironic. + """ + by_id = {} + for _name, _preferred, identifier, label in _descriptors(): + by_id.setdefault(identifier, set()).add(label) + + assert by_id.get("GO:0009056") == { + "catabolic process" + }, f"GO:0009056 carries unexpected labels: {by_id.get('GO:0009056')}" + assert by_id.get("GO:0071941") == { + "nitrogen cycle metabolic process" + }, f"GO:0071941 carries unexpected labels: {by_id.get('GO:0071941')}" + + +@pytest.mark.parametrize("identifier", sorted(_VACUOUS)) +def test_every_blocked_term_has_a_reason(identifier): + """A blocklist without reasons becomes a place things get added silently.""" + assert _VACUOUS[identifier].strip() + + +def test_the_walk_reaches_the_corpus(): + """An empty walk passes the gate as surely as a clean corpus does.""" + seen = list(_descriptors()) + assert len(seen) > 500, f"only {len(seen)} descriptors walked; the walk is broken" + + +def test_the_gate_can_fire(tmp_path): + """Mutation check, driving the real walk over a record built to offend.""" + corpus = tmp_path / "communities" + corpus.mkdir() + (corpus / "r.yaml").write_text( + "id: CommunityMech:000999\n" + "ecological_interactions:\n" + "- biological_processes:\n" + " - preferred_term: metabolic process\n" + " term:\n" + " id: GO:0008152\n" + " label: metabolic process\n", + encoding="utf-8", + ) + offenders = [d for d in _descriptors(corpus) if d[2] in _VACUOUS] + assert offenders, "the gate found nothing in a record built to contain the defect" + assert offenders[0][1] == "metabolic process" + + +def test_a_specific_process_is_not_flagged(tmp_path): + """Guard against the gate being over-broad. + + A correctly grounded specific term also has `preferred_term == label`; that + is what good curation looks like, not a defect. The gate must key on the + identifier, never on the two strings matching. + """ + corpus = tmp_path / "communities" + corpus.mkdir() + (corpus / "r.yaml").write_text( + "id: CommunityMech:000998\n" + "ecological_interactions:\n" + "- biological_processes:\n" + " - preferred_term: nitrogen cycle metabolic process\n" + " term:\n" + " id: GO:0071941\n" + " label: nitrogen cycle metabolic process\n", + encoding="utf-8", + ) + assert [d for d in _descriptors(corpus) if d[2] in _VACUOUS] == []