Skip to content

fix(extract): anchor source_file on the scan root, not the --out dir#1947

Open
SinghAman21 wants to merge 2 commits into
Graphify-Labs:v8from
SinghAman21:fix/1941-source-file-root-anchor
Open

fix(extract): anchor source_file on the scan root, not the --out dir#1947
SinghAman21 wants to merge 2 commits into
Graphify-Labs:v8from
SinghAman21:fix/1941-source-file-root-anchor

Conversation

@SinghAman21

Copy link
Copy Markdown
Contributor

Closes #1941.

@JensD-git's report is accurate, and the answer to their Q1 is "over-broad side effect" — this isn't intended behavior.

What happens

--out passes the output dir into extract() as cache_root, purely to relocate the cache (we don't want a graphify-out/ appearing inside a project that asked for external output). But cache_root also anchored path relativization. So with --out, root becomes the output dir, every scanned file fails relative_to(root), and the whole corpus falls into the out-of-root fallback added in #1899 — which then trips its updepth > 3 walk-up guard and collapses the path to a basename.

That guard was written for a stray ProjectReference pointing outside the corpus. Nothing scoped it to "this is actually an external reference," so once the anchor was wrong it fired on everything. On the reporter's Windows setup an --out on a different drive hits the cross-drive branch instead and basenames unconditionally — which is exactly why they saw 0 of ~120k source_files keep a separator rather than some partial mix.

The damage is total because nothing else carries the path: origin_file is stripped before serialization (#1516), the export has no file table or scan root, and the id slug is lossy (., /, , -, spaces all collapse to _). So 0% of nodes resolve, and their conclusion that the slug isn't a viable substitute is right.

The fix

extract() takes an explicit root anchor for source_file/ids/symbol resolution, and the CLI pins it to the scan root regardless of where the cache lives.

Two things convinced me this was the shape rather than tuning the updepth heuristic:

  • build() was already being called with root=target (cli.py:2853, 2856). The build layer always expected scan-root-relative paths — extract was the only component keying off --out. This makes the two agree instead of papering over the disagreement.
  • extract() writes AST cache into the analyzed source tree, bypassing CWD (pollutes read-only corpora) #1774 already established that cache location and the key/id anchor are separate concerns and decoupled them for the cache dir. The root = cache_root line was the leftover half of that job.

cache_root keeps its fallback-anchor role, so anything passing the scan root as cache_root (watch, the no---out path, ~50 tests where tmp_path is both) behaves identically. Cache location and #1899's out-of-root portability are untouched.

Verification

Reproduced at the API level first — same file, same call, only the anchor differing:

OLD (cache_root anchors): ['order_repository_tests.py']
NEW (explicit root): ['src/Data/Database/RepositoryTests/order_repository_tests.py']

Then end-to-end through the real CLI with a far-away --out: both files keep their directories, both resolve against the scan root, and no absolute paths or usernames leak — so #1899 doesn't regress. Regression test covers the exact updepth > 3 layout that triggered it, and asserts the point of the field (that root / source_file is a real file), not just the string.

Full suite: 3266 passed. The 4 failures in test_ollama_retry_cap.py are pre-existing on a clean v8 — unrelated to this change.

Existing broken graphs self-heal on the next extract.

Not in scope

Below the walk-up threshold, _portable_out_of_root_sf still returns things like ../../Users//proj/src/Foo.cs — which embeds the username #1899 set out to remove. Genuinely out-of-root, so it's a separate bug from this one; happy to file it if we want it tracked.

SinghAman21 and others added 2 commits July 16, 2026 23:28
…raphify-Labs#1941)

`graphify extract <root> --out <dir>` reduced every node's `source_file` to a
bare filename, so a graph.json could no longer be resolved back to files on
disk by joining source_file onto the scan root.

--out passes the output dir as cache_root to relocate the cache, but that value
also anchored relativization. Every scanned file then failed relative_to(root),
fell through to the Graphify-Labs#1899 out-of-root fallback, tripped its `updepth > 3`
walk-up guard -- written for a stray ProjectReference, not a whole corpus -- and
collapsed to a basename. On Windows an --out on another drive hit the
cross-drive branch and basenamed unconditionally, which is what the reporter
saw: 0 of ~120k source_files kept a separator. The directory survived only in
the node id slug, lossily (`.`, `/`, `\`, `-`, spaces all map to `_`), and no
other field carried it -- origin_file is stripped (Graphify-Labs#1516) and the export has no
file table -- so 0% of nodes resolved.

extract() now takes an explicit `root` anchor for source_file/ids/symbol
resolution, which the CLI pins to the scan root independent of where the cache
lives. This completes the cache/anchor decoupling Graphify-Labs#1774 started and matches
build(root=target), which already anchored on the scan root -- extract was the
lone component keying off --out.

cache_root keeps its fallback-anchor role, so callers that pass the scan root as
cache_root (watch, the no---out CLI path, tests) are unchanged, as are cache
location and out-of-root portability (Graphify-Labs#1899).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

source_file reduced to bare basename for all nodes in 0.9.16 (was relative path in 0.9.13) — breaks resolution against a code root

1 participant