Skip to content

[bugfix] definition_file keeps the build machine's absolute path while source_file is repo-relative #3223

Description

@winesoft-namjin-yun

definition_file is stored as the build machine's absolute path, while its sibling source_file is repo-relative. Any consumer that reads the graph from a different machine — or a different checkout — gets a path it cannot open, and the graph leaks the build host's directory layout.

Reproduced on 0.9.53 (stock, no local modifications).

Reproduction

Two files are enough — one C++ declaration/definition pair:

repro/
  src/Foo.h
  src/Foo.cpp
// src/Foo.h
#pragma once

class Foo {
public:
    int Bar(int x);
};
// src/Foo.cpp
#include "Foo.h"

int Foo::Bar(int x) {
    return x + 1;
}
cd repro && git init -q && git add -A && git commit -qm init
graphify update /absolute/path/to/repro

Resulting node in graphify-out/graph.json (4 nodes total, 1 carries definition_file):

{
  "id": "src_foo_foo_bar",
  "label": "Bar",
  "source_file": "src/Foo.h",
  "source_location": "L5",
  "definition_file": "/absolute/path/to/repro/src/Foo.cpp",
  "definition_location": "L3"
}

source_file is src/Foo.h. definition_file is absolute.

Expected

definition_file should be src/Foo.cpp — the same form as source_file. It names a file inside the scanned tree, so it should be portable for the same reasons source_file is.

Why it matters

The MCP get_node tool prints both, so an agent sees:

Source:     src/Foo.h L5
Defined in: /home/ci/build/repos/myproject/src/Foo.cpp L3

If the graph is built anywhere other than where it is consumed — a build server, a container, a shared graph served over --transport http — the second path does not resolve on the client, and the client cannot rewrite it because the prefix is not part of the graph. It also puts the build host's directory layout into a graph that may be shared more widely than the machine that produced it.

We hit this with graphs built on a Linux indexing host and consumed by editors on other machines: 13,682 nodes across three C++ repositories, 100% of them absolute.

Root cause

_merge_decl_def_classes copies the implementation's source_file before any normalization runs:

  • graphify/extractors/resolution.py:2260keeper["definition_file"] = definition["source_file"]

Every pass that makes stored paths portable keys on the literal string "source_file" and therefore never sees the new attribute:

  • graphify/build.py:975node["source_file"] = _norm_source_file(node["source_file"], _root)
  • graphify/watch.py:321_relativize_source_files
  • graphify/watch.py:339_rebase_relative_source_files

grep -c definition_file graphify/build.py graphify/watch.py returns 0 and 0 on 0.9.53.

The field is otherwise inert — resolvers, incremental merge, dedup and the shrink guard do not read it. As far as I can tell the only readers are graphify/serve.py:1809 (get_node output) and exporters/graphdb.py, which passes scalar attributes through. So this is an output-correctness bug, not a graph-integrity one.

Why the existing test does not catch it

tests/test_languages.py:3594:

assert str(bar.get("definition_file", "")).endswith("Foo.cpp"), bar

An absolute path ends with Foo.cpp too, so the assertion passes either way. A full-string comparison against the expected relative path would have caught it. (That test came with the original decl/def change, which I contributed — the gap is mine.)

Existing patch

PR #3023 has the fix: it relativizes definition_file alongside source_file in build_from_json and introduces a _PORTABLE_PATH_KEYS tuple in watch.py so both normalizers cover the pair. It is against v8 and still applies cleanly to 0.9.53 — happy to rebase, split, or hand it over if you would rather write it differently.

Activity

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