Skip to content

Backfill 3 evidence-backed edges, connecting 2 fragmented graphs (#183) - #294

Merged
realmarcin merged 2 commits into
mainfrom
fix/backfill-causal-edges-183
Aug 5, 2026
Merged

Backfill 3 evidence-backed edges, connecting 2 fragmented graphs (#183)#294
realmarcin merged 2 commits into
mainfrom
fix/backfill-causal-edges-183

Conversation

@realmarcin

Copy link
Copy Markdown
Contributor

First increment of #183's content half, and the establishment of a repeatable method. The detection half shipped in #185/#227; what remained was 220 fragmented graphs and one worked example.

What was disconnected, and why these edges

metabolism/dissimilatory_iron_reduction — the pair dissolved_fe3_om_complex → solid_fe3_mineral had no path to the trait. It was a free-standing geochemical rate comparison sitting beside a respiration graph. Both are pools of the very acceptor the organism reduces, so dir_trait now uses each as an electron acceptor, grounded METPO:2000008 to match the 13 existing uses electron donor edges of the same TRAIT → CHEMICAL shape.

morphology/sulfur_globulepersulfide_dioxygenase → sulfite was stranded: a correct enzymatic fact with nothing tying it to globule formation. PDO acts downstream of SQR in the same sulfide-oxidation pathway the rest of the graph models, so it now participates in oxidative_sulfur_metabolism.

The snippets are the point

#247 established that a deep-research report is not a snippet source — its evidence text paraphrases, so which of quote-or-paraphrase you are copying is unknowable without opening the paper. #267 made that enforceable. So this backfill could not take the obvious shortcut.

Each snippet was pulled from the PubMed abstract via efetch and checked character-for-character against it in the same run:

edge source verbatim
dir_trait → solid_fe3_mineral PMID:15518832
dir_trait → dissolved_fe3_om_complex PMID:15518832
persulfide_dioxygenase → oxidative_sulfur_metabolism PMID:35653932

That constraint turns out to be load-bearing rather than ceremonial. An edge added with a reference and no snippet is schema-valid, and is exactly what 63% of existing evidence items do — but a new edge is a new locator, so audit-snippets sees a new MISSING_SNIPPET and qc exits 1. The ratchet from #267 makes the honest path the only path. That interaction was predicted in #247 and this is the first time it has actually bitten.

Numbers

FRAGMENTED_GRAPH        220 → 218
UNREACHABLE_FROM_TRAIT  1321 → 1317
audit-snippets          0 new findings
backfill                1 → 3 of 221

The causal-graph baseline is regenerated so the ratchet tightens rather than tolerating the old count — per #183's own instruction to re-freeze after each batch. pages/ is regenerated because the page stamp derives from the corpus (#228), so appending curation history rebuilds all 489.

Scope, stated plainly

3 of 221. This is deliberately small: it is the canary for a method, not the campaign. What it demonstrates is that a fragmented graph can be repaired with genuinely verbatim evidence at roughly one PubMed lookup per edge, and that the two ratchets (audit-graphs, audit-snippets) between them make a lazy repair impossible to land. Scaling it is per-trait research work and wants its own sequence of PRs.

🤖 Generated with Claude Code

The detection half of #183 shipped in #185/#227; this is the first increment of
the content half, and establishes the method.

metabolism/dissimilatory_iron_reduction: the Fe(III)-pool pair
(dissolved_fe3_om_complex -> solid_fe3_mineral) sat with no path to the trait —
a free-standing geochemical rate comparison beside a respiration graph. Both are
pools of the acceptor the organism reduces, so dir_trait now uses each as an
electron acceptor, grounded METPO:2000008 to match the 13 existing
uses-electron-donor edges of the same TRAIT -> CHEMICAL shape.

morphology/sulfur_globule: persulfide_dioxygenase -> sulfite was stranded as a
correct enzymatic fact with nothing tying it to globule formation. PDO acts
downstream of SQR in the same sulfide-oxidation pathway the rest of the graph
models, so it now participates in oxidative_sulfur_metabolism.

THE SNIPPETS ARE THE POINT. #247 established that a research report is not a
snippet source — its evidence text paraphrases, so what it quotes is unknowable
without opening the paper — and #267 made that enforceable, which means the
backfill cannot take the shortcut. Each snippet here was pulled from the PubMed
abstract via efetch and checked character-for-character against it in the same
run: PMID:15518832 for both iron edges, PMID:35653932 for the sulfur edge.

That constraint is load-bearing rather than ceremonial. An edge added with a
reference and no snippet is schema-valid and is what 63% of existing evidence
items do — but it creates a new locator, so audit-snippets sees a new
MISSING_SNIPPET and qc exits 1. The ratchet makes the honest path the only path.

  FRAGMENTED_GRAPH        220 -> 218
  UNREACHABLE_FROM_TRAIT  1321 -> 1317
  audit-snippets          0 new findings
  backfill                1 -> 3 of 221

Baseline regenerated so the ratchet tightens rather than tolerating the old
count. pages/ regenerated: the stamp derives from the corpus, so appending
curation history rebuilds all 489.

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

culturebot-reviewer Bot commented Aug 5, 2026

Copy link
Copy Markdown

Claude finished @realmarcin's task in 3m 29s —— View job


Review complete — approved

  • Read README.md / docs/ conventions (CURATION_PLAYBOOK.md, GROUNDING_POLICY.md, history/README.md)
  • Reviewed both trait YAML diffs (grounding, graph structure, snippets)
  • Verified CURIEs resolve against mappings/predicate_grounding.tsv and labels match
  • Verified graph reachability logic in scripts/audit_causal_graphs.py
  • Checked baseline regeneration + history/ record
  • Verify snippets verbatim against PubMedblocked, no network egress (see below)
  • Posted verdict via gh pr review --approve

Everything I can check locally is correct and internally consistent. No 🔴, no 🟡.

Grounding is right, including the one that reads wrong in the PR body. The description says the acceptor edges are grounded "METPO:2000008 to match the 13 existing uses electron donor edges" — which sounds like a donor/acceptor inversion. It isn't: mappings/predicate_grounding.tsv:6 maps uses electron acceptorMETPO:2000008 "uses as electron acceptor" (skos:exactMatch), and the 13 donor edges use METPO:2000009. What is being borrowed is the TRAIT → CHEMICAL shape, not the CURIE. biolink:participates_in likewise matches predicate_grounding.tsv:16 and the biolink:located_in already in that graph.

The connectivity claim holds. audit_causal_graphs.py computes reachability undirected, so persulfide_dioxygenase → oxidative_sulfur_metabolism does reattach that branch even though the trait node is a sink. Two independent signals say the audit was actually re-run rather than the baseline hand-edited: conf/causal_graph_audit_baseline.tsv and reports/causal_graph_audit.tsv drop the same 6 rows, and the baseline diff is +0/-6 — it strictly tightens, no new findings smuggled in.

🔵 Suggestions (optional)

  1. data/traits/metabolism/dissimilatory_iron_reduction.yaml:148 — the node is dissolved Fe(III)-organic-matter complex (ambient OM-complexed Fe), but the snippet is about microbially produced chelators and electron shuttles. Those are adjacent, not identical, and the shuttle half of the quote describes reduction of oxides not in contact — a different mechanism from "chelated Fe(III) is the acceptor pool". The edge is defensible; the description overstates how directly the quote lands.
  2. data/traits/morphology/sulfur_globule.yaml:135 — the quote's scope is heterotrophic bacteria and human mitochondria, while this trait's canonical examples are Thiomargarita / Beggiatoa. It supports the generic PDO participates in oxidative sulfur metabolism claim fine, since that node is generic, but it is not evidence about globule-forming organisms.
  3. METPO:2000008/:2000009 are declared with an organism domain (docs/CURATION_PLAYBOOK.md, "Chemical-use relations"), yet the subject here is a TRAIT node. That is exactly what the 13 existing donor edges already do, so this PR is consistent with practice — but it is a corpus-wide modelling question worth its own issue rather than something to fix here.

Advisory

No record under history/ for either edit. curation_history in both YAMLs is thorough, so nothing is lost, but history/README.md's own worked example is literally --issue .../183 --summary "Connect fragmented cellulolysis causal graph" — this is the case it was written for.

What I could not verify

I have no network egress in this job — curl and WebFetch were both unavailable — so I could not re-fetch PMID:15518832 or PMID:35653932 and check the three snippets character-for-character. Given that verbatim-ness is the PR's central claim, that gap is worth naming rather than glossing. (uv was also unavailable, so I could not run just qc / just audit-graphs directly; I read the scripts and diffed the generated reports instead.)

One detail argues for the claim: sulfide: quinone oxidoreductase with a space after the colon looks like a typo, but it is the house style of the group that publishes the SQR/PDO sulfide-oxidation work, and a curator paraphrasing or tidying would have normalised it to sulfide:quinone. Preserving an oddity is the fingerprint of a real copy. That is an inference, not a check — a reviewer with network access should still diff all three against efetch output.

Scope discipline is right: 3 of 221, framed as a method canary, with the ratchet re-frozen tighter.
· branch fix/backfill-causal-edges-183

@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.

Review — approve

Everything I can check locally is correct and internally consistent. No 🔴, no 🟡.

Grounding is right, including the one that reads wrong in the PR body. The description says the acceptor edges are grounded "METPO:2000008 to match the 13 existing uses electron donor edges" — which sounds like a donor/acceptor inversion. It isn't: mappings/predicate_grounding.tsv:6 maps uses electron acceptorMETPO:2000008 "uses as electron acceptor" (skos:exactMatch), and the 13 donor edges use METPO:2000009. What is being borrowed is the TRAIT → CHEMICAL shape, not the CURIE. biolink:participates_in likewise matches predicate_grounding.tsv:16 and the biolink:located_in already in that graph.

The connectivity claim holds. audit_causal_graphs.py computes reachability undirected, so persulfide_dioxygenase → oxidative_sulfur_metabolism does reattach that branch even though the trait node is a sink. Two independent signals say the audit was actually re-run rather than the baseline hand-edited: conf/causal_graph_audit_baseline.tsv and reports/causal_graph_audit.tsv drop the same 6 rows, and the baseline diff is +0/-6 — it strictly tightens, no new findings smuggled in.

🔵 Suggestions (optional)

  1. data/traits/metabolism/dissimilatory_iron_reduction.yaml:148 — the node is dissolved Fe(III)-organic-matter complex (ambient OM-complexed Fe), but the snippet is about microbially produced chelators and electron shuttles. Those are adjacent, not identical, and the shuttle half of the quote describes reduction of oxides not in contact — a different mechanism from "chelated Fe(III) is the acceptor pool". The edge is defensible; the description overstates how directly the quote lands.

  2. data/traits/morphology/sulfur_globule.yaml:135 — the quote's scope is heterotrophic bacteria and human mitochondria, while this trait's canonical examples are Thiomargarita / Beggiatoa. It supports the generic PDO participates in oxidative sulfur metabolism claim fine, since that node is generic, but it is not evidence about globule-forming organisms.

  3. METPO:2000008/:2000009 are declared with an organism domain (docs/CURATION_PLAYBOOK.md, "Chemical-use relations"), yet the subject here is a TRAIT node. That is exactly what the 13 existing donor edges already do, so this PR is consistent with practice — but it is a corpus-wide modelling question worth its own issue rather than something to fix here.

Advisory

No record under history/ for either edit. curation_history in both YAMLs is thorough, so nothing is lost, but history/README.md's own worked example is literally --issue .../183 --summary "Connect fragmented cellulolysis causal graph" — this is the case it was written for.

What I could not verify

I have no network egress in this job, so I could not re-fetch PMID:15518832 or PMID:35653932 and check the three snippets character-for-character. Given that verbatim-ness is the PR's central claim, that gap is worth naming rather than glossing.

One detail argues for the claim: sulfide: quinone oxidoreductase with a space after the colon looks like a typo, but it is the house style of the group that publishes the SQR/PDO sulfide-oxidation work, and a curator paraphrasing or tidying would have normalised it to sulfide:quinone. Preserving an oddity is the fingerprint of a real copy. That is an inference, not a check — a reviewer with network access should still diff all three against efetch output.

Scope discipline is right: 3 of 221, framed as a method canary, with the ratchet re-frozen tighter. Approving.

…ts (#297, #296)

Review found that both snippets support their edges less directly than the
surrounding prose claimed. Both edges stand; the wording did not.

The dissolved Fe(III) quote is about chelators the organism SECRETES, and its
second half describes reduction of oxides not in contact with the cell — a
different mechanism from "chelated Fe(III) is an acceptor pool", while the node
is ambient Fe(III)-organic-matter complex. The description no longer says
"chelator-solubilised", and the notes now say the quote supports the pool's
existence rather than its origin.

The PDO quote covers heterotrophic bacteria and human mitochondria, not the
Thiomargarita/Beggiatoa exemplars of this trait. It supports the generic pathway
claim its object node makes, and the notes now say so and no more.

Both caveats are written into the record rather than into a commit message,
because that is the failure mode #183's backfill will most easily repeat at
scale: a true quote attached to a slightly wider claim. Verbatim-ness does not
protect against it — it is exactly what verbatim-ness cannot see. Tracked in
#297.

Also added the history/ records the review flagged as missing. history/README's
own worked example is this issue, and the one existing record is what let the
#282 review catch a false retraction the current data alone could not explain.
Written by hand because `just new-history` requires a claw checkout that is not
always present — tracked in #296.

353 tests pass, `just qc` exits 0.

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

culturebot-reviewer Bot commented Aug 5, 2026

Copy link
Copy Markdown

Claude finished @realmarcin's task in 5m 5s —— View job


Re-review after 0370ee76 — approved

  • Read history/README.md, src/traitmech/schema/history.yaml, mappings/predicate_grounding.tsv
  • Reviewed the incremental diff (narrowed descriptions + 2 new history records)
  • Ran just audit-graphs218 FRAGMENTED / 1317 UNREACHABLE, 0 new, 0 blocking, working tree clean afterwards
  • Ran just validate-history history/recordsNo issues found
  • Checked CI: qc, validate-strict, pr-sanity, vendored-sync, Curation history all green
  • Confirmed pages/ was regenerated in the second commit (the narrowed notes are in the HTML)
  • Verify snippets verbatim against PubMedstill blocked, curl and WebFetch both denied in this job
  • Posted verdict

The second commit does what the prior review's 🔵 1 and 🔵 2 asked, and does it in the right place. Both edges stand; only the prose narrowed. 🔵 3 (organism-domain METPO terms on a TRAIT subject) is correctly deferred to its own issue rather than patched here.

Two things I could newly verify this run that the last one couldn't:

  • The audit was genuinely re-run. just audit-graphs reproduces reports/causal_graph_audit.tsv byte-for-byte — git status is clean after — and reports baselined: 1535, new: 0. The 218/1317 in the PR body is real, not transcribed.
  • Both history records are schema-valid. just validate-history passes over all three records. Required fields are present, session.id equals each filename stem, and the details pattern (which rejects the scaffolder placeholder) is satisfied.

Putting the scope caveats in evidence[].notes rather than only in the commit message is the right call. A commit message is not reachable from the record, and the failure mode being guarded against — a true quote attached to a slightly wider claim — is invisible to audit-snippets by construction, so prose in the record is the only place it can live.

🔵 Suggestion (optional)

session.timestamp: '2026-08-05T00:00:00Z' in both new records is a midnight placeholder; the commit is dated 2026-08-05T06:05Z. Schema calls that field "ISO-8601 UTC start time", and the scaffolded dumbbell_shaped record carries a real one (23:09:03Z). Same for the 183a01/183a02 shortids, which are sequential where the schema's stated rationale wants them unguessable — the collision argument is about two agents on one slug, so nothing breaks here, but a future hand-written 183a03 for the same slug is exactly the case the random id was meant to rule out. Both are consequences of hand-writing rather than scaffolding, which is already tracked in #296; worth mentioning only because it is the concrete cost of that gap.

What I could not verify

No network egress again — curl and WebFetch are both denied in this job, so I could not efetch PMID:15518832 or PMID:35653932 and diff the three snippets character-for-character. Since verbatim-ness is the PR's central claim, that remains unchecked by a reviewer, and I would rather name it twice than let it read as settled.

What I can say from local evidence: neither PMID appears anywhere else in the repo, including in either trait's own research report — so there is no ECHOES_RESEARCH_REPORT exposure and no in-repo text the snippets could have been lifted from. Both quotes also read as the genuine article on content grounds (the Geobacter-versus-Shewanella/Geothrix contrast is the structural signature of the Lovley 2004 Adv Microb Physiol abstract). That is corroboration, not verification. A reviewer with network access should still run the diff.

No 🔴, no 🟡. Approving.
· branch fix/backfill-causal-edges-183

@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.

Re-review after 0370ee76 — approve

The second commit does what the prior review's 🔵 1 and 🔵 2 asked, and does it in the right place. Both edges stand; only the prose narrowed. 🔵 3 (organism-domain METPO terms on a TRAIT subject) is correctly deferred to its own issue rather than patched here.

Two things I could newly verify this run that the last one couldn't:

  • The audit was genuinely re-run. just audit-graphs reproduces reports/causal_graph_audit.tsv byte-for-byte — git status is clean after — and reports baselined: 1535, new: 0. The 218/1317 in the PR body is real, not transcribed.
  • Both history records are schema-valid. just validate-history passes over all three records. Required fields present, session.id equals each filename stem, and the details pattern that rejects the scaffolder placeholder is satisfied.

Putting the scope caveats in evidence[].notes rather than only in the commit message is the right call. A commit message is not reachable from the record, and the failure mode being guarded against — a true quote attached to a slightly wider claim — is invisible to audit-snippets by construction, so prose in the record is the only place it can live.

🔵 Suggestion (optional)

session.timestamp: '2026-08-05T00:00:00Z' in both new records is a midnight placeholder; the commit is dated 2026-08-05T06:05Z. The schema calls that field "ISO-8601 UTC start time", and the scaffolded dumbbell_shaped record carries a real one (23:09:03Z). Same for the 183a01/183a02 shortids, which are sequential where the schema's stated rationale wants them unguessable — the collision argument is about two agents on one slug, so nothing breaks here, but a future hand-written 183a03 for the same slug is exactly the case the random id was meant to rule out. Both follow from hand-writing rather than scaffolding, already tracked in #296; worth naming only as the concrete cost of that gap.

What I could not verify

No network egress again — curl and WebFetch are both denied in this job, so I could not efetch PMID:15518832 or PMID:35653932 and diff the three snippets character-for-character. Since verbatim-ness is this PR's central claim, that remains unchecked by a reviewer, and I would rather name it twice than let it read as settled.

What I can say from local evidence: neither PMID appears anywhere else in the repo, including in either trait's own research report — so there is no ECHOES_RESEARCH_REPORT exposure and no in-repo text the snippets could have been lifted from. Both quotes also read as the genuine article on content grounds (the Geobacter-versus-Shewanella/Geothrix contrast is the structural signature of the Lovley 2004 Adv Microb Physiol abstract). That is corroboration, not verification. A reviewer with network access should still run the diff.

CI is green across qc, validate-strict, pr-sanity, vendored-sync, and Curation history. No 🔴, no 🟡. Approving.

@realmarcin
realmarcin merged commit 4ee68b4 into main Aug 5, 2026
6 checks passed
@realmarcin
realmarcin deleted the fix/backfill-causal-edges-183 branch August 5, 2026 06:14
realmarcin added a commit that referenced this pull request Aug 5, 2026
* Let new-history write a record without a claw checkout (#296)

`just new-history` was gated on _require-claw, so on a machine without a
culturebotai-claw checkout no record got written at all. Nothing about writing
one actually needs claw: the schema is already vendored at
src/traitmech/schema/history.yaml, which is what `just validate-history` and CI
check against.

Claw stays PREFERRED — the recipe tries it first, so the canonical scaffolder
keeps producing the canonical shape across the four Mech repos — and
scripts/new_history_record.py fills the gap.

A correction worth recording, since it is why this issue exists. history/README
asserted that "anyone writing curation records has claw checked out". That is an
assumption, not a guarantee: it fails for a fresh clone, for CI (claw is
private), and for a contributor outside the fleet. It also did not hold in
practice for a duller reason — claw IS present on this machine, and #294's two
records were still hand-written, because the recipe was gated and assuming the
gate would fail was cheaper than testing it. A fallback removes the incentive.

The interface is claw's exactly, and running both is how that got fixed rather
than asserted. The first version defaulted --kind, renamed --actor-name to
--tool and required --details; invoking the recipe on a machine that HAS claw
surfaced all three at once. A fallback taking different arguments is the same
trap in a new form.

Parity is now byte-identical apart from the id's hash suffix and one deliberate
divergence: a bare `--issue 296` is expanded to a full URL, because the schema
declares those `range: uri` and the pre-existing committed record carries URLs,
while claw passes the string through. Documented as a divergence, and the README
carries a runnable parity check rather than a claim.

Also fixed a crash the parity harness found: --history-root outside the repo
made the final relative_to() raise after the record was already written.

367 tests pass (14 new), `just qc` exits 0.

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

* Fix three defects the parity harness could not see (#296)

Review found three, and the common thread is that the README's parity check
could not reach any of them: it pinned --history-root and exercised only
--kind record with --details supplied. Widened accordingly.

The kind directory was hardcoded to records/. The layout is
history/<kind-dir>/<slug>/ — history/infrastructure/curation-history/ is a live
example — so `--kind infrastructure` wrote to history/records/ under the
fallback and history/infrastructure/ under claw. The schema does not constrain
the path, so the misplaced record validated clean and nothing noticed. This PR
is itself an infrastructure change, so a record scaffolded for it would have
gone to the wrong place.

The default --details placeholder evaded the guard built to catch it. The
vendored schema carries `pattern: '^(?!TODO: replace this placeholder)'` and
history/README promises an unfilled record cannot slip through. The fallback
wrote "TODO: fill in the substance of this change." — a different string the
negative lookahead permits — so an unfilled record was committable. The
near-miss was load-bearing rather than careless: emitting claw's real string
would have made validate() delete the file. Now emits claw's placeholder
byte-for-byte and skips validation only when --details was omitted, which is
the write-then-edit workflow claw implements; the record then fails
validate-history exactly as documented, pinned by a test that asserts the
failure.

--force plus a validation failure destroyed the record it was correcting: the
script wrote the target, validated, then unlinked on failure, in a directory
whose whole policy is append-only. Now writes a scratch file, validates that,
and moves it into place. Canaried by forcing a rewrite that fails validation —
the original comes back byte-identical.

The scratch file keeps a .yaml suffix, because linkml-validate picks its loader
from the extension and refuses a .tmp outright.

372 tests pass (5 new), `just qc` exits 0.

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

* Order sections as the schema declares, and verify KIND_DIRS instead of guessing (#296)

Four review items, and the second is the one worth reading.

`sections` was appended after `details`, while the schema declares it between
`outcome` and `summary` and the one committed record carrying it agrees. That
put the DOCUMENTED invocation on the divergent path — history/README's headline
example passes `--sections causal_graphs,grounding` — and the parity harness
could not see it, because the harness did not pass `--sections`. It does now,
and the diff is again only the id hash and the deliberate links expansion.

KIND_DIRS was six values of which four were inferred, unevenly: mappings and
reports pluralised, schema and other not. That asymmetry was the tell. Verified
two ways rather than argued: the table is byte-identical to claw's
kg_microbe_history/scaffold.py, and scaffolding all six kinds through claw
produces exactly those six directories. Cited in the comment, and the key check
now parses permissible_values out of the vendored schema instead of comparing
two hand-copied literals — the old test could not have noticed the schema
growing a kind.

With --details omitted, validation was skipped ENTIRELY rather than just for the
placeholder, so `--timestamp nonsense` wrote a record and exited 0. Now
validates a copy with `details` substituted, keeping every other check. Writing
that test immediately found a scratch file leaking on the failure path, so the
write-validate-move is wrapped in try/finally.

history/README said every kind but record/schema *needs* an explicit --path.
Neither scaffolder enforces that — both derive <target-root>/<slug>.yaml for any
kind — so it now says what happens if you rely on it: a target path that does
not exist.

375 tests pass (3 new), `just qc` exits 0.

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

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
realmarcin added a commit that referenced this pull request Aug 5, 2026
Two independent reviews converged on the same defect: RO:0002327 `enables`
has `range: biological process or activity` (data/raw/biolink-model.yaml:5099),
so pointing it at a TRAIT node trades the METPO domain violation for an RO
range violation. Verified — the claim is correct.

It is not, however, introduced here: 149 `enables` edges already had TRAIT
objects on main, and this PR took that to 164. Gating the `enables` row would
block all 164 and fail `just qc`, so the range question is filed as #302
(with #303 for the donor/acceptor distinction these 15 edges collapsed) rather
than solved in a PR about 15 edges.

What changes here is the over-claim:

- CURATION_PLAYBOOK.md no longer prescribes `<chemical> enables <trait>` as
  the general remedy for all 66 predicates in the family. That block would
  have been the reference for #301's 366 edges and the #183 backfill. It now
  states the range caveat, points at #302/#303, and recommends the
  range-correct form available today — chemical to the graph's *process* node.
- The `blocked_by_node_type` claim is future-tense: the reversal removed every
  edge carrying those labels, so no residual row exists to go looking for.

Also from review:

- 14 trait files get a REVERSE_CAUSAL_EDGE_DIRECTION curation_history event;
  they were rewritten with no provenance. The dissimilatory_iron_reduction
  event explicitly supersedes the #294 entry that still describes those two
  edges in their old direction and grounding — added as a correcting event
  rather than editing history in place.
- load_mapping now fails if CausalNodeTypeEnum ever defines NONE, which would
  let the sentinel silently shadow a real node type — the failure class the
  surrounding code deliberately makes fatal. Test added.
- conf/evidence_snippet_baseline.tsv restored to canonical _key sort order;
  it was re-keyed in place, which would have produced spurious diff noise on
  the next --write-baseline. Still exactly 3 rows changed vs main.

just qc exit 0; 380 tests pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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