Files
Yatharth Anand 993e081a6d fix(attributes): preserve prior node attributes when no entity type applies
`extract_attributes_from_nodes` assigns `node.attributes = attributes`
unconditionally, and `_extract_entity_attributes` returned a bare `{}` whenever
no entity type applied to the node. Because a deduplicated node is the node
hydrated from the database (`_promote_resolved_node` returns the existing
candidate, and `get_entity_node_from_record` populates `attributes`), and the
entity save queries replace attributes wholesale, that `{}` silently cleared
attributes a previous typed pass had stored.

Two paths reach it:

- `entity_types=None` on a later episode for a node whose attributes were
  written by an earlier call that did pass `entity_types`.
- `entity_types` supplied but missing the node's label, since
  `entity_types.get(...)` then resolves to None. A node whose only label is
  `Entity` resolves the lookup key to `''` and takes the same path.

The typed path already preserves prior values via
`apply_capped_attributes(..., merge_mode='overlay')`; only the
no-applicable-type early return bypassed it. Returning the node's prior
attributes makes the documented contract at the call site ("returns the
already-merged attribute dict, so direct assignment is the merge") true on both
paths, so no call-site guard is needed.

This matches the invariant `add_triplet` already enforces ("Update attributes
dictionary (merge rather than replace)"), covered by
`test_add_triplet_empty_attributes_preserved`. That test depends on the
`graph_driver` fixture, which parametrizes to zero drivers when all backends are
disabled, so it does not run in the default unit-test CI job. The two tests added
here need no database and do run there.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ax326xYkRzfFiZZu9n3Fjz
2026-08-20 18:52:19 -07:00
..
2025-09-05 11:30:46 -04:00
2024-08-25 10:07:50 -07:00
2024-09-11 08:44:06 -04:00