Graph.filter() unpacked self.node(node) with ** into addnode() for every
input id, but self.node() returns None for an id not present in the
graph. **None raises TypeError instead of the filter simply skipping an
id it doesn't recognize - the docstring says nodes are "nodes to select",
with no stated precondition that every id must already exist.
Skip ids where self.node() returns None instead of crashing.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Graph.insert() decided whether a document consumed an indexid slot based
on whether the resolved text/object value was not None. But the actual
vector index offset, computed by Transform.stream() (embeddings/index/
transform.py), increments based on key presence (`self.text in
document[1]`), regardless of the value. A document like {"text": None}
consumes a real vector index slot but was skipped by Graph.insert()
without incrementing its own index counter, so every subsequent document
in the batch got assigned to the wrong graph node id - a silent,
permanent misattribution of node ids to the wrong document data.
Track slot consumption with the same key-presence criterion Transform.
stream() uses (hasfield), separately from whether a node actually gets
created for that slot (still gated on the resolved value being non-None,
so no node is created for a genuinely empty document - but the index
still advances, leaving a gap instead of shifting every later id).
Co-Authored-By: Claude Opus 4.8 <[email protected]>