build_from_json now folds name->label, path->source_file, edge type->relation,
and confidence_score->confidence=INFERRED before validation, so alias-carrying
nodes stop entering the graph without label/source_file (invisible, unmergeable
ghosts); the same folds run before dedup. _semantic_id_remap now also learns the
absolute-path stem form, so a Windows absolute-derived semantic id re-keys to the
canonical root-relative id. The extraction warning now breaks errors down by cause.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
validate_extraction() is documented to return a list of error strings ("empty
list means valid"), but raised TypeError: unhashable type: 'list' when a node
id -- or an edge source/target -- was a non-hashable value such as a list. This
occurs in practice when an LLM extraction subagent emits malformed JSON like
{"id": ["foo", "bar"], ...}. Crash sites: the node_ids set comprehension and
the `edge[...] not in node_ids` membership tests.
Because build_from_json() validates at its start, a single malformed node
aborted the entire build, losing an otherwise-complete extraction of a large
corpus. build_from_json() itself would also raise (G.add_node(<list>) and the
`not in node_set` test) if the validator were bypassed.
- validate.py: build node_ids during the node pass, adding only hashable ids;
report a non-hashable id/endpoint as an error string instead of crashing.
All existing messages and the dangling-edge checks are preserved.
- build.py: skip dict nodes with a missing/non-hashable id and edges with
non-hashable endpoints (stderr warning). Non-dict nodes are deliberately
left to raise so the multigraph diagnostic still observes shape errors.
- tests: 3 cases in test_validate.py and 2 in test_build.py.