Skip to content

fix(query): name the graph the answer came from (#2789) - #2808

Open
abhay-codes07 wants to merge 1 commit into
Graphify-Labs:v8from
abhay-codes07:fix/query-names-the-graph-it-opened
Open

fix(query): name the graph the answer came from (#2789)#2808
abhay-codes07 wants to merge 1 commit into
Graphify-Labs:v8from
abhay-codes07:fix/query-names-the-graph-it-opened

Conversation

@abhay-codes07

Copy link
Copy Markdown
Contributor

Fixes #2789.

Thanks @cdtatarevich — "confident, well-formed answer from the wrong corpus" is the right framing, and it is what made this worth fixing rather than documenting.

The change

The query header now leads with the graph it opened, and its size:

Graph: graphify-out/graph.json (355 nodes) | Traversal: BFS depth=2 | Start: [...] | 33 nodes found

Relative to the CWD when the graph sits underneath it — the ordinary case, and short enough not to be noise. Absolute when it does not, which is exactly the case worth noticing:

Graph: C:/other/proj/graphify-out/graph.json (3178 nodes) | Traversal: BFS depth=2 | ...

Two details that are deliberate rather than incidental:

The count is the corpus, not the traversal. The line already ends with N nodes found, which is the traversal result. The number next to the path is the whole graph, because your report's tell was a 355-node subproject answering as a 3178-node parent. Reusing the traversal count would have hidden precisely that.

Absolute is the loud form. Elsewhere in the codebase absolute host paths are something to avoid (#2598, #2628) because they get written into artifacts and travel between machines. This is terminal output for the operator standing in a directory, so the reasoning inverts: a path outside the CWD should look different from one inside it.

Scope

graph_path is optional and the header is byte-identical when it is not passed, so no existing caller changes behaviour. The two call sites that do know their path — the CLI query command and the MCP query tool — now pass it, and both already had the resolved path in hand for querylog.log_query(corpus=...), so nothing new is computed.

I did not add a --graph-mismatch warning or make query search upward for a nearer graphify-out/. Both were tempting, but changing which graph is opened is a behavioural decision with its own failure modes, and the issue's actual complaint is that the choice was invisible. Making it visible is the smaller, complete fix; happy to follow up on resolution if you want it.

_display_graph_path swallows resolution failures and falls back to the string it was handed — a display helper must never be the reason a query fails, and there is a test feeding it null bytes and 300-character garbage.

Tests

tests/test_query_names_its_graph.py (9 tests): the relative and absolute forms, POSIX separators on both platforms, the unchanged header when no path is supplied, the corpus-vs-traversal count distinction, hostile input, and an end-to-end check that a parent project and a vendored subproject produce headers you can tell apart — the exact scenario from the report.

Validation

Windows 11, Python 3.12, branched off 4fca621 (0.9.44).

  • Full suite: 20 failed, 4469 passed -> 20 failed, 4478 passed. Identical failure set — no regressions; the +9 are the new tests.

graphify-out/ resolves against the CWD. Running `graphify query` from a parent
project while thinking about a vendored subproject answers from the parent's
graph, and the answer is well-formed and confidently wrong: nothing in the output
named the graph file, the scan root, or the node count. The reporter only caught
it because the community labels were ones they had not assigned in that project.

The header now leads with the graph and its size:

    Graph: graphify-out/graph.json (355 nodes) | Traversal: BFS depth=2 | ...

Shown relative to the CWD when the graph sits underneath it -- the ordinary case,
and short -- and absolute when it does not, which is exactly the case worth
noticing:

    Graph: C:/other/proj/graphify-out/graph.json (3178 nodes) | Traversal: ...

The node count travels with the path because "355 nodes" against "3178 nodes" is
often the first thing that looks wrong, and it is deliberately the corpus size
rather than the traversal's `N nodes found`; conflating the two would hide the
mismatch this exists to surface.

`graph_path` is optional and the header is byte-identical when it is not passed,
so nothing that calls _query_graph_text without it changes. Both call sites that
do know their path -- the CLI `query` command and the MCP query tool -- now pass
it, and both already had the resolved path on hand for querylog.

_display_graph_path is a display helper and swallows resolution failures: it must
never be the reason a query fails.
Copilot AI lite review requested due to automatic review settings August 16, 2026 19:37

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@graphify-labs graphify-labs Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Graphify reviewed this change.

Looks safe to merge — no coupling regressions and no blocking issues, checked against the code graph (not a self-assessment).

Formal verification. 1 change(s) tested, no difference found (not proven).


Graphify review — findings

Adds a Graph: line to the query header naming the source graph file and its node count, via a new _display_graph_path helper in serve.py (relative under CWD, absolute otherwise, always POSIX). Threads graph_path through _query_graph_text and passes it from both the CLI dispatch_command and the MCP _build_server call sites. Covers the helper and header behavior in a new tests/test_query_names_its_graph.py.

No blocking issues surfaced. 1 lower-confidence candidate did not survive cross-model review.

Analysis details — impact, health, verification

Impact & health

Graphify review

Impact — 527 functions depend on the 179 functions this change touches.

Health — this change adds coupling hotspots:

  • new: dispatch_command() — 2 callers, 117 callees
  • new: _query_graph_text() — 20 callers, 9 callees
  • new: _score_query() — 15 callers, 5 callees
  • new: _query_terms() — 17 callers, 3 callees
  • new: run_benchmark() — 16 callers, 3 callees
  • new: _stale_graph_sources() — 7 callers, 6 callees
  • new: _build_server() — 2 callers, 16 callees
  • new: _load_graph() — 9 callers, 3 callees
  • …and 11 more — each is listed as a finding

Verification — 527 functions in the blast radius were not formally verified this run (proofs are advisory here).

Gate & verification

graphify gate

PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.

Advisory (not blocking):

  • verification_scope: 484 function(s) in the blast radius were not formally verified this run

Formal verification

Could not verify: Could not verify dispatch\_command.

The verifier did not have enough to check dispatch\_command, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: not verifiable: all 23 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly SystemExit — names the real obstacle, not a sampling gap)

Could not verify: Could not verify \_build\_server.

The verifier did not have enough to check \_build\_server, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: not verifiable: all 23 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly ImportError — names the real obstacle, not a sampling gap)

No difference found (not proven): No behavior difference found in \_query\_graph\_text (not a proof).

The verifier ran both versions of \_query\_graph\_text on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.

Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.

Note: An input the sampler did not try could still differ.

· 1 grounded finding(s) anchored inline below; 18 more finding(s) on lines outside this diff (see the check run).

Comment thread graphify/serve.py
return str(graph_path)


def _query_graph_text(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Health regression_query_graph_text()

fans out to 9 callees (efferent coupling); 20 callers depend on it (afferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.

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.

query resolves graphify-out/ against CWD and never echoes which graph it opened — answers silently come from another project's graph

2 participants