Repository navigation
v0.9.27 incremental --no-cluster preserves nodes but loses and mis-targets cross-file edges after #2169 #2213
Description
Summary
The v0.9.27 fix for #2169 prevents the catastrophic changed-file-subset overwrite, but incremental code-only, no-cluster extraction still does not produce the same graph as a forced full extraction of the identical source tree.
In a minimal public corpus, an edit that only appends a Python comment preserves all 218 nodes but leaves the incremental graph with 575 edges versus 577 from a forced rebuild. Three canonical cross-file edges are missing and one absolute-path-shaped import target appears instead.
Environment
- Graphify:
0.9.27 - Python:
3.11.15 - NetworkX:
3.6.1 - OS: Ubuntu 26.04 on WSL2 (
Linux 6.18.33.2-microsoft-standard-WSL2, x86_64) - Target corpus: public
pallets/itsdangerous, commit672971d66a2ef9f85151e53283113f33d642dabd - Extraction mode:
--code-only --no-cluster, with a separate--outroot
Reproduction
root="$(mktemp -d)"
git clone https://github.com/pallets/itsdangerous.git "$root/target"
# Baseline.
HOME="$root/home" XDG_CACHE_HOME="$root/home/.cache" \
uvx --from graphifyy==0.9.27 graphify extract "$root/target" \
--code-only --no-cluster --out "$root/out" --force
# Change one file without changing its AST.
printf '\n# Graphify v0.9.27 incremental-rebuild verification marker.\n' \
>> "$root/target/src/itsdangerous/encoding.py"
# Incremental extraction.
HOME="$root/home" XDG_CACHE_HOME="$root/home/.cache" \
uvx --from graphifyy==0.9.27 graphify extract "$root/target" \
--code-only --no-cluster --out "$root/out"
# Forced full extraction of the exact same changed source tree.
HOME="$root/home" XDG_CACHE_HOME="$root/home/.cache" \
uvx --from graphifyy==0.9.27 graphify extract "$root/target" \
--code-only --no-cluster --out "$root/full" --forceCompare node and edge dictionaries as order-independent JSON sets.
Observed result
Incremental scan: 1 changed, 17 unchanged, 0 deleted
Incremental graph: 218 nodes, 575 edges
Forced full graph: 218 nodes, 577 edges
Node sets equal: True
Edge sets equal: False
Incremental graph SHA-256:
0edc5402423eee96db8c157ef359043721f85fd379679a37401dcde02d4af29d
Forced-full graph SHA-256:
e2436f2d8342e12f4d4ba9a58c0d374ff7a419705a5fb364f7ec0c58406809e4
Missing from the incremental graph:
src_itsdangerous_encoding_base64_decode
--calls--> src_itsdangerous_exc_baddata
src_itsdangerous_encoding
--imports--> src_itsdangerous_exc_baddata
src_itsdangerous_encoding
--imports_from--> src_itsdangerous_exc
The incremental graph instead adds:
src_itsdangerous_encoding
--imports_from--> tmp_graphify_v0927_verify_target_src_itsdangerous_exc_py
The affected edges all originate from src/itsdangerous/encoding.py. The changed source only gained a comment after the final function, so these import/call relationships should be identical.
Expected result
For a change that does not alter the AST, incremental extraction and a forced full extraction of the same source tree should have equivalent node and edge sets. Cross-file targets should remain canonical rather than becoming absolute-path-shaped IDs.
Relation to #2169
#2169 was closed as fixed in v0.9.27, with the release note stating that incremental --no-cluster now:
- merges the existing graph forward; and
- canonicalises cross-file edge targets.
The first part is materially improved: unchanged nodes are no longer discarded. The reproduction above shows a residual failure in the second part and a remaining incremental/full edge-set mismatch.
Suggested regression test
- Build a two-module corpus where module A imports and calls a symbol from module B.
- Force-extract the full graph.
- Append a comment to module A.
- Incrementally extract.
- Force-extract the same changed tree to another output root.
- Assert equality of normalised node and edge sets, including canonical cross-file targets.
Activity
fix(extract): stamp target_file on Python imports and markdown refs s…
chore: bump to 0.9.28; changelog for #2211/#2213/#2210/#2212/#2215/#2221
safishamsi commentedon Jul 27, 2026
The edge-loss half of this shipped in v0.9.28: incremental --no-cluster no longer leaves a dangling absolute-path import target (Python relative imports now canonicalize like the JS path). The remaining half — INFERRED cross-file edges to an UNCHANGED neighbor not being regenerated on a subset extraction (a pre-existing, batch-scoped-resolution limitation that also affects the clustered path) — is tracked separately in #2230 with a fix spec. Closing this as the reported --no-cluster regression is resolved; follow #2230 for the resolution-completeness work. https://github.com/Graphify-Labs/graphify/releases/tag/v0.9.28
Merge upstream/v8 (0.9.28) into feat/clusters
fix(extract): stamp target_file on Python imports and markdown refs s…
chore: bump to 0.9.28; changelog for Graphify-Labs#2211/Graphify-Labs…