feat: lexical path boost + oversized-top budget guarantee for ctx smart#25
Merged
Conversation
Collaborator
Author
|
Added It can't drive the real CLI in CI (the fastembed model downloads ~90MB and isn't byte-stable across arches), so instead it hand-builds a tiny on-disk index and seeds exact embedding vectors via the library API, then calls
|
Two residual `ctx smart` misses remained after #18 (which made selection deterministic and semantic-first), each with a distinct cause: - A candidate whose path literally contains a task word could still be outranked by the semantic matches and squeezed out by the token budget (e.g. `embeddings/openai.rs` for "…openai" — present only via a call-graph edge). A lexical signal fixes this. - The single most-relevant file could exceed the whole budget and be dropped by greedy first-fit, which then backfilled with smaller, less-relevant files (e.g. `parser/solidity.rs`, 9074 tokens > the 8000 budget, was already rank 1 yet vanished). A budget-policy fix is needed. Part A — lexical path boost: - New shared `utils::lexical_tokens`: lowercases, splits on non-alphanumeric and camelCase boundaries, drops length-≤-1 tokens and a small stopword set. - `FileSelection` gains `lexical_score` = count of distinct task tokens that appear in the file's PATH (path only, not symbol names: matching identifiers would let ubiquitous names like `ctx` — a test helper across `tests/*_cli.rs` — score a hit on nearly every file and drown out the on-topic one). - `rank_files` treats a lexical hit as top-tier alongside semantic matches and orders lexical hits first within the tier — surfacing the on-topic file without any tunable float weight. Determinism (path tie-break) is preserved. Part B — budget guarantee: - `select_with_guaranteed_top` always includes the rank-1 file even when it alone exceeds the budget, then first-fits the remainder (reusing `select_by_token_budget`). The handler prints a one-line stderr note when the top file is oversized. Adds unit tests for the tokenizer, lexical promotion/ordering, path-only scoring (asserting `ctx` in a symbol name does not match), and the oversized- top budget policy. #18's determinism/tiering tests still pass with `lexical_score = 0`. Verified end-to-end: "…openai" now selects `embeddings/openai.rs` at rank 1; "parse solidity contracts" includes `parser/solidity.rs` with the oversize note; "add a new output format to ctx sql" keeps `src/commands/sql.rs` at rank 1 and stays deterministic across runs.
Adds tests/smart_relevance.rs — a library-level integration test that guards the smart file-selection ranking against regressions without needing the fastembed model (which downloads ~90MB and is not byte-stable across arches, so it cannot run in CI). It hand-builds a tiny on-disk index (files + symbols + edges) and seeds exact embedding vectors via the library API (Database::store_embedding), then calls smart_context_with_embedding directly with a crafted query vector — exercising the full seed -> call-graph expand -> lexical -> rank -> budget pipeline deterministically and offline. Two scenarios: - path_match_beats_symbol_name_noise: a task token in a file's PATH must promote the on-topic file over a higher-semantic-scored file whose only match is a symbol NAME. This reproduces the `ctx` test-helper regression that was originally caught by hand. Verified as a real guard via mutation: reverting compute_lexical_score to also match symbol names makes this test fail. - graph_only_path_match_is_promoted: a candidate reachable only through call-graph expansion whose path matches the task is surfaced to the top rather than being outranked by the semantic matches and dropped.
saldestechnology
force-pushed
the
worktree-smart-lexical-budget
branch
from
July 11, 2026 13:32
5d29601 to
57d6f6c
Compare
…atures The project-config load added in #28 was named `config`, shadowing the `SmartConfig config` above it, so `smart_context_with_embedding(..., config)` passed the wrong type. Only compiled under the `mcp` feature, so the default build (and macOS CI) missed it; `--all-features` failed to compile. Rename the project config to `project_config`. (Latent on main from #28; this fix lands there via this PR.)
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.
Follow-up to #18. After #18 made
ctx smartdeterministic and semantic-first, two residual misses remained — and diagnosis (viactx smart --dry-run, which lists the full candidate set) showed they have different root causes:embeddings/openai.rsmissing for "generate embeddings with openai"Calls) whose path contains "openai", but ranks below the semantic matches and is squeezed out by the budgetparser/solidity.rsmissing for "parse solidity contracts"SemanticMatch, but 9074 tokens > the 8000 budget, so greedy first-fit drops it and backfills with smaller files(The original #18 case — "add a new output format to ctx sql" →
src/commands/sql.rsat rank 1 — must not regress.)There is no relevance-eval harness in the repo to tune against (the merged
perf/,snapshot, and run-record work are timing / code-metrics / agent-outcome tools, not file-selection evals), so this uses integer token-overlap counts — no float coefficient — and is validated against concrete queries + unit tests.Part A — lexical path boost
utils::lexical_tokens: lowercases, splits on non-alphanumeric and camelCase boundaries, drops length-≤-1 tokens and a small stopword set.FileSelection.lexical_score= count of distinct task tokens present in the file's path. Path only, not symbol names — matching identifiers is low-precision:ctx(the tool's own name) appears as a test helper acrosstests/*_cli.rsand would score a hit on nearly every file, demoting the on-topic one. (This was caught during end-to-end verification: an earlier symbol-name variant regressed the sql case by promoting test files; path-only fixes it.)rank_filestreats a lexical hit as top-tier alongside semantic matches and orders lexical hits first within the tier. fix: make ctx smart deterministic and semantic-first #18's determinism (path tie-break) and tiering are preserved.Part B — budget guarantee
select_with_guaranteed_topalways includes the rank-1 file even when it alone exceeds the budget, then first-fits the remainder (reusing the sharedselect_by_token_budget). The handler emits a one-line stderr note when the top file is oversized.Verification
lexical_score = 0).src/embeddings/openai.rsat rank 1.src/parser/solidity.rsincluded, with the oversize stderr note.src/commands/sql.rsat rank 1, identical set across 5 runs (no fix: make ctx smart deterministic and semantic-first #18 regression; thectx-noise test files are correctly demoted).perf/impact:smartis not a perf scenario.