Skip to content

[travsr-mcp] guard get_execution_path against ambiguous endpoints - #799

Open
ritikpal1122 wants to merge 2 commits into
Travsr-com:masterfrom
ritikpal1122:fix/779-execution-path-ambiguity
Open

[travsr-mcp] guard get_execution_path against ambiguous endpoints#799
ritikpal1122 wants to merge 2 commits into
Travsr-com:masterfrom
ritikpal1122:fix/779-execution-path-ambiguity

Conversation

@ritikpal1122

Copy link
Copy Markdown
Collaborator

Closes #779.

The bug

get_execution_path took the first hit from search_nodes_by_name:

Ok(n) => n.into_iter().find(|n| filter.allow(...))   // first match, silently

So an ambiguous name became a guess, and the tool then reported "no path found" between the pair it happened to pick. That is a wrong answer, not a missing one: the caller is told two symbols are disconnected when the tool simply looked at the wrong ones. On fastlane/fastlane, Runner.run has four definitions and only match's calls fetch_certificate.

The fix

Resolution now goes through resolve_reference_targets, the tiered resolver graph and search_symbol already use. get_execution_path was the one tool not going through it, so this removes an inconsistency rather than adding a mechanism.

An ambiguous endpoint returns the candidate list and says which side is ambiguous. The listed exact signatures each resolve uniquely through Tier 1, so they are directly re-runnable:

source 'Runner.run' is ambiguous, 4 definitions. Re-run with one of the exact
signatures below, which each resolve uniquely, instead of the bare name:
  method:Runner.run (method) at gym/runner.rb
  method:Runner.run (method) at scan/runner.rb
  ...

Ambiguity is checked before resolution failure, because "I found several and will not choose" is more actionable than "I found none".

SEC P0: why the filter runs before the count

This is the part worth reviewing closely. Ambiguity is a property of what this caller may see:

candidates.retain(visible);
match candidates.len() { 0 => None, 1 => Unique(..), _ => Ambiguous(..) }

A name with four definitions of which the caller may see one resolves uniquely for them, and they are never told the other three exist. Counting first and filtering after would leak them through the candidate list, which is the existence oracle get_execution_path_denied_matches_not_found exists to prevent.

Ambiguous also collapses to None in into_unique(), so the non-diagnose path (which returns an empty string for every failure) cannot proceed on a guessed endpoint.

Tests, all mutation checked

Three added. Each was verified to fail when the fix is reverted, not just to pass:

Mutation Result
revert to first-match ambiguous_endpoint_lists_candidates FAILED, ambiguous_sink_names_the_sink FAILED
swap filter and count ambiguity_never_leaks_denied_definitions FAILED

Worth flagging from the second row: swapping the order does not fail the pre-existing get_execution_path_denied_matches_not_found. That ordering was previously unguarded, so the leak test covers a real gap rather than restating an existing check.

Verification

cargo test -p travsr-mcp --lib          594 passed, 0 failed, 2 ignored
cargo clippy -p travsr-mcp --all-targets  clean
cargo fmt --all -- --check                clean
cargo check --workspace --all-targets     clean
check-em-dash.sh                          OK

One file changed.

Note

The candidate list uses " at <path>" rather than the em-dash graph uses for the same list. This is a diagnostic message, not the <sig> (<kind>) — <path> wire header packages/travsr-vscode parses, so it has no separator to preserve and falls under the no-em-dash rule the CI gate enforces. The gate caught my first attempt.

get_execution_path took the first hit from search_nodes_by_name, so an
ambiguous name silently became a guess and the tool then reported "no path
found" between the pair it happened to pick. That is a wrong answer rather
than a missing one: the caller is told two symbols are disconnected when the
tool simply looked at the wrong ones. On fastlane/fastlane, Runner.run has four
definitions and only match's calls fetch_certificate.

Resolution now goes through resolve_reference_targets, the tiered resolver
graph and search_symbol already use. get_execution_path was the one tool not
going through it, so this removes an inconsistency rather than adding a
mechanism. An ambiguous endpoint returns the candidate list and names which
side is ambiguous, and the listed exact signatures each resolve uniquely
through Tier 1, so they are directly re-runnable.

Ambiguity is reported before resolution failure, because "I found several and
will not choose" is more actionable than "I found none".

SEC P0, and the reason resolve_endpoint filters before it counts: ambiguity is
a property of what THIS caller may see. A name with four definitions of which
the caller may see one resolves uniquely for them, and they are never told the
other three exist. Counting first and filtering after would leak them through
the candidate list, which is the existence oracle
get_execution_path_denied_matches_not_found exists to prevent. Ambiguous also
collapses to None in into_unique, so the non-diagnose path cannot proceed on a
guessed endpoint.

Three tests, all mutation checked. Reverting to first-match fails both
ambiguity tests. Swapping the filter and the count fails the leak test, and
notably does NOT fail the pre-existing denied-matches-not-found test, so that
ordering was previously unguarded.
@ritikpal1122
ritikpal1122 requested a review from raj-rkv as a code owner August 25, 2026 10:43
`VName::new` takes `impl Into<String>`, so `&format!(..)` borrows a value that
already satisfies the bound. Three sites in the new tests, all rejected by
`needless_borrows_for_generic_args`.

My local check ran `cargo clippy -p travsr-mcp --all-targets` without
`-D warnings`, so these surfaced as warnings I did not read rather than as a
failure. CI runs `cargo clippy --workspace --all-targets -- -D warnings`;
verified against that exact command now, workspace wide.
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.

get_execution_path silently resolves ambiguous endpoints and reports misleading 'no path found' (no ambiguity guard like graph/search_symbol)

1 participant