Skip to content

fix(query): allow disabling doc demotion for doc-first corpora (#5084) - #5116

Open
mohitdixit02 wants to merge 1 commit into
Graphify-Labs:v8from
mohitdixit02:fix/query-doc-demotion-option-5084
Open

mohitdixit02 wants to merge 1 commit into
Graphify-Labs:v8from
mohitdixit02:fix/query-doc-demotion-option-5084

Conversation

@mohitdixit02

Copy link
Copy Markdown

What does this PR do?

In #4982, _query_path_weight introduced a 0.3x demotion multiplier for all documentation, markdown (.md, .rst), and configuration files to prioritize source code over test and documentation nodes. In documentation-first repositories, concept wikis, and markdown knowledge bases, this unconditionally penalizes high-relevance content pages and causes them to lose seed slots to incidental code fragments.

This PR adds an opt-out mechanism (demote_docs=False) to _query_path_weight, _score_query, and _query_graph_text, and exposes --no-doc-demotion (and --no-demote-docs) in the graphify query CLI while preserving demote_docs=True as the default for existing code-first corpora.

Example:

# Query markdown-first knowledge bases without 0.3x doc penalty
graphify query "agent memory patterns" --no-doc-demotion

Fixes #5084

Type of change

  • Bug fix
  • New feature
  • Documentation
  • Tests or CI
  • Refactor
  • Security fix

Verification & Invariants

This change preserves existing default behavior (demote_docs=True) for all existing callers of graphify query and _query_graph_text. Callers of _score_nodes, path, and explain remain unchanged and unaffected.

  • Read the CONTRIBUTING.md /CONTRIBUTING.md guide.
  • Reproduced the issue and identified the invariant.
  • Made the smallest fix necessary.
  • Added a regression test (if bug fix) or isolated boundary test.
  • Kept the PR description synchronized with the final implementation.
  • Documented any limitations / unsupported cases explicitly.

How was this tested?

    uv run pytest tests/test_query_source_first.py
    uv run pytest tests/test_query_cli.py
    uv run pytest tests/test_serve.py
    uv run ruff check graphify/cli.py graphify/serve.py tests/test_query_cli.py tests/test_query_source_first.py

Graphify-specific checklist

  • I updated generated skill artifacts (uv run python -m tools.skillgen --bless) when changing their source fragments.
  • I confirmed that AST/structural extraction remains deterministic (no ambient state dependencies like ENV variables).
  • I reviewed changes for security implications (no unsafe interpolation into shell/Python).
  • I confirmed no API keys or local-only graph data are included.
  • (If applicable) I disclosed AI authorship in my commit messages.

@github-actions

github-actions Bot commented Nov 9, 2026

Copy link
Copy Markdown

Thanks for the pull request, @mohitdixit02. A maintainer will review it soon.

Want to talk it through while it is in review? Come join us on our Discord server. For longer-form discussion there is also GitHub Discussions.

A couple of things that speed up review: make sure the test suite passes on Python 3.10 and 3.13, and that the change keeps extraction deterministic.

@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.

Worth a look — the grounded gate found no coupling regressions or blocking issues, but 1 advisory finding(s) below merit a look before merge.

Not checked: tests were not run; formal verification proved 0 of 5 changed function(s) (1 sampled, not proven, 4 not verified).

Formal verification. PR-changed functions: 1/5 verified (0 proven, 1 may-equivalent, 0 distinguished) · 4 not verified (4 vacuous).

Not verified on this run: dispatch\_command (vacuous: never exercised), \_build\_server (vacuous: never exercised), \_query\_path\_weight (vacuous: never exercised), \_score\_query (vacuous: never exercised).


Graphify review — findings

Adds an opt-out for doc demotion in query: --no-doc-demotion (alias --no-demote-docs) and --demote-docs set it from the CLI, the MCP query_graph tool takes a demote_docs argument, and project settings can set demote_docs or query_demote_docs. With demotion off, _query_path_weight keeps markdown, config and other doc nodes at full weight, while test nodes are still demoted unless the question is about tests. The default stays on, and the resolved value is forwarded to both _query_graph_text and querylog.log_query, a call whose signature this diff does not change.

Worth a look

  • CLI passes new demote_docs kwarg to querylog.log_query, which this diff does not update — graphify/cli.py:2251 · Escalate · high
    • NOT verified (no proof, no reproducing execution) — needs human review
Analysis details — impact, health, verification

Impact & health

Graphify review

Impact — 1041 functions depend on the 360 functions this change touches.

Health — this change adds coupling hotspots:

  • worse: _query_graph_text() — 53 callers, 7 callees
  • worse: _score_query() — 24 callers, 8 callees
  • worse: _query_terms() — 29 callers, 3 callees

Verification — 1041 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: 981 function(s) in the blast radius were not formally verified this run

Test selection

Test selection

62 of 541 test file(s) selected (11%) via static blast radius.

  • tests/test_4874_jvm_resolution.py — impact
  • tests/test_affected_cli.py — impact
  • tests/test_affected_path_file_node.py — impact
  • tests/test_affected_seed_reference_forms.py — impact
  • tests/test_agents_platform.py — impact
  • tests/test_benchmark.py — impact
  • tests/test_benchmark_raw_graph.py — impact
  • tests/test_benchmark_real_query.py — impact
  • tests/test_cli_export.py — impact
  • tests/test_cloud_cta.py — impact
  • tests/test_codebuddy.py — impact
  • tests/test_compact_graph_json.py — impact
  • tests/test_confidence_score_output.py — impact
  • tests/test_dedup_shrink_refuses_force_write.py — impact
  • tests/test_devin.py — impact
  • tests/test_edge_also_seen.py — impact
  • tests/test_explain_cli.py — impact
  • tests/test_explain_same_file_tie.py — impact
  • tests/test_extract.py — impact
  • tests/test_extract_cli.py — impact
  • tests/test_extract_cost_ledger.py — impact
  • tests/test_extract_memory_release.py — impact
  • tests/test_file_label_disambiguation.py — impact
  • tests/test_global_add_tag_inference.py — impact
  • tests/test_god_nodes_cli.py — impact
  • tests/test_hollow_chunks_arm_shrink_guard.py — impact
  • tests/test_hook_content_freshness.py — impact
  • tests/test_hook_guard_token_match.py — impact
  • tests/test_hook_nudge_wording.py — impact
  • tests/test_hook_out_of_project_paths.py — impact
  • tests/test_hook_strict.py — impact
  • tests/test_hook_strict_grep.py — impact
  • tests/test_incomplete_build_guard.py — impact
  • tests/test_install.py — impact
  • tests/test_install_references.py — impact
  • tests/test_lookup_exit_codes.py — impact
  • tests/test_mcp_graph_view_cache.py — impact
  • tests/test_merge_chunks_validation.py — impact
  • tests/test_multigraph_diagnostics.py — impact
  • tests/test_no_dedup_flag.py — impact
  • tests/test_opencode_plugin_refresh.py — impact
  • tests/test_partial_cache.py — impact
  • tests/test_path_cli.py — impact
  • tests/test_path_confidence.py — impact
  • tests/test_path_type_member.py — impact
  • tests/test_provider_registry.py — impact
  • tests/test_query_cli.py — impact, changed-test
  • tests/test_query_induced_edges.py — impact
  • tests/test_query_mcp_direction.py — impact
  • tests/test_query_names_its_graph.py — impact
  • … and 12 more

Selection is safe under the controlled-regression assumption; always-run tests + a periodic full run are the backstops. Advisory — it never changes the check verdict.

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: no capturable inputs from the test suite; property tier: not verifiable: all 40 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: no capturable inputs from the test suite; property tier: not verifiable: all 40 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly ValueError — 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.

Could not verify: Could not verify \_query\_path\_weight.

The verifier did not have enough to check \_query\_path\_weight, 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: no capturable inputs from the test suite; property tier: not verifiable: all 11 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous

Could not verify: Could not verify \_score\_query.

The verifier did not have enough to check \_score\_query, 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: no capturable inputs from the test suite; property tier: not verifiable: all 47 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly TypeError — names the real obstacle, not a sampling gap)

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

Comment thread graphify/serve.py
@@ -697,7 +697,12 @@ def _score_nodes(G: nx.Graph, terms: list[str]) -> list[tuple[float, str]]:


def _score_query(

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 — _score_query()

fans out to 8 callees (efferent coupling); 24 callers depend on it (afferent coupling).

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

This branch has not been deployed

No deployments
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.

[Bug]: query doc demotion (#4982) hurts markdown-first corpora — no way to turn it off

1 participant