Skip to content

State the PATHWAY vs BIOLOGICAL_PROCESS rule, then apply it (#356) - #392

Merged
realmarcin merged 3 commits into
mainfrom
fix/356-tranche3-pathway-rule
Aug 16, 2026
Merged

State the PATHWAY vs BIOLOGICAL_PROCESS rule, then apply it (#356)#392
realmarcin merged 3 commits into
mainfrom
fix/356-tranche3-pathway-rule

Conversation

@realmarcin

Copy link
Copy Markdown
Contributor

Third #356 tranche, and the first needing a rule rather than a lookup. Tranche 1 was decided by the schema naming the nodes as its own examples; tranche 2 by #352's already-settled "a protein is not its activity". This one had neither — PATHWAY is "a pathway or pathway-like mechanism" and BIOLOGICAL_PROCESS is "a biological process", which decides nothing.

The descriptions didn't decide it either, unlike tranche 2 where the typings visibly described different things. Here they describe the same thing in the same words — carotenoid_biosynthesis is "Enzymatic pathway producing carotenoid pigments" under BIOLOGICAL_PROCESS.

The grounding could not decide it, and that's the finding

The standing doctrine here is that the grounding settles typing arguments (#352, #360, #382). It can't, because GO has no pathway branch.

All ten families ground to a GO biological_processGO:0019491, GO:0022900, GO:0009767, GO:0016117, GO:0006113, GO:0006119, GO:0006636, GO:0002047 — and five carry the identical CURIE under both typings. Even the METPO groundings sit under METPO:1000060 "metabolism", itself defined as "A biological process that maintain life in an organism".

Read literally, the grounding says all of these are processes and PATHWAY shouldn't exist.

But PATHWAY isn't vestigial — the same shape as #352's CAPACITY finding. Twelve CURIEs corpus-wide are typed PATHWAY and never BIOLOGICAL_PROCESS, and they're the canonical named routes: GO:0006099 (TCA cycle), GO:0006097 (glyoxylate cycle), GO:0019253 (Calvin cycle). The distinction is real; GO just doesn't draw it.

So: a stated convention, written into the playbook

type test
PATHWAY a named route whose steps you could enumerate — TCA cycle, ectoine biosynthesis (lysC/asd/ectB/ectA/ectC), the respiratory chain (nuo, cyo, ndh, sdh)
BIOLOGICAL_PROCESS everything else — a strategy (salt_in_strategy), a reaction class (amino_acid_decarboxylation), or a class of routes (fermentation)

It goes in docs/CURATION_PLAYBOOK.md because an unwritten convention just re-splits.

The majority is not the rule. It types ectoine_biosynthesis and carotenoid_biosynthesis as PATHWAY against their majorities (4:2 and 5:1) — both are named biosynthetic routes with their enzymes listed in the corpus's own descriptions. A convention that only ratified the commoner typing wouldn't be one, and I'd rather you check those two than the eight that agreed.

INCONSISTENT_NODE_TYPE   199 -> 148     the 51 predicted, 0 new findings

Found and filed rather than fixed

fermentation in chemoorganoheterotrophic.yaml is grounded METPO:1000845 = Acetogenesis, a different concept (#391). Left untouched deliberately: retyping a node while carrying a wrong CURIE along unchanged would make it look reviewed. It also exposes a gap — one node_id with two different groundings across records is undetected, the grounding analogue of #356.

just qc green · 540 tests pass · ruff clean · history record per #325.

Third burn-down tranche, and the first needing a RULE rather than a lookup.
Tranche 1 was decided by the schema naming the nodes as its own examples;
tranche 2 by #352's already-settled "a protein is not its activity". This one
had neither: PATHWAY is "a pathway or pathway-like mechanism" and
BIOLOGICAL_PROCESS is "a biological process", which decides nothing.

The descriptions did not decide it either — unlike tranche 2, where the two
typings visibly described different things. Here they describe the same thing in
the same words: carotenoid_biosynthesis is "Enzymatic PATHWAY producing
carotenoid pigments" under BIOLOGICAL_PROCESS and "Biosynthetic PATHWAY
producing carotenoid pigments" under PATHWAY.

THE GROUNDING COULD NOT DECIDE IT, and that is the finding worth keeping,
because the standing doctrine here is that the grounding settles typing
arguments. GO HAS NO PATHWAY BRANCH. All ten families ground to a GO
biological_process — GO:0019491, GO:0022900, GO:0009767, GO:0016117, GO:0006113,
GO:0006119, GO:0006636, GO:0002047 — and five carry the IDENTICAL CURIE under
both typings. Even the METPO groundings sit under METPO:1000060 "metabolism",
defined as "A biological process that maintain life in an organism". Read
literally, the grounding says all of these are processes and PATHWAY should not
exist.

BUT PATHWAY IS NOT VESTIGIAL — the same shape as #352's CAPACITY finding. Twelve
CURIEs corpus-wide are typed PATHWAY and never BIOLOGICAL_PROCESS, and they are
the canonical named routes: GO:0006099 (TCA cycle), GO:0006097 (glyoxylate
cycle), GO:0019253 (Calvin cycle). The distinction is real; GO just does not
draw it.

So the rule is a stated convention, and it goes in docs/CURATION_PLAYBOOK.md
because an unwritten one simply re-splits:

    PATHWAY             a NAMED route whose steps you could ENUMERATE
    BIOLOGICAL_PROCESS  everything else — a strategy, a reaction class, or a
                        process with no canonical step list

THE MAJORITY IS NOT THE RULE. It types ectoine_biosynthesis and
carotenoid_biosynthesis as PATHWAY AGAINST their majorities (4:2 and 5:1), both
being named biosynthetic routes with their enzymes listed in the corpus's own
descriptions (lysC/asd/ectB/ectA/ectC). A convention that only ratified the
commoner typing would not be one.

    INCONSISTENT_NODE_TYPE   199 -> 148     the 51 predicted, 0 new findings

FOUND AND FILED RATHER THAN FIXED: fermentation in chemoorganoheterotrophic.yaml
is grounded METPO:1000845, which is ACETOGENESIS — a different concept (#391).
Left untouched deliberately: retyping a node while carrying a wrong CURIE along
unchanged would make it look reviewed.

540 tests pass - ruff clean.

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

@culturebot-reviewer culturebot-reviewer Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The rule is sound and the reasoning behind it is the most useful part of the PR — "GO has no pathway branch, so the grounding cannot settle this one" is worth having written down, and stating a convention rather than ratifying the majority is the right call. I checked the ten families: every one is now internally consistent, the majority counts quoted in each curation event are correct, the 51 removed baseline rows are exactly the sum of the ten families' occurrence counts, and the 3 added rows are re-keyed UNREACHABLE_FROM_TRAIT details (the type name is in the detail string), not new findings. METPO:1000845 is indeed acetogenesis — deferring that one to #391 rather than fixing it in passing is the right instinct.

Two things need changing.

🟡 The encodes edge in green_pigmented.yaml now violates its own predicate's declared range.

data/traits/morphology/green_pigmented.yaml:45 retypes phenazine_biosynthesis to PATHWAY. The edge at line 91 — phz_operons --encodes--> phenazine_biosynthesis, predicate_id: METPO:2007813 — is gated in mappings/predicate_grounding.tsv to object_types = GENE_OR_PROTEIN|BIOLOGICAL_PROCESS|ORGANELLE. PATHWAY is not in that set, so the corpus now carries a grounded edge the repo's own gate would have refused.

Nothing will catch it. ground_causal_predicates.py:162 returns early on any edge that already has a predicate_id, so the gate is only ever consulted at first grounding; audit_causal_graphs.py has no defect class for it, and audit_predicate_domains.py treats PATHWAY and BIOLOGICAL_PROCESS alike in ACTIVITY_NODE_TYPES, so it sees nothing either. This is the one edge in the corpus where the retyping crosses an asymmetric gate — I checked produces, encodes, reduces, powers and transports against every retyped node in both directions, and this is the only hit.

The fix is probably to add PATHWAY to that row's object_types: the v9 proposal note for METPO:2007813 already describes its objects as "a protein COMPLEX or a biosynthetic PROCESS", which is what this edge is. Retyping the node back would also work. Either way it should be a deliberate decision in this PR, since it is the same question #352 settled — whether the typing is compatible with what the record's predicates already assert.

(Related, not this PR's problem: white_pigmented.yaml:37 staphyloxanthin_biosynthesis is BIOLOGICAL_PROCESS and is also the object of an encodes edge. It is a named biosynthetic route by the new rule, so whoever applies the rule to it next will land on the same gate.)

🟡 The rule creates a new same-label split at ph_delta_mid3.yaml:81.

aa_decarboxylation there carries the label amino-acid decarboxylation pathways — byte-identical to ph_optimum_mid1.yaml:60, which this PR moves to BIOLOGICAL_PROCESS. Both were PATHWAY before; now they disagree. Its description, "Decarboxylation pathways that consume protons and store energy as PMF", is a reaction class under the rule you just wrote, so the intended type is clear.

The audit cannot see this because it keys on node_id and these two differ, and TARGET in migrate_pathway_vs_process.py is keyed the same way — so the tranche skipped it. Adding aa_decarboxylation to TARGET closes it. Worth noting that ph_growth_preference.yaml gets this right by accident: it holds both amino_acid_decarboxylase_systems (PATHWAY) and amino_acid_decarboxylation (BIOLOGICAL_PROCESS), which is exactly the two-node_ids case the playbook describes.

🔵 Two smaller notes, both optional.

The same node_id-keyed scope leaves other pre-existing contradictions with the new rule — metabolism/photosynthesis.yaml:64 electron_transport (BIOLOGICAL_PROCESS) against anoxygenic_photosynthesis.yaml:48 photosynthetic_etc (PATHWAY) under the same label, and pyoverdine_biosynthesis sitting as BIOLOGICAL_PROCESS next to the now-PATHWAY phenazine_biosynthesis inside one graph. Leaving them is defensible now that the rule is written down; a follow-up issue on the label axis would be the natural companion to the grounding-axis gap you filed as #391.

The per-file curation_history rationale quotes a description from a different record — acidotolerant.yaml quotes neutrophilic.yaml's wording, and halotolerant/nacl_delta_low/slightly_halophilic quote euryhaline's five-step description. All real corpus text, and the ectoine ones say "the corpus's own description" so they read correctly; the amino_acid_decarboxylation one has no such attribution and reads as if it were quoting the record it sits in.

History record under history/infrastructure/pathway-vs-process/ is present and the pages are regenerated consistently.

Both from the dispatched review of this PR, and the first is a defect this
tranche introduced.

#393 — retyping phenazine_biosynthesis to PATHWAY put
`phz_operons -encodes-> phenazine_biosynthesis` (METPO:2007813) outside its own
gate: object_types was GENE_OR_PROTEIN|BIOLOGICAL_PROCESS|ORGANELLE. Nothing
would have caught it. ground_causal_predicates skips any edge that already has a
predicate_id, so the gate is consulted ONLY at first grounding; audit_predicate_domains
treats PATHWAY and BIOLOGICAL_PROCESS alike; audit_causal_graphs has no defect
class for it. A write-time gate with no read-time counterpart.

Widened rather than retyped back, because the row's own note already describes
its objects as "a protein COMPLEX or a biosynthetic PROCESS" — a named
biosynthetic route is what the playbook now calls a PATHWAY — and because
object_types was written DESCRIPTIVELY ("admits the three node types actually
observed"), making it a snapshot of the corpus rather than a statement about the
term. The node's type follows the rule; the gate was the stale half.

AND THE SAME CLASS WAS ALREADY ON MAIN. Running the check #393 asks for across
the whole corpus turned up one more: #382 retyped proton_motive_force to STATE
(the schema's own STATE example) and thereby put ph_optimum_high.yaml's
`terminal_oxidases -produces-> proton_motive_force` outside METPO:2007800's
object_types. That shipped, undetected, in a PR of mine that is already merged.
STATE added, with the reasoning recorded in the row: an enzyme complex
generating a proton gradient is production in exactly the agent-brings-into-
existence sense the row already demands of its subject.

    grounded edges violating their predicate's declared types:  1 -> 0

(The first run of that check said 3385, because I treated the `*` wildcard as a
literal type name. Fixed before believing it.)

#394 — aa_decarboxylation in ph_delta_mid3.yaml carries a BYTE-IDENTICAL label
to amino_acid_decarboxylation ("amino-acid decarboxylation pathways") under a
shorter id, so retyping only the long-named one would have opened a fresh split
between two ids nothing compares — INCONSISTENT_NODE_TYPE and this migration's
TARGET both key on node_id. Added to TARGET. It is the label axis of the same
problem #356 tracks on the type axis and #391 on the grounding axis.

Also took the reviewer's attribution note: the amino_acid_decarboxylation
rationale quoted acidotolerant.yaml's wording without saying so, reading as if
it quoted the record it sits in.

540 tests pass - ruff clean.

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

@culturebot-reviewer culturebot-reviewer Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The rule is sound and the application checks out. I verified independently:

  • All 11 families are now single-typed corpus-wide — no residual PATHWAY/BIOLOGICAL_PROCESS split on any of the retyped node_ids.
  • The baseline diff is 51 INCONSISTENT_NODE_TYPE removals plus 4 pre-existing UNREACHABLE_FROM_TRAIT rows whose type= substring changed; each of the 4 has a matching deletion. Nothing new was baselined.
  • The #391 note is accurate: data/embeddings/metpo_to_kgm_node.tsv gives METPO:1000845 = Acetogenesis, METPO:1002005 = Fermentation, METPO:1000060 = metabolism. Leaving it alone and filing it was the right call.
  • Both gate widenings are real, not convenient. ph_optimum_high.yaml does have terminal_oxidases -produces-> proton_motive_force with proton_motive_force typed STATE, and phz_operons -encodes-> phenazine_biosynthesis is the only encodes edge this tranche pushed to PATHWAY. I checked the other grounded edges touching retyped nodes; the rest land on *-gated predicates or on has output, which already admits PATHWAY.
  • Pages and reports regenerate consistently; the history record follows the tranche-1/2 shape.

Two things to fix, both in the quoting.

🟡 salinity_phenotype_with_numerical_limits.yaml:254 quotes a different record's description as this record's. The new curation_history reads "A STRATEGY, not a route: 'Osmoadaptation by intracellular accumulation of inorganic ions'." That string is haloalkaliphilic.yaml:65's node description (truncated before (e.g. K+)). This record's own description is "Osmoadaptation by accumulating high intracellular salt, mainly potassium." This is exactly the defect #394's attribution note was about; the note was applied to amino_acid_decarboxylation only, and salt_in_strategy has the same shape. Either attribute the quote or quote this record.

🟡 The #394 attribution fix landed in the script but not in the corpus — and names the wrong file. scripts/migrate_pathway_vs_process.py now says "The corpus describes it that way in acidotolerant.yaml -- 'Enzyme-catalyzed decarboxylation reaction that consumes cytoplasmic protons'". That string is neutrophilic.yaml:85's description. acidotolerant.yaml:63 says "Proton-consuming amino-acid decarboxylation (e.g. glutamate/Gad system) that consumes intracellular H+" — not the quoted wording. Separately, acidotolerant.yaml:257 and ph_optimum_mid1.yaml still carry the pre-#394 text with no attribution at all, so the shipped records read the way the reviewer objected to, and the script no longer reproduces the records it wrote. Fix the filename to neutrophilic.yaml and re-run so the data matches.

🔵 The carotenoid_biosynthesis rationale asserts "every description calls it one", and that sentence is written into red_pigmented.yaml, whose description is "Phytoene synthase condenses two GGPP to phytoene, then desaturation/isomerization yields lycopene" — the label says pathway, the description doesn't. Same quoting-precision class as the two above; optional.

Advisory: the history record predates the second commit, so the two predicate_grounding.tsv widenings and the aa_decarboxylation addition aren't in its details.

Three findings from the second dispatched review, all one habit: the rationale
strings quote corpus text as if it were the host record's own.

- salt_in_strategy's rationale quoted haloalkaliphilic.yaml's wording
  ("Osmoadaptation by intracellular accumulation of inorganic ions") into
  salinity_phenotype_with_numerical_limits.yaml, whose own description is
  "Osmoadaptation by accumulating high intracellular salt, mainly potassium".
- The #394 attribution fix named the WRONG FILE: the quoted string is
  neutrophilic.yaml's, not acidotolerant.yaml's.
- carotenoid_biosynthesis claimed "every description calls it one", but
  red_pigmented.yaml's does not — it enumerates the steps instead ("Phytoene
  synthase condenses two GGPP to phytoene, then desaturation/isomerization
  yields lycopene"). Which is the rule's own test for PATHWAY met EXPLICITLY
  rather than by naming, so the claim is now stated that way round.

AND THE #394 FIX HAD NOT REACHED THE DATA. The migration appends a
curation_history event only to files it CHANGES, so re-running after editing a
rationale refreshes nothing — the script and the shipped records had diverged.
Restored the 22 touched trait files to main and re-ran, so the corpus carries
the corrected text. Verified: zero records still carry the wrong-file
attribution.

The history record predated the second commit, so it said nothing about the two
gate widenings or aa_decarboxylation. Addendum added rather than a second
record, since it is one session's work on one target.

540 tests pass - ruff clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@realmarcin
realmarcin merged commit 021d06f into main Aug 16, 2026
7 checks passed
@realmarcin
realmarcin deleted the fix/356-tranche3-pathway-rule branch August 16, 2026 03:52
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.

1 participant