Skip to content

Audit the isolates the validators already cover (#350, #521) - #520

Open
realmarcin wants to merge 2 commits into
mainfrom
isolates-network-audit-350
Open

Audit the isolates the validators already cover (#350, #521)#520
realmarcin wants to merge 2 commits into
mainfrom
isolates-network-audit-350

Conversation

@realmarcin

@realmarcin realmarcin commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Most of #350 was already fixed

Reconciled before starting. The issue overstates what remains:

#350 claimed Today
validate_strict.DEFAULT_ROOTS is kb/communities only already includes data/isolates
validate-all is communities-only already iterates data/isolates/*.yaml
validate-terms-all is communities-only already iterates data/isolates/*.yaml
validate-references-all is communities-only already iterates data/isolates/*.yaml
all 4 isolates fail linkml-term-validator --labels no longer reproduces — clean on all four
the network auditor is communities-only still true — this PR

What was actually left

NetworkIntegrityAuditor.__init__ kept communities_dir: Path = Path("kb/communities"), so the 12 interactions across 3 isolate records were never audited. And the workflow didn't list data/isolates in its trigger paths — both halves missing: a gate that never fired, in front of a job that wouldn't have covered it anyway.

All 12 are clean, which is why this went unnoticed. The gap produced no findings either way and would have stayed invisible until an isolate gained a dangling reference.

The fix

default_record_roots() lives in communitymech.paths; the auditor and validate_strict source it rather than restating it. A single directory is still accepted — NetworkIntegrityAuditor(tmp_path) keeps meaning what it did.

The canary earned its keep, twice

First: after the auditor was fixed, audit-network still reported 312 records. Click was passing "kb/communities" explicitly on every invocation. The fix looked wired and wasn't — the exit code never changed, only the count did.

Second (#521): review then found BatchReporter doing the identical thing — same literal default, passed explicitly to the auditor, constructed as bare BatchReporter() in four places in cli.py. So the LLM repair path could not see records the audit now reports on: two halves of one workflow disagreeing about which records exist. It now takes the roots off the auditor rather than recomputing them, so they cannot disagree by construction.

That second find is why test_no_constructor_hardcodes_the_old_root is written against the shape — any communities_dir default hardcoding a record directory — rather than naming the known offenders. A test checking one of two identical sites is exactly how the second one survives.

Visualisation paths (browser_export, render, umap_generator) are exempt by name with the reason attached: whether isolates belong in a UMAP or the browser UI is a modelling question (#519), and widening a coverage test would answer it silently.

Checks

  • uv run communitymech audit-network — 316 records (312 + 4), was 312
  • just validate-strict — 316 files, exit 0
  • just lint — exit 0
  • uv run pytest tests/ — 2382 passed, 16 skipped
  • just check-network-quality — exit 1, unchanged: that is the pre-existing "warnings" classification and is 1 with communities alone too; the workflow step maps 1 → warnings and always exits 0
  • Mutation-checked four ways, each by reverting, running, restoring: restoring the click default reddens the CLI test; restoring BatchReporter's literal reddens the constructor test; removing the trigger path reddens the workflow test; injecting a bogus participant into an isolate is now reported by the audit and was silently ignored before

Closes #350, closes #521. Found #519.

🤖 Generated with Claude Code

Most of #350 had been fixed since it was filed: validate_strict's roots,
validate-all, validate-terms-all and validate-references-all all cover
data/isolates today, and the four term-validator failures it measured no longer
reproduce. What remained is the network audit, which kept its own
`Path("kb/communities")` default and so never looked at the 12 interactions in
data/isolates.

They are clean, and that is the point — the gap produced no findings either way
and would have stayed invisible until an isolate gained a dangling reference.
The workflow did not even trigger on data/isolates, so both halves were missing:
a gate that did not fire, in front of a job that would not have covered it.

`default_record_roots()` in communitymech.paths is now the one list; the auditor
and validate_strict source it rather than restating it. A single directory is
still accepted, since callers and tests pass one deliberately.

The canary earned its keep. After the auditor was fixed the audit still reported
312 records, because click passed "kb/communities" explicitly on every
invocation — the fix looked wired and was not, and only running it and reading
the count showed that. `test_the_cli_does_not_override_the_default` exists for
exactly that. It is scoped to audit_network's decorators: generate-umap takes
the same option name, but whether isolates belong in a visualisation is a
modelling question, filed as #519 rather than settled by widening a test.

Mutation-checked: restoring the click default reddens the CLI test, removing the
trigger path reddens the workflow test, and injecting a bogus participant into
an isolate is now reported by the audit.

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

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.

Review of this PR found BatchReporter doing exactly what the click option did:
`communities_dir: Path = Path("kb/communities")`, passed to the auditor
explicitly, so widening the auditor's default did nothing for it. cli.py builds
it as bare `BatchReporter()` in four places, so the literal is what ran every
time — meaning the LLM repair path could not see records the audit now reports
on. Two halves of one workflow disagreeing about which records exist.

It now takes the roots back off the auditor rather than recomputing them, so
they cannot disagree by construction.

The test that caught it is written against the shape rather than the two known
names: any `communities_dir` default that hardcodes a record directory fails.
A check naming only the click option is how the second site survived in the
first place. Visualisation paths are exempt by name with the reason attached —
whether isolates belong in a UMAP or the browser UI is #519, and widening a
coverage test would answer it silently.

Mutation-checked: restoring the literal reddens it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@realmarcin realmarcin changed the title Audit the isolates the validators already cover (#350) Audit the isolates the validators already cover (#350, #521) Aug 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant