Skip to content

fix(tools): return zero-match probe paths - #80714

Closed
zhongwater123 wants to merge 1 commit into
NousResearch:mainfrom
zhongwater123:fix/search-zero-match-paths
Closed

fix(tools): return zero-match probe paths#80714
zhongwater123 wants to merge 1 commit into
NousResearch:mainfrom
zhongwater123:fix/search-zero-match-paths

Conversation

@zhongwater123

@zhongwater123 zhongwater123 commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

What / Why

A zero-result content search already runs bounded recovery probes, but those probes reduce ripgrep per-file counts to warning text and discard the paths. The model therefore learns that differently-cased, hidden/ignored, or literal matches exist without receiving an actionable location.

This change preserves the bounded path/count rows in structured result fields while keeping the primary exact-search result at total_count=0.

Fixes #80522

Product impact path

User/model invokes the supported core search_files tool -> tools.file_tools.search_tool -> ShellFileOperations.search -> _search_content -> _zero_match_probe -> SearchResult.to_dict(densify=True) -> tool JSON returned to the model.

On the base commit, searching for token_alpha against files containing TOKEN_ALPHA reports two case-insensitive matches but omits both paths. On this head, the same public entry returns case_insensitive_matches with each path and count. Hidden/ignored and literal recovery probes receive equivalent structured fields.

Root cause and scope

_zero_match_probe parsed every ripgrep path:count row, summed the counts, and discarded the path variable before returning a warning string. The fix introduces a small SearchProbeMatch value, parses those already-bounded rows once, attaches the relevant structured field, serializes it, and applies the existing credential/read-block filter before model delivery.

The existing head -50 bound remains in place. No additional searches are introduced.

Commit evidence

  • Base: c0106e50e7ecedb3ce34e785d949725dc4e0e457 (upstream/main fetched immediately before rebase)
  • Head: 51cffe422bacc3c71ba3719b8c040fbbfea1a302
  • History: one commit whose sole parent is the stated base
  • git range-diff reports the pre-rebase a24988f6 patch and this head as equivalent

Main / head / mutation proof

Canonical command:

scripts/run_tests.sh tests/tools/test_search_zero_match_and_multipath.py -q
  • Exact base production files plus the new tests: 4 failed, 12 passed. The case-insensitive, literal, and hidden fields were absent, and the .env probe path never reached the read-block omission path.
  • PR head: 16 passed.
  • Mutation proof: after temporarily removing only the production setattr that attaches probe matches, while leaving warnings and search commands intact, 4 failed, 12 passed for the same four contracts.
  • Restoring that line returned the suite to 16 passed. The final tree was verified identical to the rebased head.

The base production-file state was checked with:

git diff --exit-code upstream/main -- tools/file_operations.py tools/file_tools.py

Additional verification

  • scripts/run_tests.sh tests/agent/test_file_safety_credentials.py -q: 9 passed
  • scripts/run_tests.sh tests/tools/test_file_operations.py -k TestSearchResult -q: 5 passed
  • scripts/run_tests.sh tests/tools/test_file_tools.py -k TestSearch -q: 4 passed
  • ruff 0.15.10 check on all three changed files: passed
  • python scripts/check-windows-footguns.py --all: passed, 944 files scanned
  • git diff --check: passed

Checks not passing or not run

  • Full ty 0.0.21 check did not complete: Ty panicked in unmodified tools/checkpoint_manager.py (too many cycle iterations) and reported missing optional dependencies throughout the repository.
  • A three-file Ty run reported 21 existing/environment diagnostics (including unresolved pytest/yaml and pre-existing None annotations); none points to an added line. This is not represented as a passing type check.
  • Not run: the full Python test suite or live production gateway traffic.

Platform and test harness

Windows NT 10.0.26200.0; Windows PowerShell 5.1.26100.7920; Python 3.12.2; Git 2.52.0.windows.1.

The focused public-entry tests used the official ripgrep 15.1.0 portable binary through a temporary, non-repository Git Bash shim that only cleared Hermes' MSYS_NO_PATHCONV / MSYS2_ARG_CONV_EXCL variables before launching native rg.exe. The initial attempts without that shim were invalid environment failures and are not counted as code evidence. All temporary files were removed afterward.

Independent review and overlapping work

monerostar independently exercised the live public search_tool entry on Ubuntu 26.04 with ripgrep 15.1.0 against the pre-rebase head a24988f6: main returned warning counts only; the PR returned all three structured path/count fields, and the .env credential filter omitted the field while setting _omitted. Their focused suite passed 16/16. That review is attached to the old commit; despite the equivalent range-diff, this PR does not claim a current formal approval and may need review refresh.

Non-goals

This does not change exact regex matching, automatically include hidden/ignored files, alter pagination, add probe invocations, change search engine selection, or solve the separate Windows native-rg path issue.

Prepared with Codex assistance; evidence verified against the stated commits.

@alt-glitch alt-glitch added type/bug Something isn't working tool/file File tools (read, write, patch, search) P3 Low — cosmetic, nice to have labels Aug 7, 2026

@monerostar monerostar left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Ubuntu 26.04 on linux-5800x, rg 15.1.0. Live public search_tool entry on main vs this tip.

Synthetic tree with a.py/b.py (TOKEN_ALPHA), meta.py (lookup[key+1]), and .secret/conf.cfg (HIDDEN_ONLY_TOKEN):

main:

  • case mismatch: warning counts only, no case_insensitive_matches field
  • literal / hidden: same, counts in warning text only

this PR:

  • case mismatch: total_count=0 plus case_insensitive_matches for a.py and b.py (count 1 each)
  • literal: literal_matches -> meta.py count 1
  • hidden: hidden_matches -> .secret/conf.cfg count 1

Credential filter check: search for a token only in .env on the PR tip keeps total_count=0, omits hidden_matches, and sets _omitted to the secret-bearing-files message. Good.

Focused tests:
pytest tests/tools/test_search_zero_match_and_multipath.py -q -o addopts=
-> 16 passed in 1.53s

Sibling note: #80525 puts probe paths into the warning string (smaller). This tip keeps structured fields plus the read-block omit path, which is stronger for tool JSON consumers. #79605 / #77157 are more about grep fallback / Windows rg paths. Prefer this shape for #80522 on Linux here.

Looks good.

@zhongwater123
zhongwater123 force-pushed the fix/search-zero-match-paths branch from a24988f to 51cffe4 Compare August 11, 2026 10:15
@zhongwater123

Copy link
Copy Markdown
Contributor Author

Thanks @monerostar for the independent Linux validation and detailed review.

#80525 has since been merged, and #80522 is closed as completed. That merged change addresses the reported user-visible failure by returning the paths found by the zero-match probes. To keep the review queue clear and avoid maintaining a competing implementation for a resolved issue, I am closing this PR as superseded.

This PR also explored structured probe fields and routing them through the existing credential/read-block filter. I will not ask maintainers to evaluate that additional API shape after the reported issue has been resolved. If maintainers later want that separate contract, it can be proposed independently with a fresh issue and current-main evidence.

Prepared with Codex assistance; evidence verified against the stated commits.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

P3 Low — cosmetic, nice to have tool/file File tools (read, write, patch, search) type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: zero-match casing probe is hint-only — withholds the paths it already found, sends weak models into re-search spirals (measured +6 turns)

3 participants