Re-ground the last substrate-to-product edges, and harden the gate to zero (#327, closes #301) - #332
Conversation
…327) RO:0001001 (derives into) holds between an old and a new material entity where the new begins to exist as the old ceases and inherits the significant portion of its matter -- substrate-to-product chemistry. biolink maps it as biolink:derives_into. That is exactly what these edges say, so #327 resolves with an upstream term and no minting at all: cellulose --is hydrolyzed to--> cellobiose METPO:2000013 -> RO:0001001 thiosulfate --derives into--> sulfate METPO:2007800 -> RO:0001001 thiosulfate --derives into--> sulfide METPO:2007800 -> RO:0001001 The first was the defect #327 was filed for: METPO:2000013 reads agent-to-substrate, so grounding a substrate-to-product edge to it asserted that cellulose hydrolyses cellobiose. Its label is kept, since it names the mechanism the general relation does not. The other two had a CHEMICAL subject on `produces`, and a chemical does not bring another into existence the way an agent does. METPO:2007405 (oxidized to, proposals v2) was considered for the sulfate half and rejected twice over: it fits only the oxidised half, so it would split a matched pair from one disproportionation reaction, and it is an unminted placeholder where RO:0001001 is real. acetyl_phosphate --produces--> ATP was examined and deliberately NOT moved: ATP's matter comes mostly from ADP, only the phosphoryl group transfers, so it fails RO:0001001's "inherits the significant portion of the matter" test. Filed separately rather than forced. MICROBE_DOMAIN_ON_NONORGANISM: 1 -> 0. #301's 366 edges are fully retired. So the check is now a HARD GATE. It shipped as a ratchet over 530 findings (#314) because nothing was fixable without the decisions that have since landed. `just audit-predicate-domains` now passes --fail-on any, and conf/predicate_domain_audit_baseline.tsv is deleted rather than kept empty -- an empty baseline invites a --write-baseline run to refill it. Canaried: injecting one microbe-domain edge exits 1 with nothing to forgive it. The ratchet machinery in the script is deliberately kept, since it is what makes a future violation class landable the same way this one was. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Claude finished @realmarcin's task in 5m 28s —— View job Review posted — changes requested
The ontology call is right: Three defects:
Plus one 🔵 ( Note: I could not run |
There was a problem hiding this comment.
The ontology call is right. RO:0001001 is biolink:derives_into's exact_mapping in the vendored model (data/raw/biolink-model.yaml:5126-5138), and its definition there matches the text the mapping notes quote, so the substrate→product reading is sound and no minting is needed. The METPO:2007405 rejection is correct, and so is the alternative I checked for you: biolink's more specific has metabolite (is_a: derives into) is exact-mapped only to CHEBI:25212, which its own comment flags as a role rather than a predicate. Deferring acetyl_phosphate --produces--> ATP to #331 rather than forcing it is the right call for the same reason.
Three things to fix.
🟡 justfile:105 names the wrong CURIE. The recipe comment says the last edge was "re-grounded to RO:0002234 in #327". RO:0002234 is has output; the edge went to RO:0001001. The script docstring (scripts/audit_predicate_domains.py:35) has it right, so this is just the justfile copy — but it is the curator-facing one, and a wrong CURIE in the comment documenting the grounding decision is exactly the thing this repo is careful about.
🟡 The is hydrolyzed to mapping row covers a second edge that was not grounded. data/traits/metabolism/starch_degradation.yaml:81 is starch --is hydrolyzed to--> maltooligosaccharides, CHEMICAL→CHEMICAL, no predicate_id. It matches the new row's gate, so ground_causal_predicates.py now considers it mapped — which is why its row disappeared from reports/predicate_grounding_residual.tsv in this diff. But the YAML edge is still ungrounded. Net effect: the edge is in neither the grounded set nor the residual backlog, and just ground-predicates --apply would now produce an uncommitted diff that nothing gates on. Either ground it here (with a curation event, as the other two got) or leave it visible.
🟡 docs/CURATION_PLAYBOOK.md is stale in two places this PR makes false. Line 139 still reads "#301 tracks the 366 edges that still carry them" — that count is now 0 and #301 closes with this PR. Line 124 says "Nothing in just qc catches it", which stopped being true when the audit joined qc and is now emphatically untrue under --fail-on any. The sibling enables paragraph was updated the same way when #302/#303 hit zero ("The count is now 0, and the audit ... fails any new one"), so the convention is already established here.
🔵 The comment says not to reintroduce a baseline, but just audit-predicate-domains --write-baseline still writes conf/predicate_domain_audit_baseline.tsv, and any invocation that does not go through the recipe defaults to --fail-on new and would load it. Keeping the machinery is the right decision; if you want the norm enforced rather than documented, that is the seam.
Advisory, not a defect: neither trait got a record under history/records/ — though only three exist repo-wide, so the practice is clearly still partial.
I could not run just qc, the audits, or pytest in this environment (Bash approvals are restricted to a read-only set), so the green results in the description are taken at face value; the findings above come from reading the diff, the mapping table, the grounding script's gate semantics, and the vendored biolink model.
…or (PR #332 review) Three review findings, all valid. The new `is hydrolyzed to` mapping row also matched starch_degradation.yaml's `starch --is hydrolyzed to--> maltooligosaccharides`, a CHEMICAL->CHEMICAL edge with no predicate_id. Adding the row dropped it from the residual report because the grounding tool now considered it mapped, but nothing had grounded it -- so it sat in neither the grounded set nor the backlog. Grounded to RO:0001001; it is the same substrate-to-product shape as the cellulose edge. justfile:105 named RO:0002234 (has output) as what the last edge was re-grounded to. It was RO:0001001 (derives into). The script had it right, the recipe comment did not. docs/CURATION_PLAYBOOK.md said "Nothing in `just qc` catches it" and "#301 tracks the 366 edges that still carry them". Both were true when written and are now false: the audit is a qc member running --fail-on any, and the count is 0. Rewritten to describe a mistake the tooling prevents rather than a backlog to work around, mirroring what #323 did for the enables paragraph. Also acted on the 🔵, which was a real hole rather than a nit: --write-baseline can still recreate the deleted baseline, and a non-recipe invocation defaulted to --fail-on new, so it would have loaded that file and passed. Deleting the baseline while leaving the default permissive only hardened the path that goes through `just`. The default is now `any`; the ratchet is still available via an explicit --fail-on new for reintroducing this check over a NEW violation class. That flip broke test_write_baseline_freezes_then_passes, which had encoded the old contract -- the test was right to fail. Updated to assert both halves of the new one, plus a new test_default_fail_on_is_any pinning that a stray baseline cannot weaken a default run, so the hardening cannot be undone by an argparse edit. Canaried both paths: injecting one microbe-domain edge exits 1 via the recipe AND via a direct no-flag invocation. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
All three 🟡 fixed, and the 🔵 turned out to be a real hole rather than a nit — so I acted on it too. 🟡 The second
|
|
Claude finished @realmarcin's task in 3m 1s —— View job Review posted — approved
All three 🟡 are fixed, and the 🔵 was closed rather than documented away.
The label side is confirmed independently of the description: 🔵, not blocking: the Advisory as before: no |
There was a problem hiding this comment.
All three 🟡 are fixed, and the 🔵 was closed properly rather than documented away.
Re-verified against the diff:
starch --is hydrolyzed to--> maltooligosaccharides(data/traits/metabolism/starch_degradation.yaml:82) now carriespredicate_id: RO:0001001, with aGROUND_CAUSAL_PREDICATEScuration event and the residual row correctly gone. Both nodes areCHEMICAL, so the edge sits inside the mapping row's ownCHEMICAL|CHEMICALgate rather than being admitted by a wildcard. Grepping the corpus,is hydrolyzed toandderives intooccur on exactly 4 edges total and all 4 are grounded — no third edge slipped through the same seam.justfile:105now namesRO:0001001 (derives into).docs/CURATION_PLAYBOOK.mdno longer claimsjust qcmisses this, and the #301 paragraph reads as a closed migration.- The default flip to
--fail-on anyis the right shape, and the recipe still composes:{{args}}is appended after--fail-on any, sojust audit-predicate-domains --fail-on newstill reaches the ratchet when someone deliberately wants it.test_default_fail_on_is_anypins the half that mattered — a stray baseline on disk cannot weaken a no-flag run.
The label side checks out independently: label-correspondence is green on this head, which is the gate that verifies (RO:0001001, "derives into") against RO, so the two new mapping rows are confirmed by CI and not only by the description. qc — which now contains the hard-gated audit — validate-strict, pytest and curation-history are all green as well.
One 🔵, nothing blocking: the is hydrolyzed to row's note (mappings/predicate_grounding.tsv:110) still describes a single edge — "Substrate-to-product hydrolysis: cellulose -> cellobiose" and "This is the last of the 366 #301 edges." The row now grounds two edges, and the starch one was never among the 366 (it had no predicate_id at all). Not false, but the note is the curator-facing documentation for a gate that admits more than it says.
Advisory as before: no history/records/ entry for either trait.
Approving.
Retires the
MICROBE_DOMAIN_ON_NONORGANISMclass entirely: 1 → 0, and converts thecheck from a ratchet into a hard gate.
No minting needed
RO:0001001(derives into,biolink:derives_into) holds between an old and a newmaterial entity where the new "begins to exist when the old ceases to exist, and inherits
the significant portion of the matter of the old" — substrate-to-product chemistry. That
is exactly what these edges assert, so #327 resolves with an upstream term and no new
METPO placeholder:
cellulose --is hydrolyzed to--> cellobioseMETPO:2000013RO:0001001thiosulfate --derives into--> sulfateMETPO:2007800RO:0001001thiosulfate --derives into--> sulfideMETPO:2007800RO:0001001The first is the defect #327 was filed for —
METPO:2000013reads agent→substrate, sogrounding a substrate→product edge to it asserted cellulose hydrolyses cellobiose. Its
label is kept, since it names the mechanism the general relation doesn't. The other two had
a
CHEMICALsubject onproduces, and a chemical doesn't bring another into existence theway an agent does.
METPO:2007405(oxidized to, v2) was considered and rejected twice over: it fits onlythe oxidised half, so it would split a matched pair from one disproportionation reaction,
and it's an unminted placeholder where
RO:0001001is real.One edge was examined and deliberately left:
acetyl_phosphate --produces--> ATPfailsderives into's own test — ATP's matter comes mostly from ADP, only the phosphoryl grouptransfers. Filed as #331 rather than forced into a relation that doesn't fit.
The gate is now hard
audit_predicate_domainsshipped as a ratchet over 530 findings (#314) because nothingwas fixable without decisions that hadn't been made. Those landed — v8 for #302/#303
(#320, #323), v9 for #301 (#326, #328, #329), and this PR — so:
just audit-predicate-domainspasses--fail-on any;conf/predicate_domain_audit_baseline.tsvis deleted, not kept empty, because anempty baseline invites a
--write-baselinerun to refill it;The ratchet machinery in the script is deliberately kept — it's what makes a future
violation class landable without blocking unrelated work, exactly as this one was.
What was checked
baseline to forgive it. File restored.
RO:0001001's label verified via OAK as "derives into" before writing the mapping rows,since
label-correspondencechecks (id, label) pairs against RO.just qcgreen,just validate-strict477 / 0 errors,uv run pytest420 passed,ruff clean.
curation_historyverified to sort in order.Closes
#327, and #301 — whose 366 edges are now fully retired.
🤖 Generated with Claude Code