Skip to content

Fix #67: context export named parent uuids that are not in the network - #74

Merged
adamjohnwright merged 2 commits into
mainfrom
fix/67-context-export-orphans
Sep 14, 2026
Merged

adamjohnwright merged 2 commits into
mainfrom
fix/67-context-export-orphans

Conversation

@adamjohnwright

Copy link
Copy Markdown
Contributor

Closes #67.

append_regulators decomposes each catalyst and regulator to its terminal
members and wires the edges from those member uuids. The context export
wrote the uuid carried on the raw fetch row instead — the undecomposed
parent — so it named nodes that do not exist in the network.

Measured on a ten-pathway catalog before the fix:

role rows orphaned
input 3,641 0 0.0%
output 3,491 0 0.0%
catalyst 2,228 2,228 100.0%
regulator 1,018 1,018 100.0%
TOTAL 10,378 3,246 31.3%

Every consumer asking "which node is this entity, at this reaction, in this
role" got a wrong answer for exactly the two roles that carry the causality.

The fix

Catalyst and regulator rows are now read from the emitted network rather than
from the fetch rows, which makes the export correct by construction: it
reports what was wired, not what was requested. Those edges carry no
edge_reaction_id, but their target is the virtual-reaction node, so the
reaction is recovered through reaction_id_map exactly as the input/output
rows already do.

Two guards, because the quiet failure is what let this survive:

  • the export refuses to write when a row names a node absent from the
    network, and reports the count by role rather than shipping it;
  • omitting the network while catalyst rows exist now raises, instead of
    falling back to the fetch rows and silently restoring the bug.

The bug was held in place by a test

test_export_node_reaction_context asserted that the parent uuid c1
appears in the export — the exact wrong behaviour — so the suite was green
while a third of the export was broken. That test is inverted and kept as the
record, alongside a new one that fails when the network and the export
disagree.

Verified as a negative control: reintroducing the parent uuid makes the new
test fail; restoring the fix makes it pass.

Verification

  • 167 tests pass in the no-database tier.
  • Regenerated the ten-pathway catalog: orphans 3,246 → 0, and coverage
    0 → 790 of 790 distinct (node, reaction, role) triples. Zero orphans
    is trivially achievable by dropping rows, so coverage was checked
    separately.
  • The row count falls from 2,228 catalyst rows to 571 and that is not loss:
    3,758 catalyst edges collapse to 790 distinct triples because the same
    member catalyses the same reaction through many parallel virtual-reaction
    instances. The old count enumerated parent × reaction, a different and
    wrong population.
  • Export-only: all ten regenerated networks are structurally identical to
    the originals — edge counts, node counts and the
    edge_type/sign/and_or/stoichiometry signature match 10 of 10 — so no
    benchmark number moves because of this.

Known, not fixed here

Catalyst and regulator edges carry no edge_reaction_id at all (3,758 and
1,124 of them). The export now routes around it via the target reaction node,
so nothing is blocked, but the column is empty where it should not be.

🤖 Generated with Claude Code

append_regulators decomposes each catalyst and regulator to its terminal
members and wires the edges from those member uuids. The context export
wrote the uuid carried on the raw catalyst/regulator fetch row instead —
the undecomposed parent — so it named nodes that do not exist.

Measured on the ten-pathway catalog before the fix:

  input      3,641 rows      0 orphaned   0.0%
  output     3,491 rows      0 orphaned   0.0%
  catalyst   2,228 rows  2,228 orphaned 100.0%
  regulator  1,018 rows  1,018 orphaned 100.0%
  TOTAL     10,378        3,246          31.3%

Every consumer asking "which node is this entity, at this reaction, in this
role" got a wrong answer for exactly the two roles that carry the causality.

Catalyst and regulator rows are now read from the emitted network rather
than from the fetch rows, which makes the export correct by construction:
it reports what was wired, not what was requested. Those edges carry no
edge_reaction_id, but their target IS the virtual-reaction node, so the
reaction is recovered through reaction_id_map exactly as the input/output
rows already do.

Two guards, because the quiet failure is what made this survive:
- the export refuses to write when a row names a node absent from the
  network, and reports the count by role rather than shipping it;
- omitting the network while catalyst rows exist now raises, instead of
  falling back to the fetch rows and silently restoring the bug.

The bug was held in place by a test. test_export_node_reaction_context
asserted that the parent uuid "c1" appears in the export — the exact wrong
behaviour — so the suite was green while a third of the export was broken.
That test is inverted and kept as the record, alongside a new one that
fails when the network and the export disagree. Verified as a negative
control: reintroducing the parent uuid makes the new test fail, restoring
the fix makes it pass. 167 tests pass in the no-database tier.

Found while specifying deltasignal's 005-node-identity-mapping, where the
same 31.3% turns out to be 25 of the 42 diagram-glyph join misses.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
CI runs mypy on src/ and I only ran pytest locally, so this failed on all
three Python versions with a green local run.

The catalyst/regulator loop reused node_uuid and rid, both of which the
registry loop above binds as str from Dict[tuple, str]. Rebinding them with
DataFrame values, which are Any | None, is an assignment type error. Renamed
to edge_node and edge_reaction, which also reads better — they are a
different thing from the registry's entries.

Verified locally with the full CI command set this time: mypy clean on 10
files, ruff clean, 167 tests passing at 46.79% coverage against the 40%
floor.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@adamjohnwright
adamjohnwright merged commit 2a4d43a into main Sep 14, 2026
4 checks passed
@adamjohnwright
adamjohnwright deleted the fix/67-context-export-orphans branch September 14, 2026 16:47
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.

All 26,058 catalyst/regulator rows in node_reaction_context.csv point at UUIDs that do not exist in the network

1 participant