Skip to content

METPO proposal v8: trait-realization and electron-role predicates (#302, #303) - #320

Merged
realmarcin merged 2 commits into
mainfrom
claude/metpo-traitmech-v8-proposal
Aug 6, 2026
Merged

METPO proposal v8: trait-realization and electron-role predicates (#302, #303)#320
realmarcin merged 2 commits into
mainfrom
claude/metpo-traitmech-v8-proposal

Conversation

@realmarcin

Copy link
Copy Markdown
Contributor

Implements the "mint METPO terms" decision for the #301/#302/#303 cluster.

The problem, in one line

The corpus has no range-correct relation whose object is a trait, and both
available candidates are wrong in opposite directions.

That squeeze is exactly what forced PR #300 to reverse 15 electron edges onto
enables, collapsing donor and acceptor onto one relation and losing the role
for 7 of them (#303).

What this proposes

Cohort proposals/metpo_traitmech_v8/, three predicates, following the v1
convention of domain = range = METPO:1007401 (trait causal node) — the choice
that sidesteps the microbe domain and the process range at once:

ID Label Edges Records
METPO:2007700 confers 146 114
METPO:2007701 has electron donor 13 13
METPO:2007702 has electron acceptor 5 3

The partition is exhaustive and verified: 146 + 13 + 5 = 164, computed by
walking every causal_graphs[].edges[] and cross-checked against the
independently-derived ENABLES_RANGE_ON_TRAIT count in
reports/predicate_domain_audit.tsv (the audit added in #314).

The donor/acceptor pair deliberately reverses direction back to
<trait> has electron donor <chemical> — restoring what METPO:2000008/2000009
expressed before their organism domain made them unusable here. That reversal is
what lets one relation per role replace one generic relation, so
dissimilatory_iron_reduction's two acceptor edges stop being indistinguishable
from its donor-shaped edge.

What was checked

  • just verify-proposal metpo_traitmech_v8PASS (failures: 0).
  • just robot-validate-proposal metpo_traitmech_v8PASS: template compiles,
    merges with metpo.owl, ELK reasons with no UNSAT (+6 axioms, matching v6's
    delta for a comparable cohort).
  • just qc green; uv run pytest420 passed; ruff clean.
  • Every definition_source anchor verified to be a real graph_id in the cited
    file. Cross-ontology alignments live in xrefs + metpo_proposal_mappings.sssom.tsv
    as skos:closeMatch, never in definition_source (Emit cross-ontology equivalents as mappings, not definition_source, in METPO proposals #83).
  • ID-block collision checked against v1–v7 and CommunityMech v1; 2007700 is free,
    and the highest real minted METPO property is METPO:2000516.

Also fixed here: #318

Building this cohort hit a pre-existing bug. verify_metpo_proposal's Scope-A
check ran even for cohorts shipping no classes template, so class_tsv_text was
"" and every corpus traitmech: id read as uncited — failing every
predicate-only cohort (v2, v4, v6) once the corpus gained its first synthetic id.
The script already printed "Scope C/A check will skip" for that case and
check_scope_c already honoured it; only Scope-A did not.

Fixed, with 4 regression tests. The fix was canaried against the pre-fix code:
the key test fails without the guard and passes with it, so it genuinely bites.
v1 and v7 still fail this check — correctly, because 120 synthetic ids really are
un-lifted, which is filed separately as #319.

Scope: proposal only

No corpus edges are grounded here. #301 warns that doing the whole sweep at once
would be unreviewable, so the 164-edge migration follows as a separate PR,
where the proof of landing is ENABLES_RANGE_ON_TRAIT falling 164 → 0 in the
ratcheted audit. The placeholder IDs are not minted upstream yet; the round-trip
plan is in proposal.md.

Closes nothing on its own — #302 and #303 stay open until the migration lands.

🤖 Generated with Claude Code

Both defects share one root cause: the corpus has no range-correct
relation whose object is a trait. RO:0002327 (enables) has been doing
that job, but biolink gives it range 'biological process or activity',
and a TRAIT node is a disposition — so 164 edges entail
trait ⊑ BiologicalProcessOrActivity (#302). The obvious alternative is
worse: METPO's chemical-interaction predicates inherit rdfs:domain
microbe via METPO:2000001, which no causal-graph node can satisfy (#301).
That squeeze is what forced PR #300 to collapse donor and acceptor onto
one generic relation, losing the role for 7 of 15 edges (#303).

Cohort v8 proposes the three predicates that resolve it, following the
v1 convention of domain = range = METPO:1007401 (trait causal node),
which sidesteps the microbe domain and the process range at once:

  METPO:2007700  confers              146 edges / 114 records
  METPO:2007701  has electron donor    13 edges /  13 records
  METPO:2007702  has electron acceptor  5 edges /   3 records

The partition is exhaustive: 146 + 13 + 5 = 164, computed from the
corpus and cross-checked against ENABLES_RANGE_ON_TRAIT in
reports/predicate_domain_audit.tsv. Cross-ontology alignments go in
xrefs + a SSSOM file (skos:closeMatch to RO:0002327, METPO:2000009 and
METPO:2000008), never in definition_source (#83).

No corpus edges are grounded here; the 164-edge migration follows as a
separate PR, since #301 notes doing it all at once would be unreviewable.

Also fixes a pre-existing bug this cohort ran into (#318):
verify_metpo_proposal's Scope-A check ran even for cohorts shipping no
classes template, so class_tsv_text was "" and every corpus traitmech:
id read as uncited — failing every predicate-only cohort (v2, v4, v6).
The script already announced it would skip that case and check_scope_c
already honoured it. v1/v7 still fail, correctly: 120 synthetic ids are
genuinely un-lifted, tracked in #319.

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

culturebot-reviewer Bot commented Aug 6, 2026

Copy link
Copy Markdown

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


Review: METPO proposal v8

The mechanical parts hold up. What I verified independently:

  • METPO:1007401 = trait causal node (v1 classes template) ✅
  • METPO:2000008 = uses as electron acceptor, METPO:2000009 = uses as electron donor, both subPropertyOf METPO:2000001, whose rdfs:domain is METPO:1000525 = microbe (data/raw/metpo.owl:159-163, 265-285, 3913-3919) ✅ — the 366 causal-graph edges assert microbe-domain METPO predicates on non-organism subjects #301 argument is real
  • enables range = biological process or activity (data/raw/biolink-model.yaml:5098-5110) ✅
  • 164 ENABLES_RANGE_ON_TRAIT rows in reports/predicate_domain_audit.tsv
  • Highest minted METPO property is METPO:2000516; 20077002007702 clear of v1–v7 and of any existing confers-labelled term ✅
  • All three definition_source anchors are real graph_ids and each really does carry an enables→TRAIT edge ✅
  • Subset tag metpo_traitmech_2026_11 is the next in sequence after v7's _10
  • No data/traits/** changes, so no history/ record is owed ✅

The three problems below are all in prose that gets copy-pasted upstream (the observations column of the ROBOT template), so they matter more than their size suggests.


🟡 The confers subject-type breakdown sums to 142, not 146

metpo_proposal_properties_robot.tsv:3 (and proposal.md:71-73):

BIOLOGICAL_PROCESS 81, GENE_OR_PROTEIN 32, ENVIRONMENTAL_FACTOR 12, QUALITY 5, CELLULAR_LOCALIZATION 5, PATHWAY 4, CHEMICAL 4, MOLECULAR_FUNCTION 2, STATE 1

81+32+12+5+5+4+2+1 = 142, against a claimed 146 edges. Either four subjects have a type not in the list (undeclared node → node_type None?) or one of the two numbers is wrong. Since the 146+13+5=164 identity is the load-bearing claim of the whole cohort, this needs resolving rather than papering over. If the four are untyped, say so explicitly — that's itself a corpus finding.

Fix this →

🟡 The dissimilatory_iron_reduction narrative is factually wrong

metpo_proposal_properties_robot.tsv:5, proposal.md:105-106, and the PR body:

two reversed acceptor edges became indistinguishable in predicate terms from a pre-existing donor-shaped edge

DIR has exactly one causal graph and exactly three enablesdir_trait edges (data/traits/metabolism/dissimilatory_iron_reduction.yaml:90, 133, 148): ferric_iron, solid_fe3_mineral, dissolved_fe3_om_complex. All three are Fe(III) species, and the file's own node descriptions call them terminal electron acceptors ("Terminal electron acceptor reduced in DIR", line 48). There is no donor edge in DIR. The file's 2026-08-05 curation note says the reversed edges "share a predicate with the donor convention" — the corpus-wide convention, not a DIR edge; that got compressed into "its donor-shaped edge" and lost its meaning.

The cohort's own arithmetic confirms it: 5 acceptor edges across 3 records only closes as DIR×3 + oxygen_preference + dissimilatory_metal_reduction. So the partition already treats all three DIR edges as acceptors, and the prose contradicts it. A METPO curator reading this observations cell will look for a donor edge in DIR and not find one.

Fix this →

🟡 One has electron acceptor object is not a chemical species

The only 5-edge/3-record partition consistent with the corpus puts oxygen_preference.yaml#oxygen_terminal_electron_acceptor under METPO:2007702. That node is:

node_type: MOLECULAR_FUNCTION
label: O2 as terminal electron acceptor
description: Use of molecular oxygen as the terminal electron acceptor of the respiratory chain.

(data/traits/environment/oxygen_preference.yaml:71-76)

It is a function, not a chemical, so <trait> has electron acceptor <the use of O2 as an acceptor> doesn't satisfy the proposed definition ("a relation between a trait ... and a chemical species"). Either that edge belongs in confers — which reads fine — or the node should be retyped to CHEMICAL and grounded to CHEBI:15379, in which case say so and note it as migration work. Please confirm which of the 164 edges actually land in each bucket; right now the partition is only inferrable from the counts.

Fix this →

🔵 The OWL range is weaker than the definitions promise

domain = range = METPO:1007401 for all three is the v1 convention and correctly dodges both the microbe domain and the process range. But it means has electron donor/has electron acceptor are unconstrained on the object side, while their definitions say "a chemical species". You already plan to gate this in predicate_grounding.tsv (proposal.md:181-183); worth stating in the proposal so upstream doesn't read the range as tight.


The verify_metpo_proposal.py fix (#318)

Correct and minimal. main() already prints "classes TSV: missing (Scope C/A check will skip)" at scripts/verify_metpo_proposal.py:207 and check_scope_c already honours the same condition at line 160-162 — the guard just makes Scope-A agree with what the script already said it would do. The four tests are the right ones, and the second (test_scope_a_still_fires_for_a_classes_cohort) is what stops the fix from being a blanket disarm.

Unrelated to this PR, but missing = sorted(i for i in ids if i not in class_tsv_text) (line 144) is a substring match — traitmech:000001 is satisfied by a cell containing traitmech:0000010. Pre-existing; mentioning it only because you're in the file.


Not run in this environment: just verify-proposal, just robot-validate-proposal, and uv run pytest all require approvals this sandbox didn't grant, so I could not independently confirm the PASS / no-UNSAT / 420-passed claims. Everything above was verified by reading the corpus, the audit report, metpo.owl and biolink-model.yaml directly.


  • Read repo conventions
  • Review proposal files (CURIEs, labels, mappings, ID block)
  • Verify the edge partition against the corpus
  • Review verify_metpo_proposal.py fix + tests
  • Post verdict — changes requested
    · branch

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

Three accuracy problems in text that ships upstream to METPO; the ID hygiene, ontology grounding and the verify_metpo_proposal fix all check out. Details in the review comment: #320 (comment)

…review)

Review found the dissimilatory_iron_reduction prose factually wrong. All
three of DIR's Fe(III) species are terminal electron acceptors — the
file's own node descriptions say so ("Terminal electron acceptor reduced
in DIR") — so there is no donor-shaped edge in that graph to be confused
with. The real #303 loss is that after PR #300 reversed two of them onto
enables, nothing in predicate terms records the acceptor role at all.
Rewritten in both the observations cell and proposal.md.

Also from the review:

- Publish the explicit 164-edge partition as an appendix, so the buckets
  are checkable rather than inferrable from counts. This resolves the
  query about oxygen_preference: its "O2 as terminal electron acceptor"
  node is typed MOLECULAR_FUNCTION (it denotes the USE of O2, not O2), so
  it lands in confers, not under has electron acceptor whose definition
  requires a chemical species. Retyping it is corpus work, left to the
  migration PR.
- State that the declared OWL range is deliberately weaker than the
  definitions, and why tightening it was rejected.
- Fix this cohort's own Scope-A row, which repeated v2's now-stale "none
  in corpus" claim; there are 120, tracked in #319.

The reviewer's arithmetic finding does not hold: the published breakdown
has nine terms summing to 146 (81+32+12+5+5+4+4+2+1); the review's tally
omitted CHEMICAL 4 and reached 142. Verified against the corpus and left
unchanged. The substring-match observation is real but pre-existing and
is filed as #321.

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

Copy link
Copy Markdown
Contributor Author

Thanks — three of the four findings were real and are fixed; one does not hold. Detail below, with what I verified against the corpus in each case.

🟡 DIR narrative — valid, fixed

You're right, and the error was mine. All three of DIR's Fe(III) species are terminal electron acceptors — data/traits/metabolism/dissimilatory_iron_reduction.yaml's own node descriptions say so ("Terminal electron acceptor reduced in DIR") — so there is no donor-shaped edge in that graph. I had compressed the file's curation note about the corpus-wide donor convention into a claim about a DIR edge, exactly as you diagnosed.

Rewritten in both the observations cell and proposal.md: the real #303 loss is that after PR #300 reversed two of them onto enables, nothing in predicate terms records the acceptor role at all.

🟡 oxygen_preferenceinference was wrong, but the underlying ask was right

That edge is not in the acceptor bucket. Its node is typed MOLECULAR_FUNCTION ("O2 as terminal electron acceptor" denotes the use of O2, not O2 itself), and my classifier requires a CHEMICAL subject for the role-bearing pair — so it lands in confers, as one of the two MOLECULAR_FUNCTION subjects. The actual 5 acceptor edges are DIR×3 + dissimilatory_metal_reduction + dissimilatory_manganese_reduction, which is the 5-edges/3-records partition.

But your underlying point stands — the partition was only inferrable from counts. I've published the explicit 164-edge partition as an appendix (every donor and acceptor edge listed by record, confers broken out by node type), and called out the oxygen_preference typing explicitly, including that retyping it to CHEMICAL/CHEBI:15379 is defensible but is corpus work for the migration PR.

🔵 OWL range weaker than the definitions — valid, added

New subsection saying so plainly, plus why tightening was rejected (committing the range to CHEBI re-imports the cross-ontology coupling the v1 METPO:1007401 convention exists to avoid) and that the constraint is enforced data-side via subject_types/object_types.

🔴 The 142-vs-146 arithmetic — does not hold

The published breakdown has nine terms, not eight:

BIOLOGICAL_PROCESS 81 + GENE_OR_PROTEIN 32 + ENVIRONMENTAL_FACTOR 12
+ QUALITY 5 + CELLULAR_LOCALIZATION 5 + PATHWAY 4 + CHEMICAL 4
+ MOLECULAR_FUNCTION 2 + STATE 1  =  146

The review's tally 81+32+12+5+5+4+2+1 omits CHEMICAL 4 and so reaches 142. Recomputed from the corpus and left unchanged; the new appendix shows the same nine rows summing to 146, so it's now checkable rather than asserted. No untyped subjects exist in this set.

Also

Re-verified after the changes: just verify-proposal PASS (0 failures), just robot-validate-proposal PASS (no UNSAT, +6), just qc green, 420 tests pass. Understood that this sandbox couldn't run those independently.

@culturebot-reviewer

culturebot-reviewer Bot commented Aug 6, 2026

Copy link
Copy Markdown

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


Re-review: METPO proposal v8 (commit 2dd3a6ce) — approved

All three real findings are resolved, and the fourth was mine to withdraw.

🔴→ withdrawn: the 142-vs-146 arithmetic

You're right and I was wrong. 81+32+12+5+5+4+**4**+2+1 = 146 — my tally dropped CHEMICAL 4. The published breakdown was correct as written. Nothing to change.

Verified against the corpus

The partition is real, not asserted. I walked the audit rows for each bucket:

  • 5 acceptor edges / 3 recordsdissimilatory_iron_reduction (ferric_iron, solid_fe3_mineral, dissolved_fe3_om_complex), dissimilatory_manganese_reduction (mn_iv_oxide), dissimilatory_metal_reduction (metal_terminal_acceptor). All five subjects are node_type: CHEMICAL, so the "a chemical species" definition holds for every member. The other enables→TRAIT edge in DMR (anoxia) correctly falls to confers.
  • 13 donor edges / 13 records — one per record across the lithotrophy/organotrophy family. chemoorganoheterotrophic contributes two enables→TRAIT edges and only the chemical one (organic_molecule) is in the donor bucket; substrate_transport_system goes to confers. That's the distinction the count depends on and it's made correctly.
  • 146 = 164 − 18 is therefore forced, independent of the node-type breakdown.

The appendix labels match the corpus character-for-character, including the near-collision between chemolithotrophic's "inorganic chemical electron donor" and chemolithoheterotrophic's "inorganic chemical donor" — both node id inorganic_chemical_donor. And the stated regeneration rule ("label or edge description names the role") is exactly what separates mn_iv_oxide and organic_molecule from their siblings; I checked those two edge descriptions and the rule bites as written.

oxygen_preference — you were right, I inferred wrong. The node is MOLECULAR_FUNCTION, your classifier requires a CHEMICAL subject, so it lands in confers. My 5-edge reconstruction had substituted it for dissimilatory_manganese_reduction. The appendix now says this explicitly and flags the CHEBI:15379 retyping as migration work, which is the right place for it.

DIR narrative (metpo_proposal_properties_robot.tsv:5, proposal.md:105-113) is now accurate on every point I can check: all three edges are <Fe(III) species> enables dir_trait, all three nodes describe terminal acceptors, and "once two of them were reversed onto enables by PR #300" matches the record's REVERSE_CAUSAL_EDGE_DIRECTION note at line 202-215 — ferric_iron was already enables, exactly two were reversed. The claim that the acceptor role now survives only in free text is the correct reading of #303.

OWL range subsection — states the gap plainly and gives the reason tightening was rejected. mappings/predicate_grounding.tsv does carry subject_types/object_types columns, so the data-side enforcement it points to exists.

Scope-A row — good catch on your own table; the corrected row no longer contradicts #319.

Not re-verified

Same sandbox limits as before: python3 and gh issue view both require approvals this environment didn't grant, so I could not recompute the nine-way node_type breakdown of the 146 confers edges edge-by-edge, nor confirm #319/#321 exist. The 146 total is confirmed by subtraction; the internal split is the one number still resting on your run. Everything else above came from reading reports/predicate_domain_audit.tsv, the trait YAMLs and the proposal directly.


  • Re-read the changed proposal files
  • Re-check the 142-vs-146 arithmetic — my error, withdrawn
  • Verify the DIR narrative rewrite against the corpus
  • Verify the published 164-edge partition
  • Check the OWL-range subsection and Scope-A row fix
  • Post verdict — approve
    · branch

@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-reviewed after 2dd3a6c. All three prior findings are resolved and verified against the corpus; the 142-vs-146 finding was my arithmetic error (I dropped CHEMICAL 4) and the published tally is correct. Details in #320 (comment)

@realmarcin
realmarcin merged commit 47c80eb into main Aug 6, 2026
5 checks passed
@realmarcin
realmarcin deleted the claude/metpo-traitmech-v8-proposal branch August 6, 2026 01:29
realmarcin added a commit that referenced this pull request Aug 6, 2026
…review)

Hand-editing that file's curation event to describe the O2 reversal
overwrote the half the script had written: the record claimed "Migrated 1
causal edge(s) ... (1 to has electron acceptor)" while the file actually
migrated two — multicopper_oxidase --confers--> the trait as well. A
128-file migration where one record disclaims half its own change is
exactly what misleads a later reader of the audit trail.

Rewritten to the form the script emits for the other mixed file
(dissimilatory_manganese_reduction), so the record now matches both the
file and what a re-run would reproduce, while keeping the explanation of
why the O2 edge was reversed.

Audited every MIGRATE_ENABLES_TRAIT_EDGES event in the corpus against its
own file rather than fixing only the reported one: 0 remaining
disagreements across all 128.

Also fixed the pre-existing numerator at proposal.md:93 (from #320): "114
of these 145 subjects" was the trait-RECORD count leaking into a sentence
about subjects. By the appendix it is 125 (81 BIOLOGICAL_PROCESS + 32
GENE_OR_PROTEIN + 12 ENVIRONMENTAL_FACTOR), now stated with the breakdown
so it cannot drift from the table again.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
realmarcin added a commit that referenced this pull request Aug 6, 2026
…323)

* Migrate 164 enables->TRAIT edges onto the v8 predicates (#302, #303)

RO:0002327 (enables) has range 'biological process or activity', and a
TRAIT node is a disposition, so all 164 of these edges entailed
trait ⊑ BiologicalProcessOrActivity. They now carry the predicates
proposed in proposals/metpo_traitmech_v8 (merged in #320):

  confers                METPO:2007700   146 edges, direction unchanged
  has electron donor     METPO:2007701    13 edges, direction reversed
  has electron acceptor  METPO:2007702     5 edges, direction reversed

The 18 electron edges are reversed back to <trait> -> <chemical>, which
restores the donor/acceptor role PR #300 had to collapse onto enables
(#303). The organism-subject problem that forced that collapse does not
arise here: these predicates take a causal-node domain, not
METPO:2000001's microbe domain (#301).

The three mapping rows are GATED rather than the '*'/'*' used by earlier
placeholder rows: confers is object_types=TRAIT with the 9 observed
subject types, and the electron pair is TRAIT -> CHEMICAL. That enforces
the "a chemical species" clause the declared OWL range deliberately does
not, and is why oxygen_preference's MOLECULAR_FUNCTION node ("O2 as
terminal electron acceptor", the USE of O2 rather than O2) grounds to
confers instead.

Applied by scripts/migrate_enables_trait_edges.py, which edits the raw
lines rather than round-tripping through write_validated_trait: that
helper is not byte-identical on hand-edited YAML (#322) and would have
buried 164 real changes in reflow churn across 128 files.

Baseline deltas, both intended and both verified:

- predicate_domain_audit: 530 -> 366. ENABLES_RANGE_ON_TRAIT is fully
  burned down, 164 -> 0. The remaining 366 are the untouched #301
  microbe-domain class.
- evidence_snippet: 6 rows re-keyed, count unchanged at 2737. Reversing
  an edge changes its baseline key (subject->object), so 6 pre-existing
  findings re-key; all 6 old keys are present in the previous baseline.
  No new snippet defects.

validate-strict: 477 files, 0 errors. pages/ regenerated.

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

* Catch the O2 acceptor edge, and update the playbook (PR #323 review)

Two review findings, both valid.

manganese_oxidation described O2 as the "terminal oxidant", which names
the terminal-electron-acceptor role, but ACCEPTOR_RE matched only
`electron acceptor|terminal electron` so the edge fell to the `confers`
catch-all. That is not false, just strictly weaker — precisely the loss
#303 exists to stop. Widened the rule to `oxidant` (and `reductant`, for
symmetry on the donor side) and reversed that edge onto METPO:2007702.
Verified against the pre-migration corpus that the widened rule
reclassifies exactly one edge and no others, so the partition moves
146/13/5 -> 145/13/6 and nothing else shifts. proposals/metpo_traitmech_v8
counts, appendix and observations cells updated to match.

docs/CURATION_PLAYBOOK.md still told curators the #302 decision "is not
settled", cited the now-zero 164-edge count, and gave a worked `enables`
example — the opposite of what this PR establishes, in the one place the
repo documents the convention. Rewritten to state the settled convention
with a table of the three predicates, the trait -> chemical direction of
the electron pair, the node-type gating, and the placeholder-id caveat.

Also closed the loop the proposal had deferred TO this PR: the
oxygen_preference MOLECULAR_FUNCTION node keeps its type and grounds to
`confers`, and the mapping gate (object_types=CHEMICAL) stops it drifting
onto the electron pair later. Recorded in the proposal's classification
notes rather than left dangling.

Two advisory findings filed rather than fixed: #324 (the script's
mid-loop abort can leave a repo-wide partial migration) and #325 (no
history/records entry for a 128-file change, and nothing enforces one).

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

* Correct the five prose count sites left at 146/13/5 (PR #323 review)

The previous commit corrected the partition tables, the appendix, the
grand total and both ROBOT traits_addressed cells, but not the prose
around them — so proposal.md asserted two different partitions about 60
lines apart. Fixed:

  proposal.md:93    114 of these 146 subjects        -> 145
  proposal.md:99    The 18 electron edges            -> 19  (13 + 6)
  proposal.md:187   Edge partition (146 + 13 + 5)    -> (145 + 13 + 6)
  proposal.md:288   Repoint the 146 ... reverse 18   -> 145 ... 19
  predicate_grounding.tsv:102  across the 146 migrated edges -> 145

Line 288 mattered most: it is the adoption plan someone follows
literally when METPO mints the real ids, and it was wrong on both the
repoint count and the reversal count.

The subject_types pipe-list on the confers mapping row is deliberately
unchanged: CHEMICAL still has 3 edges, so all nine node types remain
observed. Line 254 keeps "146/13/5 to 145/13/6" because that sentence
records the correction itself.

Every remaining count claim cross-checked against the corpus:
confers 145, donor 13, acceptor 6, electron 19, total 164. PR
description synced too, since it becomes the squash commit message.

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

* Restore the confers edge to manganese_oxidation's own record (PR #323 review)

Hand-editing that file's curation event to describe the O2 reversal
overwrote the half the script had written: the record claimed "Migrated 1
causal edge(s) ... (1 to has electron acceptor)" while the file actually
migrated two — multicopper_oxidase --confers--> the trait as well. A
128-file migration where one record disclaims half its own change is
exactly what misleads a later reader of the audit trail.

Rewritten to the form the script emits for the other mixed file
(dissimilatory_manganese_reduction), so the record now matches both the
file and what a re-run would reproduce, while keeping the explanation of
why the O2 edge was reversed.

Audited every MIGRATE_ENABLES_TRAIT_EDGES event in the corpus against its
own file rather than fixing only the reported one: 0 remaining
disagreements across all 128.

Also fixed the pre-existing numerator at proposal.md:93 (from #320): "114
of these 145 subjects" was the trait-RECORD count leaking into a sentence
about subjects. By the appendix it is 125 (81 BIOLOGICAL_PROCESS + 32
GENE_OR_PROTEIN + 12 ENVIRONMENTAL_FACTOR), now stated with the breakdown
so it cannot drift from the table again.

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

---------

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