Skip to content

v0.9.27 incremental --no-cluster preserves nodes but loses and mis-targets cross-file edges after #2169 #2213

Closed

Description

@Sarakael78

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, commit 672971d66a2ef9f85151e53283113f33d642dabd
  • Extraction mode: --code-only --no-cluster, with a separate --out root

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" --force

Compare 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

  1. Build a two-module corpus where module A imports and calls a symbol from module B.
  2. Force-extract the full graph.
  3. Append a comment to module A.
  4. Incrementally extract.
  5. Force-extract the same changed tree to another output root.
  6. Assert equality of normalised node and edge sets, including canonical cross-file targets.

Activity

  1. safishamsi commented on Jul 27, 2026

    @safishamsi
    Member

    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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions