fix(tools): detect content-search truncation so the model paginates - #41439
Open
pprism13 wants to merge 1 commit into
Open
fix(tools): detect content-search truncation so the model paginates#41439pprism13 wants to merge 1 commit into
pprism13 wants to merge 1 commit into
Conversation
## What does this PR do?
`search_files` (target=`content`) silently dropped every match past the
first page. `ShellFileOperations._search_with_rg` and
`_search_with_grep` compute `fetch_limit = limit + offset` for the
non-context case and cap the pipeline with `| head -n {fetch_limit}`, so
the parsed match list can contain at most `offset + limit` rows. The
truncation flag is then derived as `truncated = total > offset + limit` —
which can never be True because `total` is itself bounded by that same
cap. With the flag stuck at False, `search_tool` in `tools/file_tools.py`
never appends its "Results truncated. Use offset=… to see more" hint, so
the agent has no signal that more matches exist and stops after one page.
The fix fetches one extra sentinel row past the page window
(`offset + limit + 1`) — the standard n+1 pagination technique — so the
existing `truncated = total > offset + limit` comparison can actually
fire. The sentinel row is never returned to the caller because the page
is still sliced as `matches[offset:offset + limit]`. This also realigns
the code with the inline comment that already claimed it fetched "extra
rows so we can report the true total before slicing" — only the
context-mode branch (`+ 200`) was doing so.
## Related Issue
N/A
## Type of Change
- [x] 🐛 Bug fix (non-breaking change that fixes an issue)
- [ ] ✨ New feature (non-breaking change that adds functionality)
- [ ] 🔒 Security fix
- [ ] 📝 Documentation update
- [ ] ✅ Tests (adding or improving test coverage)
- [ ] ♻️ Refactor (no behavior change)
- [ ] 🎯 New skill (bundled or hub)
## Changes Made
- `tools/file_operations.py`: in `_search_with_rg`, change
`fetch_limit` to `limit + offset + 1 + (200 if context > 0 else 0)`
so the non-context content path grabs the sentinel row needed for
truncation detection (context mode keeps its `+ 200` slack).
- `tools/file_operations.py`: apply the same `+ 1` to the `fetch_limit`
in `_search_with_grep` for parity across the rg and grep backends.
- `tests/tools/test_file_operations.py`: add `TestSearchContentTruncation`
driving both backends through the real local terminal env — asserts
`truncated` is True when matches exceed the page and stays False on an
exact fit, and that the sentinel row never leaks into the returned page.
## How to Test
1. Create a file with 10 matching lines, e.g.
`printf 'MATCH %d\n' $(seq 0 9) > /tmp/hits.txt`.
2. Run a content search with a page smaller than the match count:
`ShellFileOperations(env).search("MATCH", path="/tmp", target="content", limit=3)`.
Before: `truncated=False`, `total_count=3` (7 matches lost silently).
After: `truncated=True`, page holds lines 1–3, and `search_tool`
emits the `offset=3` continuation hint.
3. Repeat with `limit=10` against the same file and confirm
`truncated` stays False — the exact-fit boundary must not false-positive.
4. `pytest tests/tools/test_file_operations.py -q` (the new
`TestSearchContentTruncation` cases run against both rg and grep).
## Checklist
### Code
- [x] I've read the Contributing Guide
- [x] My commit messages follow Conventional Commits (`fix(scope):`, `feat(scope):`, etc.)
- [x] I searched for existing PRs to make sure this isn't a duplicate
- [x] My PR contains **only** changes related to this fix/feature (no unrelated commits)
- [x] I've run the file-operations and search suites; the pre-existing failures are unrelated env-isolation cases in `test_file_tools.py`
- [x] I've added tests for my changes (required for bug fixes, strongly encouraged for features)
- [x] I've tested on my platform: macOS 15 (Darwin 25.5)
### Documentation & Housekeeping
- [x] I've updated relevant documentation (README, `docs/`, docstrings) — or N/A
- [x] I've updated `cli-config.yaml.example` if I added/changed config keys — or N/A
- [x] I've updated `CONTRIBUTING.md` or `AGENTS.md` if I changed architecture or workflows — or N/A
- [x] I've considered cross-platform impact (Windows, macOS) per the compatibility guide — or N/A
- [x] I've updated tool descriptions/schemas if I changed tool behavior — or N/A
tonydwb
approved these changes
Jun 7, 2026
tonydwb
left a comment
There was a problem hiding this comment.
Code Review Summary
Reviewed PR #41439: fix(tools): detect content-search truncation so the model paginates
✅ Approve
- Well-scoped behavior change. Truncation flag enables the model to continue paginating instead of silently consuming incomplete content.
- Tool-side test coverage added for both truncated and complete result paths.
💡 Suggestion
- Consider surfacing the truncation reason in the returned tool output so higher-level flows don’t need to guess why pagination happened.
Reviewed in batch on 2026-06-08
Contributor
|
Thanks for isolating the default content-mode off-by-one. The premise is confirmed on current main: Problems
Suggested changes
Automated hermes-sweeper review. |
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.
What does this PR do?
search_files(target=content) silently dropped every match past thefirst page.
ShellFileOperations._search_with_rgand_search_with_grepcomputefetch_limit = limit + offsetfor thenon-context case and cap the pipeline with
| head -n {fetch_limit}, sothe parsed match list can contain at most
offset + limitrows. Thetruncation flag is then derived as
truncated = total > offset + limit—which can never be True because
totalis itself bounded by that samecap. With the flag stuck at False,
search_toolintools/file_tools.pynever appends its "Results truncated. Use offset=… to see more" hint, so
the agent has no signal that more matches exist and stops after one page.
The fix fetches one extra sentinel row past the page window
(
offset + limit + 1) — the standard n+1 pagination technique — so theexisting
truncated = total > offset + limitcomparison can actuallyfire. The sentinel row is never returned to the caller because the page
is still sliced as
matches[offset:offset + limit]. This also realignsthe code with the inline comment that already claimed it fetched "extra
rows so we can report the true total before slicing" — only the
context-mode branch (
+ 200) was doing so.Related Issue
N/A
Type of Change
Changes Made
tools/file_operations.py: in_search_with_rg, changefetch_limittolimit + offset + 1 + (200 if context > 0 else 0)so the non-context content path grabs the sentinel row needed for
truncation detection (context mode keeps its
+ 200slack).tools/file_operations.py: apply the same+ 1to thefetch_limitin
_search_with_grepfor parity across the rg and grep backends.tests/tools/test_file_operations.py: addTestSearchContentTruncationdriving both backends through the real local terminal env — asserts
truncatedis True when matches exceed the page and stays False on anexact fit, and that the sentinel row never leaks into the returned page.
How to Test
printf 'MATCH %d\n' $(seq 0 9) > /tmp/hits.txt.ShellFileOperations(env).search("MATCH", path="/tmp", target="content", limit=3).Before:
truncated=False,total_count=3(7 matches lost silently).After:
truncated=True, page holds lines 1–3, andsearch_toolemits the
offset=3continuation hint.limit=10against the same file and confirmtruncatedstays False — the exact-fit boundary must not false-positive.pytest tests/tools/test_file_operations.py -q(the newTestSearchContentTruncationcases run against both rg and grep).Checklist
Code
fix(scope):,feat(scope):, etc.)test_file_tools.pyDocumentation & Housekeeping
docs/, docstrings) — or N/Acli-config.yaml.exampleif I added/changed config keys — or N/ACONTRIBUTING.mdorAGENTS.mdif I changed architecture or workflows — or N/A