Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
294 changes: 294 additions & 0 deletions conf/causal_graph_audit_baseline.tsv

Large diffs are not rendered by default.

44 changes: 44 additions & 0 deletions docs/CURATION_PLAYBOOK.md
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,50 @@ Those numbers came from measuring the graphs by hand. #359 makes it routine —
`reports/causal_graph_connectivity.tsv`, one row per graph, arriving with
**#363**. Once it lands, quote that table rather than the finding counts.

### One `node_id` means one thing — across the whole corpus

`audit-graphs` flags `INCONSISTENT_NODE_TYPE` when one `node_id` carries
different `node_type`s in different records (#356). It is the only
**cross-record** check here, and that is exactly why the defect survived: read
alone, neither record is wrong.

Baselined at **294 occurrences across 63 `node_id`s**. The worst is
`proton_motive_force` — `STATE`×18, `BIOLOGICAL_PROCESS`×13, `CHEMICAL`×2,
`CAPACITY`×2, for one concept.

**Why it stopped being cosmetic.** #355 minted `powers` (`METPO:2007900`) gated
to `subject_types = BIOLOGICAL_PROCESS|STATE`. Two byte-identical assertions now
behave differently purely by subject typing:

```
physiology/carboxydotrophic.yaml proton_motive_force (STATE) -> atp_synthase grounds
physiology/phototrophic.yaml proton_motive_force (CAPACITY) -> atp_synthase blocked_by_node_type
```

Typing decides groundings now. A disagreement is no longer just untidy.

**A hit is not automatically a defect.** `terminal electron acceptor` is
deliberately both `CHEMICAL` and `MOLECULAR_FUNCTION`; `mappings/node_grounding.tsv`
carries a row for each, noting that one METPO class covers both senses and the
`MOLECULAR_FUNCTION` typing "surfaces the role-of interpretation". Same
two-senses shape as `reduces` (#330/#333) and the `CAPACITY` table above.

So ask **does this id mean one thing?**

| answer | fix |
|---|---|
| yes, and one type is right | normalise the outliers to it |
| yes, but the right type is arguable (`STATE` vs `BIOLOGICAL_PROCESS`) | decide once, record why, normalise |
| **no — it means two things** | **split into two `node_id`s**, not one type |

The gradient *is* a state; generating and maintaining it *is* a process. If a
record means the second, it should not be reusing the id for the first.

**Do not repeat #352's mistake.** The test is not "is this type defensible in
isolation" — it is "is it compatible with what the record and its predicates
already assert". #352 spent three review rounds learning that on the
neighbouring `DISPOSITION_MISTYPED` family, and the grounding is what settles it.

### `enables` needs a process-or-activity object

Separately from the domain rule above, `enables` (`RO:0002327`) has a
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
history_version: 1
target:
kind: infrastructure
path: scripts/audit_causal_graphs.py
slug: inconsistent-node-type
session:
id: 2026-08-12T000912Z-claude-code-779670
timestamp: '2026-08-12T00:09:12Z'
actors:
- type: ai_agent
name: claude-code
links:
issues:
- https://github.com/CultureBotAI/TraitMech/issues/356
events:
- type: EDIT
outcome: changed
sections:
- causal_graphs
summary: Detect one node_id carrying several node_types across records
details: 'Every other check in audit_causal_graphs.py is scoped to one graph. This defect
is not: two records disagree about what a node IS, and neither is wrong read alone, which
is why nothing caught it. Added node_type_index() plus an INCONSISTENT_NODE_TYPE finding,
baselined at 294 occurrences across 63 node_ids. The issue understated the scale by about
7x: it reports proton_motive_force typed four ways across 9 records; measured, it is 35
records (STATE 18, BIOLOGICAL_PROCESS 13, CHEMICAL 2, CAPACITY 2), and 63 node_ids disagree
with themselves corpus-wide including membrane_potential at four types. It stopped being
cosmetic when 355 minted powers (METPO:2007900) gated to subject_types BIOLOGICAL_PROCESS
or STATE: carboxydotrophic.yaml''s proton_motive_force (STATE) grounds while phototrophic.yaml''s
(CAPACITY) is blocked_by_node_type, for byte-identical assertions. Reported per occurrence
rather than on a presumed-wrong minority, because nothing here knows which type is right
- proton_motive_force splits 18 STATE to 13 BIOLOGICAL_PROCESS and the gradient genuinely
is a state while generating it is a process, so the majority is an observation and not
a verdict; per-occurrence rows also mean a family clears together the moment it is normalised.
The detail leads with node_id so _key discriminates by node within a graph, deliberately
NOT with the type set, since a family part-way through a burn-down would otherwise re-key
on every step and un-suppress rows nobody has reached yet. A test pins that. Documented
that not every hit is a defect: terminal electron acceptor is deliberately both CHEMICAL
and MOLECULAR_FUNCTION and node_grounding.tsv carries a row for each saying one METPO
class covers both senses, the same two-senses shape as reduces (330/333). Where a family
really means two things the fix is two node_ids rather than one type, which is written
into the playbook as a decision table. Detection only - no data changed, and the burn-down
is the next step. 534 tests pass (4 new), ruff clean.'
Loading