Skip to content

Stop a plant genus from carrying a bacterial GTDB lineage (#365) - #436

Merged
realmarcin merged 2 commits into
mainfrom
prokaryote-lineage-gate-365
Aug 6, 2026
Merged

Stop a plant genus from carrying a bacterial GTDB lineage (#365)#436
realmarcin merged 2 commits into
mainfrom
prokaryote-lineage-gate-365

Conversation

@realmarcin

Copy link
Copy Markdown
Contributor

Closes #365.

The defect

NCBITaxon:169215 is the plant genus Bosea (Viridiplantae → Amaranthaceae). Two records used it for the alphaproteobacterium of the same name and carried a GTDB block derived from it:

  • kb/communities/MSC1_Dominant_Core.yamlBosea sp.
  • kb/communities/EcoFAB_Ring_Trial_SynCom17.yamlBosea sp. OAE506

both reading d__Bacteria;p__Pseudomonadota;…;g__Bosea. The KB asserted that a plant genus is a bacterium, in the field that looks most independently sourced.

It came from a name collision, not a typo: gtdb_ground.py's higher-rank path matches on the cleaned label string rather than the id, so "Bosea" resolved to the bacterial genus while the id pointed at the plant.

Invisible to every gate. ncbi_source_id == term.id, so #364's freshness test passes. "Bosea" genuinely is that id's label, so id↔label passes. linkml-validate has nothing to say. And #292's shared-id gate can't help: the id appears once per record, so there is no second organism to disagree with.

The gate

prokaryotic_lineage uses a signal that needs no curation judgement: GTDB classifies only Bacteria and Archaea. An id outside those two domains can never carry a GTDB lineage, which makes this a contradiction rather than a suspicion. Both directions are checked, since both are the same contradiction — a non-prokaryotic id under a prokaryotic lineage (the #365 defect), and an archaeon under d__Bacteria or the reverse. GTDB and NCBI disagree about phyla and genera, which is what is_reclassified records; they do not disagree about domain.

Measured over all 316 records: flags exactly the two known records, nothing else. As with the #292 gate, an id whose domain cannot be resolved is never judged, and a missing NCBITaxon warns on stderr rather than passing silently.

Wired into validate_strict.py (category gtdb_lineage_contradicts_id_domain) with a single-file just validate-gtdb-domain recipe.

The fix

Both records move to NCBITaxon:85413 — the bacterium NCBI renamed Allobosea, which still carries the alias "Bosea Das et al. 1996". Confirmed via its type species: NCBITaxon:53254 "Allobosea thiooxidans" (which the GTDB mapping table still calls Bosea thiooxidans) has parent NCBITaxon:85413. The two genera are homonyms and the bacterial one was renamed.

The interaction participants are corrected too — both records also named the plant id as an interaction target, where there is no lineage for the gate to compare.

The GTDB block itself was always right for the organism, so it is pinned with curated: true + curation_note rather than dropped. The mapping snapshot still carries the pre-rename genus name, so the tool cannot re-derive it from the corrected label. Canaried: --apply on both records reports applied 0 block(s), logs skipping curated NCBITaxon:85413, and leaves the files byte-identical — no drift on the next tool run.

is_reclassified becomes true, which is now the honest value: NCBI says Allobosea, GTDB says Bosea.

Also

NEXT_TASKS.md reconciled — it was three days and 38 merged PRs stale. The new section names the thread these gates belong to (a grounding that is wrong in a way every gate reads as right) and records the method that found all three, since none came from a sweep.

Validation

just qc green. 26 new tests, including the KB-clean sweep, the malformed-input guards (#429's lesson), an adapter-liveness assertion so the sweep can't pass vacuously (#433's lesson), and an end-to-end check that the gate fires through validate-strict with a vendored fixture (#428's lesson).

🤖 Generated with Claude Code

`NCBITaxon:169215` is the plant genus Bosea (Amaranthaceae). Two records used
it for the alphaproteobacterium of the same name and carried a GTDB block
derived from it, so the KB asserted a plant was a bacterium — in the field that
looks most independently sourced.

Nothing could see it. `ncbi_source_id == term.id`, so #364's freshness test
passes; "Bosea" really is that id's label, so id<->label passes; and the id
appears once per record, so #292's shared-id gate has nothing to compare. The
block came from a name collision, not a typo: gtdb_ground.py's higher-rank path
matches the cleaned label string rather than the id.

Add `prokaryotic_lineage`, which needs no curation judgement: GTDB classifies
only Bacteria and Archaea, so an id outside those domains cannot carry a GTDB
lineage at all, and an archaeon under d__Bacteria is the same contradiction.
Measured over the KB it flags exactly the two known records and nothing else.
Wired into validate_strict.py with a `just validate-gtdb-domain` recipe.

Correct both records to NCBITaxon:85413 — the bacterium NCBI renamed
*Allobosea*, aliased "Bosea Das et al. 1996"; its type species
Allobosea thiooxidans confirms the parentage. The ids in the interaction
participants are corrected too, where no lineage exists for the gate to check.

The GTDB block itself was always right for the organism, so it is pinned with
`curated: true` rather than dropped: the mapping snapshot still carries the
pre-rename genus name, so the tool cannot re-derive it from the corrected
label. Canaried — `--apply` skips both and leaves the files byte-identical.

Also reconcile NEXT_TASKS.md, which was three days and 38 merged PRs stale.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown

Network integrity findings

Warnings only — a member with no interaction yet, or a participant matched by ontology id rather than by name, or one on a community-level interaction that resolves to no member. Reported, but does not fail the build.

Network Integrity Audit Report
================================================================================

0 error, 55 warning across 27 records with findings
Only error-severity findings fail the build.

ANME_SRB_Anaerobic_Methanotrophic_Syntrophic_Consortia
--------------------------------------------------------------------------------
  [warning] NAME_MISMATCH: Source 'ANME-1' matches no taxonomy entry by name; resolved to 'ANME-1 (anaerobic methanotrophic archaea, clade 1)' by source_id NCBITaxon:588814
  [warning] NAME_MISMATCH: Target 'Desulfofervidus' matches no taxonomy entry by name; resolved to 'Desulfofervidus (sulfate-reducing bacterial partner of ANME-1)' by target_id NCBITaxon:1902583
  [warning] NAME_MISMATCH: Source 'ANME-2a' matches no taxonomy entry by name; resolved to 'ANME-2a (anaerobic methanotrophic archaea, clade 2a)' by source_id NCBITaxon:588816
  [warning] NAME_MISMATCH: Target 'Seep-SRB1' matches no taxonomy entry by name; resolved to 'Seep-SRB1 (sulfate-reducing bacterial partner of ANME-2a)' by target_id NCBITaxon:213119
  [warning] NAME_MISMATCH: Source 'ANME-2c' matches no taxonomy entry by name; resolved to 'ANME-2c (anaerobic methanotrophic archaea, clade 2c)' by source_id NCBITaxon:3386252
  [warning] NAME_MISMATCH: Target 'Seep-SRB2' matches no taxonomy entry by name; resolved to 'Seep-SRB2 (additional sulfate-reducing bacterial partner)' by target_id NCBITaxon:213118

Total: 6 issues (0 error, 6 warning)

Aalborg_East_Full_Scale_EBPR_Community
--------------------------------------------------------------------------------
  [warning] DISCONNECTED: Taxon 'Bacteroidetes flanking community members' has no interactions
  [warning] DISCONNECTED: Taxon 'Tetrasphaera-related actinobacterial PAOs' has no interactions

Total: 2 issues (0 error, 2 warning)

Bacteroides_Methanobrevibacter_Gnotobiotic_Mouse_Mutualism
--------------------------------------------------------------------------------
  [warning] DISCONNECTED: Taxon 'Desulfovibrio piger comparator' has no interactions

Total: 1 issues (0 error, 1 warning)

BioModels_MODEL2204300001_Kefir_Community_Model
--------------------------------------------------------------------------------
  [warning] DISCONNECTED: Taxon 'Lactobacillus kefiri' has no interactions
  [warning] DISCONNECTED: Taxon 'Lactococcus lactis' has no interactions

Total: 2 issues (0 error, 2 warning)

BioModels_MODEL2405300001_Infant_Gut_HMO_SynCom
--------------------------------------------------------------------------------
  [warning] DISCONNECTED: Taxon 'Bacteroides ovatus' has no interactions
  [warning] DISCONNECTED: Taxon 'Bacteroides vulgatus' has no interactions
  [warning] DISCONNECTED: Taxon 'Bifidobacterium bifidum' has no interactions
  [warning] DISCONNECTED: Taxon 'Bifidobacterium breve' has no interactions
  [warning] DISCONNECTED: Taxon 'Blautia producta' has no interactions
  [warning] DISCONNECTED: Taxon 'Enterococcus faecalis' has no interactions
  [warning] DISCONNECTED: Taxon 'Escherichia coli K-12' has no interactions
  [warning] DISCONNECTED: Taxon 'Lacticaseibacillus rhamnosus' has no interactions
  [warning] DISCONNECTED: Taxon 'Ruminococcus gnavus' has no interactions
  [warning] DISCONNECTED: Taxon 'Streptococcus thermophilus' has no interactions

Total: 10 issues (0 error, 10 warning)

Crucian_Carp_Gut_Disease_Resistance_SynCom
--------------------------------------------------------------------------------
  [warning] UNKNOWN_TARGET: Target taxon 'Aeromonas hydrophila' not found in taxonomy section (community-level scope)

Total: 1 issues (0 error, 1 warning)

Drosophila_FiveSpecies_Gnotobiotic_Gut_Microbiota
--------------------------------------------------------------------------------
  [warning] UNKNOWN_SOURCE: Source taxon 'Drosophila five-species bacterial microbiota' not found in taxonomy section (community-level scope)

Total: 1 issues (0 error, 1 warning)

East_River_Floodplain_Core_Microbiome
--------------------------------------------------------------------------------
  [warning] UNKNOWN_SOURCE: Source taxon 'core floodplain bacteria' not found in taxonomy section (community-level scope)
  [warning] UNKNOWN_SOURCE: Source taxon 'East River floodplain bacteria' not found in taxonomy section (community-level scope)

Total: 2 issues (0 error, 2 warning)

Ensifer_YF2_Sphingobacterium_Y2_Polyethylene_Degrading_Consortium
--------------------------------------------------------------------------------
  [warning] DISCONNECTED: Taxon 'Chryseobacterium sp. MF1' has no interactions

Total: 1 issues (0 error, 1 warning)

GLBRC_UFMP_Fermentation_Community
--------------------------------------------------------------------------------
  [warning] UNKNOWN_SOURCE: Source taxon 'Olsenella (Actinobacteriota)' not found in taxonomy section (community-level scope)
  [warning] NAME_MISMATCH: Target 'Clostridium (Firmicutes)' matches no taxonomy entry by name; resolved to 'Clostridium_B sp. (MAG CLOS1)' by target_id NCBITaxon:1485

Total: 2 issues (0 error, 2 warning)

Hanford_300_Area_Unconfined_Aquifer_Community
--------------------------------------------------------------------------------
  [warning] NAME_MISMATCH: Source 'intrusion-associated Actinobacteria' matches no taxonomy entry by name; resolved to 'Actinobacteria/Actinomycetota aquifer bacteria' by source_id NCBITaxon:201174
  [warning] UNKNOWN_TARGET: Target taxon 'Hanford groundwater bacteria' not found in taxonomy section (community-level scope)
  [warning] UNKNOWN_SOURCE: Source taxon 'aquifer redox guild bacteria and archaea' not found in taxonomy section (community-level scope)

Total: 3 issues (0 error, 3 warning)

High_Solids_Switchgrass_Methanogenic_Microbiome
--------------------------------------------------------------------------------
  [warning] DISCONNECTED: Taxon 'Bacilli' has no interactions

Total: 1 issues (0 error, 1 warning)

KB1_Chlorinated_Ethene_Dechlorinating_Consortium
--------------------------------------------------------------------------------
  [warning] DISCONNECTED: Taxon 'Sporomusa spp. in KB-1' has no interactions

Total: 1 issues (0 error, 1 warning)

Legume_Rhizobia_Mars_Simulant_Symbiosis
--------------------------------------------------------------------------------
  [warning] NAME_MISMATCH: Source 'Sinorhizobium spp. (rhizobial symbionts)' matches no taxonomy entry by name; resolved to 'Sinorhizobium meliloti' by source_id NCBITaxon:382
  [warning] UNKNOWN_TARGET: Target taxon 'Medicago truncatula (host legume)' not found in taxonomy section (community-level scope)

Total: 2 issues (0 error, 2 warning)

Lunar_Martian_Simulant_PGPB_Lettuce_SynCom
--------------------------------------------------------------------------------
  [warning] UNKNOWN_TARGET: Target taxon 'Lactuca sativa (lettuce host)' not found in taxonomy section (community-level scope)
  [warning] UNKNOWN_TARGET: Target taxon 'Lactuca sativa (lettuce host)' not found in taxonomy section (community-level scope)

Total: 2 issues (0 error, 2 warning)

Lunar_Simulant_Phosphate_Solubilizing_Bacteria_Nicotiana
--------------------------------------------------------------------------------
  [warning] UNKNOWN_TARGET: Target taxon 'Nicotiana benthamiana' not found in taxonomy section (community-level scope)

Total: 1 issues (0 error, 1 warning)

Model_Lignocellulose_Formaldehyde_Crossfeeding_Community
--------------------------------------------------------------------------------
  [warning] UNKNOWN_SOURCE: Source taxon 'model lignocellulose consortium members' not found in taxonomy section (community-level scope)
  [warning] UNKNOWN_TARGET: Target taxon 'model lignocellulose consortium members' not found in taxonomy section (community-level scope)

Total: 2 issues (0 error, 2 warning)

Moss_Microbe_Complex_Regolith_Biofertilizer
--------------------------------------------------------------------------------
  [warning] UNKNOWN_SOURCE: Source taxon 'Hypnum plumaeforme (moss host)' not found in taxonomy section (community-level scope)
  [warning] UNKNOWN_TARGET: Target taxon 'Hordeum vulgare (barley model crop)' not found in taxonomy section (community-level scope)

Total: 2 issues (0 error, 2 warning)

ORNL_Clostridium_Desulfovibrio_Geobacter_Trophic_Model
--------------------------------------------------------------------------------
  [warning] UNKNOWN_TARGET: Target taxon 'Desulfovibrio vulgaris Hildenborough and Geobacter sulfurreducens' not found in taxonomy section (community-level scope)
  [warning] UNKNOWN_TARGET: Target taxon 'three-species model community' not found in taxonomy section (community-level scope)

Total: 2 issues (0 error, 2 warning)

Oak_Ridge_FRC_Uranium_Nitrate_Groundwater_Community
--------------------------------------------------------------------------------
  [warning] UNKNOWN_TARGET: Target taxon 'other groundwater bacteria' not found in taxonomy section (community-level scope)

Total: 1 issues (0 error, 1 warning)

PET_Artificial_FourSpecies_Degradation_Consortium
--------------------------------------------------------------------------------
  [warning] UNKNOWN_TARGET: Target taxon 'engineered PETase/MHETase and TPA-utilization members' not found in taxonomy section (community-level scope)

Total: 1 issues (0 error, 1 warning)

PMI_Variovorax_Thermotolerance_Collection
--------------------------------------------------------------------------------
  [warning] UNKNOWN_SOURCE: Source taxon 'Variovorax' not found in taxonomy section (community-level scope)
  [warning] UNKNOWN_TARGET: Target taxon 'Arabidopsis thaliana' not found in taxonomy section (community-level scope)

Total: 2 issues (0 error, 2 warning)

Rice_Duckweed_Bacillus_SynCom
--------------------------------------------------------------------------------
  [warning] UNKNOWN_SOURCE: Source taxon 'Bacillus SynCom' not found in taxonomy section (community-level scope)
  [warning] UNKNOWN_TARGET: Target taxon 'Rhizoctonia solani' not found in taxonomy section (community-level scope)

Total: 2 issues (0 error, 2 warning)

Saanich_Inlet_OMZ_Redox_Gradient_Community
--------------------------------------------------------------------------------
  [warning] UNKNOWN_SOURCE: Source taxon 'Saanich Inlet redox-gradient microorganisms' not found in taxonomy section (community-level scope)

Total: 1 issues (0 error, 1 warning)

Shewanella_Geobacter_Exoelectrogenic_Biofilm_Community
--------------------------------------------------------------------------------
  [warning] UNKNOWN_TARGET: Target taxon 'anode-associated biofilm community' not found in taxonomy section (community-level scope)
  [warning] UNKNOWN_TARGET: Target taxon 'anode' not found in taxonomy section (community-level scope)

Total: 2 issues (0 error, 2 warning)

Sulfide_Spring_Autotrophic_CPR_Biofilm
--------------------------------------------------------------------------------
  [warning] DISCONNECTED: Taxon 'Beggiatoa' has no interactions

Total: 1 issues (0 error, 1 warning)

Thermophilic_Lignocellulose_Composting_SynCom_Biosanitization
--------------------------------------------------------------------------------
  [warning] UNKNOWN_TARGET: Target taxon 'Pseudomonas aeruginosa' not found in taxonomy section (community-level scope)

Total: 1 issues (0 error, 1 warning)

The full report is attached to the workflow run as an artifact.

…i_domain

The review refuted a claim in this gate's own docstring. It asserted that NCBI
and GTDB never disagree about Bacteria vs Archaea, and flagged the disagreement
as a contradiction. They do disagree: 8 rows of this repo's own NCBI2GTDB.tsv.gz
do, and the gate fired on every block gtdb_ground.py would build from them
(#437). A gate that rejects its own grounding tool's output is worse than no
gate, so that arm is gone. What remains is the prokaryote-only rule, which has
no counterexamples because GTDB models no other domain: a taxon that is provably
a eukaryote or a virus can carry no GTDB block at all, whatever the block says.

That also makes the check independent of parsing the lineage, which is what the
removed arm needed.

Reuse ncbi_domain instead of re-deriving it (#438). This module had shipped its
own copy of the adapter, the four domain roots and the ancestor query — the same
lookup #393 already added, differing only in return type. Two copies is how two
gates come to disagree about one taxon; `outside_gtdb_scope` already said
exactly what this needed.

Fix a crash of the same class as #429: `gtdb_lineage` has no `range` in the
schema, so a YAML list is schema-valid, and `(lineage or "").split` raised
AttributeError out of the worker, aborting all 316 files and writing no TSV.

Walk interaction participants too (#439). `source_taxon`/`target_taxon` share
`taxon_term`'s range, so a GTDB block is schema-valid there — and both defective
records named the plant id in exactly that position.

Test fixes (#440): a wrong organism name in the file whose subject is wrong
organism names (NCBITaxon:2172 is Methanobrevibacter, not Methanosarcina); the
once-only warning flag leaked across the session and the warning had no test;
and the legitimate cases were conflated with the merely-unjudgeable ones, which
now assert their domain really is unresolvable rather than only that the gate
stayed quiet.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@realmarcin

Copy link
Copy Markdown
Contributor Author

Review round 2 — findings addressed

Three independent adversarial passes (gate correctness, the taxonomic call, tests/enforcement). Six issues filed: #437#442. Four fixed here; two deferred.

The review refuted a claim in this gate's own docstring — #437

It asserted that NCBI and GTDB never disagree about Bacteria vs Archaea, and treated a disagreement as a contradiction. They do disagree: 8 rows of this repo's own NCBI2GTDB.tsv.gz do (5 Archaea→Bacteria, 3 the other way), and building the block gtdb_ground.py would emit for each, the gate fired on all 8. A gate that rejects its own grounding tool's output is worse than no gate, so that arm is removed.

What remains is the half with no counterexamples, because GTDB models no other domain: a taxon that is provably a eukaryote or a virus can carry no GTDB block at all, whatever the block says. That also makes the check independent of parsing the lineage — which only the removed arm needed.

Also fixed

Plus a wrong PR number in the NEXT_TASKS.md table (#426#436).

Deferred

What the review confirmed rather than broke

The taxonomic call stands: 85413 is genus rank like 169215, the only node labelled exactly "Bosea" is the plant, and 34/34/1.0 reproduces exactly for the bacterium via resolve_higher('bosea') — the plant contributes zero genomes, so ncbi_source_id: NCBITaxon:85413 is now truthful where the old value was false. resolve_higher('allobosea') → None confirms the curation note's claim. Enforcement is real and coverage is not hollow: 716 GTDB-bearing taxa, 716 resolve, 0 unjudged, 0 findings.

just qc green.

@realmarcin
realmarcin merged commit 8b98543 into main Aug 6, 2026
7 checks passed
@realmarcin
realmarcin deleted the prokaryote-lineage-gate-365 branch August 6, 2026 03:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

A GTDB block sits on a plant taxon: NCBITaxon:169215 is Bosea (Amaranthaceae), used for a bacterium

1 participant