Resolve both duplicate-key records; empty the waiver list (#289) - #296
Merged
Conversation
Two records carried a duplicate mapping key. PyYAML keeps the last of a pair and reports nothing, so in each case one curated value was being discarded at parse time while every gate stayed green. **Geobacter/Clostridium — a retracted claim was winning.** The evidence item for PMID:28287150 had two `explanation` values. Git shows why: PR #262 ("Re-scope 000031 away from DIET: the contact/nanowire mechanism was never sourced") *inserted* its correction but left the old line in place as context, so the parsed value was the very claim #262 set out to retract — its re-scoping was inert in the data while looking applied in the file. Which one survives is settled by the record itself, not by preference: the surviving text must be the one beginning "PARTIAL -", because the item's own `supports: PARTIAL` agrees with it, and because that text explicitly describes the other as the wording it replaced. The stale line is deleted. **Trichodesmium/Alteromonas — an orphaned note, not a redundant one.** The iron(2+) metabolite had two `notes`; the ROS one was winning, so the note explaining the iron CHEBI grounding was lost. But the ROS note is not surplus: the same interaction cites "detoxification of reactive oxygen species" as evidence, and the record already curates reactive oxygen species as a compound (CHEBI:26523) with its own relevance statement. The interaction's metabolite list was simply missing that third entry. So rather than delete a curated statement, the note is given its proper home — a `reactive oxygen species` metabolite grounded to the CHEBI term the record already uses — and the iron note is restored to iron(2+). KNOWN_DUPLICATES is now empty: zero duplicate keys repo-wide. The comment above it explains the bar for adding one back. Filed separately, not fixed here: #295 — the same DIET-background snippet is still cited as `supports: SUPPORT` with no explanation in this record's second interaction, so #262's judgement was applied to one occurrence and not the other. 589 tests pass; both records pass schema and id<->label validation. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Network integrity findingsReporting only — this check does not fail the build (see issue #273). The full report is attached to the workflow run as an artifact. |
There was a problem hiding this comment.
Pull request overview
This PR resolves #289 by eliminating two duplicate YAML mapping keys that were causing PyYAML to silently discard curated content, and then tightening the duplicate-key guard by emptying the waiver list (KNOWN_DUPLICATES) so the whole repo must now remain duplicate-free.
Changes:
- Remove the stale duplicate
explanationentry in the Geobacter/Clostridium coculture record so the intendedPARTIALexplanation is the one that actually parses. - Restore the intended
iron(2+)grounding note and give the ROS detoxification note its correct home by adding areactive oxygen species (CHEBI:26523)metabolite entry in the Trichodesmium/Alteromonas record. - Set
KNOWN_DUPLICATESto an explicitly typed empty dict so the duplicate-key test suite runs with zero waivers.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| tests/test_no_duplicate_yaml_keys.py | Removes the two fixed records from the waiver list by setting KNOWN_DUPLICATES to {}, making the duplicate-key guard strict repo-wide. |
| kb/communities/Trichodesmium_Alteromonas_Marine_Consortium.yaml | Splits previously-colliding notes into two metabolite entries by adding ROS as its own metabolite with CHEBI:26523. |
| kb/communities/Geobacter_Clostridium_Interspecies_Electron_Transfer_Coculture.yaml | Deletes the duplicate explanation line so the “PARTIAL - … generic background …” explanation is the single surviving value. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
realmarcin
added a commit
that referenced
this pull request
Aug 8, 2026
…297) (#495) #296 rehomed an orphaned note onto a `metabolites` entry reading "Reactive oxygen species detoxification is an inferred interaction process" - a note that says *process* while sitting on a compound. #297 recorded that as a modelling question rather than a defect, and left the choice open: compound, process, or both. Both, because the record already asserted both in prose. CHEBI:26523 is the species; GO:0098869 `cellular oxidant detoxification` is what is done to it. Grounding the process adds no new claim - it makes the claim the note already made machine-readable. GO:0098869 chosen over two nearer-looking terms: * GO:0141082 "symbiont-mediated detoxification of host-generated reactive oxygen species" would assert a host-symbiont relation this consortium does not have - Trichodesmium and Alteromonas are a consortium, and the ROS is photosynthetic rather than host-generated. * GO:0061692 is hydrogen peroxide only, where the record says ROS generally. GO:0098869's definition - "reduces or removes the toxicity of superoxide radicals or hydrogen peroxide" - is the general oxidant case and matches. Verified non-obsolete, canonical label; the id-label gate goes 6094 -> 6095 OK_CANONICAL with no exceptions added. #297 also asks whether the other two "inferred interaction axis" metabolites - phosphate and iron(2+) - want the same treatment. Checked, and the answer is no, for a reason worth recording: clean GO terms exist (GO:0006826 iron ion transport, GO:0006817 phosphate ion transport) but both are *cellular transport* processes, while the record's notes describe ecological *acquisition axes*. Grounding them would introduce a claim the record does not make - the same over-claiming that rules out GO:0141082 above. The ROS case is different precisely because its note already named the process. The new entry inherits the interaction's COMPUTATIONAL evidence; this is the inferred axis the source names, not a measured one. docs/ regenerated - the #477 gate caught the stale page, which is what it is for. 2364 passed, validate-strict 0 errors.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #289.
Two records carried a duplicate mapping key. PyYAML keeps the last of a pair and reports nothing, so in each case one curated value was being discarded at parse time while every gate stayed green.
Geobacter/Clostridium — a retracted claim was winning
The PMID:28287150 evidence item had two
explanationvalues. Git shows how: PR #262 ("Re-scope 000031 away from DIET: the contact/nanowire mechanism was never sourced") inserted its correction but left the old line in place as unchanged context —snippet: Direct interspecies electron transfer (DIET) mechanism has been recently characterised with Geobacter species which couple the electron balance with other species through physical contacts + explanation: PARTIAL - this is the paper's generic background about Geobacter species in + prior literature, NOT a finding about this coculture. It was previously cited here as + demonstrating contact-mediated transfer between these two organisms, which it does not. explanation: Establishes the mechanism of electron transfer via physical contact— so the value that actually parsed was the very claim #262 set out to retract. Its re-scoping was inert in the data while looking applied in the file.
Which value survives is settled by the record, not by preference. The surviving text must be the one beginning
PARTIAL -: the item's ownsupports: PARTIALagrees with it, and that text explicitly describes the other as the wording it replaced ("It was previously cited here as demonstrating contact-mediated transfer … which it does not"). The stale line is deleted.Trichodesmium/Alteromonas — an orphaned note, not a redundant one
The
iron(2+)metabolite had twonotes, and the ROS one was winning, so the note explaining the iron CHEBI grounding was being lost.The obvious fix — delete the loser — would have been wrong. The ROS note is not surplus: the same interaction cites "detoxification of reactive oxygen species" as evidence, and the record already curates reactive oxygen species as a compound (
CHEBI:26523) with its own relevance statement. The interaction's metabolite list was simply missing that third entry, and the note had nowhere to live.So the note is given its proper home — a
reactive oxygen speciesmetabolite grounded to the CHEBI term the record already uses — and the iron note is restored toiron(2+). Both curated statements are preserved rather than one being dropped.Waiver list is now empty
KNOWN_DUPLICATESis{}: zero duplicate keys repo-wide. The comment above it states the bar for adding one back — an issue reference, and only when the choice genuinely cannot be made in the same change.Filed, not fixed here
#295 — the same DIET-background snippet is still cited as
supports: SUPPORTwith no explanation in this record's other interaction, so #262's judgement was applied to one occurrence and not the other. The record now simultaneously holds that the snippet is generic background establishing nothing about this coculture, and that it fully supports a second interaction. That is asupports-level curation call rather than duplicate-key data loss, and it wants the curator who made #262's decision.(Worth noting the missing
explanationis not itself the defect — 84 of 1300 evidence items repo-wide have none.)Verification
black,ruff,mypyclean.CHEBI:26523term/label pair.🤖 Generated with Claude Code
Review pass
Checked the ROS placement rather than assuming it. The schema defines the slot as "Metabolites involved in the interaction" — not exchanged — so ROS qualifies, and the entry reuses
CHEBI:26523, a grounding the record had already committed to for this compound. But the note itself calls ROS detoxification a process, andEcologicalInteractionhas abiological_processesslot for exactly that.I checked whether that slot was the better home and deliberately did not use it: this interaction has no
biological_processesat all, and the record grounds nothing to GO anywhere. Choosing a GO term would mean introducing a new grounded claim on a record whose sourcing is explicitly inferential (evidence_source: COMPUTATIONAL, "inferred interaction axis"), which is not what a duplicate-key repair should do. Recorded as #297 so the choice is deliberate rather than inherited.Confirmed the deletion was safe. The Geobacter line removed is the retracted claim itself, and git history preserves it; nothing else in the record cites it.
Confirmed the guard is not merely passing vacuously.
KNOWN_DUPLICATESis empty and the parametrised test still runs across all 305 records, withtest_there_are_community_files_to_checkguarding against the glob silently matching nothing.