feat(query): add relevance-aware scoped retrieval#1856
Open
javargasm wants to merge 4 commits into
Open
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
query,path, andexplaindeterministic and ambiguity-safe across CLI, MCP, and HTTP transports.Problem
Graphify could build a structurally valid graph and still return a noisy or misleading search result.
Saved
Q:work-memory nodes competed with source code during ordinary queries. Broad BFS/DFS traversals discovered nodes without considering the requested token budget and only truncated the rendered text afterward. Common labels such asmain(),Config, orhandle()could resolve to whichever matching node happened to be encountered first, and CLI and MCP used different resolution paths. Community labels existed in.graphify_labels.json, but query retrieval could not use those names as scopes.That combination made large graphs harder to navigate precisely:
The issue is not graph construction. It is the retrieval policy between a valid graph and the answer presented to the user.
Root causes
What changed
Relevance-aware retrieval
graphify querynow separates semantic text from explicit control directives:Default retrieval is staged across code, documentation, tests, and likely communities. Saved work-memory remains available as a fallback when it is the only lexical evidence, but it does not compete with normal source results unless
include:memoryis intentionally present. A global relevance quota preserves strong cross-scope and cross-community matches so staging does not become a hard information silo.Community filters load canonical names from the adjacent
.graphify_labels.jsonartifact and continue to accept numeric IDs. God filters scope retrieval and traversal to a selected hub. Unknown filters return copy/pasteable suggestions instead of silently falling back to an unscoped search.Deterministic, budget-aware traversal
BFS and DFS now apply a deterministic node bound derived from
--budgetbefore rendering. Neighbor ordering is stable, high-degree transit hubs remain guarded, and equivalent graphs no longer return different bounded node sets merely because edges were inserted in a different order.The renderer can still truncate edge-heavy output at the final character budget, but traversal itself is now bounded rather than discovering an arbitrarily large subgraph first.
One ambiguity-safe resolver
CLI and MCP now share a resolver for exact node IDs, readable labels, source paths, and path-qualified identities:
Plain labels resolve only when the result is unambiguous. Otherwise:
pathandexplainprint ranked candidates and exit with status 2;The resolver uses graph-scoped exact/path/basename indexes. Source suffixes are checked only after narrowing by basename, avoiding the quadratic retained-memory amplification that would come from materializing every suffix of a graph-controlled path.
Truthful source display and bounded untrusted input
Source rendering now preserves the distinction between:
(file only)ownership;origin_file;(no source)concepts.Graph-controlled output is sanitized for control, formatting, surrogate, and Unicode line-separator categories before it reaches CLI/MCP text. Query and sidecar processing are bounded:
Malformed or oversized label sidecars are rejected atomically and retrieval safely falls back to embedded names and numeric IDs.
Skills follow the implementation
All 16 host variants now receive one canonical query contract covering:
The generator renders 14 split variants plus the Aider and Devin monoliths. It verifies exact slot cardinality, byte-compares the canonical query section inside monoliths, retains the frozen-v8 historical guard for all surrounding content, and rejects missing, duplicated, or tampered insertion points.
Why this approach
The retrieval stages deliberately rerank the existing graph instead of changing clustering or inventing semantic edges. This keeps the trust boundary clear: extraction decides what exists, while retrieval decides what evidence should be considered first.
Memory is fallback-only rather than deleted because saved outcomes remain useful when the corpus has no other lexical match. A small global quota remains outside the preferred scope because architecture questions often cross community boundaries. Ambiguity is surfaced rather than automatically broken because a deterministic wrong answer is still wrong.
Community names are read from the canonical sidecar instead of copied into graph nodes during query time. That preserves the existing build artifact model and lets relabeling take effect without rebuilding graph topology.
Review map
The PR is organized as two independently reviewable commits:
814a956graphify/serve.py, CLI/security changes, focused tests0065aa0tools/skillgen/gen.py,tests/test_skillgen.pyReview the canonical generator inputs before generated outputs. The generated
graphify/skill*.md, split query references, andtools/skillgen/expected/**files are deterministic products checked byskillgen --check.Size impact
This is intentionally one PR because runtime behavior and the instructions consumed by agents must land together. Splitting them would leave a window where installed skills invoke semantics the engine does not support, or where the engine ships without safe guidance for reserved directives and ambiguous identities.
Most deletion churn comes from replacing duplicated query runbooks in 16 host variants and their expected snapshots with one shorter canonical contract. This follows the repository precedent in #1737: one subsystem per commit, explicit generated-size accounting, and strong byte/test evidence inside a single large PR.
Validation
Compatibility and intentional behavior changes
path/explaincalls now fail with exit 2 instead of silently selecting a candidate.Out of scope
origin_fileprovenance.Related work